Use capacitor preferences package instead of localStorage

This commit is contained in:
Matthew Remmel
2025-08-29 11:06:50 -04:00
committed by Jon Staab
parent 2672a8f922
commit c94d314f6d
11 changed files with 96 additions and 19 deletions

View File

@@ -12,6 +12,7 @@ dependencies {
implementation project(':capacitor-community-safe-area') implementation project(':capacitor-community-safe-area')
implementation project(':capacitor-app') implementation project(':capacitor-app')
implementation project(':capacitor-keyboard') implementation project(':capacitor-keyboard')
implementation project(':capacitor-preferences')
implementation project(':capacitor-push-notifications') implementation project(':capacitor-push-notifications')
implementation project(':capawesome-capacitor-badge') implementation project(':capawesome-capacitor-badge')
implementation project(':nostr-signer-capacitor-plugin') implementation project(':nostr-signer-capacitor-plugin')

View File

@@ -11,6 +11,9 @@ project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacito
include ':capacitor-keyboard' include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/.pnpm/@capacitor+keyboard@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/keyboard/android') project(':capacitor-keyboard').projectDir = new File('../node_modules/.pnpm/@capacitor+keyboard@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/keyboard/android')
include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../node_modules/.pnpm/@capacitor+preferences@7.0.2_@capacitor+core@7.2.0/node_modules/@capacitor/preferences/android')
include ':capacitor-push-notifications' include ':capacitor-push-notifications'
project(':capacitor-push-notifications').projectDir = new File('../node_modules/.pnpm/@capacitor+push-notifications@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/push-notifications/android') project(':capacitor-push-notifications').projectDir = new File('../node_modules/.pnpm/@capacitor+push-notifications@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/push-notifications/android')

View File

@@ -14,6 +14,7 @@ def capacitor_pods
pod 'CapacitorCommunitySafeArea', :path => '../../node_modules/.pnpm/@capacitor-community+safe-area@7.0.0-alpha.1_@capacitor+core@7.2.0/node_modules/@capacitor-community/safe-area' pod 'CapacitorCommunitySafeArea', :path => '../../node_modules/.pnpm/@capacitor-community+safe-area@7.0.0-alpha.1_@capacitor+core@7.2.0/node_modules/@capacitor-community/safe-area'
pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/app' pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/app'
pod 'CapacitorKeyboard', :path => '../../node_modules/.pnpm/@capacitor+keyboard@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/keyboard' pod 'CapacitorKeyboard', :path => '../../node_modules/.pnpm/@capacitor+keyboard@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/keyboard'
pod 'CapacitorPreferences', :path => '../../node_modules/.pnpm/@capacitor+preferences@7.0.2_@capacitor+core@7.2.0/node_modules/@capacitor/preferences'
pod 'CapacitorPushNotifications', :path => '../../node_modules/.pnpm/@capacitor+push-notifications@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/push-notifications' pod 'CapacitorPushNotifications', :path => '../../node_modules/.pnpm/@capacitor+push-notifications@7.0.1_@capacitor+core@7.2.0/node_modules/@capacitor/push-notifications'
pod 'CapawesomeCapacitorBadge', :path => '../../node_modules/.pnpm/@capawesome+capacitor-badge@7.0.1_@capacitor+core@7.2.0/node_modules/@capawesome/capacitor-badge' pod 'CapawesomeCapacitorBadge', :path => '../../node_modules/.pnpm/@capawesome+capacitor-badge@7.0.1_@capacitor+core@7.2.0/node_modules/@capawesome/capacitor-badge'
pod 'NostrSignerCapacitorPlugin', :path => '../../node_modules/.pnpm/nostr-signer-capacitor-plugin@0.0.4_@capacitor+core@7.2.0/node_modules/nostr-signer-capacitor-plugin' pod 'NostrSignerCapacitorPlugin', :path => '../../node_modules/.pnpm/nostr-signer-capacitor-plugin@0.0.4_@capacitor+core@7.2.0/node_modules/nostr-signer-capacitor-plugin'

View File

@@ -45,6 +45,7 @@
"@capacitor/core": "^7.0.1", "@capacitor/core": "^7.0.1",
"@capacitor/ios": "^7.0.0", "@capacitor/ios": "^7.0.0",
"@capacitor/keyboard": "^7.0.0", "@capacitor/keyboard": "^7.0.0",
"@capacitor/preferences": "^7.0.2",
"@capacitor/push-notifications": "^7.0.1", "@capacitor/push-notifications": "^7.0.1",
"@capawesome/capacitor-badge": "^7.0.1", "@capawesome/capacitor-badge": "^7.0.1",
"@getalby/sdk": "^5.1.0", "@getalby/sdk": "^5.1.0",

BIN
pnpm-lock.yaml generated

Binary file not shown.

View File

@@ -78,6 +78,7 @@ import {
userRoomsByUrl, userRoomsByUrl,
userSettingsValues, userSettingsValues,
} from "@app/core/state" } from "@app/core/state"
import {preferencesStorageProvider} from "@src/lib/storage"
// Utils // Utils
@@ -124,6 +125,7 @@ export const logout = async () => {
await clearStorage() await clearStorage()
localStorage.clear() localStorage.clear()
await preferencesStorageProvider.clear()
} }
// Synchronization // Synchronization

