Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85710585
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/components/lists/lists.js b/src/components/lists/lists.js
index 56d68430a2..b3527dc131 100644
--- a/src/components/lists/lists.js
+++ b/src/components/lists/lists.js
@@ -1,27 +1,28 @@
+import { useListsStore } from '../../stores/lists'
import ListsCard from '../lists_card/lists_card.vue'
const Lists = {
data () {
return {
isNew: false
}
},
components: {
ListsCard
},
computed: {
lists () {
- return this.$store.state.lists.allLists
+ return useListsStore().allLists
}
},
methods: {
cancelNewList () {
this.isNew = false
},
newList () {
this.isNew = true
}
}
}
export default Lists
diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js
index d929dbdfeb..9980511ad4 100644
--- a/src/components/lists_edit/lists_edit.js
+++ b/src/components/lists_edit/lists_edit.js
@@ -1,146 +1,148 @@
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'
import UserAvatar from '../user_avatar/user_avatar.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faSearch,
faChevronLeft
} from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
+import { useListsStore } from '../../stores/lists'
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
- 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 => {
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
}),
- ...mapGetters(['findUser', 'findListTitle', 'findListAccounts'])
+ ...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) {
- 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
},
onSearchLoadingDone (results) {
this.searchLoading = false
},
onSearchResults (results) {
this.searchLoading = false
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) => {
this.$router.push({ name: 'lists-timeline', params: { id: listId } })
})
.catch((e) => {
useInterfaceStore().pushGlobalNotice({
messageKey: 'lists.error',
messageArgs: [e.message],
level: 'error'
})
})
},
deleteList () {
- this.$store.dispatch('deleteList', { listId: this.id })
+ useListsStore().deleteList({ listId: this.id })
this.$router.push({ name: 'lists' })
}
}
}
export default ListsNew
diff --git a/src/components/lists_menu/lists_menu_content.js b/src/components/lists_menu/lists_menu_content.js
index 97b3221074..d941127c13 100644
--- a/src/components/lists_menu/lists_menu_content.js
+++ b/src/components/lists_menu/lists_menu_content.js
@@ -1,22 +1,26 @@
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: [
'showPin'
],
components: {
NavigationEntry
},
computed: {
+ ...mapPiniaState(useListsStore, {
+ lists: getListEntries
+ }),
...mapState({
- lists: getListEntries,
currentUser: state => state.users.currentUser,
privateMode: state => state.instance.private,
federating: state => state.instance.federating
})
}
}
export default ListsMenuContent
diff --git a/src/components/lists_timeline/lists_timeline.js b/src/components/lists_timeline/lists_timeline.js
index c3f408bd55..8c13d5b5a6 100644
--- a/src/components/lists_timeline/lists_timeline.js
+++ b/src/components/lists_timeline/lists_timeline.js
@@ -1,36 +1,37 @@
+import { useListsStore } from '../../stores/lists'
import Timeline from '../timeline/timeline.vue'
const ListsTimeline = {
data () {
return {
listId: null
}
},
components: {
Timeline
},
computed: {
timeline () { return this.$store.state.statuses.timelines.list }
},
watch: {
$route: function (route) {
if (route.name === 'lists-timeline' && route.params.id !== this.listId) {
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 () {
this.$store.dispatch('stopFetchingTimeline', 'list')
this.$store.commit('clearTimeline', { timeline: 'list' })
}
}
export default ListsTimeline
diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js
index e8e77f8f02..68f9f0d895 100644
--- a/src/components/navigation/filter.js
+++ b/src/components/navigation/filter.js
@@ -1,19 +1,19 @@
export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFederating, isPrivate, currentUser }) => {
return list.filter(({ criteria, anon, anonRoute }) => {
const set = new Set(criteria || [])
if (!isFederating && set.has('federating')) return false
if (!currentUser && isPrivate && set.has('!private')) return false
if (!currentUser && !(anon || anonRoute)) return false
if ((!currentUser || !currentUser.locked) && set.has('lockedUser')) return false
if (!hasChats && set.has('chats')) return false
if (!hasAnnouncements && set.has('announcements')) return false
return true
})
}
-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,
iconLetter: list.title[0]
}))
diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js
index ef78e44c0a..5001a8c372 100644
--- a/src/components/navigation/navigation_pins.js
+++ b/src/components/navigation/navigation_pins.js
@@ -1,87 +1,91 @@
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'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faUsers,
faGlobe,
faBookmark,
faEnvelope,
faComments,
faBell,
faInfoCircle,
faStream,
faList
} from '@fortawesome/free-solid-svg-icons'
+import { useListsStore } from '../../stores/lists'
library.add(
faUsers,
faGlobe,
faBookmark,
faEnvelope,
faComments,
faBell,
faInfoCircle,
faStream,
faList
)
const NavPanel = {
props: ['limit'],
methods: {
getRouteTo (item) {
return routeTo(item, this.currentUser)
}
},
computed: {
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,
federating: state => state.instance.federating,
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
}),
pinnedList () {
if (!this.currentUser) {
return filterNavigation([
{ ...TIMELINES.public, name: 'public' },
{ ...TIMELINES.twkn, name: 'twkn' },
{ ...ROOT_ITEMS.about, name: 'about' }
],
{
hasChats: this.pleromaChatMessagesAvailable,
isFederating: this.federating,
isPrivate: this.privateMode,
currentUser: this.currentUser
})
}
return filterNavigation(
[
...Object
.entries({ ...TIMELINES })
.filter(([k]) => this.pinnedItems.has(k))
.map(([k, v]) => ({ ...v, name: k })),
...this.lists.filter((k) => this.pinnedItems.has(k.name)),
...Object
.entries({ ...ROOT_ITEMS })
.filter(([k]) => this.pinnedItems.has(k))
.map(([k, v]) => ({ ...v, name: k }))
],
{
hasChats: this.pleromaChatMessagesAvailable,
isFederating: this.federating,
isPrivate: this.privateMode,
currentUser: this.currentUser
}
).slice(0, this.limit)
}
}
}
export default NavPanel
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
index a9e7893cf7..79c944b709 100644
--- a/src/components/timeline_menu/timeline_menu.js
+++ b/src/components/timeline_menu/timeline_menu.js
@@ -1,98 +1,99 @@
import Popover from '../popover/popover.vue'
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import { mapState } from 'vuex'
import { ListsMenuContent } from '../lists_menu/lists_menu_content.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { TIMELINES } from 'src/components/navigation/navigation.js'
import { filterNavigation } from 'src/components/navigation/filter.js'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
+import { useListsStore } from '../../stores/lists'
library.add(faChevronDown)
// Route -> i18n key mapping, exported and not in the computed
// because nav panel benefits from the same information.
export const timelineNames = () => {
return {
friends: 'nav.home_timeline',
bookmarks: 'nav.bookmarks',
dms: 'nav.dms',
'public-timeline': 'nav.public_tl',
'public-external-timeline': 'nav.twkn'
}
}
const TimelineMenu = {
components: {
Popover,
NavigationEntry,
ListsMenuContent
},
data () {
return {
isOpen: false
}
},
created () {
if (timelineNames()[this.$route.name]) {
useInterfaceStore().setLastTimeline(this.$route.name)
}
},
computed: {
useListsMenu () {
const route = this.$route.name
return route === 'lists-timeline'
},
...mapState({
currentUser: state => state.users.currentUser,
privateMode: state => state.instance.private,
federating: state => state.instance.federating
}),
timelinesList () {
return filterNavigation(
Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })),
{
hasChats: this.pleromaChatMessagesAvailable,
isFederating: this.federating,
isPrivate: this.privateMode,
currentUser: this.currentUser
}
)
}
},
methods: {
openMenu () {
// $nextTick is too fast, animation won't play back but
// instead starts in fully open position. Low values
// like 1-5 work on fast machines but not on mobile, 25
// seems like a good compromise that plays without significant
// added lag.
setTimeout(() => {
this.isOpen = true
}, 25)
},
blockOpen (event) {
// For the blank area inside the button element.
// Just setting @click.stop="" makes unintuitive behavior when
// menu is open and clicking on the blank area doesn't close it.
if (!this.isOpen) {
event.stopPropagation()
}
},
timelineName () {
const route = this.$route.name
if (route === 'tag-timeline') {
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
}
}
}
export default TimelineMenu
diff --git a/src/components/user_list_menu/user_list_menu.js b/src/components/user_list_menu/user_list_menu.js
index 21996031be..7b2fa8c456 100644
--- a/src/components/user_list_menu/user_list_menu.js
+++ b/src/components/user_list_menu/user_list_menu.js
@@ -1,93 +1,94 @@
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)
const UserListMenu = {
props: [
'user'
],
data () {
return {}
},
components: {
DialogModal,
Popover
},
created () {
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))
},
lists () {
if (!this.user.inLists) return []
return this.allLists.map(list => ({
...list,
inList: this.inListsSet.has(list.id)
}))
}
},
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)
})
}
},
toggleRight (right) {
const store = this.$store
if (this.user.rights[right]) {
store.state.api.backendInteractor.deleteRight({ user: this.user, right }).then(response => {
if (!response.ok) { return }
store.commit('updateRight', { user: this.user, right, value: false })
})
} else {
store.state.api.backendInteractor.addRight({ user: this.user, right }).then(response => {
if (!response.ok) { return }
store.commit('updateRight', { user: this.user, right, value: true })
})
}
},
toggleActivationStatus () {
this.$store.dispatch('toggleActivationStatus', { user: this.user })
},
deleteUserDialog (show) {
this.showDeleteUserDialog = show
},
deleteUser () {
const store = this.$store
const user = this.user
const { id, name } = user
store.state.api.backendInteractor.deleteUser({ user })
.then(e => {
this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id)
const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'
const isTargetUser = this.$route.params.name === name || this.$route.params.id === id
if (isProfile && isTargetUser) {
window.history.back()
}
})
},
setToggled (value) {
this.toggled = value
}
}
}
export default UserListMenu
diff --git a/src/main.js b/src/main.js
index 383b7d9840..4ebfb4ffba 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,94 +1,92 @@
import { createStore } from 'vuex'
import { createPinia } from 'pinia'
import 'custom-event-polyfill'
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'
import serverSideConfigModule from './modules/serverSideConfig.js'
import serverSideStorageModule from './modules/serverSideStorage.js'
import oauthModule from './modules/oauth.js'
import authFlowModule from './modules/auth_flow.js'
import oauthTokensModule from './modules/oauth_tokens.js'
import chatsModule from './modules/chats.js'
import { createI18n } from 'vue-i18n'
import createPersistedState from './lib/persisted_state.js'
import pushNotifications from './lib/push_notifications_plugin.js'
import messages from './i18n/messages.js'
import afterStoreSetup from './boot/after_store.js'
const currentLocale = (window.navigator.language || 'en').split('-')[0]
const i18n = createI18n({
// By default, use the browser locale, we will update it if neccessary
locale: 'en',
fallbackLocale: 'en',
messages: messages.default
})
messages.setLanguage(i18n, currentLocale)
const persistedStateOptions = {
paths: [
'serverSideStorage.cache',
'config',
'users.lastLoginName',
'oauth'
]
};
(async () => {
let storageError = false
const plugins = [pushNotifications]
const pinia = createPinia()
try {
const persistedState = await createPersistedState(persistedStateOptions)
plugins.push(persistedState)
} catch (e) {
console.error(e)
storageError = true
}
// Temporarily storing as a global variable while we migrate to Pinia
window.vuex = createStore({
modules: {
instance: instanceModule,
// TODO refactor users/statuses modules, they depend on each other
users: usersModule,
statuses: statusesModule,
- lists: listsModule,
api: apiModule,
config: configModule,
serverSideConfig: serverSideConfigModule,
serverSideStorage: serverSideStorageModule,
oauth: oauthModule,
authFlow: authFlowModule,
oauthTokens: oauthTokensModule,
chats: chatsModule
},
plugins,
strict: false // Socket modifies itself, let's ignore this for now.
// strict: process.env.NODE_ENV !== 'production'
})
const store = window.vuex
// Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete.
afterStoreSetup({ pinia, store, storageError, i18n })
})()
// These are inlined by webpack's DefinePlugin
/* eslint-disable */
window.___pleromafe_mode = process.env
window.___pleromafe_commit_hash = COMMIT_HASH
window.___pleromafe_dev_overrides = DEV_OVERRIDES
diff --git a/src/services/lists_fetcher/lists_fetcher.service.js b/src/services/lists_fetcher/lists_fetcher.service.js
index 8d9dae6665..c0306085dd 100644
--- a/src/services/lists_fetcher/lists_fetcher.service.js
+++ b/src/services/lists_fetcher/lists_fetcher.service.js
@@ -1,22 +1,23 @@
+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(() => {})
}
const startFetching = ({ credentials, store }) => {
const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })
boundFetchAndUpdate()
return promiseInterval(boundFetchAndUpdate, 240000)
}
const listsFetcher = {
startFetching
}
export default listsFetcher
diff --git a/src/stores/lists.js b/src/stores/lists.js
new file mode 100644
index 0000000000..3d4dedbf2b
--- /dev/null
+++ 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
deleted file mode 100644
index e43106eac5..0000000000
--- a/test/unit/specs/modules/lists.spec.js
+++ /dev/null
@@ -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
new file mode 100644
index 0000000000..299d53a6e0
--- /dev/null
+++ 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'])
+ })
+ })
+})
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Sep 19, 1:11 AM (18 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1765502
Default Alt Text
(33 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment