diff --git a/.all-contributorsrc b/.all-contributorsrc index 83469f4d..59b04bb8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,7 +1,5 @@ { - "files": [ - "README.md" - ], + "files": ["README.md"], "imageSize": 100, "commit": false, "commitType": "docs", @@ -12,54 +10,42 @@ "name": "Tomek Marciniak", "avatar_url": "https://avatars.githubusercontent.com/u/16132011?v=4", "profile": "https://github.com/mrcnk", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "teddyjfpender", "name": "Teddy Pender", "avatar_url": "https://avatars.githubusercontent.com/u/92999717?v=4", "profile": "https://github.com/teddyjfpender", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "rago4", "name": "Rafał Goławski", "avatar_url": "https://avatars.githubusercontent.com/u/19167236?v=4", "profile": "https://dev.to/rgolawski", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "mich3lang3lo", "name": "Mariusz", "avatar_url": "https://avatars.githubusercontent.com/u/164676295?v=4", "profile": "https://github.com/mich3lang3lo", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "aliraza556", "name": "Ali Raza", "avatar_url": "https://avatars.githubusercontent.com/u/87068339?v=4", "profile": "https://github.com/aliraza556", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "yaodingyd", "name": "Yao Ding", "avatar_url": "https://avatars.githubusercontent.com/u/11392695?v=4", "profile": "https://yaodingyd.github.io/", - "contributions": [ - "code" - ] + "contributions": ["code"] } ], "contributorsPerLine": 7, diff --git a/packages/features/src/router.tsx b/packages/features/src/router.tsx index 6bf09140..659ff9ef 100644 --- a/packages/features/src/router.tsx +++ b/packages/features/src/router.tsx @@ -1,10 +1,10 @@ +import { MemoryRouter, Outlet, Route, Routes } from "react-router-dom" + import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" import { ErrorBoundary } from "react-error-boundary" import { MixpanelProvider } from "react-mixpanel-browser" -import { MemoryRouter, Outlet, Route, Routes } from "react-router-dom" import { Toaster } from "sonner" - import { AddressBookRoute } from "./address-book/routes/address-book" import { NewAddressRoute } from "./address-book/routes/new-address" import { useAppStore } from "./common/store/app" @@ -26,6 +26,7 @@ import { TransactionErrorRoute } from "./send/routes/transaction-error" import { TransactionSuccessRoute } from "./send/routes/transaction-success" import { TransactionSummaryRoute } from "./send/routes/transaction-summary" import { AboutRoute } from "./settings/routes/about" +import { AuthorizedZkAppsRoute } from "./settings/routes/authorized-zkapps" import { CurrencyRoute } from "./settings/routes/currency" import { DisplayRoute } from "./settings/routes/display" import { LanguageRoute } from "./settings/routes/language" @@ -123,6 +124,10 @@ export const Router = () => { } /> } /> + } + /> } /> } /> diff --git a/packages/features/src/settings/index.stories.tsx b/packages/features/src/settings/index.stories.tsx index 4b8291e0..caef17b7 100644 --- a/packages/features/src/settings/index.stories.tsx +++ b/packages/features/src/settings/index.stories.tsx @@ -2,6 +2,7 @@ import { action } from "@ladle/react" import type { StoryDefault } from "@ladle/react" import { AboutView } from "./views/about" +import { AuthorizedZkAppsView } from "./views/authorized-zkapps" import { CurrencyView } from "./views/currency" import { DisplayView } from "./views/display" import { LanguageView } from "./views/language" @@ -18,6 +19,9 @@ export const Settings = () => ( ) export const About = () => +export const AuthorizedZkApps = () => ( + +) export const Support = () => diff --git a/packages/features/src/settings/routes/authorized-zkapps.tsx b/packages/features/src/settings/routes/authorized-zkapps.tsx new file mode 100644 index 00000000..a439aaf9 --- /dev/null +++ b/packages/features/src/settings/routes/authorized-zkapps.tsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from "react" + +import { fetcher } from "@/common/lib/fetch" +import { useNavigate } from "react-router-dom" +import { AuthorizedZkAppsView } from "../views/authorized-zkapps" + +export type ZkApp = { + title: string + description: string + image: string + url: string +} +export const AuthorizedZkAppsRoute = () => { + const navigate = useNavigate() + const [connectedApps, setConnectedApps] = useState([] as ZkApp[]) + + useEffect(() => { + fetchConnectedApps() + }, []) + + const fetchConnectedApps = async () => { + const { permissions } = (await chrome.storage.local.get({ + permissions: [], + })) as Record + // https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url + const URLRegex = + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/ + const appsWithMetadata = await Promise.all( + Object.keys(permissions) + .filter((key) => URLRegex.test(key)) + .map(async (url) => { + const metadata = (await fetcher( + `https://api.dub.co/metatags?url=${url}`, + )) as Omit + return { url, ...metadata } + }), + ) + setConnectedApps(appsWithMetadata) + } + + const handleDeleteApp = async (appToDelete: ZkApp) => { + const filteredApps = connectedApps.filter( + (app) => app.url !== appToDelete.url, + ) + await chrome.storage.local.set({ permissions: filteredApps }) + setConnectedApps(filteredApps) + } + + return ( + navigate(-1)} + /> + ) +} diff --git a/packages/features/src/settings/views/authorized-zkapps.tsx b/packages/features/src/settings/views/authorized-zkapps.tsx new file mode 100644 index 00000000..cdf7f707 --- /dev/null +++ b/packages/features/src/settings/views/authorized-zkapps.tsx @@ -0,0 +1,69 @@ +import { AppWindowMac, X } from "lucide-react" + +import { truncateString } from "@/common/lib/string" +import { AppLayout } from "@/components/app-layout" +import { SettingsPageLayout } from "@/components/settings-page-layout" +import type { ZkApp } from "../routes/authorized-zkapps" + +type AuthorizedZkAppsViewProps = { + onCloseClicked: () => void + apps: ZkApp[] + handleDeleteApp: (appToDelete: ZkApp) => void +} + +export const AuthorizedZkAppsView = ({ + onCloseClicked, + handleDeleteApp, + apps: connectedApps, +}: AuthorizedZkAppsViewProps) => { + return ( + + + {connectedApps.map((app) => ( + + + {app.image ? ( + + ) : ( + + + + )} + + + {truncateString({ + value: app.title, + firstCharCount: 20, + endCharCount: 2, + })} + + + {truncateString({ + value: app.url, + firstCharCount: 20, + endCharCount: 2, + })} + + + + + handleDeleteApp(app)}> + + + + + ))} + + + ) +} diff --git a/packages/features/src/settings/views/settings.tsx b/packages/features/src/settings/views/settings.tsx index 01678bee..ca37271e 100644 --- a/packages/features/src/settings/views/settings.tsx +++ b/packages/features/src/settings/views/settings.tsx @@ -1,6 +1,7 @@ +import { AppWindowMac, Eye, Info } from "lucide-react" + import { AppLayout } from "@/components/app-layout" import { SettingsPageLayout } from "@/components/settings-page-layout" -import { Eye, Info } from "lucide-react" import { Link } from "react-router-dom" const Links = [ @@ -11,6 +12,12 @@ const Links = [ // href: "/settings/wallet", // Icon: WalletMinimal, // }, + { + label: "Authorized zkApps", + description: "zkApps connected to your wallet", + href: "/settings/authorized-zkapps", + Icon: AppWindowMac, + }, { label: "Privacy", description: "Data sharing",
+ {truncateString({ + value: app.title, + firstCharCount: 20, + endCharCount: 2, + })} +
+ {truncateString({ + value: app.url, + firstCharCount: 20, + endCharCount: 2, + })} +