-
-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Storage reactivity #111
Comments
In fact, instead of launching three different parts of the extension, it could be the same vue instance in the three parts : background, popup and options page. And handle each part with |
Seems related to this: https://github.com/vueuse/vueuse/blob/658444bf9f8b96118dbd06eba411bb6639e24e88/packages/core/useStorageAsync/index.ts#L101
|
My current solution: useWebExtensionStorage.tsimport { StorageSerializers } from '@vueuse/core'
import { toValue, watchWithFilter, tryOnScopeDispose } from '@vueuse/shared'
import { ref, shallowRef } from 'vue-demi'
import { storage } from 'webextension-polyfill'
import type {
UseStorageAsyncOptions,
StorageLikeAsync,
} from '@vueuse/core'
import type { MaybeRefOrGetter, RemovableRef } from '@vueuse/shared'
import type { Ref } from 'vue-demi'
import type { Storage } from 'webextension-polyfill';
export type WebExtensionStorageOptions<T> = UseStorageAsyncOptions<T>
// https://github.com/vueuse/vueuse/blob/658444bf9f8b96118dbd06eba411bb6639e24e88/packages/core/useStorage/guess.ts
export function guessSerializerType<T extends(string | number | boolean | object | null)>(rawInit: T) {
return rawInit == null
? 'any'
: rawInit instanceof Set
? 'set'
: rawInit instanceof Map
? 'map'
: rawInit instanceof Date
? 'date'
: typeof rawInit === 'boolean'
? 'boolean'
: typeof rawInit === 'string'
? 'string'
: typeof rawInit === 'object'
? 'object'
: Number.isNaN(rawInit)
? 'any'
: 'number'
}
const storageInterface: StorageLikeAsync = {
removeItem(key: string) {
return storage.local.remove(key)
},
setItem(key: string, value: string) {
return storage.local.set({ [key]: value })
},
async getItem(key: string) {
const storedData = await storage.local.get(key)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return storedData[key]
},
}
/**
* https://github.com/vueuse/vueuse/blob/658444bf9f8b96118dbd06eba411bb6639e24e88/packages/core/useStorageAsync/index.ts
*
* @param key
* @param initialValue
* @param options
*/
export function useWebExtensionStorage<T extends(string | number | boolean | object | null)>(
key: string,
initialValue: MaybeRefOrGetter<T>,
options: WebExtensionStorageOptions<T> = {},
): RemovableRef<T> {
const {
flush = 'pre',
deep = true,
listenToStorageChanges = true,
writeDefaults = true,
mergeDefaults = false,
shallow,
eventFilter,
onError = (e) => {
console.error(e)
},
} = options
const rawInit: T = toValue(initialValue)
const type = guessSerializerType<T>(rawInit)
const data = (shallow ? shallowRef : ref)(initialValue) as Ref<T>
const serializer = options.serializer ?? StorageSerializers[type]
async function read(event?: { key: string, newValue: string | null }) {
if (event && event.key !== key) {
return
}
try {
const rawValue = event ? event.newValue : await storageInterface.getItem(key)
if (rawValue == null) {
data.value = rawInit
if (writeDefaults && rawInit !== null)
await storageInterface.setItem(key, await serializer.write(rawInit))
}
else if (mergeDefaults) {
const value = await serializer.read(rawValue) as T
if (typeof mergeDefaults === 'function')
data.value = mergeDefaults(value, rawInit)
else if (type === 'object' && !Array.isArray(value))
data.value = { ...(rawInit as Record<keyof unknown, unknown>), ...(value as Record<keyof unknown, unknown>) } as T
else data.value = value
}
else {
data.value = await serializer.read(rawValue) as T
}
}
catch (error) {
onError(error)
}
}
void read()
if (listenToStorageChanges) {
const listener = async (changes: Record<string, Storage.StorageChange>) => {
for (const [key, change] of Object.entries(changes)) {
await read({
key,
newValue: change.newValue as string | null,
})
}
}
storage.onChanged.addListener(listener)
tryOnScopeDispose(() => {
storage.onChanged.removeListener(listener);
})
}
watchWithFilter(
data,
async () => {
try {
await (data.value == null ? storageInterface.removeItem(key) : storageInterface.setItem(key, await serializer.write(data.value)));
}
catch (error) {
onError(error)
}
},
{
flush,
deep,
eventFilter,
},
)
return data as RemovableRef<T>
} I just copied useStorageAsync and some internal stuff from vueuse and hardcoded the webextension storage interface. Should behave like any other useStorage from vueuse, but didnt test it extensively. Any suggestions are welcome. Will create a PR later this week. |
Describe the bug
Open the option page and the popup, then try to change
storageDemo
value in the option page, it should change in the popup in realtime. It's not easy to see because the popup close when you activate the option page. With firefox, you can toggle theDisable popup auto-hide
option to see it in action.It's particularly relevant with the opposite ; changing
storageDemo
in the popup.Furthermore, add this in the background :
We can see that it's also not reactive in the background.
I know this issue #55, but the background, option page and popup «live» in the same context, not the contentScripts. The storage should be reactive between background, option page and popup.
Edit: I add this code below to the reproduction branch.
You can try with vueuse
useStorage
by inserting this code in those three parts of the extension :And add an input in the popup to edit the value :
The reactivity works through the local storage reactivity provided by
useStorage
.Reproduction
https://github.com/ManUtopiK/vitesse-webext/tree/storageReactivityIssue
System Info
Used Package Manager
pnpm
Validations
The text was updated successfully, but these errors were encountered: