diff options
Diffstat (limited to 'extension/src/entries/popup')
-rw-r--r-- | extension/src/entries/popup/App.vue | 22 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/IdentitySelection.vue | 46 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/Login.vue | 40 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/PageContent.vue | 104 | ||||
-rw-r--r-- | extension/src/entries/popup/index.html | 11 | ||||
-rw-r--r-- | extension/src/entries/popup/main.js | 32 |
6 files changed, 255 insertions, 0 deletions
diff --git a/extension/src/entries/popup/App.vue b/extension/src/entries/popup/App.vue new file mode 100644 index 0000000..0181bbb --- /dev/null +++ b/extension/src/entries/popup/App.vue @@ -0,0 +1,22 @@ +<template> + <main> + <PageContent /> + </main> +</template> + +<script setup lang="ts"> +import PageContent from "./components/PageContent.vue"; + +</script> + +<style lang="scss"> +main { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-align: center; + color: #2c3e50; + + @apply dark:bg-dark-800 bg-white dark:text-gray-200; +} +</style> diff --git a/extension/src/entries/popup/Components/IdentitySelection.vue b/extension/src/entries/popup/Components/IdentitySelection.vue new file mode 100644 index 0000000..d95dedb --- /dev/null +++ b/extension/src/entries/popup/Components/IdentitySelection.vue @@ -0,0 +1,46 @@ +<template> + <div class="px-3 text-left"> + <div class="w-full"> + <div class=""> + <select class="w-full primary" + :disabled="waiting" + :value="selected?.Id" + @change.prevent="onSelected" + > + <option disabled value="">Select an identity</option> + <option v-for="key in allKeys" :value="key.Id">{{ key.UserName }}</option> + </select> + </div> + </div> + + </div> +</template> + +<script setup lang="ts"> +import { find } from 'lodash' +import { computed } from "vue"; +import { useStatus, useManagment, NostrPubKey } from "~/bg-api/popup.ts"; +import { useWait } from '@vnuge/vnlib.browser' +import { computedAsync } from '@vueuse/core'; + +const { selectedKey } = useStatus(); +const { waiting } = useWait(); +const { getAllKeys, selectKey } = useManagment(); + +const allKeys = computedAsync<NostrPubKey[]>(async () => await getAllKeys(), []); + +const onSelected = async ({target}) =>{ + //Select the key of the given id + const selected = find(allKeys.value, {Id: target.value}) + if(selected){ + await selectKey(selected) + } +} + +const selected = computed(() => selectedKey?.value || { Id:"0" }) + +</script> + +<style lang="scss"> + +</style>
\ No newline at end of file diff --git a/extension/src/entries/popup/Components/Login.vue b/extension/src/entries/popup/Components/Login.vue new file mode 100644 index 0000000..495b64e --- /dev/null +++ b/extension/src/entries/popup/Components/Login.vue @@ -0,0 +1,40 @@ +<template> + <div id="login-template" class="py-4"> + <form class="" @submit.prevent="onSubmit"> + <fieldset class="px-4 input-container"> + <label class="">Please enter your authentication token</label> + <textarea class="w-full primary" v-model="token" rows="5"> + </textarea> + </fieldset> + <div class="flex justify-end mt-2"> + <div class="px-3"> + <button class="w-24 rounded btn sm primary"> + <fa-icon v-if="waiting" icon="spinner" class="animate-spin" /> + <span v-else>Submit</span> + </button> + </div> + </div> + </form> + </div> +</template> + +<script setup lang="ts"> +import { useWait } from "@vnuge/vnlib.browser"; +import { ref } from "vue"; +import { useManagment } from "~/bg-api/popup.ts"; + +const { login } = useManagment() +const { waiting } = useWait() + +const token = ref('') + +const onSubmit = async () => { + //console.log(token.value) + await login(token.value) +} + +</script> + +<style lang="scss"> + +</style>
\ No newline at end of file diff --git a/extension/src/entries/popup/Components/PageContent.vue b/extension/src/entries/popup/Components/PageContent.vue new file mode 100644 index 0000000..c9b2d5f --- /dev/null +++ b/extension/src/entries/popup/Components/PageContent.vue @@ -0,0 +1,104 @@ +<template> + <div + id="injected-root" + class="flex flex-col text-left w-[20rem] min-h-[25rem]" + > + + <div class="flex flex-row w-full px-1 pl-4"> + <div class="flex-auto my-auto font-mono text-sm"> + A nostr credential vault + </div> + <div class="my-auto" v-if="loggedIn"> + <button class="rounded btn sm red" @click.prevent="logout"> + <fa-icon icon="arrow-right-from-bracket" /> + </button> + </div> + <div class="p-2 my-auto"> + <button class="rounded btn sm" @click="openOptions"> + <fa-icon :icon="['fas', 'gear']"/> + </button> + </div> + </div> + <div v-if="!loggedIn"> + <Login></Login> + </div> + <div v-else class="flex justify-center pb-4"> + <div class="w-full m-auto"> + <div class="mt-2 text-center"> + {{ userName }} + <div class="mt-4"> + <IdentitySelection></IdentitySelection> + </div> + <div class="mt-2.5 min-h-[6rem]"> + <div class="flex flex-col justify-center"> + + <div class="flex flex-row gap-2 p-2 mx-3 my-3 bg-gray-100 border border-gray-200 rounded dark:bg-dark-700 dark:border-dark-400"> + <div class="text-sm break-all"> + {{ pubKey ?? 'No key selected' }} + </div> + <div class="my-auto ml-auto cursor-pointer" :class="{'text-primary-500': copied }"> + <fa-icon class="mr-1" icon="copy" @click="copy(pubKey)"/> + </div> + </div> + + </div> + </div> + <div class="mt-3 text-sm"> + Always on NIP-07: <span class="font-semibold" :class="{'text-blue-500':autoInject}">{{ autoInject }}</span> + </div> + </div> + </div> + </div> + + <notifications class="toaster" group="form" position="top-right" /> + + </div> +</template> + +<script setup lang="ts"> +import { computed, watchEffect } from "vue"; +import { useStatus, useManagment } from "~/bg-api/popup.ts"; +import { configureNotifier } from "@vnuge/vnlib.browser"; +import { asyncComputed, useClipboard, watchDebounced } from '@vueuse/core' +import { notify } from "@kyvg/vue3-notification"; +import { runtime } from "webextension-polyfill"; +import Login from "./Login.vue"; +import IdentitySelection from "./IdentitySelection.vue"; + +configureNotifier({notify, close:notify.close}) + +const { loggedIn, userName, selectedKey, darkMode } = useStatus() +const { logout, getProfile, getSiteConfig } = useManagment() + +const { copy, copied } = useClipboard() + +const pubKey = computed(() => selectedKey.value?.PublicKey) +const qrCode = computed(() => pubKey.value ? `nostr:npub1${pubKey.value}` : null) + +watchDebounced(loggedIn, async () => { + //Manually update the user's profile if they are logged in and the profile is not yet loaded + if(loggedIn.value && !userName.value){ + getProfile() + } +},{ debounce:100, immediate: true }) + +const openOptions = () => runtime.openOptionsPage(); + +//Watch for dark mode changes and update the body class +watchEffect(() => darkMode.value ? document.body.classList.add('dark') : document.body.classList.remove('dark')); + +const autoInject = asyncComputed(() => getSiteConfig().then<Boolean>(p => p.autoInject), false) + +</script> + +<style lang="scss"> + +.toaster{ + position: fixed; + top: 15px; + right: 0; + z-index: 9999; + max-width: 230px; +} + +</style> diff --git a/extension/src/entries/popup/index.html b/extension/src/entries/popup/index.html new file mode 100644 index 0000000..8ffe33b --- /dev/null +++ b/extension/src/entries/popup/index.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <title>Popup</title> + </head> + <body style="min-width: 100px"> + <div id="app"></div> + <script type="module" src="./main.js"></script> + </body> +</html> diff --git a/extension/src/entries/popup/main.js b/extension/src/entries/popup/main.js new file mode 100644 index 0000000..d9101ab --- /dev/null +++ b/extension/src/entries/popup/main.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 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 +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// 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 { createApp } from "vue"; +import App from "./App.vue"; +import Notifications from "@kyvg/vue3-notification"; +import '@fontsource/noto-sans-masaram-gondi' +import "~/assets/tailwind.scss"; + +/* FONT AWESOME CONFIG */ +import { library } from '@fortawesome/fontawesome-svg-core' +import { faArrowRightFromBracket, faCopy, faEdit, faGear, faSpinner } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' + +library.add(faSpinner, faEdit, faGear, faCopy, faArrowRightFromBracket) + +createApp(App) + .use(Notifications) + .component('fa-icon', FontAwesomeIcon) + .mount("#app"); |