From 7b4256e79115025963a3c87e07b025461fa71ff1 Mon Sep 17 00:00:00 2001 From: Hendrik Eeckhaut Date: Tue, 17 Sep 2024 14:51:06 +0200 Subject: [PATCH 1/6] feat: add button to open extension in a browser page #77 --- src/pages/Options/index.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/pages/Options/index.tsx b/src/pages/Options/index.tsx index df37f39e..5ce6a53e 100644 --- a/src/pages/Options/index.tsx +++ b/src/pages/Options/index.tsx @@ -76,6 +76,18 @@ export default function Options(): ReactElement { [onSave], ); + const openExtensionDetails = () => { + browser.tabs.create({ + url: `chrome://extensions/?id=${chrome.runtime.id}`, + }); + }; + + const openExtensionInPage = () => { + browser.tabs.create({ + url: `chrome-extension://${chrome.runtime.id}/popup.html`, + }); + }; + const onAdvanced = useCallback(() => { setAdvanced(!advanced); }, [advanced]); @@ -152,6 +164,14 @@ export default function Options(): ReactElement { Save +
+ + +
); } From c4edbfd90d0a2c826a53a8f38e94afba89da9f2f Mon Sep 17 00:00:00 2001 From: tsukino <0xtsukino@gmail.com> Date: Tue, 8 Oct 2024 23:37:31 +0800 Subject: [PATCH 2/6] feat: add options menu --- src/components/Menu/index.tsx | 92 +++++++++++++++++++++++++++++++++++ src/entries/Popup/Popup.tsx | 10 ++-- src/entries/Popup/index.scss | 4 ++ src/pages/Options/index.tsx | 20 -------- 4 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 src/components/Menu/index.tsx diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx new file mode 100644 index 00000000..cc82a211 --- /dev/null +++ b/src/components/Menu/index.tsx @@ -0,0 +1,92 @@ +import React, { + MouseEventHandler, + ReactElement, + useCallback, + useState, +} from 'react'; +import Icon from '../Icon'; +import browser from 'webextension-polyfill'; +import classNames from 'classnames'; +import { useNavigate } from 'react-router'; + +export function MenuIcon(): ReactElement { + const [opened, setOpen] = useState(false); + + const toggleMenu = useCallback(() => { + setOpen(!opened); + }, [opened]); + + return ( +
+ {opened && ( + <> +
+ + + )} + +
+ ); +} + +export default function Menu(props: { + opened: boolean; + setOpen: (opened: boolean) => void; +}): ReactElement { + const navigate = useNavigate(); + const openExtensionInPage = () => { + props.setOpen(false); + browser.tabs.create({ + url: `chrome-extension://${chrome.runtime.id}/popup.html`, + }); + }; + + return ( +
+
+ + + { + props.setOpen(false); + navigate('/options'); + }} + /> +
+
+ ); +} + +function MenuRow(props: { + fa: string; + label: string; + onClick?: MouseEventHandler; + className?: string; +}): ReactElement { + return ( +
+ +
{props.label}
+
+ ); +} diff --git a/src/entries/Popup/Popup.tsx b/src/entries/Popup/Popup.tsx index f8d25578..51dfe6d3 100644 --- a/src/entries/Popup/Popup.tsx +++ b/src/entries/Popup/Popup.tsx @@ -33,6 +33,7 @@ import Icon from '../../components/Icon'; import classNames from 'classnames'; import { getConnection } from '../Background/db'; import { useIsConnected, setConnection } from '../../reducers/requests'; +import { MenuIcon } from '../../components/Menu'; const Popup = () => { const dispatch = useDispatch(); @@ -84,7 +85,7 @@ const Popup = () => { }, []); return ( -
+
{ alt="logo" onClick={() => navigate('/')} /> - +
+ + +
} /> @@ -141,7 +145,7 @@ function AppConnectionLogo() { return (
setShowConnectionDetails(true)} >
diff --git a/src/entries/Popup/index.scss b/src/entries/Popup/index.scss index b8ff5d09..75e96211 100644 --- a/src/entries/Popup/index.scss +++ b/src/entries/Popup/index.scss @@ -30,6 +30,10 @@ code { width: 100vw; height: 100vh; overflow: hidden; + + @media (min-width: 1024px) { + @apply bg-slate-400; + } } .button { diff --git a/src/pages/Options/index.tsx b/src/pages/Options/index.tsx index 5ce6a53e..df37f39e 100644 --- a/src/pages/Options/index.tsx +++ b/src/pages/Options/index.tsx @@ -76,18 +76,6 @@ export default function Options(): ReactElement { [onSave], ); - const openExtensionDetails = () => { - browser.tabs.create({ - url: `chrome://extensions/?id=${chrome.runtime.id}`, - }); - }; - - const openExtensionInPage = () => { - browser.tabs.create({ - url: `chrome-extension://${chrome.runtime.id}/popup.html`, - }); - }; - const onAdvanced = useCallback(() => { setAdvanced(!advanced); }, [advanced]); @@ -164,14 +152,6 @@ export default function Options(): ReactElement { Save
-
- - -
); } From 6510c42b4dd41befb8ba639f314e6de9c8a9d618 Mon Sep 17 00:00:00 2001 From: tsukino <0xtsukino@gmail.com> Date: Wed, 9 Oct 2024 02:13:54 +0800 Subject: [PATCH 3/6] feat: new ui for home view --- src/components/Menu/index.tsx | 21 +++- src/components/PluginList/index.tsx | 7 +- src/components/RequestTable/index.tsx | 21 +++- src/entries/Background/rpc.ts | 2 +- src/entries/Popup/Popup.tsx | 6 +- src/pages/Home/index.scss | 5 + src/pages/Home/index.tsx | 145 ++++++++++++++++++++------ src/pages/Plugins/index.tsx | 10 ++ src/pages/Requests/index.tsx | 4 +- 9 files changed, 174 insertions(+), 47 deletions(-) create mode 100644 src/pages/Home/index.scss create mode 100644 src/pages/Plugins/index.tsx diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index cc82a211..e346e0e2 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -50,8 +50,23 @@ export default function Menu(props: { return (
-
- +
+ { + props.setOpen(false); + }} + /> + { + props.setOpen(false); + navigate('/plugins'); + }} + /> -
+
-
+
diff --git a/src/entries/Background/rpc.ts b/src/entries/Background/rpc.ts index bbbdee23..f0fb5f4b 100644 --- a/src/entries/Background/rpc.ts +++ b/src/entries/Background/rpc.ts @@ -224,7 +224,7 @@ function handleGetProveRequests( sendResponse: (data?: any) => void, ): boolean { getNotaryRequests().then(async (reqs) => { - for (const req of reqs) { + for (const req of reqs.reverse()) { await browser.runtime.sendMessage({ type: BackgroundActiontype.push_action, data: { diff --git a/src/entries/Popup/Popup.tsx b/src/entries/Popup/Popup.tsx index 51dfe6d3..56fd0ab8 100644 --- a/src/entries/Popup/Popup.tsx +++ b/src/entries/Popup/Popup.tsx @@ -34,6 +34,7 @@ import classNames from 'classnames'; import { getConnection } from '../Background/db'; import { useIsConnected, setConnection } from '../../reducers/requests'; import { MenuIcon } from '../../components/Menu'; +import Plugins from '../../pages/Plugins'; const Popup = () => { const dispatch = useDispatch(); @@ -103,10 +104,11 @@ const Popup = () => { } /> } /> } /> - } /> - } /> + {/*} />*/} + } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/pages/Home/index.scss b/src/pages/Home/index.scss new file mode 100644 index 00000000..b5b8fec3 --- /dev/null +++ b/src/pages/Home/index.scss @@ -0,0 +1,5 @@ +#home { + &::-webkit-scrollbar { + display: none; + } +} \ No newline at end of file diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 74713e8d..10a34e95 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -2,78 +2,163 @@ import React, { MouseEventHandler, ReactElement, ReactNode, + useCallback, + useEffect, + useRef, useState, } from 'react'; import Icon from '../../components/Icon'; import classNames from 'classnames'; import { useNavigate } from 'react-router'; import { useRequests } from '../../reducers/requests'; -import { PluginList } from '../../components/PluginList'; -import PluginUploadInfo from '../../components/PluginInfo'; import { ErrorModal } from '../../components/ErrorModal'; +import History from '../History'; +import './index.scss'; +import Requests from '../Requests'; -export default function Home(): ReactElement { - const requests = useRequests(); +export default function Home(props: { + tab?: 'history' | 'network'; +}): ReactElement { const navigate = useNavigate(); const [error, showError] = useState(''); + const [tab, setTab] = useState<'history' | 'network'>(props.tab || 'history'); + const scrollableContent = useRef(null); + const [shouldFix, setFix] = useState(false); + + useEffect(() => { + const element = scrollableContent.current; + if (!element) return; + let timer = Date.now(); + const onScroll = () => { + const now = Date.now(); + if (now - timer > 20) { + timer = now; + console.log(element.scrollTop); + if (element.scrollTop >= 95) { + setFix(true); + } else { + setFix(false); + } + } + }; + element.addEventListener('scroll', onScroll); + + return () => { + element.removeEventListener('scroll', onScroll); + }; + }, [scrollableContent]); return ( -
+
{error && showError('')} message={error} />} -
- navigate('/requests')}> - Requests - {`(${requests.length})`} - - navigate('/custom')}> +
+ {/* navigate('/requests')}*/} + {/*>*/} + {/* Network*/} + {/**/} + navigate('/custom')} + title="Build a custom request" + > Custom navigate('/verify')} + title="Visualize an attestation" > Verify - navigate('/history')}> + {/* navigate('/history')}>*/} + {/* History*/} + {/**/} + {/**/} + {/* */} + {/* Install Plugin*/} + {/**/} +
+
+ setTab('network')} + selected={tab === 'network'} + > + Network + + setTab('history')} + selected={tab === 'history'} + > History - - - - Add a plugin - - navigate('/options')}> - Options - + +
+
+ {tab === 'history' && } + {tab === 'network' && }
-
); } +function TabSelector(props: { + children: string; + className?: string; + selected?: boolean; + onClick: MouseEventHandler; +}): ReactElement { + return ( + + ); +} + function NavButton(props: { fa: string; children?: ReactNode; onClick?: MouseEventHandler; className?: string; + title?: string; disabled?: boolean; }): ReactElement { return ( diff --git a/src/pages/Plugins/index.tsx b/src/pages/Plugins/index.tsx new file mode 100644 index 00000000..c69d62ad --- /dev/null +++ b/src/pages/Plugins/index.tsx @@ -0,0 +1,10 @@ +import React, { ReactElement } from "react"; +import { PluginList } from "../../components/PluginList"; + +export default function Plugins(): ReactElement { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/src/pages/Requests/index.tsx b/src/pages/Requests/index.tsx index f4816f86..c71636a4 100644 --- a/src/pages/Requests/index.tsx +++ b/src/pages/Requests/index.tsx @@ -2,11 +2,11 @@ import React, { ReactElement } from 'react'; import RequestTable from '../../components/RequestTable'; import { useRequests } from '../../reducers/requests'; -export default function Requests(): ReactElement { +export default function Requests(props: { shouldFix?: boolean }): ReactElement { const requests = useRequests(); return ( <> - + ); } From d38ddc8c362657c2d6c91f73b38f9584f172f287 Mon Sep 17 00:00:00 2001 From: tsukino <0xtsukino@gmail.com> Date: Wed, 9 Oct 2024 16:30:02 +0800 Subject: [PATCH 4/6] feat: new action panel ui --- src/components/Menu/index.tsx | 30 ++-- src/entries/Popup/Popup.tsx | 2 +- src/entries/SidePanel/SidePanel.tsx | 2 +- src/pages/History/index.tsx | 2 +- src/pages/Home/index.tsx | 204 +++++++++++++++++++++++----- src/reducers/plugins.tsx | 33 +++++ 6 files changed, 222 insertions(+), 51 deletions(-) diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index e346e0e2..2fb23f70 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -1,6 +1,7 @@ import React, { MouseEventHandler, ReactElement, + ReactNode, useCallback, useState, } from 'react'; @@ -8,6 +9,7 @@ import Icon from '../Icon'; import browser from 'webextension-polyfill'; import classNames from 'classnames'; import { useNavigate } from 'react-router'; +import PluginUploadInfo from '../PluginInfo'; export function MenuIcon(): ReactElement { const [opened, setOpen] = useState(false); @@ -53,34 +55,40 @@ export default function Menu(props: {
{ props.setOpen(false); }} - /> + > + + Install Plugin + { props.setOpen(false); navigate('/plugins'); }} - /> + > + Plugins + + > + Expand + { props.setOpen(false); navigate('/options'); }} - /> + > + Options +
); @@ -88,20 +96,20 @@ export default function Menu(props: { function MenuRow(props: { fa: string; - label: string; + children?: ReactNode; onClick?: MouseEventHandler; className?: string; }): ReactElement { return (
-
{props.label}
+ {props.children}
); } diff --git a/src/entries/Popup/Popup.tsx b/src/entries/Popup/Popup.tsx index 56fd0ab8..052d3425 100644 --- a/src/entries/Popup/Popup.tsx +++ b/src/entries/Popup/Popup.tsx @@ -104,7 +104,7 @@ const Popup = () => { } /> } /> } /> - {/*} />*/} + } /> } /> } /> } /> diff --git a/src/entries/SidePanel/SidePanel.tsx b/src/entries/SidePanel/SidePanel.tsx index 738a36cf..ba8046b1 100644 --- a/src/entries/SidePanel/SidePanel.tsx +++ b/src/entries/SidePanel/SidePanel.tsx @@ -235,7 +235,7 @@ function StepContent( )} onClick={viewProofInPopup} > - View Proof + View ); } else if (notaryRequest?.status === 'pending' || pending || notarizationId) { diff --git a/src/pages/History/index.tsx b/src/pages/History/index.tsx index f8150ab3..1fef73e1 100644 --- a/src/pages/History/index.tsx +++ b/src/pages/History/index.tsx @@ -160,7 +160,7 @@ export function OneRequestHistory(props: { className="bg-slate-600 text-slate-200 hover:bg-slate-500 hover:text-slate-100" onClick={onView} fa="fa-solid fa-receipt" - ctaText="View Proof" + ctaText="View" hidden={hideActions.includes('view')} /> (props.tab || 'history'); const scrollableContent = useRef(null); const [shouldFix, setFix] = useState(false); + const [actionPanelElement, setActionPanelElement] = + useState(null); + const [scrollTop, setScrollTop] = useState(0); + + useEffect(() => { + fetchPluginHashes(); + }, []); useEffect(() => { const element = scrollableContent.current; if (!element) return; + if (!actionPanelElement) return; + let timer = Date.now(); const onScroll = () => { const now = Date.now(); if (now - timer > 20) { timer = now; - console.log(element.scrollTop); - if (element.scrollTop >= 95) { + setScrollTop(element.scrollTop); + if (element.scrollTop >= actionPanelElement.clientHeight) { setFix(true); } else { setFix(false); } } }; + element.addEventListener('scroll', onScroll); return () => { element.removeEventListener('scroll', onScroll); }; - }, [scrollableContent]); + }, [scrollableContent, actionPanelElement]); return (
{error && showError('')} message={error} />} -
- {/* navigate('/requests')}*/} - {/*>*/} - {/* Network*/} - {/**/} - navigate('/custom')} - title="Build a custom request" - > - Custom - - navigate('/verify')} - title="Visualize an attestation" - > - Verify - - {/* navigate('/history')}>*/} - {/* History*/} - {/**/} - {/**/} - {/* */} - {/* Install Plugin*/} - {/**/} -
+
void; +}) { + const pluginHashes = usePluginHashes(); + const navigate = useNavigate(); + const container = useRef(null); + const [isOverflow, setOverflow] = useState(false); + const [expanded, setExpand] = useState(false); + + useEffect(() => { + const element = container.current; + + if (!element) return; + + setActionPanelElement(element); + + const onCheckSize = () => { + if (element.scrollWidth > element.clientWidth) { + setOverflow(true); + } else { + setOverflow(false); + } + }; + + onCheckSize(); + + window.addEventListener('resize', onCheckSize); + + return () => { + window.removeEventListener('resize', onCheckSize); + }; + }, [container, pluginHashes]); + + useEffect(() => { + const element = container.current; + + if (!element) return; + + if (scrollTop >= element.clientHeight) { + setExpand(false); + } + }, [container, scrollTop]); + + return ( +
+ navigate('/custom')} + title="Build a custom request" + > + Custom + + navigate('/verify')} + title="Visualize an attestation" + > + Verify + + {pluginHashes.map((hash) => ( + + ))} + {pluginHashes.map((hash) => ( + + ))} + {pluginHashes.map((hash) => ( + + ))} + {pluginHashes.map((hash) => ( + + ))} + + +
+ ); +} + +function PluginIcon({ hash }: { hash: string }) { + const config = usePluginConfig(hash); + const onPluginClick = useOnPluginClick(hash); + + const onClick = useCallback(() => { + if (!config) return; + onPluginClick(); + }, [onPluginClick, config]); + + return ( + + ); +} + function TabSelector(props: { children: string; className?: string; @@ -158,7 +288,7 @@ function NavButton(props: { fa={props.fa} size={0.875} /> - + {props.children} diff --git a/src/reducers/plugins.tsx b/src/reducers/plugins.tsx index 592b4fa3..7fa12461 100644 --- a/src/reducers/plugins.tsx +++ b/src/reducers/plugins.tsx @@ -1,6 +1,11 @@ import { useSelector } from 'react-redux'; import { AppRootState } from './index'; import deepEqual from 'fast-deep-equal'; +import { useCallback, useEffect, useState } from 'react'; +import { getPluginConfigByHash } from '../entries/Background/db'; +import { PluginConfig } from '../utils/misc'; +import { runPlugin } from '../utils/rpc'; +import browser from 'webextension-polyfill'; enum ActionType { '/plugin/addPlugin' = '/plugin/addPlugin', @@ -52,3 +57,31 @@ export const usePluginHashes = (): string[] => { return state.plugins.order; }, deepEqual); }; + +export const usePluginConfig = (hash: string) => { + const [config, setConfig] = useState(null); + useEffect(() => { + (async function () { + setConfig(await getPluginConfigByHash(hash)); + })(); + }, [hash]); + return config; +}; + +export const useOnPluginClick = (hash: string) => { + return useCallback(async () => { + await runPlugin(hash, 'start'); + + const [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + + await browser.storage.local.set({ plugin_hash: hash }); + + // @ts-ignore + if (chrome.sidePanel) await chrome.sidePanel.open({ tabId: tab.id }); + + window.close(); + }, [hash]); +}; From b36352703aa506e407d3a6f49d0b9078cc44859a Mon Sep 17 00:00:00 2001 From: tsukino <0xtsukino@gmail.com> Date: Wed, 9 Oct 2024 16:37:29 +0800 Subject: [PATCH 5/6] fix: remove dup plugins --- src/pages/Home/index.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index daaaea42..139dc34d 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -178,15 +178,6 @@ function ActionPanel({ {pluginHashes.map((hash) => ( ))} - {pluginHashes.map((hash) => ( - - ))} - {pluginHashes.map((hash) => ( - - ))} - {pluginHashes.map((hash) => ( - - ))}
+
+ + +
); }