From 0dfac89e26473ffac4ca487aa75233ddc9cbdf4a Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 8 Sep 2021 13:04:03 +1000 Subject: [PATCH] grid: permalink to apps and treaties --- pkg/grid/src/app.tsx | 7 +- pkg/grid/src/logic/useQuery.ts | 46 +++++++++++++ pkg/grid/src/nav/search/TreatyInfo.tsx | 10 ++- pkg/grid/src/pages/Grid.tsx | 9 --- pkg/grid/src/pages/PermalinkRoutes.tsx | 92 ++++++++++++++++++++++++++ pkg/grid/src/state/docket.ts | 2 +- pkg/grid/src/state/kiln.ts | 10 ++- 7 files changed, 162 insertions(+), 14 deletions(-) create mode 100644 pkg/grid/src/logic/useQuery.ts create mode 100644 pkg/grid/src/pages/PermalinkRoutes.tsx diff --git a/pkg/grid/src/app.tsx b/pkg/grid/src/app.tsx index 098f9ddfc..9b2ac4e2b 100644 --- a/pkg/grid/src/app.tsx +++ b/pkg/grid/src/app.tsx @@ -3,6 +3,8 @@ import Mousetrap from 'mousetrap'; import { BrowserRouter, Switch, Route, useHistory } from 'react-router-dom'; import { Grid } from './pages/Grid'; import useDocketState from './state/docket'; +import {PermalinkRoutes} from './pages/PermalinkRoutes'; +import useKilnState from './state/kiln'; const AppRoutes = () => { const { push } = useHistory(); @@ -13,7 +15,9 @@ const AppRoutes = () => { const { fetchAllies, fetchCharges } = useDocketState.getState(); fetchCharges(); fetchAllies(); - + const { fetchVats, fetchLag } = useKilnState.getState(); + fetchVats(); + fetchLag(); Mousetrap.bind(['command+/', 'ctrl+/'], () => { push('/leap/search'); }); @@ -21,6 +25,7 @@ const AppRoutes = () => { return ( + ); diff --git a/pkg/grid/src/logic/useQuery.ts b/pkg/grid/src/logic/useQuery.ts new file mode 100644 index 000000000..142a15aff --- /dev/null +++ b/pkg/grid/src/logic/useQuery.ts @@ -0,0 +1,46 @@ +import _ from 'lodash'; +import { useCallback, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; + +function mergeQuery(search: URLSearchParams, added: Record) { + _.forIn(added, (v, k) => { + if (v) { + search.append(k, v); + } else { + search.delete(k); + } + }); +} + +export function useQuery() { + const { search, pathname } = useLocation(); + + const query = useMemo(() => new URLSearchParams(search), [search]); + + const appendQuery = useCallback( + (added: Record) => { + const q = new URLSearchParams(search); + mergeQuery(q, added); + return q.toString(); + }, + [search] + ); + + const toQuery = useCallback( + (params: Record, path = pathname) => { + const q = new URLSearchParams(search); + mergeQuery(q, params); + return { + pathname: path, + search: q.toString() + }; + }, + [search, pathname] + ); + + return { + query, + appendQuery, + toQuery + }; +} diff --git a/pkg/grid/src/nav/search/TreatyInfo.tsx b/pkg/grid/src/nav/search/TreatyInfo.tsx index c9710d569..c4bbee27f 100644 --- a/pkg/grid/src/nav/search/TreatyInfo.tsx +++ b/pkg/grid/src/nav/search/TreatyInfo.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { AppInfo } from '../../components/AppInfo'; import { ShipName } from '../../components/ShipName'; -import { useCharge, useTreaty } from '../../state/docket'; +import useDocketState, { useCharge, useTreaty } from '../../state/docket'; import { useVat } from '../../state/kiln'; import { useLeapStore } from '../Nav'; @@ -13,10 +13,16 @@ export const TreatyInfo = () => { const vat = useVat(desk); const charge = useCharge(desk); + useEffect(() => { + if(!charge) { + useDocketState.getState().requestTreaty(host, desk); + } + }, [host, desk]); + useEffect(() => { select( <> - Apps by : {treaty?.title} + {treaty?.title} ); }, [treaty?.title]); diff --git a/pkg/grid/src/pages/Grid.tsx b/pkg/grid/src/pages/Grid.tsx index cf7a0a410..43a6066a7 100644 --- a/pkg/grid/src/pages/Grid.tsx +++ b/pkg/grid/src/pages/Grid.tsx @@ -17,15 +17,6 @@ export const Grid: FunctionComponent = ({ match }) => { const charges = useCharges(); const chargesLoaded = Object.keys(charges).length > 0; - useEffect(() => { - const { fetchCharges, fetchAllies } = useDocketState.getState(); - const { fetchVats, fetchLag } = useKilnState.getState(); - fetchCharges(); - fetchAllies(); - fetchVats(); - fetchLag(); - }, []); - return (
diff --git a/pkg/grid/src/pages/PermalinkRoutes.tsx b/pkg/grid/src/pages/PermalinkRoutes.tsx new file mode 100644 index 000000000..745864e34 --- /dev/null +++ b/pkg/grid/src/pages/PermalinkRoutes.tsx @@ -0,0 +1,92 @@ +import React, { useEffect } from 'react'; +import { Switch, Route, Redirect, RouteComponentProps } from 'react-router-dom'; +import { Spinner } from '../components/Spinner'; +import { useQuery } from '../logic/useQuery'; +import { useCharge } from '../state/docket'; +import useKilnState, { useKilnLoaded, useVat } from '../state/kiln'; +import { getAppHref } from '../state/util'; + +function getDeskByForeignRef(ship: string, desk: string): string | undefined { + const { vats } = useKilnState.getState(); + console.log(ship, desk); + const found = Object.entries(vats).find( + ([d, vat]) => vat.arak.ship === ship && vat.arak.desk === desk + ); + return !!found ? found[0] : undefined; +} + +interface AppLinkProps + extends RouteComponentProps<{ + ship: string; + desk: string; + link: string; + }> {} + +function AppLink(props: AppLinkProps) { + const { ship, desk, link = '' } = props.match.params; + const ourDesk = getDeskByForeignRef(ship, desk); + + if (ourDesk) { + return ; + } + return ; +} + +function AppLinkNotFound(props: AppLinkProps) { + const { ship, desk } = props.match.params; + return (); +} + +function AppLinkInvalid(props: AppLinkProps) { + return ( +
+

Link was malformed

+

The link you tried to follow was invalid

+
+ ); +} +function AppLinkRedirect({ desk, link }: { desk: string; link: string }) { + const vat = useVat(desk); + const charge = useCharge(desk); + useEffect(() => { + const query = new URLSearchParams({ + 'grid-link': encodeURIComponent(`/${link}`) + }); + const url = `${getAppHref(charge.href)}?${query.toString()}`; + window.open(url, desk); + }, []); + return ; +} + +const LANDSCAPE_SHIP = '~zod'; +const LANDSCAPE_DESK = 'groups'; + +function LandscapeLink(props: RouteComponentProps<{ link: string }>) { + const { link } = props.match.params; + + return ; +} + +export function PermalinkRoutes() { + const loaded = useKilnLoaded(); + + const { query } = useQuery(); + + if (query.has('ext')) { + const ext = query.get('ext')!; + const url = `/perma${ext.slice(16)}`; + return ; + } + + if (!loaded) { + return ; + } + + return ( + + + + + + ); +} diff --git a/pkg/grid/src/state/docket.ts b/pkg/grid/src/state/docket.ts index a9f8b2996..628bfb413 100644 --- a/pkg/grid/src/state/docket.ts +++ b/pkg/grid/src/state/docket.ts @@ -87,7 +87,7 @@ const useDocketState = create((set, get) => ({ return treaties[key]; } - const result = await api.subscribeOnce('docket', `/treaty/${key}`, 20000); + const result = await api.subscribeOnce('treaty', `/treaty/${key}`, 20000); const treaty = { ...normalizeDocket(result, desk), ship }; set((state) => ({ treaties: { ...state.treaties, [key]: treaty } diff --git a/pkg/grid/src/state/kiln.ts b/pkg/grid/src/state/kiln.ts index 7e9b1973b..33bfcc0ad 100644 --- a/pkg/grid/src/state/kiln.ts +++ b/pkg/grid/src/state/kiln.ts @@ -8,6 +8,7 @@ import { mockVats } from './mock-data'; interface KilnState { vats: Vats; + loaded: boolean; fetchVats: () => Promise; lag: boolean; fetchLag: () => Promise; @@ -16,13 +17,15 @@ interface KilnState { export const useKilnState = create((set) => ({ vats: useMockData ? mockVats : {}, lag: !!useMockData, + loaded: false, fetchVats: async () => { if (useMockData) { await fakeRequest({}, 500); + set({ loaded: true }); return; } const vats = await api.scry(getVats); - set({ vats }); + set({ vats, loaded: true }); }, fetchLag: async () => { const lag = await api.scry(scryLag); @@ -53,4 +56,9 @@ export function useLag() { return useKilnState(selLag); } +const selLoaded = (s: KilnState) => s.loaded; +export function useKilnLoaded() { + return useKilnState(selLoaded); +} + export default useKilnState;