migrate drafts store to pinia as-is

This commit is contained in:
Henry Jameson 2026-09-02 16:58:31 +03:00
commit 148ce1eb99
8 changed files with 96 additions and 123 deletions

View file

@ -7,6 +7,7 @@ import StatusContent from 'src/components/status_content/status_content.vue'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useStatusesStore } from 'src/stores/statuses.js'
import { useDraftsStore } from 'src/stores/drafts.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faPollH } from '@fortawesome/free-solid-svg-icons'
@ -77,9 +78,9 @@ const Draft = {
editing(newVal) {
if (newVal) return
if (this.safeToSave) {
this.$store.dispatch('addOrSaveDraft', { draft: this.draft })
useDraftsStore().addOrSaveDraft(this.draft)
} else {
this.$store.dispatch('addOrSaveDraft', { draft: this.referenceDraft })
useDraftsStore().addOrSaveDraft(this.referenceDraft)
}
},
},
@ -91,7 +92,7 @@ const Draft = {
this.showingConfirmDialog = true
},
doAbandon() {
this.$store.dispatch('abandonDraft', { id: this.draft.id }).then(() => {
useDraftsStore().abandonDraft(this.draft.id).then(() => {
this.hideConfirmDialog()
})
},

View file

@ -1,4 +1,5 @@
import { defineAsyncComponent } from 'vue'
import { useDraftsStore } from 'src/stores/drafts.js'
import Draft from 'src/components/draft/draft.vue'
import List from 'src/components/list/list.vue'
@ -18,7 +19,7 @@ const Drafts = {
},
computed: {
drafts() {
return this.$store.getters.draftsArray
return useDraftsStore().draftsArray
},
},
methods: {
@ -26,8 +27,7 @@ const Drafts = {
this.showingConfirmDialog = true
},
doAbandonAll() {
this.$store
.dispatch('abandonAllDrafts')
useDraftsStore().abandonAllDrafts()
.then(() => this.hideConfirmDialog())
},
hideConfirmDialog() {

View file

@ -32,6 +32,7 @@ import { useMediaViewerStore } from 'src/stores/media_viewer.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { useUsersStore } from 'src/stores/users.js'
import { useDraftsStore } from 'src/stores/drafts.js'
import { pollFormToMasto } from 'src/services/poll/poll.service.js'
@ -985,13 +986,11 @@ const PostStatusForm = {
saveDraft() {
if (!this.disableDraft && !this.saveInhibited) {
if (this.safeToSaveDraft) {
return this.$store
.dispatch('addOrSaveDraft', {
draft: {
type: this.statusType,
refId: this.refId,
...this.newStatus,
},
return useDraftsStore()
.addOrSaveDraft({
type: this.statusType,
refId: this.refId,
...this.newStatus,
})
.then((id) => {
if (this.newStatus.id !== id) {
@ -1024,14 +1023,14 @@ const PostStatusForm = {
}
},
abandonDraft() {
return this.$store.dispatch('abandonDraft', { id: this.draftId })
return useDraftsStore().abandonDraft(this.draftId)
},
getDraft() {
const maybeDraft = this.$store.state.drafts.drafts[this.draftId]
const maybeDraft = useDraftsStore().drafts.get(this.draftId)
if (this.draftId && maybeDraft) {
return maybeDraft
} else {
const existingDrafts = this.$store.getters.draftsByTypeAndRefId(
const existingDrafts = useDraftsStore().draftsByTypeAndRefId(
this.statusType,
this.refId,
)

View file

@ -1,5 +1,4 @@
import { mapActions, mapState } from 'pinia'
import { mapGetters } from 'vuex'
import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
import UserCard from 'src/components/user_card/user_card.vue'
@ -15,6 +14,7 @@ import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useShoutStore } from 'src/stores/shout'
import { useUsersStore } from 'src/stores/users.js'
import { useDraftsStore } from 'src/stores/drafts.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -110,7 +110,7 @@ const SideDrawer = {
hideSitename: (store) => store.instanceIdentity.hideSitename,
}),
...mapState(useChatsStore, ['unreadChatsCount']),
...mapGetters(['draftCount']),
...mapState(useDraftsStore, ['draftCount']),
},
methods: {
toggleDrawer() {

View file

@ -1,99 +0,0 @@
import { storage } from 'src/lib/storage.js'
export const defaultState = {
drafts: {},
}
export const mutations = {
addOrSaveDraft(state, { draft }) {
state.drafts[draft.id] = draft
},
abandonDraft(state, { id }) {
delete state.drafts[id]
},
loadDrafts(state, data) {
state.drafts = data
},
}
const storageKey = 'pleroma-fe-drafts'
/*
* Note: we do not use the persist state plugin because
* it is not impossible for a user to have two windows at
* the same time. The persist state plugin is just overriding
* everything with the current state. This isn't good because
* if a draft is created in one window and another draft is
* created in another, the draft in the first window will just
* be overriden.
* Here, we can't guarantee 100% atomicity unless one uses
* different keys, which will just pollute the whole storage.
* It is indeed best to have backend support for this.
*/
const getStorageData = async () =>
(await storage.getItem(storageKey)) ||
{
/* no-op */
}
const saveDraftToStorage = async (draft) => {
const currentData = await getStorageData()
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
await storage.setItem(storageKey, currentData)
}
const deleteDraftFromStorage = async (ids) => {
const currentData = await getStorageData()
ids.forEach((id) => {
delete currentData[id]
})
await storage.setItem(storageKey, currentData)
}
export const actions = {
async addOrSaveDraft(store, { draft }) {
const id = draft.id || new Date().getTime().toString()
const draftWithId = { ...draft, id }
store.commit('addOrSaveDraft', { draft: draftWithId })
await saveDraftToStorage(draftWithId)
return id
},
async abandonDraft(store, { id }) {
store.commit('abandonDraft', { id })
await deleteDraftFromStorage([id])
},
async abandonAllDrafts(store) {
const ids = Object.keys(store.state.drafts)
ids.forEach((id) => store.commit('abandonDraft', { id }))
await deleteDraftFromStorage(ids)
},
async loadDrafts(store) {
const currentData = await getStorageData()
store.commit('loadDrafts', currentData)
},
}
export const getters = {
draftsByTypeAndRefId(state) {
return (type, refId) => {
return Object.values(state.drafts).filter(
(draft) => draft.type === type && draft.refId === refId,
)
}
},
draftsArray(state) {
return Object.values(state.drafts)
},
draftCount(state) {
return Object.values(state.drafts).length
},
}
const drafts = {
state: defaultState,
mutations,
getters,
actions,
}
export default drafts

View file

@ -1,5 +1 @@
import drafts from './drafts.js'
export default {
drafts,
}
export default {}

75
src/stores/drafts.js Normal file
View file

@ -0,0 +1,75 @@
import { defineStore } from 'pinia'
import { storage } from 'src/lib/storage.js'
const storageKey = 'pleroma-fe-drafts'
/*
* Note: we do not use the persist state plugin because
* it is not impossible for a user to have two windows at
* the same time. The persist state plugin is just overriding
* everything with the current state. This isn't good because
* if a draft is created in one window and another draft is
* created in another, the draft in the first window will just
* be overriden.
* Here, we can't guarantee 100% atomicity unless one uses
* different keys, which will just pollute the whole storage.
* It is indeed best to have backend support for this.
*/
const getStorageData = async () => await storage.getItem(storageKey)
const saveDraftToStorage = async (draft) => {
const currentData = await getStorageData() ?? {}
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
await storage.setItem(storageKey, currentData)
}
const deleteDraftFromStorage = async (ids) => {
const currentData = await getStorageData() ?? {}
ids.forEach((id) => {
delete currentData[id]
})
await storage.setItem(storageKey, currentData)
}
export const useDraftsStore = defineStore('drafts', {
state: () => ({
drafts: new Map()
}),
getters: {
draftsByTypeAndRefId(state) {
return (type, refId) => {
return [...state.drafts.values()].filter(
(draft) => draft.type === type && draft.refId === refId,
)
}
},
draftsArray(state) {
return [...state.drafts.values()]
},
draftsCount(state) {
return state.drafts.size
},
},
actions: {
async abandonDraft(id) {
this.drafts.delete(id)
await deleteDraftFromStorage([id])
},
async loadDrafts() {
const currentData = await getStorageData()
this.drafts = new Map(Object.entries(currentData))
},
async addOrSaveDraft(draft) {
const id = draft.id ?? new Date().getTime().toString()
const draftWithId = { ...draft, id }
this.drafts.set(draft.id, draftWithId)
await saveDraftToStorage(draftWithId)
return id
},
async abandonAllDrafts(store) {
const ids = this.drafts.keys()
ids.forEach((id) => this.abandonDraft(id))
await deleteDraftFromStorage(ids)
},
}
})

View file

@ -21,6 +21,7 @@ import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { useProfileConfigStore } from 'src/stores/profile_config.js'
import { useTimelinesStore } from 'src/stores/timelines.js'
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
import { useDraftsStore } from 'src/stores/drafts.js'
import { revokeToken } from 'src/api/oauth.js'
import {
@ -700,7 +701,7 @@ export const useUsersStore = defineStore('users', {
useAnnouncementsStore().startFetching()
this.fetchMutes()
dispatch('loadDrafts')
useDraftsStore().loadDrafts()
} catch (error) {
console.error(error)