Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85649670
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Aug 29, 6:19 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1737240
Default Alt Text
(13 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment