Page MenuHomePhorge

No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None
diff --git a/changelog.d/create-list-members.fix b/changelog.d/create-list-members.fix
new file mode 100644
index 0000000000..2940d67e34
--- /dev/null
+++ b/changelog.d/create-list-members.fix
@@ -0,0 +1 @@
+Fix creating lists with selected members
diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js
index 483a9c02a4..f4f8851dff 100644
--- a/src/components/lists_edit/lists_edit.js
+++ b/src/components/lists_edit/lists_edit.js
@@ -1,151 +1,152 @@
import { mapState as mapPiniaState } from 'pinia'
import { mapGetters, mapState } from 'vuex'
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import ListsUserSearch from '../lists_user_search/lists_user_search.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useListsStore } from 'src/stores/lists.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
library.add(faSearch, faChevronLeft)
const ListsNew = {
components: {
BasicUserCard,
UserAvatar,
ListsUserSearch,
TabSwitcher,
PanelLoading,
},
data() {
return {
title: '',
titleDraft: '',
membersUserIds: [],
removedUserIds: new Set([]), // users we added for members, to undo
searchUserIds: [],
addedUserIds: new Set([]), // users we added from search, to undo
searchLoading: false,
reallyDelete: false,
}
},
created() {
if (!this.id) return
useListsStore()
.fetchList({ listId: this.id })
.then(() => {
this.title = this.findListTitle(this.id)
this.titleDraft = this.title
})
useListsStore()
.fetchListAccounts({ listId: this.id })
.then(() => {
this.membersUserIds = this.findListAccounts(this.id)
this.membersUserIds.forEach((userId) => {
this.$store.dispatch('fetchUserIfMissing', userId)
})
})
},
computed: {
id() {
return this.$route.params.id
},
membersUsers() {
return [...this.membersUserIds, ...this.addedUserIds]
.map((userId) => this.findUser(userId))
.filter((user) => user)
},
searchUsers() {
return this.searchUserIds
.map((userId) => this.findUser(userId))
.filter((user) => user)
},
...mapState({
currentUser: (state) => state.users.currentUser,
}),
...mapPiniaState(useListsStore, ['findListTitle', 'findListAccounts']),
...mapGetters(['findUser']),
},
methods: {
onInput() {
this.search(this.query)
},
toggleRemoveMember(user) {
if (this.removedUserIds.has(user.id)) {
this.id && this.addUser(user)
this.removedUserIds.delete(user.id)
} else {
this.id && this.removeUser(user.id)
this.removedUserIds.add(user.id)
}
},
toggleAddFromSearch(user) {
if (this.addedUserIds.has(user.id)) {
this.id && this.removeUser(user.id)
this.addedUserIds.delete(user.id)
} else {
this.id && this.addUser(user)
this.addedUserIds.add(user.id)
}
},
isRemoved(user) {
return this.removedUserIds.has(user.id)
},
isAdded(user) {
return this.addedUserIds.has(user.id)
},
addUser(user) {
useListsStore().addListAccount({ accountId: user.id, listId: this.id })
},
removeUser(userId) {
useListsStore().removeListAccount({ accountId: userId, listId: this.id })
},
onSearchLoading() {
this.searchLoading = true
},
onSearchLoadingDone() {
this.searchLoading = false
},
onSearchResults(results) {
this.searchLoading = false
this.searchUserIds = results
},
updateListTitle() {
useListsStore().setList({ listId: this.id, title: this.titleDraft })
this.title = this.findListTitle(this.id)
},
createList() {
- useListsStore()
+ return useListsStore()
.createList({ title: this.titleDraft })
.then((list) => {
- useListsStore().setListAccounts({
- listId: list.id,
- accountIds: [...this.addedUserIds],
- })
- return list.id
+ return useListsStore()
+ .setListAccounts({
+ listId: list.id,
+ accountIds: [...this.addedUserIds],
+ })
+ .then(() => list.id)
})
.then((listId) => {
this.$router.push({ name: 'lists-timeline', params: { id: listId } })
})
.catch((e) => {
useInterfaceStore().pushGlobalNotice({
messageKey: 'lists.error',
messageArgs: [e.message],
level: 'error',
})
})
},
deleteList() {
useListsStore().deleteList({ listId: this.id })
this.$router.push({ name: 'lists' })
},
},
}
export default ListsNew
diff --git a/src/stores/lists.js b/src/stores/lists.js
index b33a119ccd..a1ee92c169 100644
--- a/src/stores/lists.js
+++ b/src/stores/lists.js
@@ -1,116 +1,122 @@
import { find, remove } from 'lodash'
import { defineStore } from 'pinia'
export const useListsStore = defineStore('lists', {
state: () => ({
allLists: [],
allListsObject: {},
}),
getters: {
findListTitle() {
return (id) => {
if (!this.allListsObject[id]) return
return this.allListsObject[id].title
}
},
findListAccounts() {
return (id) => [...this.allListsObject[id].accountIds]
},
},
actions: {
setLists(value) {
this.allLists = value
},
createList({ title }) {
return window.vuex.state.api.backendInteractor
.createList({ title })
.then((list) => {
this.setList({ listId: list.id, title })
return list
})
},
fetchList({ listId }) {
return window.vuex.state.api.backendInteractor
.getList({ listId })
.then((list) => this.setList({ listId: list.id, title: list.title }))
},
fetchListAccounts({ listId }) {
return window.vuex.state.api.backendInteractor
.getListAccounts({ listId })
.then((accountIds) => {
if (!this.allListsObject[listId]) {
this.allListsObject[listId] = { accountIds: [] }
}
this.allListsObject[listId].accountIds = accountIds
})
},
setList({ listId, title }) {
window.vuex.state.api.backendInteractor.updateList({ listId, title })
if (!this.allListsObject[listId]) {
this.allListsObject[listId] = { accountIds: [] }
}
this.allListsObject[listId].title = title
const entry = find(this.allLists, { id: listId })
if (!entry) {
this.allLists.push({ id: listId, title })
} else {
entry.title = title
}
},
setListAccounts({ listId, accountIds }) {
const saved = this.allListsObject[listId]?.accountIds || []
const added = accountIds.filter((id) => !saved.includes(id))
const removed = saved.filter((id) => !accountIds.includes(id))
+ const requests = []
if (!this.allListsObject[listId]) {
this.allListsObject[listId] = { accountIds: [] }
}
this.allListsObject[listId].accountIds = accountIds
if (added.length > 0) {
- window.vuex.state.api.backendInteractor.addAccountsToList({
- listId,
- accountIds: added,
- })
+ requests.push(
+ window.vuex.state.api.backendInteractor.addAccountsToList({
+ listId,
+ accountIds: added,
+ }),
+ )
}
if (removed.length > 0) {
- window.vuex.state.api.backendInteractor.removeAccountsFromList({
- listId,
- accountIds: removed,
- })
+ requests.push(
+ window.vuex.state.api.backendInteractor.removeAccountsFromList({
+ listId,
+ accountIds: removed,
+ }),
+ )
}
+ return Promise.all(requests)
},
addListAccount({ listId, accountId }) {
return window.vuex.state.api.backendInteractor
.addAccountsToList({ listId, accountIds: [accountId] })
.then((result) => {
if (!this.allListsObject[listId]) {
this.allListsObject[listId] = { accountIds: [] }
}
this.allListsObject[listId].accountIds.push(accountId)
return result
})
},
removeListAccount({ listId, accountId }) {
return window.vuex.state.api.backendInteractor
.removeAccountsFromList({ listId, accountIds: [accountId] })
.then((result) => {
if (!this.allListsObject[listId]) {
this.allListsObject[listId] = { accountIds: [] }
}
const { accountIds } = this.allListsObject[listId]
const set = new Set(accountIds)
set.delete(accountId)
this.allListsObject[listId].accountIds = [...set]
return result
})
},
deleteList({ listId }) {
window.vuex.state.api.backendInteractor.deleteList({ listId })
delete this.allListsObject[listId]
remove(this.allLists, (list) => list.id === listId)
},
},
})
diff --git a/test/unit/specs/stores/lists.spec.js b/test/unit/specs/stores/lists.spec.js
index 083730856a..ef078309db 100644
--- a/test/unit/specs/stores/lists.spec.js
+++ b/test/unit/specs/stores/lists.spec.js
@@ -1,106 +1,134 @@
import { createPinia, setActivePinia } from 'pinia'
import { createStore } from 'vuex'
import { useListsStore } from 'src/stores/lists.js'
import apiModule from 'src/modules/api.js'
setActivePinia(createPinia())
const store = useListsStore()
window.vuex = createStore({
modules: {
api: apiModule,
},
})
describe('The lists store', () => {
+ let backendInteractor
+
+ beforeEach(() => {
+ store.$reset()
+ backendInteractor = {
+ updateList: vi.fn(() => Promise.resolve()),
+ addAccountsToList: vi.fn(() => Promise.resolve()),
+ removeAccountsFromList: vi.fn(() => Promise.resolve()),
+ deleteList: vi.fn(() => Promise.resolve()),
+ }
+ window.vuex.state.api.backendInteractor = backendInteractor
+ })
+
describe('actions', () => {
it('updates array of all lists', () => {
- store.$reset()
const list = { id: '1', title: 'testList' }
store.setLists([list])
expect(store.allLists).to.have.length(1)
expect(store.allLists).to.eql([list])
})
it('adds a new list with a title, updating the title for existing lists', () => {
- store.$reset()
const list = { id: '1', title: 'testList' }
const modList = { id: '1', title: 'anotherTestTitle' }
store.setList({ listId: list.id, title: list.title })
expect(store.allListsObject[list.id]).to.eql({
title: list.title,
accountIds: [],
})
expect(store.allLists).to.have.length(1)
expect(store.allLists[0]).to.eql(list)
store.setList({ listId: modList.id, title: modList.title })
expect(store.allListsObject[modList.id]).to.eql({
title: modList.title,
accountIds: [],
})
expect(store.allLists).to.have.length(1)
expect(store.allLists[0]).to.eql(modList)
})
- it('adds a new list with an array of IDs, updating the IDs for existing lists', () => {
- store.$reset()
+ it('adds a new list with an array of IDs, updating the IDs for existing lists', async () => {
const list = { id: '1', accountIds: ['1', '2', '3'] }
const modList = { id: '1', accountIds: ['3', '4', '5'] }
- store.setListAccounts({ listId: list.id, accountIds: list.accountIds })
+ const addRequest = store.setListAccounts({
+ listId: list.id,
+ accountIds: list.accountIds,
+ })
+ expect(addRequest).to.have.property('then').that.is.a('function')
+ await addRequest
+
expect(store.allListsObject[list.id].accountIds).to.eql(list.accountIds)
+ expect(backendInteractor.addAccountsToList).toHaveBeenCalledWith({
+ listId: list.id,
+ accountIds: list.accountIds,
+ })
- store.setListAccounts({
+ await store.setListAccounts({
listId: modList.id,
accountIds: modList.accountIds,
})
expect(store.allListsObject[modList.id].accountIds).to.eql(
modList.accountIds,
)
+ expect(backendInteractor.addAccountsToList).toHaveBeenLastCalledWith({
+ listId: modList.id,
+ accountIds: ['4', '5'],
+ })
+ expect(backendInteractor.removeAccountsFromList).toHaveBeenCalledWith({
+ listId: modList.id,
+ accountIds: ['1', '2'],
+ })
})
it('deletes a list', () => {
store.$patch({
allLists: [{ id: '1', title: 'testList' }],
allListsObject: {
1: { title: 'testList', accountIds: ['1', '2', '3'] },
},
})
const listId = '1'
store.deleteList({ listId })
expect(store.allLists).to.have.length(0)
expect(store.allListsObject).to.eql({})
})
})
describe('getters', () => {
it('returns list title', () => {
store.$patch({
allLists: [{ id: '1', title: 'testList' }],
allListsObject: {
1: { title: 'testList', accountIds: ['1', '2', '3'] },
},
})
const id = '1'
expect(store.findListTitle(id)).to.eql('testList')
})
it('returns list accounts', () => {
store.$patch({
allLists: [{ id: '1', title: 'testList' }],
allListsObject: {
1: { title: 'testList', accountIds: ['1', '2', '3'] },
},
})
const id = '1'
expect(store.findListAccounts(id)).to.eql(['1', '2', '3'])
})
})
})

File Metadata

Mime Type
text/x-diff
Expires
Sat, Aug 29, 6:19 PM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1737240
Default Alt Text
(13 KB)

Event Timeline