commit: 8eff0814681190619acf1a185f5f429c81ee4479
parent ad7d47f44076b7b0e7234c1cc70fd350dac5d842
Author: Sean King <seanking2919@protonmail.com>
Date: Thu, 6 Apr 2023 22:13:30 -0600
Migrates lists module to store
Diffstat:
13 files changed, 248 insertions(+), 109 deletions(-)
diff --git a/src/components/lists/lists.js b/src/components/lists/lists.js
@@ -1,3 +1,4 @@
+import { useListsStore } from '../../stores/lists'
import ListsCard from '../lists_card/lists_card.vue'
const Lists = {
@@ -11,7 +12,7 @@ const Lists = {
},
computed: {
lists () {
- return this.$store.state.lists.allLists
+ return useListsStore().allLists
}
},
methods: {
diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js
@@ -1,4 +1,5 @@
import { mapState, mapGetters } from 'vuex'
+import { mapState as mapPiniaState } from 'pinia'
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import ListsUserSearch from '../lists_user_search/lists_user_search.vue'
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
@@ -10,6 +11,7 @@ import {
faChevronLeft
} from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
+import { useListsStore } from '../../stores/lists'
library.add(
faSearch,
@@ -38,12 +40,12 @@ const ListsNew = {
},
created () {
if (!this.id) return
- this.$store.dispatch('fetchList', { listId: this.id })
+ useListsStore().fetchList({ listId: this.id })
.then(() => {
this.title = this.findListTitle(this.id)
this.titleDraft = this.title
})
- this.$store.dispatch('fetchListAccounts', { listId: this.id })
+ useListsStore().fetchListAccounts({ listId: this.id })
.then(() => {
this.membersUserIds = this.findListAccounts(this.id)
this.membersUserIds.forEach(userId => {
@@ -65,7 +67,8 @@ const ListsNew = {
...mapState({
currentUser: state => state.users.currentUser
}),
- ...mapGetters(['findUser', 'findListTitle', 'findListAccounts'])
+ ...mapPiniaState(useListsStore, ['findListTitle', 'findListAccounts']),
+ ...mapGetters(['findUser'])
},
methods: {
onInput () {
@@ -96,10 +99,10 @@ const ListsNew = {
return this.addedUserIds.has(user.id)
},
addUser (user) {
- this.$store.dispatch('addListAccount', { accountId: user.id, listId: this.id })
+ useListsStore().addListAccount({ accountId: user.id, listId: this.id })
},
removeUser (userId) {
- this.$store.dispatch('removeListAccount', { accountId: userId, listId: this.id })
+ useListsStore().removeListAccount({ accountId: userId, listId: this.id })
},
onSearchLoading (results) {
this.searchLoading = true
@@ -112,17 +115,16 @@ const ListsNew = {
this.searchUserIds = results
},
updateListTitle () {
- this.$store.dispatch('setList', { listId: this.id, title: this.titleDraft })
+ useListsStore().setList({ listId: this.id, title: this.titleDraft })
.then(() => {
this.title = this.findListTitle(this.id)
})
},
createList () {
- this.$store.dispatch('createList', { title: this.titleDraft })
+ useListsStore().createList({ title: this.titleDraft })
.then((list) => {
- return this
- .$store
- .dispatch('setListAccounts', { listId: list.id, accountIds: [...this.addedUserIds] })
+ return useListsStore()
+ .setListAccounts({ listId: list.id, accountIds: [...this.addedUserIds] })
.then(() => list.id)
})
.then((listId) => {
@@ -137,7 +139,7 @@ const ListsNew = {
})
},
deleteList () {
- this.$store.dispatch('deleteList', { listId: this.id })
+ useListsStore().deleteList({ listId: this.id })
this.$router.push({ name: 'lists' })
}
}
diff --git a/src/components/lists_menu/lists_menu_content.js b/src/components/lists_menu/lists_menu_content.js
@@ -1,6 +1,8 @@
import { mapState } from 'vuex'
+import { mapState as mapPiniaState } from 'pinia'
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import { getListEntries } from 'src/components/navigation/filter.js'
+import { useListsStore } from '../../stores/lists'
export const ListsMenuContent = {
props: [
@@ -10,8 +12,10 @@ export const ListsMenuContent = {
NavigationEntry
},
computed: {
+ ...mapPiniaState(useListsStore, {
+ lists: getListEntries
+ }),
...mapState({
- lists: getListEntries,
currentUser: state => state.users.currentUser,
privateMode: state => state.instance.private,
federating: state => state.instance.federating
diff --git a/src/components/lists_timeline/lists_timeline.js b/src/components/lists_timeline/lists_timeline.js
@@ -1,3 +1,4 @@
+import { useListsStore } from '../../stores/lists'
import Timeline from '../timeline/timeline.vue'
const ListsTimeline = {
data () {
@@ -17,14 +18,14 @@ const ListsTimeline = {
this.listId = route.params.id
this.$store.dispatch('stopFetchingTimeline', 'list')
this.$store.commit('clearTimeline', { timeline: 'list' })
- this.$store.dispatch('fetchList', { listId: this.listId })
+ useListsStore().fetchList({ listId: this.listId })
this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId })
}
}
},
created () {
this.listId = this.$route.params.id
- this.$store.dispatch('fetchList', { listId: this.listId })
+ useListsStore().fetchList({ listId: this.listId })
this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId })
},
unmounted () {
diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js
@@ -11,7 +11,7 @@ export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFede
})
}
-export const getListEntries = state => state.lists.allLists.map(list => ({
+export const getListEntries = store => store.allLists.map(list => ({
name: 'list-' + list.id,
routeObject: { name: 'lists-timeline', params: { id: list.id } },
labelRaw: list.title,
diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js
@@ -1,4 +1,5 @@
import { mapState } from 'vuex'
+import { mapState as mapPiniaState } from 'pinia'
import { TIMELINES, ROOT_ITEMS, routeTo } from 'src/components/navigation/navigation.js'
import { getListEntries, filterNavigation } from 'src/components/navigation/filter.js'
@@ -14,6 +15,7 @@ import {
faStream,
faList
} from '@fortawesome/free-solid-svg-icons'
+import { useListsStore } from '../../stores/lists'
library.add(
faUsers,
@@ -38,8 +40,10 @@ const NavPanel = {
getters () {
return this.$store.getters
},
+ ...mapPiniaState(useListsStore, {
+ lists: getListEntries
+ }),
...mapState({
- lists: getListEntries,
currentUser: state => state.users.currentUser,
followRequestCount: state => state.api.followRequests.length,
privateMode: state => state.instance.private,
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
@@ -9,6 +9,7 @@ import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
+import { useListsStore } from '../../stores/lists'
library.add(faChevronDown)
@@ -87,7 +88,7 @@ const TimelineMenu = {
return '#' + this.$route.params.tag
}
if (route === 'lists-timeline') {
- return this.$store.getters.findListTitle(this.$route.params.id)
+ return useListsStore().findListTitle(this.$route.params.id)
}
const i18nkey = timelineNames()[this.$route.name]
return i18nkey ? this.$t(i18nkey) : route
diff --git a/src/components/user_list_menu/user_list_menu.js b/src/components/user_list_menu/user_list_menu.js
@@ -1,9 +1,10 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import { faChevronRight } from '@fortawesome/free-solid-svg-icons'
-import { mapState } from 'vuex'
+import { mapState } from 'pinia'
import DialogModal from '../dialog_modal/dialog_modal.vue'
import Popover from '../popover/popover.vue'
+import { useListsStore } from '../../stores/lists'
library.add(faChevronRight)
@@ -22,8 +23,8 @@ const UserListMenu = {
this.$store.dispatch('fetchUserInLists', this.user.id)
},
computed: {
- ...mapState({
- allLists: state => state.lists.allLists
+ ...mapState(useListsStore, {
+ allLists: store => store.allLists
}),
inListsSet () {
return new Set(this.user.inLists.map(x => x.id))
@@ -39,12 +40,12 @@ const UserListMenu = {
methods: {
toggleList (listId) {
if (this.inListsSet.has(listId)) {
- this.$store.dispatch('removeListAccount', { accountId: this.user.id, listId }).then((response) => {
+ useListsStore().removeListAccount({ accountId: this.user.id, listId }).then((response) => {
if (!response.ok) { return }
this.$store.dispatch('fetchUserInLists', this.user.id)
})
} else {
- this.$store.dispatch('addListAccount', { accountId: this.user.id, listId }).then((response) => {
+ useListsStore().addListAccount({ accountId: this.user.id, listId }).then((response) => {
if (!response.ok) { return }
this.$store.dispatch('fetchUserInLists', this.user.id)
})
diff --git a/src/main.js b/src/main.js
@@ -6,7 +6,6 @@ import './lib/event_target_polyfill.js'
import instanceModule from './modules/instance.js'
import statusesModule from './modules/statuses.js'
-import listsModule from './modules/lists.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
@@ -66,7 +65,6 @@ const persistedStateOptions = {
// TODO refactor users/statuses modules, they depend on each other
users: usersModule,
statuses: statusesModule,
- lists: listsModule,
api: apiModule,
config: configModule,
serverSideConfig: serverSideConfigModule,
diff --git a/src/services/lists_fetcher/lists_fetcher.service.js b/src/services/lists_fetcher/lists_fetcher.service.js
@@ -1,10 +1,11 @@
+import { useListsStore } from '../../stores/lists.js'
import apiService from '../api/api.service.js'
import { promiseInterval } from '../promise_interval/promise_interval.js'
const fetchAndUpdate = ({ store, credentials }) => {
return apiService.fetchLists({ credentials })
.then(lists => {
- store.commit('setLists', lists)
+ useListsStore().setLists(lists)
}, () => {})
.catch(() => {})
}
diff --git a/src/stores/lists.js b/src/stores/lists.js
@@ -0,0 +1,116 @@
+import { defineStore } from 'pinia'
+
+import { remove, find } from 'lodash'
+
+export const defaultState = {
+ allLists: [],
+ allListsObject: {}
+}
+
+export const getters = {
+ findListTitle (state) {
+ return (id) => {
+ if (!this.allListsObject[id]) return
+ return this.allListsObject[id].title
+ }
+ },
+ findListAccounts (state) {
+ return (id) => [...this.allListsObject[id].accountIds]
+ }
+}
+
+export const 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 }) {
+ 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))
+ 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 })
+ }
+ if (removed.length > 0) {
+ window.vuex.state.api.backendInteractor.removeAccountsFromList({ listId, accountIds: removed })
+ }
+ },
+ 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)
+ }
+}
+
+export const useListsStore = defineStore('lists', {
+ state: () => (defaultState),
+ getters,
+ actions
+})
diff --git a/test/unit/specs/modules/lists.spec.js b/test/unit/specs/modules/lists.spec.js
@@ -1,83 +0,0 @@
-import { cloneDeep } from 'lodash'
-import { defaultState, mutations, getters } from '../../../../src/modules/lists.js'
-
-describe('The lists module', () => {
- describe('mutations', () => {
- it('updates array of all lists', () => {
- const state = cloneDeep(defaultState)
- const list = { id: '1', title: 'testList' }
-
- mutations.setLists(state, [list])
- expect(state.allLists).to.have.length(1)
- expect(state.allLists).to.eql([list])
- })
-
- it('adds a new list with a title, updating the title for existing lists', () => {
- const state = cloneDeep(defaultState)
- const list = { id: '1', title: 'testList' }
- const modList = { id: '1', title: 'anotherTestTitle' }
-
- mutations.setList(state, { listId: list.id, title: list.title })
- expect(state.allListsObject[list.id]).to.eql({ title: list.title, accountIds: [] })
- expect(state.allLists).to.have.length(1)
- expect(state.allLists[0]).to.eql(list)
-
- mutations.setList(state, { listId: modList.id, title: modList.title })
- expect(state.allListsObject[modList.id]).to.eql({ title: modList.title, accountIds: [] })
- expect(state.allLists).to.have.length(1)
- expect(state.allLists[0]).to.eql(modList)
- })
-
- it('adds a new list with an array of IDs, updating the IDs for existing lists', () => {
- const state = cloneDeep(defaultState)
- const list = { id: '1', accountIds: ['1', '2', '3'] }
- const modList = { id: '1', accountIds: ['3', '4', '5'] }
-
- mutations.setListAccounts(state, { listId: list.id, accountIds: list.accountIds })
- expect(state.allListsObject[list.id]).to.eql({ accountIds: list.accountIds })
-
- mutations.setListAccounts(state, { listId: modList.id, accountIds: modList.accountIds })
- expect(state.allListsObject[modList.id]).to.eql({ accountIds: modList.accountIds })
- })
-
- it('deletes a list', () => {
- const state = {
- allLists: [{ id: '1', title: 'testList' }],
- allListsObject: {
- 1: { title: 'testList', accountIds: ['1', '2', '3'] }
- }
- }
- const listId = '1'
-
- mutations.deleteList(state, { listId })
- expect(state.allLists).to.have.length(0)
- expect(state.allListsObject).to.eql({})
- })
- })
-
- describe('getters', () => {
- it('returns list title', () => {
- const state = {
- allLists: [{ id: '1', title: 'testList' }],
- allListsObject: {
- 1: { title: 'testList', accountIds: ['1', '2', '3'] }
- }
- }
- const id = '1'
-
- expect(getters.findListTitle(state)(id)).to.eql('testList')
- })
-
- it('returns list accounts', () => {
- const state = {
- allLists: [{ id: '1', title: 'testList' }],
- allListsObject: {
- 1: { title: 'testList', accountIds: ['1', '2', '3'] }
- }
- }
- const id = '1'
-
- expect(getters.findListAccounts(state)(id)).to.eql(['1', '2', '3'])
- })
- })
-})
diff --git a/test/unit/specs/stores/lists.spec.js b/test/unit/specs/stores/lists.spec.js
@@ -0,0 +1,93 @@
+import { createPinia, setActivePinia } from 'pinia'
+import { useListsStore } from '../../../../src/stores/lists.js'
+import { createStore } from 'vuex'
+import apiModule from '../../../../src/modules/api.js'
+
+setActivePinia(createPinia())
+const store = useListsStore()
+window.vuex = createStore({
+ modules: {
+ api: apiModule
+ }
+})
+
+describe('The lists store', () => {
+ 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()
+ const list = { id: '1', accountIds: ['1', '2', '3'] }
+ const modList = { id: '1', accountIds: ['3', '4', '5'] }
+
+ store.setListAccounts({ listId: list.id, accountIds: list.accountIds })
+ expect(store.allListsObject[list.id].accountIds).to.eql(list.accountIds)
+
+ store.setListAccounts({ listId: modList.id, accountIds: modList.accountIds })
+ expect(store.allListsObject[modList.id].accountIds).to.eql(modList.accountIds)
+ })
+
+ 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'])
+ })
+ })
+})