View File

@@ -31,7 +31,6 @@ import {
deriveEventsMapped, deriveEventsMapped,
withGetter, withGetter,
synced, synced,
localStorageProvider,
} from "@welshman/store" } from "@welshman/store"
import { import {
getIdFilters, getIdFilters,
@@ -97,6 +96,7 @@ import {
publishThunk, publishThunk,
} from "@welshman/app" } from "@welshman/app"
import type {Thunk, Relay} from "@welshman/app" import type {Thunk, Relay} from "@welshman/app"
import {preferencesStorageProvider} from "@src/lib/storage"
export const fromCsv = (s: string) => (s || "").split(",").filter(identity) export const fromCsv = (s: string) => (s || "").split(",").filter(identity)
@@ -317,7 +317,7 @@ netContext.isEventValid = (event: TrustedEvent, url: string) =>
export const canDecrypt = synced({ export const canDecrypt = synced({
key: "canDecrypt", key: "canDecrypt",
defaultValue: false, defaultValue: false,
storage: localStorageProvider, storage: preferencesStorageProvider,
}) })
export const SETTINGS = "flotilla/settings" export const SETTINGS = "flotilla/settings"

View File

@@ -1,5 +1,5 @@
import {derived} from "svelte/store" import {derived} from "svelte/store"
import {synced, localStorageProvider, throttled} from "@welshman/store" import {synced, throttled} from "@welshman/store"
import {pubkey, relaysByUrl} from "@welshman/app" import {pubkey, relaysByUrl} from "@welshman/app"
import {prop, spec, identity, now, groupBy} from "@welshman/lib" import {prop, spec, identity, now, groupBy} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
@@ -13,13 +13,14 @@ import {
makeRoomPath, makeRoomPath,
} from "@app/util/routes" } from "@app/util/routes"
import {chats, hasNip29, getUrlsForEvent, userRoomsByUrl, repositoryStore} from "@app/core/state" import {chats, hasNip29, getUrlsForEvent, userRoomsByUrl, repositoryStore} from "@app/core/state"
import {preferencesStorageProvider} from "@src/lib/storage"
// Checked state // Checked state
export const checked = synced<Record<string, number>>({ export const checked = synced<Record<string, number>>({
key: "checked", key: "checked",
defaultValue: {}, defaultValue: {},
storage: localStorageProvider, storage: preferencesStorageProvider,
}) })
export const deriveChecked = (key: string) => derived(checked, prop(key)) export const deriveChecked = (key: string) => derived(checked, prop(key))

View File

@@ -1,7 +1,8 @@
import {synced, localStorageProvider} from "@welshman/store" import {preferencesStorageProvider} from "@src/lib/storage"
import {synced} from "@welshman/store"
export const theme = synced({ export const theme = synced({
key: "theme", key: "theme",
defaultValue: "dark", defaultValue: "dark",
storage: localStorageProvider, storage: preferencesStorageProvider,
}) })

28
src/lib/storage.ts Normal file
View File

