aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extension/src/entries/options/components/AutoRules.vue3
-rw-r--r--extension/src/entries/options/components/EventHistory.vue28
-rw-r--r--extension/src/entries/options/components/SiteSettings.vue4
-rw-r--r--extension/src/entries/popup/Components/PageContent.vue3
-rw-r--r--extension/src/entries/store/permissions.ts42
-rw-r--r--extension/src/features/auth-api.ts6
-rw-r--r--extension/src/features/history.ts1
-rw-r--r--extension/src/features/nostr-api.ts2
-rw-r--r--extension/src/features/permissions.ts7
-rw-r--r--extension/src/features/settings.ts86
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);
})
}