diff --git a/src/app/components/Badge/index.tsx b/src/app/components/Badge/index.tsx index 2fe880828a..277eb143b4 100644 --- a/src/app/components/Badge/index.tsx +++ b/src/app/components/Badge/index.tsx @@ -1,24 +1,39 @@ -import { useTranslation } from "react-i18next"; +import { PopiconsXLine } from "@popicons/react"; import { classNames } from "~/app/utils"; type Props = { - label: "budget" | "auth" | "imported"; + label: string; className: string; + onDelete?: () => void; + description?: string; }; -export default function Badge({ label, className }: Props) { - const { t: tComponents } = useTranslation("components", { - keyPrefix: "badge", - }); - +export default function Badge({ + label, + className, + onDelete, + description, +}: Props) { return ( - - {tComponents(`label.${label}`)} - + {label.toUpperCase()} + {onDelete && ( + + )} + ); } diff --git a/src/app/components/BadgesList/index.tsx b/src/app/components/BadgesList/index.tsx index 524121449d..f0b7694071 100644 --- a/src/app/components/BadgesList/index.tsx +++ b/src/app/components/BadgesList/index.tsx @@ -1,4 +1,5 @@ import Badge from "@components/Badge"; +import { useTranslation } from "react-i18next"; import type { Allowance, Badge as BadgeType } from "~/types"; type Props = { @@ -6,6 +7,9 @@ type Props = { }; export default function BadgesList({ allowance }: Props) { + const { t: tComponents } = useTranslation("components", { + keyPrefix: "badge", + }); const badges: BadgeType[] = []; if (allowance.remainingBudget > 0) { badges.push({ @@ -23,7 +27,13 @@ export default function BadgesList({ allowance }: Props) { return ( <> {badges?.map((b) => { - return ; + return ( + + ); })} ); diff --git a/src/app/components/Modal/index.tsx b/src/app/components/Modal/index.tsx index 7d9580fe51..100d484d78 100644 --- a/src/app/components/Modal/index.tsx +++ b/src/app/components/Modal/index.tsx @@ -36,7 +36,7 @@ export default function Modal({ style={{ content: { maxHeight: "80vh" } }} > {title && ( -

{title}

+

{title}

)} + diff --git a/src/common/constants.ts b/src/common/constants.ts index 2daff15ba6..79413ab2aa 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -7,6 +7,11 @@ export const ABORT_PROMPT_ERROR = "Prompt was closed"; export const USER_REJECTED_ERROR = "User rejected"; export const NO_KEYS_ERROR = "No keys available"; +// permissions constants +export const ASK_EVERYTIME = "ask_everytime"; +export const DONT_ASK_ANY = "dont_ask_any"; +export const DONT_ASK_CURRENT = "dont_ask_current"; + // Currently only relevant for connectors which provide stablecoins // all other connectors fall back to BTC export type ACCOUNT_CURRENCIES = "EUR" | "USD" | "BTC"; diff --git a/src/extension/background-script/actions/ln/__tests__/getBalance.test.ts b/src/extension/background-script/actions/ln/__tests__/getBalance.test.ts index 9f9b37a1d3..a69d997bba 100644 --- a/src/extension/background-script/actions/ln/__tests__/getBalance.test.ts +++ b/src/extension/background-script/actions/ln/__tests__/getBalance.test.ts @@ -123,7 +123,7 @@ describe("prompts the user first and then calls getBalance", () => { args: { requestPermission: { method: "getBalance", - description: "webln.getbalance", + description: "webln.getbalance.description", }, }, origin: message.origin, @@ -147,7 +147,7 @@ describe("prompts the user first and then calls getBalance", () => { args: { requestPermission: { method: "getBalance", - description: "webln.getbalance", + description: "webln.getbalance.description", }, }, origin: message.origin, diff --git a/src/extension/background-script/actions/nostr/signEventOrPrompt.ts b/src/extension/background-script/actions/nostr/signEventOrPrompt.ts index 3a43cb4617..3e557b1777 100644 --- a/src/extension/background-script/actions/nostr/signEventOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/signEventOrPrompt.ts @@ -6,6 +6,7 @@ import { } from "~/extension/background-script/permissions"; import { MessageSignEvent, PermissionMethodNostr, Sender } from "~/types"; +import { DONT_ASK_ANY, DONT_ASK_CURRENT } from "~/common/constants"; import state from "../../state"; import { validateEvent } from "./helpers"; @@ -25,21 +26,33 @@ const signEventOrPrompt = async (message: MessageSignEvent, sender: Sender) => { }; } - const hasPermission = await hasPermissionFor( - PermissionMethodNostr["NOSTR_SIGNMESSAGE"], - host - ); + const hasPermission = + (await hasPermissionFor( + PermissionMethodNostr["NOSTR_SIGNMESSAGE"], + host + )) || + (await hasPermissionFor( + PermissionMethodNostr["NOSTR_SIGNMESSAGE"] + "/" + event.kind, + host + )); if (!hasPermission) { const promptResponse = await utils.openPrompt<{ - enabled: boolean; blocked: boolean; + permissionOption: string; }>({ ...message, action: "public/nostr/confirmSignMessage", }); // add permission to db only if user decided to always allow this request - if (promptResponse.data.enabled) { + if (promptResponse.data.permissionOption == DONT_ASK_CURRENT) { + await addPermissionFor( + PermissionMethodNostr["NOSTR_SIGNMESSAGE"] + "/" + event.kind, + host + ); + } + + if (promptResponse.data.permissionOption == DONT_ASK_ANY) { await addPermissionFor( PermissionMethodNostr["NOSTR_SIGNMESSAGE"], host diff --git a/src/extension/background-script/actions/webln/getBalanceOrPrompt.ts b/src/extension/background-script/actions/webln/getBalanceOrPrompt.ts index cd7987cbef..bc9c67d8ea 100644 --- a/src/extension/background-script/actions/webln/getBalanceOrPrompt.ts +++ b/src/extension/background-script/actions/webln/getBalanceOrPrompt.ts @@ -43,7 +43,7 @@ const getBalanceOrPrompt = async (message: MessageDefault) => { args: { requestPermission: { method: "getBalance", - description: `webln.getbalance`, + description: `webln.getbalance.description`, }, }, origin: message.origin, diff --git a/src/extension/background-script/permissions/addPermissionFor.ts b/src/extension/background-script/permissions/addPermissionFor.ts index 9fdef71f00..94dcab3fc8 100644 --- a/src/extension/background-script/permissions/addPermissionFor.ts +++ b/src/extension/background-script/permissions/addPermissionFor.ts @@ -10,15 +10,34 @@ export async function addPermissionFor(method: string, host: string) { if (!allowance?.id || !accountId) { return false; } - const permissionIsAdded = await db.permissions.add({ - createdAt: Date.now().toString(), - accountId: accountId, - allowanceId: allowance.id, - host: host, - method: method, - enabled: true, - blocked: false, - }); - return !!permissionIsAdded && (await db.saveToStorage()); + const existingPermission = await db.permissions + .filter( + (x) => + x.accountId === accountId && + x.allowanceId === allowance.id && + x.host === host && + x.method === method + ) + .first(); + + let affectedRows = 0; + if (!existingPermission) { + affectedRows = await db.permissions.add({ + createdAt: Date.now().toString(), + accountId: accountId, + allowanceId: allowance.id, + host: host, + method: method, + enabled: true, + blocked: false, + }); + } else { + affectedRows = await db.permissions.update(existingPermission.id!, { + enabled: true, + blocked: false, + }); + } + + return !!affectedRows && (await db.saveToStorage()); } diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index fe97155fa2..523cf5a769 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -922,30 +922,102 @@ "block_added": "Added {{host}} to the blocklist, please reload the website.", "recipient": "Recipient", "kinds": { - "unknown": "kind {{kind}}", - "0": "metadata", - "1": "short text note", - "2": "recommend relay", - "3": "contacts", - "4": "encrypted direct messages", - "5": "event deletion", - "7": "reaction", - "8": "badge award", - "40": "channel creation", - "41": "channel metadata", - "42": "channel message", - "43": "channel hide message", - "44": "channel mute user", - "1984": "reporting", - "9734": "zap request", - "9735": "zap", - "10002": "relay list metadata", - "22242": "client authentication", - "24133": "nostr connect", - "30008": "profile badges", - "30009": "badge definition", - "30023": "long-form content", - "30078": "application-specific data" + "unknown": { + "title": "kind {{kind}}", + "description": "Unidentified action" + }, + "0": { + "title": "update profile", + "description": "Update your name, description, profile picture, Nostr address and additional profile metadata." + }, + "1": { + "title": "short note", + "description": "Sign a short text note" + }, + "2": { + "title": "recommend relay", + "description": "Share a recommended relay" + }, + "3": { + "title": "update follow list", + "description": "Update the list of accounts you follow" + }, + "4": { + "title": "direct message", + "description": "Send an encrypted direct message" + }, + "5": { + "title": "delete note", + "description": "Request relays to delete an event" + }, + "7": { + "title": "react", + "description": "Sign a reaction to a note" + }, + "8": { + "title": "award badge", + "description": "Award a badge to a Nostr profile" + }, + "40": { + "title": "create channel", + "description": "Create a public channel chat" + }, + "41": { + "title": "update channel", + "description": "Update channel's name, description, picture and additional metadata" + }, + "42": { + "title": "send channel message", + "description": "Send a text message to a channel" + }, + "43": { + "title": "hide channel message", + "description": "Hide a text message in a channel" + }, + "44": { + "title": "mute channel user", + "description": "Hide all messages from a Nostr profile" + }, + "1984": { + "title": "report note", + "description": "Report a note for spam, illegal or explicit content " + }, + "9734": { + "title": "request zap", + "description": "Request a lightning payment" + }, + "9735": { + "title": "zap receipt", + "description": "Display a confirmation of a paid lightning invoice" + }, + "10002": { + "title": "relay list", + "description": "Update preferred relays for discovering your notes and receiving others' notes" + }, + "22242": { + "title": "authenticate", + "description": "Authenticate to a relay" + }, + "24133": { + "title": "remote sign", + "description": "Request and sign events remotely" + }, + "30008": { + "title": "profile badge", + "description": "Accept, reject and change display order of awarded badges" + }, + "30009": { + "title": "create badge", + "description": "Create a new badge" + }, + "30023": { + "title": "long note", + "description": "Sign a long-form notes like articles or blog posts" + }, + "30078": { + "title": "app data", + "description": "Sign an application-specific data" + } } }, "transactions": { @@ -1062,7 +1134,7 @@ "components": { "allowance_menu": { "confirm_delete": "Are you sure you want to disconnect this website?", - "hint": "Changing this will reset the current budget", + "hint": "Changing this number will reset the current budget", "new_budget": { "label": "One-click payments budget", "link_label": "Set a budget for one-click payments" @@ -1073,9 +1145,10 @@ }, "edit_allowance": { "title": "Site settings", - "screen_reader": "Allowance Options" + "screen_reader": "Allowance Options", + "always_allow": "Always allow" }, - "edit_permissions": "Edit Permissions" + "website_permissions": "Website Permissions" }, "qrcode_scanner": { "title": "Scan QR Code", @@ -1087,6 +1160,13 @@ "allow_camera_access": "Please allow camera access in the settings screen." } }, + "permissions_modal": { + "content_label": "Permissions", + "set_permissions": "Set Permissions:", + "ask_everytime": "Ask everytime for <0>{{permission}}", + "dont_ask_current": "Don't ask again for <0>{{permission}}", + "dont_ask_any": "Don't ask again for <0>any Nostr Requests" + }, "transactions_table": { "fee": "Fee", "preimage": "Preimage", @@ -1160,30 +1240,57 @@ }, "badge": { "label": { - "auth": "LOGIN", - "imported": "IMPORTED", - "budget": "BUDGET" + "auth": "login", + "imported": "imported", + "budget": "budget" } } }, "permissions": { "webln": { - "getbalance": "Read the balance of your account" + "getbalance": { + "title": "get balance", + "description": "Read the balance of your account" + } }, "bitcoin": { - "getaddress": "Read your Bitcoin receive address" + "getaddress": { + "title": "get address", + "description": "Read your Bitcoin receive address" + } }, "liquid": { - "getaddress": "Read your Liquid receive address", - "signschnorr": "Sign message with your key" + "getaddress": { + "title": "get address", + "description": "Read your Liquid receive address" + }, + "signschnorr": { + "title": "sign schnorr", + "description": "Sign message with your key" + } }, "nostr": { - "getpublickey": "Read your public key", - "nip04encrypt": "Encrypt data", - "nip04decrypt": "Decrypt data", - "nip44encrypt": "Encrypt data", - "nip44decrypt": "Decrypt data", - "signmessage": "Sign message with your key" + "getpublickey": { + "title": "read public key", + "description": "Read your public key" + }, + "decrypt": "Decrypt data", + "nip04decrypt": { + "title": "decrypt (nip04)", + "description": "Decrypt data" + }, + "nip04encrypt": { + "title": "encrypt (nip04)", + "description": "Encrypt data" + }, + "nip44decrypt": { + "title": "decrypt (nip44)", + "description": "Decrypt data" + }, + "nip44encrypt": { + "title": "encrypt (nip44)", + "description": "Encrypt data" + } }, "commando": { "bkpr-listbalances": "List of all current and historical account balances", diff --git a/src/types.ts b/src/types.ts index 8e2737ebe5..dceae6c02b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -779,6 +779,12 @@ export interface Payment extends Omit { id: number; } +export enum PermissionOption { + ASK_EVERYTIME = "ask_everytime", + DONT_ASK_CURRENT = "dont_ask_current", + DONT_ASK_ANY = "dont_ask_any", +} + export enum PermissionMethodBitcoin { BITCOIN_GETADDRESS = "bitcoin/getAddress", }