aboutsummaryrefslogtreecommitdiff
path: root/extension/src/entries/popup
diff options
context:
space:
mode:
Diffstat (limited to 'extension/src/entries/popup')
-rw-r--r--extension/src/entries/popup/App.vue22
-rw-r--r--extension/src/entries/popup/Components/IdentitySelection.vue46
-rw-r--r--extension/src/entries/popup/Components/Login.vue40
-rw-r--r--extension/src/entries/popup/Components/PageContent.vue104
-rw-r--r--extension/src/entries/popup/index.html11
-rw-r--r--extension/src/entries/popup/main.js32
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");