diff --git a/frontend/src/components/BudgetAmountSelect.tsx b/frontend/src/components/BudgetAmountSelect.tsx index 019c1cc1..1a284960 100644 --- a/frontend/src/components/BudgetAmountSelect.tsx +++ b/frontend/src/components/BudgetAmountSelect.tsx @@ -29,8 +29,7 @@ function BudgetAmountSelect({ className={cn( "cursor-pointer rounded text-nowrap border-2 text-center p-4 dark:text-white", !customBudget && - (Number.isNaN(value) ? 100000 : value) == - budgetOptions[budget] + (Number.isNaN(value) ? 0 : value) == budgetOptions[budget] ? "border-primary" : "border-muted" )} diff --git a/frontend/src/components/Permissions.tsx b/frontend/src/components/Permissions.tsx index 25d24ab1..58e2907b 100644 --- a/frontend/src/components/Permissions.tsx +++ b/frontend/src/components/Permissions.tsx @@ -1,5 +1,5 @@ import { PlusCircle } from "lucide-react"; -import React, { useState } from "react"; +import React from "react"; import BudgetAmountSelect from "src/components/BudgetAmountSelect"; import BudgetRenewalSelect from "src/components/BudgetRenewalSelect"; import ExpirySelect from "src/components/ExpirySelect"; @@ -20,7 +20,7 @@ interface PermissionsProps { capabilities: WalletCapabilities; initialPermissions: AppPermissions; onPermissionsChange: (permissions: AppPermissions) => void; - canEditPermissions: boolean; + canEditPermissions?: boolean; budgetUsage?: number; isNewConnection?: boolean; } @@ -34,118 +34,135 @@ const Permissions: React.FC = ({ budgetUsage, }) => { const [permissions, setPermissions] = React.useState(initialPermissions); - const [budgetOption, setBudgetOption] = useState( - isNewConnection ? !!permissions.maxAmount : true - ); - const [expireOption, setExpireOption] = useState( - isNewConnection ? !!permissions.expiresAt : true - ); + // this is triggered when edit mode is cancelled in show app React.useEffect(() => { setPermissions(initialPermissions); }, [initialPermissions]); - const handlePermissionsChange = ( - changedPermissions: Partial - ) => { - const updatedPermissions = { ...permissions, ...changedPermissions }; - setPermissions(updatedPermissions); - onPermissionsChange(updatedPermissions); - }; + const canEditBudgetAmount = isNewConnection + ? Number.isNaN(permissions.maxAmount) + : canEditPermissions; + const canEditExpiry = isNewConnection + ? !permissions.expiresAt + : canEditPermissions; + const [showBudgetOptions, setShowBudgetOptions] = React.useState( + isNewConnection ? !!permissions.maxAmount : true + ); + const [showExpiryOptions, setShowExpiryOptions] = React.useState( + isNewConnection ? !!permissions.expiresAt : true + ); - const handleScopeChange = (scopes: Set) => { - handlePermissionsChange({ scopes }); - }; + const handlePermissionsChange = React.useCallback( + (changedPermissions: Partial) => { + const updatedPermissions = { ...permissions, ...changedPermissions }; + setPermissions(updatedPermissions); + onPermissionsChange(updatedPermissions); + }, + [permissions, onPermissionsChange] + ); - const handleBudgetMaxAmountChange = (amount: number) => { - handlePermissionsChange({ maxAmount: amount }); - }; + const handleScopeChange = React.useCallback( + (scopes: Set) => { + handlePermissionsChange({ scopes }); + }, + [handlePermissionsChange] + ); - const handleBudgetRenewalChange = (value: string) => { - handlePermissionsChange({ budgetRenewal: value as BudgetRenewalType }); - }; + const handleBudgetMaxAmountChange = React.useCallback( + (amount: number) => { + handlePermissionsChange({ maxAmount: amount }); + }, + [handlePermissionsChange] + ); - const handleExpiryDaysChange = (expiryDays: number) => { - if (!expiryDays) { - handlePermissionsChange({ expiresAt: undefined }); - return; - } - const currentDate = new Date(); - currentDate.setDate(currentDate.getUTCDate() + expiryDays); - currentDate.setHours(23, 59, 59); - handlePermissionsChange({ expiresAt: currentDate }); - }; + const handleBudgetRenewalChange = React.useCallback( + (value: string) => { + handlePermissionsChange({ budgetRenewal: value as BudgetRenewalType }); + }, + [handlePermissionsChange] + ); - return !canEditPermissions ? ( - <> -

Scopes

-
- {[...initialPermissions.scopes].map((rm, index) => { - const PermissionIcon = iconMap[rm]; - return ( -
- -

{scopeDescriptions[rm]}

-
- ); - })} -
- {permissions.scopes.has(NIP_47_PAY_INVOICE_METHOD) && ( -
-
-

- Budget Renewal: {permissions.budgetRenewal || "Never"} -

-

- Budget Amount:{" "} - {permissions.maxAmount - ? new Intl.NumberFormat().format(permissions.maxAmount) - : "∞"} - {" sats "} - {`(${new Intl.NumberFormat().format(budgetUsage || 0)} sats used)`} -

+ const handleExpiryDaysChange = React.useCallback( + (expiryDays: number) => { + if (!expiryDays) { + handlePermissionsChange({ expiresAt: undefined }); + return; + } + const currentDate = new Date(); + currentDate.setDate(currentDate.getUTCDate() + expiryDays); + currentDate.setHours(23, 59, 59); + handlePermissionsChange({ expiresAt: currentDate }); + }, + [handlePermissionsChange] + ); + + return ( +
+ {canEditPermissions ? ( + + ) : ( + <> +

Scopes

+
+ {[...initialPermissions.scopes].map((rm) => { + const PermissionIcon = iconMap[rm]; + return ( +
+ +

{scopeDescriptions[rm]}

+
+ ); + })}
-
+ )} -
-

Connection expiry

-

- {permissions.expiresAt && - new Date(permissions.expiresAt).getFullYear() !== 1 - ? new Date(permissions.expiresAt).toString() - : "This app will never expire"} -

-
- - ) : ( -
- - {capabilities.scopes.includes(NIP_47_PAY_INVOICE_METHOD) && - permissions.scopes.has(NIP_47_PAY_INVOICE_METHOD) && ( + permissions.scopes.has(NIP_47_PAY_INVOICE_METHOD) && + (!canEditBudgetAmount ? ( +
+
+

+ Budget Renewal: {permissions.budgetRenewal || "Never"} +

+

+ Budget Amount:{" "} + {permissions.maxAmount + ? new Intl.NumberFormat().format(permissions.maxAmount) + : "∞"} + {" sats "} + {!isNewConnection && + `(${new Intl.NumberFormat().format(budgetUsage || 0)} sats used)`} +

+
+
+ ) : ( <> - {!budgetOption && ( + {!showBudgetOptions && ( )} - {budgetOption && ( + {showBudgetOptions && ( <>

Budget Renewal

@@ -162,25 +179,39 @@ const Permissions: React.FC = ({ )} - )} + ))} - {!expireOption && ( - - )} + {!canEditExpiry ? ( +
+

Connection expiry

+

+ {permissions.expiresAt && + new Date(permissions.expiresAt).getFullYear() !== 1 + ? new Date(permissions.expiresAt).toString() + : "This app will never expire"} +

+
+ ) : ( + <> + {!showExpiryOptions && ( + + )} - {expireOption && ( - + {showExpiryOptions && ( + + )} + )}
); diff --git a/frontend/src/screens/apps/NewApp.tsx b/frontend/src/screens/apps/NewApp.tsx index bcfc2aef..957a5159 100644 --- a/frontend/src/screens/apps/NewApp.tsx +++ b/frontend/src/screens/apps/NewApp.tsx @@ -159,7 +159,9 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => { const parseExpiresParam = (expiresParam: string): Date | undefined => { const expiresParamTimestamp = parseInt(expiresParam); if (!isNaN(expiresParamTimestamp)) { - return new Date(expiresParamTimestamp * 1000); + const expiry = new Date(expiresParamTimestamp * 1000); + expiry.setHours(23, 59, 59); + return expiry; } return undefined; }; @@ -271,7 +273,7 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => { capabilities={capabilities} initialPermissions={permissions} onPermissionsChange={setPermissions} - canEditPermissions={!reqMethodsParam} + canEditPermissions isNewConnection />