diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 9c1f007b..1acdfa61 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -377,6 +377,8 @@ const afterStoreSetup = async ({ store, i18n }) => { getInstanceConfig({ store }) ]) + await store.dispatch('loadDrafts') + // Start fetching things that don't need to block the UI store.dispatch('fetchMutes') store.dispatch('startFetchingAnnouncements') diff --git a/src/components/draft/draft.vue b/src/components/draft/draft.vue index cc385c14..daa890d8 100644 --- a/src/components/draft/draft.vue +++ b/src/components/draft/draft.vue @@ -31,12 +31,12 @@ class="faint-link" :to="{ name: 'conversation', params: { id: draft.refId } }" > - {{ refStatus.external_url }} + {{ refStatus ? refStatus.external_url : $t('drafts.unavailable') }} { - return localforage + return storage })() export default function createPersistedState ({ diff --git a/src/lib/storage.js b/src/lib/storage.js new file mode 100644 index 00000000..25a49abc --- /dev/null +++ b/src/lib/storage.js @@ -0,0 +1,3 @@ +import localforage from 'localforage' + +export const storage = localforage diff --git a/src/modules/drafts.js b/src/modules/drafts.js index 01eb3dcf..7a38fe2d 100644 --- a/src/modules/drafts.js +++ b/src/modules/drafts.js @@ -1,3 +1,4 @@ +import { storage } from 'src/lib/storage.js' export const defaultState = { drafts: {} @@ -9,17 +10,55 @@ export const mutations = { }, 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)) || {}) + +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 = { - addOrSaveDraft (store, { draft }) { + async addOrSaveDraft (store, { draft }) { 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 }, - abandonDraft (store, { id }) { + async abandonDraft (store, { id }) { store.commit('abandonDraft', { id }) + await deleteDraftFromStorage(id) + }, + async loadDrafts (store) { + const currentData = await getStorageData() + store.commit('loadDrafts', currentData) } } diff --git a/src/sw.js b/src/sw.js index 70fed44b..409ade45 100644 --- a/src/sw.js +++ b/src/sw.js @@ -1,6 +1,6 @@ /* 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 { prepareNotificationObject } from './services/notification_utils/notification_utils.js' import { createI18n } from 'vue-i18n' @@ -14,7 +14,7 @@ const i18n = createI18n({ }) function isEnabled () { - return localForage.getItem('vuex-lz') + return storage.getItem('vuex-lz') .then(data => data.config.webPushNotifications) } @@ -24,7 +24,7 @@ function getWindowClients () { } const setLocale = async () => { - const state = await localForage.getItem('vuex-lz') + const state = await storage.getItem('vuex-lz') const locale = state.config.interfaceLanguage || 'en' i18n.locale = locale }