aboutsummaryrefslogtreecommitdiff
path: root/extension/src/entries/options
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-01-07 20:39:18 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2024-01-07 20:39:18 -0500
commitc438ee90e3be4e5e01ae3d045d6b841a03bd46eb (patch)
tree41c0b2ee815ce979e2af0e79f8fde9b58f5f4627 /extension/src/entries/options
parente87c4b69036e32b4fcf3df89e8158fb52df6a4e0 (diff)
losts of package updates & permissionsHEADmaster
Diffstat (limited to 'extension/src/entries/options')
-rw-r--r--extension/src/entries/options/App.vue26
-rw-r--r--extension/src/entries/options/components/AutoRules.vue119
-rw-r--r--extension/src/entries/options/components/EvHistoryTable.vue84
-rw-r--r--extension/src/entries/options/components/EventHistory.vue132
-rw-r--r--extension/src/entries/options/main.js9
5 files changed, 356 insertions, 14 deletions
diff --git a/extension/src/entries/options/App.vue b/extension/src/entries/options/App.vue
index d1da48e..3dbe94d 100644
--- a/extension/src/entries/options/App.vue
+++ b/extension/src/entries/options/App.vue
@@ -26,6 +26,11 @@
</Tab>
<Tab v-slot="{ selected }">
<button class="tab-title" :class="{ selected }">
+ Activity
+ </button>
+ </Tab>
+ <Tab v-slot="{ selected }">
+ <button class="tab-title" :class="{ selected }">
Privacy
</button>
</Tab>
@@ -64,15 +69,15 @@
<TabPanel class="mt-4">
<Identities :all-keys="allKeys" @edit-key="editKey"/>
</TabPanel>
- <TabPanel>
- <Account/>
- </TabPanel>
- <TabPanel>
- <Privacy/>
- </TabPanel>
- <TabPanel>
- <SiteSettings/>
- </TabPanel>
+
+ <TabPanel> <Account/> </TabPanel>
+
+ <TabPanel> <EventHistory/> </TabPanel>
+
+ <TabPanel> <Privacy/> </TabPanel>
+
+ <TabPanel> <SiteSettings/> </TabPanel>
+
<TabPanel>
<div class="flex flex-col px-2 mt-4">
<div class="absolute mx-auto">
@@ -130,6 +135,7 @@ import { useStore } from "../store";
import Account from "./components/Account.vue";
import ConfirmPrompt from "../../components/ConfirmPrompt.vue";
import PasswordPrompt from "../../components/PasswordPrompt.vue";
+import EventHistory from "./components/EventHistory.vue";
//Configure the notifier to use the notification library
@@ -143,7 +149,7 @@ const keyBuffer = ref<NostrPubKey>({} as NostrPubKey)
const editKey = (key: NostrPubKey) =>{
//Goto hidden tab
- selectedTab.value = 4
+ selectedTab.value = 5
//Set selected key
keyBuffer.value = { ...key }
}
diff --git a/extension/src/entries/options/components/AutoRules.vue b/extension/src/entries/options/components/AutoRules.vue
new file mode 100644
index 0000000..16fddd3
--- /dev/null
+++ b/extension/src/entries/options/components/AutoRules.vue
@@ -0,0 +1,119 @@
+<template>
+ <div class="">
+ <div class="flex flex-row justify-between mt-16">
+ <div class="font-bold">
+ Approval Rules
+ </div>
+ <div class="flex justify-center">
+ <nav aria-label="Pagination">
+ <ul class="inline-flex items-center space-x-1 text-sm rounded-md">
+ <li>
+ <button @click="prev" class="page-btn">
+ <fa-icon icon="chevron-left" class="w-4" />
+ </button>
+ </li>
+ <li>
+ <span class="inline-flex items-center px-4 py-2 space-x-1 rounded-md">
+ Page
+ <b class="mx-1">{{ currentPage }}</b>
+ of
+ <b class="ml-1">{{ pageCount }}</b>
+ </span>
+ </li>
+ <li>
+ <button @click="next" class="page-btn">
+ <fa-icon icon="chevron-right" class="w-4" />
+ </button>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </div>
+ <div class="">
+ <table class="min-w-full text-sm divide-y-2 divide-gray-200 dark:divide-dark-500">
+ <thead class="text-left bg-gray-50 dark:bg-dark-700">
+ <tr>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Rule
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Origin
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Time
+ </th>
+ <th class="p-2"></th>
+ </tr>
+ </thead>
+
+ <tbody class="divide-y divide-gray-200 dark:divide-dark-500">
+ <tr v-for="rule in currentRulePage" :key="rule.timestamp" class="">
+ <td class="p-2 t font-medium truncate max-w-[8rem] whitespace-nowrap ">
+ {{ rule.type }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ rule.origin }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ createShortDateAndTime(rule) }}
+ </td>
+ <td class="p-2 text-right whitespace-nowrap">
+ <div class="button-group">
+ <button class="rounded btn xs" @click="deleteRule(rule)">
+ Revoke
+ </button>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue';
+import { get, useOffsetPagination } from '@vueuse/core';
+import { } from '@headlessui/vue'
+import { useStore } from '../../store';
+import { storeToRefs } from 'pinia';
+import { slice } from 'lodash';
+import { type AutoAllowRule } from '../../../features'
+
+const store = useStore()
+const { } = storeToRefs(store)
+
+const rules = computed(() => store.permissions.rules)
+
+const { next, prev, currentPage, currentPageSize, pageCount } = useOffsetPagination({
+ pageSize: 10,
+ total: computed(() => rules.value.length)
+})
+
+const currentRulePage = computed(() => {
+ const start = (get(currentPage) - 1) * get(currentPageSize)
+ const end = start + 10
+ return slice(rules.value, start, end)
+})
+
+const deleteRule = (rule: AutoAllowRule) => {
+ store.plugins.permission.deleteRule(rule)
+}
+
+const createShortDateAndTime = (request: { timestamp: number}) => {
+ const date = new Date(request.timestamp)
+ const hours = date.getHours()
+ const minutes = date.getMinutes()
+ const seconds = date.getSeconds()
+ const day = date.getDate()
+ const month = date.getMonth() + 1
+ const year = date.getFullYear()
+ return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`
+}
+
+
+</script>
+
+<style lang="scss">
+
+</style> \ No newline at end of file
diff --git a/extension/src/entries/options/components/EvHistoryTable.vue b/extension/src/entries/options/components/EvHistoryTable.vue
new file mode 100644
index 0000000..6ea6cac
--- /dev/null
+++ b/extension/src/entries/options/components/EvHistoryTable.vue
@@ -0,0 +1,84 @@
+<template>
+ <table class="min-w-full divide-y-2 divide-gray-200 dark:divide-dark-500">
+ <thead class="text-left bg-gray-50 dark:bg-dark-700">
+ <tr>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Type
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Origin
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Time
+ </th>
+ <th class="p-2"></th>
+ </tr>
+ </thead>
+
+ <tbody class="divide-y divide-gray-200 dark:divide-dark-500">
+ <tr v-for="req in requests" :key="req.uuid" class="">
+ <td class="p-2 t font-medium truncate max-w-[8rem] whitespace-nowrap ">
+ {{ req.requestType }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ req.origin }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ createShortDateAndTime(req) }}
+ </td>
+ <td class="p-2 text-right whitespace-nowrap">
+ <div v-if="!readonly" class="button-group">
+ <button class="rounded btn xs" @click="approve(req)">
+ <fa-icon icon="check" class="inline" />
+ </button>
+ <button class="rounded btn red xs" @click="deny(req)">
+ <fa-icon icon="trash-can" class="inline" />
+ </button>
+ </div>
+ <div v-else class="text-sm font-bold">
+ {{ statusToString(req.status) }}
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</template>
+
+<script setup lang="ts">
+import { toRefs } from 'vue';
+import { PermissionRequest, PrStatus } from '../../../features';
+
+const emit = defineEmits(['deny', 'approve'])
+const props = defineProps<{
+ requests: PermissionRequest[],
+ readonly: boolean
+}>()
+
+const { requests, readonly } = toRefs(props)
+
+const createShortDateAndTime = (request: PermissionRequest) => {
+ const date = new Date(request.timestamp)
+ const hours = date.getHours()
+ const minutes = date.getMinutes()
+ const seconds = date.getSeconds()
+ const day = date.getDate()
+ const month = date.getMonth() + 1
+ const year = date.getFullYear()
+ return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`
+}
+
+const deny = (request: PermissionRequest) => emit('deny', request)
+const approve = (request: PermissionRequest) => emit('approve', request)
+
+const statusToString = (status: PrStatus) => {
+ switch(status) {
+ case PrStatus.Approved:
+ return 'Approved'
+ case PrStatus.Denied:
+ return 'Denied'
+ case PrStatus.Pending:
+ return 'Pending'
+ }
+}
+
+</script> \ No newline at end of file
diff --git a/extension/src/entries/options/components/EventHistory.vue b/extension/src/entries/options/components/EventHistory.vue
new file mode 100644
index 0000000..0711ae6
--- /dev/null
+++ b/extension/src/entries/options/components/EventHistory.vue
@@ -0,0 +1,132 @@
+<template>
+ <div id="ev-history" class="flex flex-col w-full mt-4 sm:px-2">
+ <form @submit.prevent="">
+ <div class="w-full max-w-xl mx-auto">
+ <h3 class="text-center">
+ Permissions
+ </h3>
+
+ <div class="flex flex-row justify-between mt-4">
+ <div class="font-bold">
+ Pending
+ </div>
+ <div class="flex justify-center">
+ </div>
+ </div>
+
+ <div class="my-6 ">
+ <EvHistoryTable :readonly="false" :requests="pending" @deny="deny" @approve="approve" />
+ </div>
+
+ <AutoRules />
+
+ <div class="flex flex-row justify-between mt-16">
+ <div class="font-bold">
+ History
+ </div>
+ <div class="flex justify-center">
+ <nav aria-label="Pagination">
+ <ul class="inline-flex items-center space-x-1 text-sm rounded-md">
+ <li>
+ <button @click="prev" class="page-btn">
+ <fa-icon icon="chevron-left" class="w-4" />
+ </button>
+ </li>
+ <li>
+ <span class="inline-flex items-center px-4 py-2 space-x-1 rounded-md">
+ Page
+ <b class="mx-1">{{ currentPage }}</b>
+ of
+ <b class="ml-1">{{ pageCount }}</b>
+ </span>
+ </li>
+ <li>
+ <button @click="next" class="page-btn">
+ <fa-icon icon="chevron-right" class="w-4" />
+ </button>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </div>
+
+ <div class="mt-1">
+ <EvHistoryTable :readonly="true" :requests="evHistoryCurrentPage" @deny="deny" @approve="approve" />
+ </div>
+
+ <div class="mt-4 ml-auto w-fit">
+ <button class="rounded btn sm red" @click="clearHistory">
+ Delete History
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { useConfirm } from '@vnuge/vnlib.browser';
+import { computed } from 'vue';
+import { get, useOffsetPagination } from '@vueuse/core';
+import { } from '@headlessui/vue'
+import { useStore } from '../../store';
+import { PermissionRequest, PrStatus } from '../../../features';
+import EvHistoryTable from './EvHistoryTable.vue';
+import { filter, slice } from 'lodash';
+import AutoRules from './AutoRules.vue';
+
+const store = useStore()
+const { reveal } = useConfirm()
+
+const pending = computed(() => store.permissions.pending)
+const notPending = computed(() => filter(store.permissions.all, r => r.status !== PrStatus.Pending))
+
+const deny = (request: PermissionRequest) => {
+ if(request.status !== PrStatus.Pending) return
+ //push deny to store
+ store.plugins.permission.deny(request.uuid)
+}
+
+const approve = (request: PermissionRequest) => {
+ if(request.status !== PrStatus.Pending) return
+ //push allow to store
+ store.plugins.permission.allow(request.uuid, false)
+}
+
+const { next, prev, currentPage, currentPageSize, pageCount } = useOffsetPagination({
+ pageSize: 10,
+ total: computed(() => notPending.value.length)
+})
+
+const evHistoryCurrentPage = computed(() => {
+ const start = (get(currentPage) - 1) * get(currentPageSize)
+ const end = start + 10
+ return slice(notPending.value, start, end)
+})
+
+const clearHistory = async () => {
+ const { isCanceled } = await reveal({
+ title: 'Clear History',
+ text: 'Are you sure you want to clear your event history?',
+ })
+
+ if(isCanceled) return
+
+ //Clear all history
+ store.plugins.permission.clearRequests()
+}
+
+</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;
+ }
+}
+</style> \ No newline at end of file
diff --git a/extension/src/entries/options/main.js b/extension/src/entries/options/main.js
index 7747735..3dd01cb 100644
--- a/extension/src/entries/options/main.js
+++ b/extension/src/entries/options/main.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -22,12 +22,12 @@ import Notifications from "@kyvg/vue3-notification";
/* FONT AWESOME CONFIG */
import { library } from '@fortawesome/fontawesome-svg-core'
-import { faChevronLeft, faChevronRight, faCopy, faDownload, faEdit, faExternalLinkAlt, faLock, faLockOpen, faMinusCircle, faMoon, faPlus, faRefresh, faSun, faTrash, faTrashCan } from '@fortawesome/free-solid-svg-icons'
+import { faCheck, faChevronLeft, faChevronRight, faCopy, faDownload, faEdit, faExternalLinkAlt, faLock, faLockOpen, faMinusCircle, faMoon, faPlus, faRefresh, faSun, faTrash, faTrashCan } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { createPinia } from "pinia";
-import { identityPlugin, mfaConfigPlugin, originPlugin, useBackgroundPiniaPlugin } from "../store";
+import { identityPlugin, mfaConfigPlugin, originPlugin, permissionsPlugin, useBackgroundPiniaPlugin } from "../store";
-library.add(faCopy, faEdit, faChevronLeft, faMoon, faSun, faLock, faLockOpen, faExternalLinkAlt, faTrash, faDownload, faChevronRight, faPlus, faRefresh, faTrashCan, faMinusCircle)
+library.add(faCopy, faEdit, faChevronLeft, faMoon, faSun, faLock, faLockOpen, faExternalLinkAlt, faTrash, faDownload, faChevronRight, faPlus, faRefresh, faTrashCan, faMinusCircle ,faTrashCan, faCheck)
//Create the background feature wiring
const bgPlugins = useBackgroundPiniaPlugin('options')
@@ -37,6 +37,7 @@ const pinia = createPinia()
.use(identityPlugin) //Add the identity plugin
.use(originPlugin) //Add the origin plugin
.use(mfaConfigPlugin) //Add the mfa config plugin
+ .use(permissionsPlugin)
createApp(App)
.use(Notifications)