diff --git a/src/composable/queryParams.ts b/src/composable/queryParams.ts index 9699abbc..7cc8cc0d 100644 --- a/src/composable/queryParams.ts +++ b/src/composable/queryParams.ts @@ -1,7 +1,8 @@ import { useRouteQuery } from '@vueuse/router'; import { computed } from 'vue'; +import { useStorage } from '@vueuse/core'; -export { useQueryParam }; +export { useQueryParam, useQueryParamOrStorage }; const transformers = { number: { @@ -16,6 +17,12 @@ const transformers = { fromQuery: (value: string) => value.toLowerCase() === 'true', toQuery: (value: boolean) => (value ? 'true' : 'false'), }, + object: { + fromQuery: (value: string) => { + return JSON.parse(value); + }, + toQuery: (value: object) => JSON.stringify(value), + }, }; function useQueryParam({ name, defaultValue }: { name: string; defaultValue: T }) { @@ -33,3 +40,27 @@ function useQueryParam({ name, defaultValue }: { name: string; defaultValue: }, }); } + +function useQueryParamOrStorage({ name, storageName, defaultValue }: { name: string; storageName: string; defaultValue: T }) { + const type = typeof defaultValue; + const transformer = transformers[type as keyof typeof transformers] ?? transformers.string; + + const storageRef = useStorage(storageName, defaultValue); + const proxyDefaultValue = transformer.toQuery(defaultValue as never); + const proxy = useRouteQuery(name, proxyDefaultValue); + + const r = ref(defaultValue); + + watch(r, + (value) => { + proxy.value = transformer.toQuery(value as never); + storageRef.value = value as never; + }, + { deep: true }); + + r.value = (proxy.value && proxy.value !== proxyDefaultValue + ? transformer.fromQuery(proxy.value) as unknown as T + : storageRef.value as T) as never; + + return r; +} diff --git a/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue b/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue index 6bd881c0..00db7e97 100644 --- a/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue +++ b/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue @@ -7,13 +7,18 @@ import TokenDisplay from './token-display.vue'; import { useStyleStore } from '@/stores/style.store'; import InputCopyable from '@/components/InputCopyable.vue'; import { computedRefreshable } from '@/composable/computedRefreshable'; +import { useQueryParamOrStorage } from '@/composable/queryParams'; const now = useTimestamp(); const interval = computed(() => (now.value / 1000) % 30); const theme = useThemeVars(); const styleStore = useStyleStore(); -const secret = ref(generateSecret()); +const secret = useQueryParamOrStorage({ + name: 'secret', + storageName: 'otp-gen:secret', + defaultValue: generateSecret(), +}); function refreshSecret() { secret.value = generateSecret();