Save drafts permanently in local storage

This commit is contained in:
tusooa 2023-03-10 21:37:03 -05:00
parent 39b42da724
commit 0256ff5f63
No known key found for this signature in database
GPG Key ID: 42AEC43D48433C51
7 changed files with 56 additions and 11 deletions

View File

@ -377,6 +377,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
getInstanceConfig({ store }) getInstanceConfig({ store })
]) ])
await store.dispatch('loadDrafts')
// Start fetching things that don't need to block the UI // Start fetching things that don't need to block the UI
store.dispatch('fetchMutes') store.dispatch('fetchMutes')
store.dispatch('startFetchingAnnouncements') store.dispatch('startFetchingAnnouncements')

View File

@ -31,12 +31,12 @@
class="faint-link" class="faint-link"
:to="{ name: 'conversation', params: { id: draft.refId } }" :to="{ name: 'conversation', params: { id: draft.refId } }"
> >
{{ refStatus.external_url }} {{ refStatus ? refStatus.external_url : $t('drafts.unavailable') }}
</router-link> </router-link>
</template> </template>
</i18n-t> </i18n-t>
<StatusContent <StatusContent
v-if="draft.refId" v-if="draft.refId && refStatus"
class="status-content" class="status-content"
:status="refStatus" :status="refStatus"
:compact="true" :compact="true"

View File

@ -1165,6 +1165,7 @@
"abandon_confirm_accept_button": "Abandon", "abandon_confirm_accept_button": "Abandon",
"abandon_confirm_cancel_button": "Keep", "abandon_confirm_cancel_button": "Keep",
"replying": "Replying to {statusLink}", "replying": "Replying to {statusLink}",
"editing": "Editing {statusLink}" "editing": "Editing {statusLink}",
"unavailable": "(unavailable)"
} }
} }

View File

@ -1,6 +1,6 @@
import merge from 'lodash.merge' import merge from 'lodash.merge'
import localforage from 'localforage'
import { each, get, set, cloneDeep } from 'lodash' import { each, get, set, cloneDeep } from 'lodash'
import { storage } from './storage.js'
let loaded = false let loaded = false
@ -26,7 +26,7 @@ const saveImmedeatelyActions = [
] ]
const defaultStorage = (() => { const defaultStorage = (() => {
return localforage return storage
})() })()
export default function createPersistedState ({ export default function createPersistedState ({

3
src/lib/storage.js Normal file
View File

@ -0,0 +1,3 @@
import localforage from 'localforage'
export const storage = localforage

View File

@ -1,3 +1,4 @@
import { storage } from 'src/lib/storage.js'
export const defaultState = { export const defaultState = {
drafts: {} drafts: {}
@ -9,17 +10,55 @@ export const mutations = {
}, },
abandonDraft (state, { id }) { abandonDraft (state, { id }) {
delete state.drafts[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)) || {})
const saveDraftToStorage = async (draft) => {
const currentData = await getStorageData()
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
await storage.setItem(storageKey, currentData)
}
const deleteDraftFromStorage = async (id) => {
const currentData = await getStorageData()
delete currentData[id]
await storage.setItem(storageKey, currentData)
}
export const actions = { export const actions = {
addOrSaveDraft (store, { draft }) { async addOrSaveDraft (store, { draft }) {
const id = draft.id || (new Date().getTime()).toString() const id = draft.id || (new Date().getTime()).toString()
store.commit('addOrSaveDraft', { draft: { ...draft, id } }) const draftWithId = { ...draft, id }
store.commit('addOrSaveDraft', { draft: draftWithId })
await saveDraftToStorage(draftWithId)
return id return id
}, },
abandonDraft (store, { id }) { async abandonDraft (store, { id }) {
store.commit('abandonDraft', { id }) store.commit('abandonDraft', { id })
await deleteDraftFromStorage(id)
},
async loadDrafts (store) {
const currentData = await getStorageData()
store.commit('loadDrafts', currentData)
} }
} }

View File

@ -1,6 +1,6 @@
/* eslint-env serviceworker */ /* eslint-env serviceworker */
import localForage from 'localforage' import { storage } from 'src/lib/storage.js'
import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js' import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js'
import { prepareNotificationObject } from './services/notification_utils/notification_utils.js' import { prepareNotificationObject } from './services/notification_utils/notification_utils.js'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
@ -14,7 +14,7 @@ const i18n = createI18n({
}) })
function isEnabled () { function isEnabled () {
return localForage.getItem('vuex-lz') return storage.getItem('vuex-lz')
.then(data => data.config.webPushNotifications) .then(data => data.config.webPushNotifications)
} }
@ -24,7 +24,7 @@ function getWindowClients () {
} }
const setLocale = async () => { const setLocale = async () => {
const state = await localForage.getItem('vuex-lz') const state = await storage.getItem('vuex-lz')
const locale = state.config.interfaceLanguage || 'en' const locale = state.config.interfaceLanguage || 'en'
i18n.locale = locale i18n.locale = locale
} }