diff --git a/assets/_locales/en/messages.json b/assets/_locales/en/messages.json index 0b8d8cf9..99c64745 100644 --- a/assets/_locales/en/messages.json +++ b/assets/_locales/en/messages.json @@ -159,6 +159,10 @@ "message": "Invalid password", "description": "Invalid password error" }, + "invalid_qty_error": { + "message": "0.001 is the lowest amount. Please change your limit to a higher amount", + "description": "Warning message for setting a limit too low" + }, "testnetLive": { "message": "Testnet is live", "description": "Testnet live message" @@ -307,6 +311,14 @@ "message": "Permissions", "description": "Permissions title" }, + "edit_permissions": { + "message": "Edit permissions", + "description": "Edit permissions title" + }, + "app_permissions": { + "message": "App permissions", + "description": "App permissions title" + }, "allowance": { "message": "Allowance", "description": "Allowance title" @@ -957,9 +969,31 @@ "message": "Connect", "description": "Connect button text" }, + "always_allow": { + "message": "Always allow", + "description": "Always allow button" + }, + "always_ask_permission": { + "message": "Always ask permission", + "description": "Always allow button" + }, + "always_ask_tooltip":{ + "message": "Always ask for confirmation before any interaction", + "description": "Always allow tooltip" + }, + "allow_selected_permissions": { + "message": "Allow selected permissions", + "description": "Always allow button" + }, "allow_these_permissions": { - "message": "Allow these permissions for the application:", - "description": "Auth permissions label" + "message": "$APPNAME$ wants to connect to your wallet with the following permissions", + "description": "Auth permissions label", + "placeholders": { + "appname": { + "content": "$1", + "example": "permafacts.arweave.dev" + } + } }, "reset_allowance": { "message": "Reset allowance", diff --git a/assets/_locales/zh_CN/messages.json b/assets/_locales/zh_CN/messages.json index 770aa7c5..76aed103 100644 --- a/assets/_locales/zh_CN/messages.json +++ b/assets/_locales/zh_CN/messages.json @@ -307,6 +307,14 @@ "message": "权限", "description": "Permissions title" }, + "edit_permissions": { + "message": "编辑权限", + "description": "Edit permissions title" + }, + "app_permissions": { + "message": "应用权限", + "description": "App permissions title" + }, "allowance": { "message": "额度", "description": "Allowance title" @@ -953,9 +961,27 @@ "message": "连接", "description": "Connect button text" }, + "always_allow": { + "message": "始终允许", + "description": "Always allow button" + }, + "always_ask_permission": { + "message": "始终询问权限", + "description": "Always allow button" + }, + "allow_selected_permissions": { + "message": "允许选择的权限", + "description": "Always allow button" + }, "allow_these_permissions": { - "message": "允许这些权限给应用程序:", - "description": "Auth permissions label" + "message": "$APPNAME$ 想要使用以下权限连接到您的钱包", + "description": "Auth permissions label", + "placeholders": { + "appname": { + "content": "$1", + "example": "permafacts.arweave.dev" + } + } }, "reset_allowance": { "message": "重置额度", diff --git a/src/api/modules/dispatch/allowance.ts b/src/api/modules/dispatch/allowance.ts index 2f984f11..e9fa3803 100644 --- a/src/api/modules/dispatch/allowance.ts +++ b/src/api/modules/dispatch/allowance.ts @@ -18,16 +18,14 @@ export async function ensureAllowanceDispatch( appData: ModuleAppData, allowance: AllowanceBigNumber, keyfile: JWKInterface, - price: number | BigNumber + price: number | BigNumber, + alwaysAsk?: boolean ) { const arweave = new Arweave(defaultGateway); // allowance or sign auth try { - if (allowance.enabled) { - await allowanceAuth(allowance, appData.appURL, price); - } else { - // get address + if (alwaysAsk) { const address = await arweave.wallets.jwkToAddress(keyfile); await signAuth( @@ -37,8 +35,13 @@ export async function ensureAllowanceDispatch( address ); } + + if (allowance.enabled) { + await allowanceAuth(allowance, appData.appURL, price, alwaysAsk); + } } catch (e) { freeDecryptedWallet(keyfile); throw new Error(e?.message || e); } + return; } diff --git a/src/api/modules/dispatch/dispatch.background.ts b/src/api/modules/dispatch/dispatch.background.ts index 6716b1d0..a3d44df7 100644 --- a/src/api/modules/dispatch/dispatch.background.ts +++ b/src/api/modules/dispatch/dispatch.background.ts @@ -81,6 +81,9 @@ const background: ModuleFunction = async ( // get allowance const allowance = await app.getAllowance(); + // always ask + const alwaysAsk = allowance.enabled && allowance.limit.eq(BigNumber("0")); + // attempt to create a bundle try { // create bundlr tx as a data entry @@ -95,7 +98,8 @@ const background: ModuleFunction = async ( appData, allowance, decryptedWallet.keyfile, - price + price, + alwaysAsk ); // sign and upload bundler tx @@ -133,7 +137,8 @@ const background: ModuleFunction = async ( appData, allowance, decryptedWallet.keyfile, - price + price, + alwaysAsk ); // sign and upload diff --git a/src/api/modules/sign/allowance.ts b/src/api/modules/sign/allowance.ts index 1d1907d1..0cfa19d6 100644 --- a/src/api/modules/sign/allowance.ts +++ b/src/api/modules/sign/allowance.ts @@ -61,7 +61,8 @@ export async function updateAllowance( export async function allowanceAuth( allowance: AllowanceBigNumber, tabURL: string, - price: number | BigNumber + price: number | BigNumber, + override: boolean = false ) { // spent amount after this transaction const total = allowance.spent.plus(price); @@ -69,8 +70,8 @@ export async function allowanceAuth( // check if the price goes over the allowed total limit const hasEnoughAllowance = total.lte(allowance.limit); - // if the allowance is enough, return - if (hasEnoughAllowance) return; + // if the allowance is enough or override is true, return + if (hasEnoughAllowance || override) return; // try to authenticate to raise the allowance amount await authenticate({ diff --git a/src/api/modules/sign/sign.background.ts b/src/api/modules/sign/sign.background.ts index 23422593..a52765c9 100644 --- a/src/api/modules/sign/sign.background.ts +++ b/src/api/modules/sign/sign.background.ts @@ -89,19 +89,13 @@ const background: ModuleFunction = async ( // get allowance const allowance = await getAllowance(appData.appURL); - // check if there is an allowance limit - // if there isn't, we need to ask the user - // to manually confirm the transaction - if (allowance.enabled && activeWallet.type === "local") { - // authenticate user if the allowance - // limit is reached - try { - await allowanceAuth(allowance, appData.appURL, price); - } catch (e) { - freeDecryptedWallet(keyfile); - throw new Error(e?.message || e); - } - } else { + // always ask + const alwaysAsk = allowance.enabled && allowance.limit.eq(BigNumber("0")); + + // check if there is an allowance limit, if there is we need to check allowance + // if alwaysAsk is true, then we'll need to signAuth popup + // if allowance is disabled, proceed with signing + if (alwaysAsk) { // get address of keyfile const addr = activeWallet.type === "local" @@ -126,6 +120,15 @@ const background: ModuleFunction = async ( throw new Error("User failed to sign the transaction manually"); } + } else if (allowance.enabled && activeWallet.type === "local") { + // authenticate user if the allowance + // limit is reached + try { + await allowanceAuth(allowance, appData.appURL, price, alwaysAsk); + } catch (e) { + freeDecryptedWallet(keyfile); + throw new Error(e?.message || e); + } } // sign the transaction if local wallet diff --git a/src/api/modules/sign_data_item/sign_data_item.background.ts b/src/api/modules/sign_data_item/sign_data_item.background.ts index 3056c537..426a0376 100644 --- a/src/api/modules/sign_data_item/sign_data_item.background.ts +++ b/src/api/modules/sign_data_item/sign_data_item.background.ts @@ -33,6 +33,10 @@ const background: ModuleFunction = async ( throw new Error(err); } + const app = new Application(appData.appURL); + const allowance = await app.getAllowance(); + const alwaysAsk = allowance.enabled && allowance.limit.eq(BigNumber("0")); + if ( dataItem.tags?.some( (tag) => tag.name === "Action" && tag.value === "Transfer" @@ -76,7 +80,6 @@ const background: ModuleFunction = async ( }); // create app - const app = new Application(appData.appURL); // create arweave client const arweave = new Arweave(await app.getGatewayConfig()); @@ -92,11 +95,11 @@ const background: ModuleFunction = async ( // check allowance // const price = await getPrice(dataEntry, await app.getBundler()); - const allowance = await app.getAllowance(); + // we are no longer checking for allowance on this page // allowance or sign auth try { - if (!allowance.enabled) { + if (alwaysAsk) { // get address const address = await arweave.wallets.jwkToAddress( decryptedWallet.keyfile diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx index 22fcbfbd..26a7336c 100644 --- a/src/components/Checkbox.tsx +++ b/src/components/Checkbox.tsx @@ -1,4 +1,10 @@ -import { useEffect, useMemo, useState, type HTMLProps } from "react"; +import { + useCallback, + useEffect, + useMemo, + useState, + type HTMLProps +} from "react"; import styled from "styled-components"; export const Checkbox = ({ @@ -10,18 +16,15 @@ export const Checkbox = ({ const [state, setState] = useState(checked); const effectiveId = useMemo(() => id || generateUniqueId(), []); - async function toggle() { - let newVal = state; - - setState((val) => { - newVal = !val; - return newVal; + const toggle = useCallback(async () => { + setState((prevState) => { + const newState = !prevState; + if (onChange) { + onChange(newState); + } + return newState; }); - - if (onChange) { - await onChange(newVal); - } - } + }, [onChange]); useEffect(() => setState(checked), [checked]); diff --git a/src/components/auth/App.tsx b/src/components/auth/App.tsx index 1facd10f..9acee5c0 100644 --- a/src/components/auth/App.tsx +++ b/src/components/auth/App.tsx @@ -2,7 +2,8 @@ import { type DisplayTheme, Section, Spacer, - Text + Text, + ListItem } from "@arconnect/components"; import { defaultGateway, type Gateway } from "~gateways/gateway"; import { useTheme as useDisplayTheme } from "~utils/theme"; @@ -22,7 +23,8 @@ export default function App({ appName, appUrl, gateway, - allowance + allowance, + showTitle = true }: Props) { // allowance spent in AR const spent = useMemo(() => { @@ -50,16 +52,28 @@ export default function App({ return ( <> - - - - + {showTitle && ( + <> + + + + + + )} - + + {/* {!appIcon && } @@ -91,7 +105,7 @@ export default function App({ {" AR"} )} - + */} ); @@ -103,12 +117,9 @@ const SidePaddingSection = styled(Section)` `; const Wrapper = styled.div<{ displayTheme: DisplayTheme }>` - background-color: rgb( - ${(props) => - props.displayTheme === "light" ? "0, 0, 0" : props.theme.cardBackground} - ); - border-radius: 27px; - padding: 1rem; + border-radius: 10px; + padding-top: 1rem; + padding-bottom: 1rem; display: flex; align-items: center; justify-content: space-between; @@ -164,4 +175,5 @@ interface Props { appUrl: string; gateway?: Gateway; allowance?: Allowance; + showTitle?: boolean; } diff --git a/src/components/auth/Permissions.tsx b/src/components/auth/Permissions.tsx new file mode 100644 index 00000000..ce640e4f --- /dev/null +++ b/src/components/auth/Permissions.tsx @@ -0,0 +1,134 @@ +import { ButtonV2, Section, Text } from "@arconnect/components"; +import browser from "webextension-polyfill"; +import styled from "styled-components"; +import { permissionData, type PermissionType } from "~applications/permissions"; +import Checkbox from "~components/Checkbox"; +import { useEffect, useState } from "react"; + +type PermissionsProps = { + requestedPermissions: PermissionType[]; + update: (updatedPermissions: PermissionType[]) => void; + closeEdit: (setEdit: boolean) => void; +}; + +export default function Permissions({ + requestedPermissions, + update, + closeEdit +}: PermissionsProps) { + const [permissions, setPermissions] = useState>( + new Map() + ); + + useEffect(() => { + setPermissions( + new Map(requestedPermissions.map((permission) => [permission, true])) + ); + }, []); + + return ( + +
+
+ {browser.i18n.getMessage("permissions")} + + {Object.keys(permissionData).map( + (permissionName: PermissionType, i) => { + let formattedPermissionName = permissionName + .split("_") + .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) + .join(" "); + + if (permissionName === "SIGNATURE") { + formattedPermissionName = "Sign Data"; + } + + return ( +
+ + { + const updated = new Map(permissions); + updated.set(permissionName, checked); + setPermissions(updated); + }} + checked={requestedPermissions.includes(permissionName)} + /> +
+ + {formattedPermissionName} + + + {browser.i18n.getMessage( + permissionData[permissionName] + )} + +
+
+
+ ); + } + )} +
+
+
+
+ { + const updatedPermissions = Array.from(permissions.entries()) + .filter(([, value]) => value) + .map(([key]) => key); + update(updatedPermissions); + closeEdit(false); + }} + > + {browser.i18n.getMessage("save")} + +
+
+ ); +} + +const Wrapper = styled.div` + display: flex; + width: 100vw; + flex-direction: column; + height: calc(100vh - 163px); + justify-content: space-between; +`; + +const Title = styled(Text).attrs({ + heading: true +})` + margin-bottom: 0.75em; + font-size: 1.125rem; +`; + +const PermissionsWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 10px; +`; + +const Permission = styled.div` + display: flex; + align-items: center; + gap: 8px; +`; + +export const PermissionDescription = styled(Text).attrs({ + noMargin: true +})` + font-size: 0.625rem; + font-weight: 500; +`; + +export const PermissionTitle = styled(Text).attrs({ + noMargin: true, + heading: true +})` + font-size: 0.875rem; + font-weight: 500; +`; diff --git a/src/components/dashboard/subsettings/AppSettings.tsx b/src/components/dashboard/subsettings/AppSettings.tsx index 29784df4..eaa7c683 100644 --- a/src/components/dashboard/subsettings/AppSettings.tsx +++ b/src/components/dashboard/subsettings/AppSettings.tsx @@ -153,16 +153,17 @@ export default function AppSettings({ app, showTitle = false }: Props) { {browser.i18n.getMessage("allowance")} - updateSettings((val) => ({ + onChange={(checked) => { + setEditingLimit(false); + return updateSettings((val) => ({ ...val, allowance: { ...defaultAllowance, ...val.allowance, enabled: checked } - })) - } + })); + }} checked={settings.allowance?.enabled} > {browser.i18n.getMessage( @@ -203,9 +204,11 @@ export default function AppSettings({ app, showTitle = false }: Props) { {browser.i18n.getMessage("limit")} {": "} - {(editingLimit && ( + {editingLimit ? ( updateSettings((val) => ({ ...val, @@ -217,14 +220,18 @@ export default function AppSettings({ app, showTitle = false }: Props) { })) } /> - )) || - arweave.ar.winstonToAr(limit)} + ) : settings?.allowance?.enabled ? ( + arweave.ar.winstonToAr(limit) + ) : ( + "∞" + )} {" AR "} setEditingLimit((val) => !val)} /> @@ -403,19 +410,21 @@ const EmptyInput = styled.input.attrs({ color: rgb(${(props) => props.theme.secondaryText}); `; -const EditLimitButton = styled(EditIcon)` +const EditLimitButton = styled(EditIcon)<{ disabled?: boolean }>` font-size: 1em; width: 1em; height: 1em; - cursor: pointer; + cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")}; transition: all 0.23s ease-in-out; + opacity: ${(props) => (props.disabled ? "0.5" : "1")}; + pointer-events: ${(props) => (props.disabled ? "none" : "auto")}; &:hover { - opacity: 0.8; + opacity: ${(props) => (props.disabled ? "0.5" : "0.8")}; } &:active { - transform: scale(0.83); + transform: ${(props) => (props.disabled ? "none" : "scale(0.83)")}; } `; diff --git a/src/routes/auth/connect.tsx b/src/routes/auth/connect.tsx index e513ee08..aeae0cdc 100644 --- a/src/routes/auth/connect.tsx +++ b/src/routes/auth/connect.tsx @@ -7,6 +7,7 @@ import { Section, Spacer, Text, + TooltipV2, useInput, useToasts } from "@arconnect/components"; @@ -26,12 +27,21 @@ import WalletSwitcher from "~components/popup/WalletSwitcher"; import Wrapper from "~components/auth/Wrapper"; import browser from "webextension-polyfill"; import Label from "~components/auth/Label"; -import Head from "~components/popup/Head"; import App from "~components/auth/App"; import styled from "styled-components"; import { EventType, trackEvent } from "~utils/analytics"; import Application from "~applications/application"; import { defaultGateway, type Gateway } from "~gateways/gateway"; +import HeadV2 from "~components/popup/HeadV2"; +import { CheckIcon, CloseIcon } from "@iconicicons/react"; +import { + InfoCircle, + ToggleSwitch +} from "~routes/popup/subscriptions/subscriptionDetails"; +import { defaultAllowance } from "~applications/allowance"; +import Arweave from "arweave"; +import Permissions from "../../components/auth/Permissions"; +import { Flex } from "~routes/popup/settings/apps/[url]"; export default function Connect() { // active address @@ -40,12 +50,16 @@ export default function Connect() { instance: ExtensionStorage }); + const arweave = new Arweave(defaultGateway); + // wallet switcher open const [switcherOpen, setSwitcherOpen] = useState(false); // page const [page, setPage] = useState<"unlock" | "permissions">("unlock"); + const allowanceInput = useInput(); + // connect params const params = useAuthParams<{ url: string; @@ -73,6 +87,12 @@ export default function Connect() { PermissionType[] >([]); + // allowance for permissions + const [allowanceEnabled, setAllowanceEnabled] = useState(true); + + // state management for edit + const [edit, setEdit] = useState(false); + useEffect(() => { (async () => { if (!params) return; @@ -93,9 +113,16 @@ export default function Connect() { setRequestedPermissions( requested.filter((p) => Object.keys(permissionData).includes(p)) ); + setRequetedPermCopy( + requested.filter((p) => Object.keys(permissionData).includes(p)) + ); })(); }, [params]); + const [requestedPermCopy, setRequetedPermCopy] = useState( + [] + ); + // permissions to add const [permissions, setPermissions] = useState([]); @@ -133,9 +160,21 @@ export default function Connect() { } // connect - async function connect() { + async function connect(alwaysAsk: boolean = false) { if (appUrl === "") return; + if ( + allowanceEnabled && + Number(allowanceInput.state) < 0.001 && + !alwaysAsk + ) { + return setToast({ + type: "error", + content: browser.i18n.getMessage("invalid_qty_error"), + duration: 2200 + }); + } + // get existing permissions const app = new Application(appUrl); const existingPermissions = await app.getPermissions(); @@ -147,13 +186,37 @@ export default function Connect() { permissions, name: appData.name, logo: appData.logo, + // alwaysAsk, + allowance: { + enabled: alwaysAsk || allowanceEnabled, + limit: alwaysAsk // if it's always ask set the limit to 0 + ? "0" + : allowanceEnabled + ? arweave.ar.arToWinston(allowanceInput.state) // If allowance is enabled and a new limit is set, use the new limit + : Number.MAX_SAFE_INTEGER.toString(), // If allowance is disabled set it to max number + spent: "0" // in winstons + }, // TODO: wayfinder gateway: params.gateway || defaultGateway }); } else { // update existing permissions, if the app // has already been added - await app.updateSettings({ permissions }); + + const allowance = await app.getAllowance(); + await app.updateSettings({ + permissions, + // alwaysAsk, + allowance: { + enabled: alwaysAsk ?? allowanceEnabled, + limit: alwaysAsk // if it's always ask set the limit to 0 + ? "0" + : allowanceEnabled + ? arweave.ar.arToWinston(allowanceInput.state) // If allowance is enabled and a new limit is set, use the new limit + : Number.MAX_SAFE_INTEGER.toString(), // If allowance is disabled set it to max number + spent: "0" // in winstons + } + }); } // send response @@ -169,23 +232,33 @@ export default function Connect() { closeWindow(); } + useEffect(() => { + allowanceInput.setState(arweave.ar.winstonToAr(defaultAllowance.limit)); + }, []); + + const removedPermissions = useMemo(() => { + return requestedPermCopy.filter( + (permission) => !requestedPermissions.includes(permission) + ); + }, [requestedPermCopy, requestedPermissions]); + return (
- setEdit(false) : cancel} /> - - + {page === "unlock" && ( @@ -231,70 +304,174 @@ export default function Connect() { )} {page === "permissions" && ( - -
- - {browser.i18n.getMessage("allow_these_permissions")} - - {requestedPermissions.map((permission, i) => ( -
- - setPermissions((val) => { - if (checked && val.includes(permission)) return val; - if (!checked && !val.includes(permission)) - return val; - if (checked && !val.includes(permission)) { - return [...val, permission]; - } - if (!checked && val.includes(permission)) { - return val.filter((p) => p !== permission); - } - }) - } - > + <> + {!edit ? ( + +
+ {browser.i18n.getMessage( - permissionData[permission.toUpperCase()] + "allow_these_permissions", + appData.name || appUrl )} - - {i !== requestedPermissions.length - 1 && ( - + + {params.url} + + + + {browser.i18n.getMessage("app_permissions")} + + { + setEdit(!edit); + }} + > + {browser.i18n.getMessage("edit_permissions")} + + + + {requestedPermissions.map((permission, i) => ( + + + + {browser.i18n.getMessage( + permissionData[permission.toUpperCase()] + )} + + + ))} + {requestedPermCopy + .filter( + (permission) => + !requestedPermissions.includes(permission) + ) + .map((permission, i) => ( + + + + {browser.i18n.getMessage( + permissionData[permission.toUpperCase()] + )} + + + ))} + + + +
{browser.i18n.getMessage("allowance")}
+ + + +
+ + +
+ {allowanceEnabled && ( + + AR} + type="number" + {...allowanceInput.bindings} + /> + )} -
- ))} -
-
+ + + ) : ( + <> + + + )} + )}
-
- { - if (page === "unlock") { - await unlock(); - } else { - await connect(); - } - }} - > - {browser.i18n.getMessage(page === "unlock" ? "sign_in" : "connect")} - - - - {browser.i18n.getMessage("cancel")} - -
+ {!edit && ( +
+ <> + { + if (page === "unlock") { + await unlock(); + } else { + await connect(); + } + }} + > + {browser.i18n.getMessage( + page === "unlock" + ? "sign_in" + : removedPermissions.length > 0 + ? "allow_selected_permissions" + : "always_allow" + )} + + + connect(true)} + > + {browser.i18n.getMessage( + page === "unlock" ? "cancel" : "always_ask_permission" + )} + + +
+ )}
); } +const InfoText: React.ReactNode = ( +
+ Set the amount you want
+ ArConnect to automatically transfer +
+); + const WalletSelectWrapper = styled.div` position: relative; `; +const StyledPermissions = styled.div` + padding-bottom: 1rem; +`; + +const Permission = styled.div` + margin: 0; + align-items: center; + display: flex; + gap: 8px; +`; + +const PermissionsTitle = styled.div` + display: flex; + width: 100%; + justify-content: space-between; +`; + const SelectIcon = styled(ChevronDownIcon)` font-size: 1rem; width: 1.375rem; @@ -303,6 +480,65 @@ const SelectIcon = styled(ChevronDownIcon)` transition: all 0.23s ease-in-out; `; +const Description = styled(Text)<{ alt?: boolean }>` + color: ${(props) => + props.alt ? `rgb(${props.theme.theme})` : props.theme.primaryTextv2}; + margin-bottom: 4px; + ${(props) => + props.alt && + ` + cursor: pointer; + `} +`; +const Url = styled(Text)` + color: ${(props) => props.theme.secondaryTextv2}; + font-size: 12px; +`; + +const StyledCheckIcon = styled(CheckIcon)` + width: 17px; + height: 17px; + min-width: 17px; + min-height: 17px; + flex-shrink: 0; + color: rgba(20, 209, 16, 1); +`; + +const StyledCloseIcon = styled(CloseIcon)` + width: 17px; + height: 17px; + min-width: 17px; + min-height: 17px; + flex-shrink: 0; + color: ${(props) => props.theme.fail}; +`; + +const AllowanceInput = styled(InputV2)` + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } +`; + +const PermissionItem = styled(Text)` + color: ${(props) => props.theme.primaryTextv2}; + margin: 0; + font-size: 14px; +`; + +const AllowanceSection = styled.div` + display: flex; + justify-content: space-between; + align-items: flex-end; + padding-top: 18px; + div { + color: ${(props) => props.theme.primaryTextv2}; + font-size: 18px; + font-weight: 00; + } +`; + const WalletSelect = styled(Card)<{ open: boolean }>` position: relative; display: flex; diff --git a/src/routes/popup/settings/apps/[url]/index.tsx b/src/routes/popup/settings/apps/[url]/index.tsx index c8c38a75..03cd9609 100644 --- a/src/routes/popup/settings/apps/[url]/index.tsx +++ b/src/routes/popup/settings/apps/[url]/index.tsx @@ -42,6 +42,10 @@ export default function AppSettings({ url }: Props) { return val.toString(); }, [settings]); + const isAllowanceDisabled = useMemo(() => { + return !settings?.allowance?.enabled; + }, [settings?.allowance?.enabled]); + // allowance limit const limit = useMemo(() => { const val = settings?.allowance?.limit; @@ -137,14 +141,17 @@ export default function AppSettings({ url }: Props) {
{ updateSettings((val) => ({ ...val, allowance: { ...defaultAllowance, ...val.allowance, - enabled: !val.allowance.enabled + enabled: enabled, + limit: enabled + ? val.allowance.limit + : Number.MAX_SAFE_INTEGER.toString() } })); }} @@ -160,10 +167,13 @@ export default function AppSettings({ url }: Props) { > updateSettings((val) => ({ @@ -424,7 +434,7 @@ const CenterText = styled(Text)` } `; -const Flex = styled.div<{ alignItems: string; justifyContent: string }>` +export const Flex = styled.div<{ alignItems: string; justifyContent: string }>` display: flex; align-items: ${(props) => props.alignItems}; justify-content: ${(props) => props.justifyContent}; diff --git a/src/utils/format.ts b/src/utils/format.ts index aceb07cc..8216a1bd 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -51,7 +51,7 @@ export function formatAddress(address: string, count = 13) { * @param addr String to validate * @returns Valid address or not */ -export const isAddressFormat = (addr: string) => /[a-z0-9_-]{43}/i.test(addr); +export const isAddressFormat = (addr: string) => /^[a-z0-9_-]{43}$/i.test(addr); /** * Capitalizes first letters of settings name and replaces "_" with " "