import { browser } from "webextension-polyfill-ts" ; import { pick } from "./pick" ; import { CauseArea , Currency } from "../../../shared/types/user" ; export type LocalStorageSchema = { activatedAffiliates : ActivatedAffiliate [ ] ; closedWebsites : string [ ] ; disabledWebsites : string [ ] ; partners : string [ ] ; installationId : string ; } ; export type SyncStorageSchema = { ref : string ; referredBy : string ; privateNotifications : PrivateNotification [ ] ; publicNotifications : PublicNotifications ; showNotifications : boolean ; highlightSearchResults : boolean ; user : User | null ; userSettings : UserSettings ; } ; export type ActivatedAffiliate = { domain : string ; timestamp : number ; } ; type StringifiedNotification = string ; export type PublicNotifications = { lastUpdated : string ; notificationsQueue : StringifiedNotification [ ] ; } ; export type PrivateNotification = { id : string ; content : StringifiedNotification ; } ; export type User = { email : string ; apiKey : string ; } ; export type UserSettings = { causeArea : CauseArea ; currency : Currency ; } ; const localStorageSchema = { installationId : "" , partners : [ ] , activatedAffiliates : [ ] , closedWebsites : [ ] , disabledWebsites : [ ] } ; const syncStorageSchema = { ref : "" , referredBy : "" , privateNotifications : [ ] , publicNotifications : { lastUpdated : "0000-00-00" , notificationsQueue : [ ] } , showNotifications : true , highlightSearchResults : true , user : null , userSettings : { causeArea : "covid" , currency : "USD" } } ; const createStorage = < LocalStorage extends object , SyncStorage extends object , LocalStorageKey = keyof LocalStorage , SyncStorageKey = keyof SyncStorage > ( localStorageSchema , syncStorageSchema ) => { const get = < SA extends "local" | "sync" , K extends LocalStorageKey | LocalStorageKey [ ] | SyncStorageKey | SyncStorageKey [ ] > ( storageArea , keys ? ) => { if ( ! keys ) { return browser . storage [ storageArea ] . get ( ) as Promise < SA extends "local" ? LocalStorage : SyncStorage > ; } else { return browser . storage [ storageArea ] . get ( keys ) as Promise < SA extends "local" ? Pick < LocalStorage , K extends LocalStorageKey [ ] ? Extract < K [ number ] , keyof LocalStorage > : Extract < K , keyof LocalStorage > > : Pick < SyncStorage , K extends SyncStorageKey [ ] ? Extract < K [ number ] , keyof SyncStorage > : Extract < K , keyof SyncStorage > > > ; } } ; const set = < SA extends "local" | "sync" > ( storageArea , update ) => { if ( typeof update === "function" ) { return get ( storageArea ) . then ( current => Promise . resolve ( update ( current as SA extends "local" ? LocalStorage : SyncStorage ) ) . then ( newValues => browser . storage [ storageArea ] . set ( newValues ) ) ) ; } else { return Promise . resolve ( update ) . then ( newValues => browser . storage [ storageArea ] . set ( newValues ) ) ; } } ; const reset = < SA extends "local" | "sync" , K extends SA extends "local" ? LocalStorageKey | LocalStorageKey [ ] : SyncStorageKey | SyncStorageKey [ ] > ( storageArea , keys ) => { const defaultValues = storageArea === "local" ? pick ( localStorageSchema , keys as any ) : pick ( syncStorageSchema , keys as any ) ; return set ( storageArea , defaultValues as any ) ; } ; set ( "local" , current => ( { ... localStorageSchema , ... current } ) ) ; set ( "sync" , current => ( { ... syncStorageSchema , ... current } ) ) ; return { get , set , reset } ; } ; export const storage = createStorage ( localStorageSchema , syncStorageSchema ) ;