@@ -0,0 +1,28 @@
import {type StorageProvider} from "@welshman/store"
import {Preferences} from "@capacitor/preferences"
export class PreferencesStorageProvider implements StorageProvider {
get = async <T>(key: string): Promise<T | undefined> => {
const result = await Preferences.get({key})
if (!result.value) return undefined
try {
return JSON.parse(result.value)
} catch (e) {
return undefined
}
}
p = Promise.resolve()
set = async <T>(key: string, value: T): Promise<void> => {
this.p = this.p.then(async () => await Preferences.set({key, value: JSON.stringify(value)}))
await this.p
}
clear = async (): Promise<void> => {
await Preferences.clear()
this.p = Promise.resolve()
}
}
// singleton instance of PreferencesStorageProvider
export const preferencesStorageProvider = new PreferencesStorageProvider()

View File

@@ -10,18 +10,18 @@
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import {sync, localStorageProvider} from "@welshman/store" import {sync, localStorageProvider} from "@welshman/store"
import { import {
identity,
call,
memoize,
spec,
sleep,
on,
defer,
ago, ago,
WEEK,
TaskQueue,
assoc, assoc,
call,
defer,
dissoc, dissoc,
identity,
memoize,
on,
sleep,
spec,
TaskQueue,
WEEK,
} from "@welshman/lib" } from "@welshman/lib"
import type {TrustedEvent, StampedEvent} from "@welshman/util" import type {TrustedEvent, StampedEvent} from "@welshman/util"
import { import {
@@ -82,6 +82,7 @@
import * as net from "@welshman/net" import * as net from "@welshman/net"
import * as app from "@welshman/app" import * as app from "@welshman/app"
import {nsecDecode} from "@lib/util" import {nsecDecode} from "@lib/util"
import {preferencesStorageProvider} from "@lib/storage"
import AppContainer from "@app/components/AppContainer.svelte" import AppContainer from "@app/components/AppContainer.svelte"
import ModalContainer from "@app/components/ModalContainer.svelte" import ModalContainer from "@app/components/ModalContainer.svelte"
import {setupTracking} from "@app/util/tracking" import {setupTracking} from "@app/util/tracking"
@@ -134,6 +135,44 @@
...notifications, ...notifications,
}) })
// migrate from localStorage to capacitor Preferences storage if needed
const runMigration = async () => {
const isSome = (item: any) => {
return item !== undefined && item !== null && item !== ""
}
const localStoragePubKey = await localStorageProvider.get("pubkey")
if (isSome(localStoragePubKey)) {
await preferencesStorageProvider.set("pubkey", localStoragePubKey)
localStorage.removeItem("pubkey")
}
const localStorageSessions = await localStorageProvider.get("sessions")
if (isSome(localStorageSessions)) {
await preferencesStorageProvider.set("sessions", localStorageSessions)
localStorage.removeItem("sessions")
}
const localStorageCanDecrypt = await localStorageProvider.get("canDecrypt")
if (isSome(localStorageCanDecrypt)) {
await preferencesStorageProvider.set("canDecrypt", localStorageCanDecrypt)
localStorage.removeItem("canDecrypt")
}
const localStorageChecked = await localStorageProvider.get("checked")
if (isSome(localStorageChecked)) {
await preferencesStorageProvider.set("checked", localStorageChecked)
localStorage.removeItem("checked")
}
const localStorageTheme = await localStorageProvider.get("theme")
if (isSome(localStorageTheme)) {
await preferencesStorageProvider.set("theme", localStorageTheme)
localStorage.removeItem("theme")
}
}
await runMigration()
// Listen for navigation messages from service worker // Listen for navigation messages from service worker
navigator.serviceWorker?.addEventListener("message", event => { navigator.serviceWorker?.addEventListener("message", event => {
if (event.data && event.data.type === "NAVIGATE") { if (event.data && event.data.type === "NAVIGATE") {
@@ -221,17 +260,17 @@
}) })
// Sync current pubkey // Sync current pubkey
sync({ await sync({
key: "pubkey", key: "pubkey",
store: pubkey, store: pubkey,
storage: localStorageProvider, storage: preferencesStorageProvider,
}) })
// Sync user sessions // Sync user sessions
sync({ await sync({
key: "sessions", key: "sessions",
store: sessions, store: sessions,
storage: localStorageProvider, storage: preferencesStorageProvider,
}) })
await initStorage("flotilla", 8, { await initStorage("flotilla", 8, {