diff options
author | vnugent <public@vaughnnugent.com> | 2024-03-05 17:16:56 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-03-05 17:16:56 -0500 |
commit | beb29f571063fc3e5147482a15275d1026aed699 (patch) | |
tree | ec487d6ccb7cecaea9a2731d79db435f3435f0f0 /extension | |
parent | a8603e00e390b07c036384285412eda5e07beda7 (diff) |
fix: api endpoint urls after discovery and reduced reactivity noise
Diffstat (limited to 'extension')
-rw-r--r-- | extension/src/entries/options/components/AutoRules.vue | 3 | ||||
-rw-r--r-- | extension/src/entries/options/components/EventHistory.vue | 28 | ||||
-rw-r--r-- | extension/src/entries/options/components/SiteSettings.vue | 4 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/PageContent.vue | 3 | ||||
-rw-r--r-- | extension/src/entries/store/permissions.ts | 42 | ||||
-rw-r--r-- | extension/src/features/auth-api.ts | 6 | ||||
-rw-r--r-- | extension/src/features/history.ts | 1 | ||||
-rw-r--r-- | extension/src/features/nostr-api.ts | 2 | ||||
-rw-r--r-- | extension/src/features/permissions.ts | 7 | ||||
-rw-r--r-- | extension/src/features/settings.ts | 86 |
10 files changed, 108 insertions, 74 deletions
diff --git a/extension/src/entries/options/components/AutoRules.vue b/extension/src/entries/options/components/AutoRules.vue index 9f46475..367064d 100644 --- a/extension/src/entries/options/components/AutoRules.vue +++ b/extension/src/entries/options/components/AutoRules.vue @@ -63,8 +63,7 @@ import { type AutoAllowRule } from '../../../features' const store = useStore() const { } = storeToRefs(store) - -const rules = computed(() => store.permissions.rules) +const { rules } = store.permissions.getRules(); const pages = useOffsetPagination({ pageSize: 10, diff --git a/extension/src/entries/options/components/EventHistory.vue b/extension/src/entries/options/components/EventHistory.vue index 4fa97d4..db7c7c4 100644 --- a/extension/src/entries/options/components/EventHistory.vue +++ b/extension/src/entries/options/components/EventHistory.vue @@ -5,7 +5,12 @@ Event History </div> <div class="flex justify-center"> - <pagination :pages="pagination" /> + <pagination :pages="pages" /> + <div class="ml-2"> + <button class="btn borderless sm" @click="refresh()"> + <fa-icon :class="{ 'animate-spin': !ready }" icon="sync" /> + </button> + </div> </div> </div> <div class="mt-2"> @@ -144,7 +149,7 @@ <script setup lang="ts"> import { apiCall, useConfirm } from '@vnuge/vnlib.browser'; import { computed } from 'vue'; -import { formatTimeAgo, get, useOffsetPagination, useTimestamp } from '@vueuse/core'; +import { formatTimeAgo, get, useOffsetPagination, useTimeout, useTimestamp } from '@vueuse/core'; import { useStore } from '../../store'; import { EventEntry, NostrEvent } from '../../../features'; import { find, map, slice } from 'lodash'; @@ -159,7 +164,7 @@ const openEvId = useQuery('activeEvent'); const { reveal } = useConfirm() -const pagination = useOffsetPagination({ +const pages = useOffsetPagination({ pageSize: 10, total: computed(() => store.eventHistory.length) }) @@ -169,8 +174,8 @@ const explodeNote = (event: EventEntry) => JSON.parse(event.EventData) as NostrE const timeStamp = useTimestamp({interval: 1000}) const evHistory = computed<Array<NostrEvent & EventEntry>>(() => { - const start = (get(pagination.currentPage) - 1) * get(pagination.currentPageSize) - const end = start + 10 + const start = (get(pages.currentPage) - 1) * get(pages.currentPageSize) + const end = start + get(pages.currentPageSize) const page = slice(store.eventHistory, start, end) return map(page, event => { const exploded = explodeNote(event) @@ -222,15 +227,18 @@ const goToKeyView = (key: { KeyId:string }) => { keyId.set(key.KeyId); } +const { ready, start } = useTimeout(500, { controls: true }) + +const refresh = () => { + store.plugins.history.refresh(); + start(); +} + + </script> <style lang="scss"> #ev-history { - button.page-btn { - @apply inline-flex items-center px-2 py-2 space-x-2 font-medium rounded-full; - @apply bg-white border border-gray-300 rounded-full hover:bg-gray-50 dark:bg-dark-600 dark:hover:bg-dark-500 dark:border-dark-300; - } - form tr { @apply sm:text-sm text-xs dark:text-gray-400 text-gray-600; } diff --git a/extension/src/entries/options/components/SiteSettings.vue b/extension/src/entries/options/components/SiteSettings.vue index 8deac92..5b5a2e2 100644 --- a/extension/src/entries/options/components/SiteSettings.vue +++ b/extension/src/entries/options/components/SiteSettings.vue @@ -71,7 +71,7 @@ <fa-icon v-if="editMode" icon="lock-open" /> <fa-icon v-else icon="lock" /> </button> - <a :href="store.status.EpConfig.apiBaseUrl" target="_blank"> + <a :href="store.status.epConfig.apiBaseUrl" target="_blank"> <button type="button" class="rounded btn sm"> <fa-icon icon="external-link-alt" /> </button> @@ -117,7 +117,7 @@ const { settings } = storeToRefs(store) const { waiting } = useWait(); const { setSiteConfig } = store.plugins.settings -const { apply, data, buffer, modified, update } = useDataBuffer(settings.value, async sb =>{ +const { apply, buffer, modified, update } = useDataBuffer(settings.value, async sb =>{ const newConfig = await setSiteConfig(sb.buffer) apply(newConfig) return newConfig; diff --git a/extension/src/entries/popup/Components/PageContent.vue b/extension/src/entries/popup/Components/PageContent.vue index 9bc9627..b090b5b 100644 --- a/extension/src/entries/popup/Components/PageContent.vue +++ b/extension/src/entries/popup/Components/PageContent.vue @@ -131,10 +131,11 @@ configureNotifier({notify, close:notify.close}) const store = useStore() const { loggedIn, selectedKey, userName, darkMode, isTabAllowed, currentOrigin, isOriginProtectionOn } = storeToRefs(store) const { copy, copied } = useClipboard() +const { rulesForCurrentOrigin } = store.permissions.getRules() const pubKey = computed(() => selectedKey!.value?.PublicKey) -const ruleTypes = computed<string[]>(() => map(store.permissions.rulesForCurrentOrigin, 'type')) +const ruleTypes = computed<string[]>(() => map(rulesForCurrentOrigin.value, 'type')) const openOptions = () => runtime.openOptionsPage(); const toggleDark = () => store.toggleDarkMode() diff --git a/extension/src/entries/store/permissions.ts b/extension/src/entries/store/permissions.ts index 6b011de..b5f6675 100644 --- a/extension/src/entries/store/permissions.ts +++ b/extension/src/entries/store/permissions.ts @@ -16,7 +16,7 @@ import 'pinia' import { filter, find } from 'lodash' import { PiniaPluginContext, storeToRefs } from 'pinia' -import { computed, shallowRef } from 'vue' +import { computed, shallowRef, type Ref } from 'vue' import { PrStatus, @@ -27,12 +27,16 @@ import { get } from '@vueuse/core' import { AutoAllowRule } from '../../features/permissions' export interface PermissionApi { + getRules(): AutoAllowApi readonly pending: PermissionRequest[] readonly all: PermissionRequest[] readonly windowPending: PermissionRequest | undefined readonly isPopup: boolean - readonly rules: AutoAllowRule[], - readonly rulesForCurrentOrigin: AutoAllowRule[] +} + +export interface AutoAllowApi{ + readonly rules: Ref<AutoAllowRule[]>, + readonly rulesForCurrentOrigin: Ref<AutoAllowRule[]> } declare module 'pinia' { @@ -50,9 +54,7 @@ export const permissionsPlugin = ({ store }: PiniaPluginContext) => { const all = shallowRef<PermissionRequest[]>([]) const activeRequests = computed(() => filter(all.value, r => r.status == PrStatus.Pending)) const windowPending = shallowRef<PermissionRequest | undefined>() - const rules = shallowRef<AutoAllowRule[]>([]) - - const rulesForCurrentOrigin = computed(() => filter(rules.value, r => r.origin == get(currentOrigin))) + const closeIfPopup = () => { const windowQueryArgs = new URLSearchParams(window.location.search) @@ -76,7 +78,7 @@ export const permissionsPlugin = ({ store }: PiniaPluginContext) => { onWatchableChange(permission, async () => { //get latest requests and current ruleset all.value = await permission.getRequests() - rules.value = await permission.getRules() + //update window pending request windowPending.value = getPendingWindowRequest() @@ -92,12 +94,34 @@ export const permissionsPlugin = ({ store }: PiniaPluginContext) => { } }, { immediate: true }) + + /** + * Get rules is now a separate function for only instances where + * rules need to be accessed. This is to avoid the overhead of + * an interval reactive update when rules are not needed. + */ + const getRules = (): AutoAllowApi => { + + const rules = shallowRef<AutoAllowRule[]>([]) + const rulesForCurrentOrigin = computed(() => filter(rules.value, r => r.origin == get(currentOrigin))) + + onWatchableChange(permission, async () => { + rules.value = await permission.getRules() + }) + + //also update rules on an interval + setInterval(async () => rules.value = await permission.getRules(), 2000) + + return { + rules, + rulesForCurrentOrigin + } + } return { permissions:{ all, - rules, - rulesForCurrentOrigin, + getRules, pending: activeRequests, isPopup: getWindowUuid() !== null, } diff --git a/extension/src/features/auth-api.ts b/extension/src/features/auth-api.ts index 4e4d0ed..c927bfd 100644 --- a/extension/src/features/auth-api.ts +++ b/extension/src/features/auth-api.ts @@ -52,9 +52,9 @@ export const useAuthApi = (): IFeatureExport<AppSettings, UserApi> => { return { background: ({ state }:BgRuntime<AppSettings>): UserApi =>{ const { loggedIn, clearLoginState } = useSession(); - const { currentConfig } = state + const { currentConfig, serverEndpoints } = state const { logout, getProfile, heartbeat, userName } = useUser(); - const currentPkiPath = computed(() => `${currentConfig.value.accountBasePath}/pki`) + const currentPkiPath = computed(() => `${serverEndpoints.value.accountBasePath}/pki`) //Use pki login controls const pkiAuth = usePkiAuth(currentPkiPath as any) @@ -120,7 +120,7 @@ export const useAuthApi = (): IFeatureExport<AppSettings, UserApi> => { })() //Configure interval to run every 5 minutes to update the status - setInterval(runHeartbeat, 60 * 1000); + setInterval(runHeartbeat, 5 * 60 * 1000); delay(runHeartbeat, 1000) //Delay 1 second to allow the extension to load return { diff --git a/extension/src/features/history.ts b/extension/src/features/history.ts index e00a9af..f351269 100644 --- a/extension/src/features/history.ts +++ b/extension/src/features/history.ts @@ -73,6 +73,7 @@ export const useHistoryApi = () : IFeatureExport<AppSettings, HistoryApi> => { 'waitForChange', 'getEvents', 'deleteEvent', + 'refresh' ]) } } diff --git a/extension/src/features/nostr-api.ts b/extension/src/features/nostr-api.ts index 7f1bedf..81b63b9 100644 --- a/extension/src/features/nostr-api.ts +++ b/extension/src/features/nostr-api.ts @@ -18,8 +18,8 @@ import { Endpoints } from "./server-api"; import { type FeatureApi, type BgRuntime, type IFeatureExport, optionsOnly, exportForegroundApi } from "./framework"; import { type AppSettings } from "./settings"; import { EventTagFilterApi } from "./tagfilter-api"; -import type { NostrRelay, EncryptionRequest, NostrEvent } from './types'; import { HistoryApi } from "./history"; +import type { NostrRelay, EncryptionRequest, NostrEvent } from './types'; /** diff --git a/extension/src/features/permissions.ts b/extension/src/features/permissions.ts index 1ffe30e..6eaaacb 100644 --- a/extension/src/features/permissions.ts +++ b/extension/src/features/permissions.ts @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -import { Mutable, get, set, toRefs, useTimestamp } from "@vueuse/core"; +import { Mutable, get, set, toRefs } from "@vueuse/core"; import { Ref } from "vue"; import { defaultTo, defaults, defer, filter, find, forEach, isEqual, isNil } from "lodash"; import { nanoid } from "nanoid"; @@ -340,11 +340,8 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> = const ruleSet = useRuleSet(ruleStore) const permissions = usePermissions(reqStore, ruleSet) - //Computed current time to trigger an update every second - const currentTime = useTimestamp({ interval: 2000 }) - return { - waitForChange: waitForChangeFn([currentConfig, loggedIn, reqStore, ruleStore, currentTime]), + waitForChange: waitForChangeFn([currentConfig, loggedIn, reqStore, ruleStore]), getRequests: () => Promise.resolve(permissions.getAll()), diff --git a/extension/src/features/settings.ts b/extension/src/features/settings.ts index ad6c2f4..78ebe5f 100644 --- a/extension/src/features/settings.ts +++ b/extension/src/features/settings.ts @@ -50,7 +50,7 @@ export interface EndpointConfig extends JsonObject { } export interface ConfigStatus { - readonly EpConfig: EndpointConfig; + readonly epConfig: EndpointConfig; readonly isDarkMode: boolean; readonly isValid: boolean; } @@ -62,6 +62,7 @@ export interface AppSettings{ setDarkMode(darkMode: boolean): void; readonly status: Readonly<Ref<ConfigStatus>>; readonly currentConfig: Readonly<Ref<PluginConfig>>; + readonly serverEndpoints: Readonly<Ref<EndpointConfig>>; } export interface SettingsApi extends FeatureApi, Watchable { @@ -79,18 +80,9 @@ interface ServerDiscoveryResult{ }[] } -const discoverAndSetEndpoints = async (discoveryUrl: string, epConfig: Ref<EndpointConfig | undefined>) => { +const discoverNvaultServer = async (discoveryUrl: string): Promise<ServerDiscoveryResult> => { const res = await fetch(discoveryUrl) - const { endpoints } = await res.json() as ServerDiscoveryResult; - - const urls: EndpointConfig = { - apiBaseUrl: new URL(discoveryUrl).origin, - accountBasePath: find(endpoints, p => p.name == "account")?.path || "/account", - nostrBasePath: find(endpoints, p => p.name == "nostr")?.path || "/nostr", - }; - - //Set once the urls are discovered - set(epConfig, urls); + return await res.json() as ServerDiscoveryResult; } export const useAppSettings = (): AppSettings => { @@ -101,39 +93,52 @@ export const useAppSettings = (): AppSettings => { const endpointConfig = shallowRef<EndpointConfig>({nostrBasePath: '', accountBasePath: '', apiBaseUrl: ''}) const status = computed<ConfigStatus>(() => { - return{ - EpConfig: get(endpointConfig), + //get current endpoint config + const { nostrBasePath, accountBasePath } = get(endpointConfig); + return { + epConfig: get(endpointConfig), isDarkMode: get(_darkMode), - isValid: !isEmpty(get(endpointConfig).nostrBasePath) + isValid: !isEmpty(nostrBasePath) && !isEmpty(accountBasePath) } }) + const discoverAndSetEndpoints = async (discoveryUrl: string, epConfig: Ref<EndpointConfig | undefined>) => { + const { endpoints } = await discoverNvaultServer(discoveryUrl); + + const urls: EndpointConfig = { + apiBaseUrl: new URL(discoveryUrl).origin, + accountBasePath: find(endpoints, p => p.name == "account")?.path || "/account", + nostrBasePath: find(endpoints, p => p.name == "nostr")?.path || "/nostr", + }; + + //Set once the urls are discovered + set(epConfig, urls); + } + //Merge the default config for nullables with the current config on startyup defaultsDeep(store.value, defaultConfig); //Watch for changes to the discovery url, then cause a discovery watch([store], ([{ discoveryUrl }]) => { - defer(() => discoverAndSetEndpoints(discoveryUrl, endpointConfig)) - }, { immediate: true }) //alaways run on startup - - watch([endpointConfig], ([epconf]) => { - //Configure the vnlib api - configureApi({ - session: { - cookiesEnabled: false, - browserIdSize: 32, - }, - user: { - accountBasePath: epconf?.accountBasePath, - }, - axios: { - baseURL: epconf?.apiBaseUrl, - tokenHeader: import.meta.env.VITE_WEB_TOKEN_HEADER, - }, - storage: localStorage + defer(async () => { + await discoverAndSetEndpoints(discoveryUrl, endpointConfig) + const { accountBasePath, apiBaseUrl } = get(endpointConfig); + if(!isEmpty(accountBasePath) && !isEmpty(apiBaseUrl)){ + configureApi({ + session: { + cookiesEnabled: false, + browserIdSize: 32, + }, + user: { accountBasePath }, + axios: { + baseURL: apiBaseUrl, + tokenHeader: import.meta.env.VITE_WEB_TOKEN_HEADER, + }, + storage: localStorage + }) + } }) - - }, { deep: true }) + }) //Save the config and update the current config const saveConfig = (config: PluginConfig) => set(store, config); @@ -149,7 +154,8 @@ export const useAppSettings = (): AppSettings => { return useStorage<T>(_storageBackend, slot, defaultValue) }, useServerApi: () => serverApi, - setDarkMode: (darkMode: boolean) => set(_darkMode, darkMode) + setDarkMode: (darkMode: boolean) => set(_darkMode, darkMode), + serverEndpoints: readonly(endpointConfig) } } @@ -157,7 +163,6 @@ export const useSettingsApi = () : IFeatureExport<AppSettings, SettingsApi> =>{ return{ background: ({ state }: BgRuntime<AppSettings>) => { - return { waitForChange: waitForChangeFn([state.currentConfig, state.status]), getSiteConfig: () => Promise.resolve(state.currentConfig.value), @@ -177,12 +182,11 @@ export const useSettingsApi = () : IFeatureExport<AppSettings, SettingsApi> =>{ }), getStatus: () => { //Since value is computed it needs to be manually unwrapped - const { isDarkMode, isValid, EpConfig } = get(state.status); - return Promise.resolve({ isDarkMode, isValid, EpConfig }) + const { isDarkMode, isValid, epConfig } = get(state.status); + return Promise.resolve({ isDarkMode, isValid, epConfig }) }, testServerAddress: optionsOnly(async (url: string) => { - const res = await fetch(url); - const data = await res.json() as ServerDiscoveryResult; + const data = await discoverNvaultServer(url) return isArray(data?.endpoints) && !isEmpty(data.endpoints); }) } |