+
{error &&
showError('')} message={error} />}
-
-
navigate('/requests')}>
- Requests
- {`(${requests.length})`}
-
-
navigate('/custom')}>
- Custom
-
-
navigate('/verify')}
+
+
+
setTab('network')}
+ selected={tab === 'network'}
+ >
+ Network
+
+
setTab('history')}
+ selected={tab === 'history'}
>
- Verify
-
- navigate('/history')}>
History
-
-
-
- Add a plugin
-
- navigate('/options')}>
- Options
-
+
+
+
+ {tab === 'history' && }
+ {tab === 'network' && }
-
);
}
+function ActionPanel({
+ setActionPanelElement,
+ scrollTop,
+}: {
+ scrollTop: number;
+ setActionPanelElement: (el: HTMLDivElement) => 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) => (
+
+ ))}
+
+
+
+ );
+}
+
+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;
+ 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/Options/index.tsx b/src/pages/Options/index.tsx
index df37f39e..9ade6b1d 100644
--- a/src/pages/Options/index.tsx
+++ b/src/pages/Options/index.tsx
@@ -80,6 +80,10 @@ export default function Options(): ReactElement {
setAdvanced(!advanced);
}, [advanced]);
+ const openInTab = useCallback((url: string) => {
+ browser.tabs.create({ url });
+ }, []);
+
return (
{showReloadModal && (
@@ -152,6 +156,22 @@ export default function Options(): ReactElement {
Save
+
+
+
+
);
}
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 (
<>
-
+
>
);
}
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]);
+};