diff options
author | vnugent <public@vaughnnugent.com> | 2024-01-07 20:39:18 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-01-07 20:39:18 -0500 |
commit | c438ee90e3be4e5e01ae3d045d6b841a03bd46eb (patch) | |
tree | 41c0b2ee815ce979e2af0e79f8fde9b58f5f4627 /extension/src/entries/options | |
parent | e87c4b69036e32b4fcf3df89e8158fb52df6a4e0 (diff) |
Diffstat (limited to 'extension/src/entries/options')
-rw-r--r-- | extension/src/entries/options/App.vue | 26 | ||||
-rw-r--r-- | extension/src/entries/options/components/AutoRules.vue | 119 | ||||
-rw-r--r-- | extension/src/entries/options/components/EvHistoryTable.vue | 84 | ||||
-rw-r--r-- | extension/src/entries/options/components/EventHistory.vue | 132 | ||||
-rw-r--r-- | extension/src/entries/options/main.js | 9 |
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) |