Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
fix: update frontend to use scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Jun 30, 2024
1 parent 7d1fefb commit 6770704
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 107 deletions.
56 changes: 10 additions & 46 deletions frontend/src/components/Permissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import { cn } from "src/lib/utils";
import {
AppPermissions,
BudgetRenewalType,
Nip47NotificationType,
Nip47RequestMethod,
Scope,
budgetOptions,
expiryOptions,
Expand Down Expand Up @@ -57,51 +55,20 @@ const Permissions: React.FC<PermissionsProps> = ({
onPermissionsChange(updatedPermissions);
};

const toScopes = (
_requestMethods: Set<Nip47RequestMethod>,
_notificationTypes: Set<Nip47NotificationType>
): Set<Scope> => {
throw new Error("TODO - map should come from backend");
};

const handleScopeChange = (scope: Scope) => {
if (!canEditPermissions) {
return;
}

const scopeToRequestMethods = (_scope: Scope): Nip47RequestMethod[] => {
throw new Error("TODO - map should come from backend");
};
const scopeToNotificationTypes = (
_scope: Scope
): Nip47NotificationType[] => {
throw new Error("TODO - map should come from backend");
};

const scopeRequestMethods = scopeToRequestMethods(scope);
const scopeNotificationTypes = scopeToNotificationTypes(scope);

const newRequestMethods = new Set(permissions.requestMethods);
for (const method of scopeRequestMethods) {
if (newRequestMethods.has(method)) {
newRequestMethods.delete(method);
} else {
newRequestMethods.add(method);
}
}

const newNotificationTypes = new Set(permissions.notificationTypes);
for (const notificationType of scopeNotificationTypes) {
if (newNotificationTypes.has(notificationType)) {
newNotificationTypes.delete(notificationType);
} else {
newNotificationTypes.add(notificationType);
}
const newScopes = new Set(permissions.scopes);
if (newScopes.has(scope)) {
newScopes.delete(scope);
} else {
newScopes.add(scope);
}

handlePermissionsChange({
requestMethods: newRequestMethods,
notificationTypes: newNotificationTypes,
scopes: newScopes,
});
};

Expand Down Expand Up @@ -146,9 +113,9 @@ const Permissions: React.FC<PermissionsProps> = ({
className={cn(
"w-full",
scope == "pay_invoice" ? "order-last" : "",
/*!canEditPermissions && !permissions.requestMethods.has(rm)
!canEditPermissions && !permissions.scopes.has(scope)
? "hidden"
: */ ""
: ""
)}
>
<div className="flex items-center mb-2">
Expand All @@ -164,10 +131,7 @@ const Permissions: React.FC<PermissionsProps> = ({
id={scope}
className={cn("mr-2", !canEditPermissions ? "hidden" : "")}
onCheckedChange={() => handleScopeChange(scope)}
checked={toScopes(
permissions.requestMethods,
permissions.notificationTypes
).has(scope)}
checked={permissions.scopes.has(scope)}
/>
<Label
htmlFor={scope}
Expand All @@ -180,7 +144,7 @@ const Permissions: React.FC<PermissionsProps> = ({
<div
className={cn(
"pt-2 pb-2 pl-5 ml-2.5 border-l-2 border-l-primary",
!permissions.requestMethods.has(scope)
!permissions.scopes.has(scope)
? canEditPermissions
? "pointer-events-none opacity-30"
: "hidden"
Expand Down
89 changes: 58 additions & 31 deletions frontend/src/screens/apps/NewApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
CreateAppResponse,
Nip47NotificationType,
Nip47RequestMethod,
Scope,
WalletCapabilities,
validBudgetRenewals,
} from "src/types";
Expand Down Expand Up @@ -69,7 +70,7 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => {
const maxAmountParam = queryParams.get("max_amount") ?? "";
const expiresAtParam = queryParams.get("expires_at") ?? "";

const initialRequestMethods: Set<Nip47RequestMethod> = React.useMemo(() => {
const initialScopes: Set<Scope> = React.useMemo(() => {
const methods = reqMethodsParam
? reqMethodsParam.split(" ")
: capabilities.methods;
Expand All @@ -86,38 +87,66 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => {
unsupportedMethods
);
}
return requestMethodsSet;
}, [capabilities.methods, reqMethodsParam]);

const initialNotificationTypes: Set<Nip47NotificationType> =
React.useMemo(() => {
const notificationTypes = notificationTypesParam
? notificationTypesParam.split(" ")
: capabilities.notificationTypes;
const notificationTypes = notificationTypesParam
? notificationTypesParam.split(" ")
: capabilities.notificationTypes;

const notificationTypesSet = new Set<Nip47NotificationType>(
notificationTypes as Nip47NotificationType[]
);
const unsupportedNotificationTypes = Array.from(
notificationTypesSet
).filter(
(notificationType) =>
capabilities.notificationTypes.indexOf(notificationType) < 0
const notificationTypesSet = new Set<Nip47NotificationType>(
notificationTypes as Nip47NotificationType[]
);
const unsupportedNotificationTypes = Array.from(
notificationTypesSet
).filter(
(notificationType) =>
capabilities.notificationTypes.indexOf(notificationType) < 0
);
if (unsupportedNotificationTypes.length) {
setUnsupportedError(
"This app requests methods not supported by your wallet: " +
unsupportedNotificationTypes
);
if (unsupportedNotificationTypes.length) {
setUnsupportedError(
"This app requests methods not supported by your wallet: " +
unsupportedNotificationTypes
);
}
return notificationTypesSet;
}, [capabilities.notificationTypes, notificationTypesParam]);
}

// TODO: check unsupported notifications here from notificationTypesParam
const scopes = new Set<Scope>();
if (
requestMethodsSet.has("pay_keysend") ||
requestMethodsSet.has("pay_invoice") ||
requestMethodsSet.has("multi_pay_invoice") ||
requestMethodsSet.has("multi_pay_keysend")
) {
scopes.add("pay_invoice");
}

// TODO: map request methods to permissions here
if (requestMethodsSet.has("get_info")) {
scopes.add("get_info");
}
if (requestMethodsSet.has("get_balance")) {
scopes.add("get_balance");
}
if (requestMethodsSet.has("make_invoice")) {
scopes.add("make_invoice");
}
if (requestMethodsSet.has("lookup_invoice")) {
scopes.add("lookup_invoice");
}
if (requestMethodsSet.has("list_transactions")) {
scopes.add("list_transactions");
}
if (requestMethodsSet.has("sign_message")) {
scopes.add("sign_message");
}
if (notificationTypes.length) {
scopes.add("notifications");
}

// TODO: DB rename
return scopes;
}, [
capabilities.methods,
capabilities.notificationTypes,
notificationTypesParam,
reqMethodsParam,
]);

const parseExpiresParam = (expiresParam: string): Date | undefined => {
const expiresParamTimestamp = parseInt(expiresParam);
Expand All @@ -128,8 +157,7 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => {
};

const [permissions, setPermissions] = useState<AppPermissions>({
requestMethods: initialRequestMethods,
notificationTypes: initialNotificationTypes,
scopes: initialScopes,
maxAmount: parseInt(maxAmountParam || "100000"),
budgetRenewal: validBudgetRenewals.includes(budgetRenewalParam)
? budgetRenewalParam
Expand All @@ -149,8 +177,7 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => {
pubkey,
budgetRenewal: permissions.budgetRenewal,
maxAmount: permissions.maxAmount,
requestMethods: Array.from(permissions.requestMethods),
notificationTypes: Array.from(permissions.notificationTypes),
scopes: Array.from(permissions.scopes),
expiresAt: permissions.expiresAt?.toISOString(),
returnTo: returnTo,
};
Expand Down
40 changes: 14 additions & 26 deletions frontend/src/screens/apps/ShowApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { useInfo } from "src/hooks/useInfo";
import {
AppPermissions,
BudgetRenewalType,
Nip47NotificationType,
Nip47RequestMethod,
Scope,
UpdateAppRequest,
} from "src/types";

import { handleRequestError } from "src/utils/handleRequestError";
Expand Down Expand Up @@ -56,29 +55,16 @@ function ShowApp() {
});

const [permissions, setPermissions] = React.useState<AppPermissions>({
requestMethods: new Set<Nip47RequestMethod>(),
notificationTypes: new Set<Nip47NotificationType>(),
scopes: new Set<Scope>(),
maxAmount: 0,
budgetRenewal: "",
expiresAt: undefined,
});

React.useEffect(() => {
const scopesToRequestMethods = (
_scopes: Scope[]
): Set<Nip47RequestMethod> => {
throw new Error("TODO - map should come from backend");
};
const scopesToNotificationTypes = (
_scopes: Scope[]
): Set<Nip47NotificationType> => {
throw new Error("TODO - map should come from backend");
};

if (app) {
setPermissions({
requestMethods: scopesToRequestMethods(app.scopes),
notificationTypes: scopesToNotificationTypes(app.scopes),
scopes: new Set(app.scopes),
maxAmount: app.maxAmount,
budgetRenewal: app.budgetRenewal as BudgetRenewalType,
expiresAt: app.expiresAt ? new Date(app.expiresAt) : undefined,
Expand All @@ -100,16 +86,20 @@ function ShowApp() {
throw new Error("No CSRF token");
}

const updateAppRequest: UpdateAppRequest = {
scopes: Array.from(permissions.scopes),
budgetRenewal: permissions.budgetRenewal,
expiresAt: permissions.expiresAt?.toISOString(),
maxAmount: permissions.maxAmount,
};

await request(`/api/apps/${app.nostrPubkey}`, {
method: "PATCH",
headers: {
"X-CSRF-Token": csrf,
"Content-Type": "application/json",
},
body: JSON.stringify({
...permissions,
requestMethods: [...permissions.requestMethods].join(" "),
}),
body: JSON.stringify(updateAppRequest),
});

await refetchApp();
Expand Down Expand Up @@ -214,17 +204,15 @@ function ShowApp() {
type="button"
variant="outline"
onClick={() => {
/*setPermissions({
requestMethods: new Set(
app.requestMethods as Scope[]
),
setPermissions({
scopes: new Set(app.scopes as Scope[]),
maxAmount: app.maxAmount,
budgetRenewal:
app.budgetRenewal as BudgetRenewalType,
expiresAt: app.expiresAt
? new Date(app.expiresAt)
: undefined,
});*/
});
setEditMode(!editMode);
// TODO: clicking cancel and then editing again will leave the days option wrong
}}
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ export interface App {
}

export interface AppPermissions {
requestMethods: Set<Nip47RequestMethod>;
notificationTypes: Set<Nip47NotificationType>;
scopes: Set<Scope>;
maxAmount: number;
budgetRenewal: BudgetRenewalType;
expiresAt?: Date;
Expand Down Expand Up @@ -172,8 +171,7 @@ export interface CreateAppRequest {
maxAmount: number;
budgetRenewal: string;
expiresAt: string | undefined;
requestMethods: Nip47RequestMethod[];
notificationTypes: Nip47NotificationType[];
scopes: Scope[];
returnTo: string;
}

Expand All @@ -185,6 +183,13 @@ export interface CreateAppResponse {
returnTo: string;
}

export type UpdateAppRequest = {
maxAmount: number;
budgetRenewal: string;
expiresAt: string | undefined;
scopes: string[];
};

export type Channel = {
localBalance: number;
localSpendableBalance: number;
Expand Down

0 comments on commit 6770704

Please sign in to comment.