mirror of
https://github.com/ilyakooo0/urbit.git
synced 2025-01-01 19:46:36 +03:00
grid: permalink to apps and treaties
This commit is contained in:
parent
b3e634db48
commit
0dfac89e26
@ -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 (
|
||||
<Switch>
|
||||
<Route path="/perma" component={PermalinkRoutes} />
|
||||
<Route path={['/leap/:menu', '/']} component={Grid} />
|
||||
</Switch>
|
||||
);
|
||||
|
46
pkg/grid/src/logic/useQuery.ts
Normal file
46
pkg/grid/src/logic/useQuery.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import _ from 'lodash';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
function mergeQuery(search: URLSearchParams, added: Record<string, string>) {
|
||||
_.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<string, string>) => {
|
||||
const q = new URLSearchParams(search);
|
||||
mergeQuery(q, added);
|
||||
return q.toString();
|
||||
},
|
||||
[search]
|
||||
);
|
||||
|
||||
const toQuery = useCallback(
|
||||
(params: Record<string, string>, path = pathname) => {
|
||||
const q = new URLSearchParams(search);
|
||||
mergeQuery(q, params);
|
||||
return {
|
||||
pathname: path,
|
||||
search: q.toString()
|
||||
};
|
||||
},
|
||||
[search, pathname]
|
||||
);
|
||||
|
||||
return {
|
||||
query,
|
||||
appendQuery,
|
||||
toQuery
|
||||
};
|
||||
}
|
@ -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 <ShipName name={ship} className="font-mono" />: {treaty?.title}
|
||||
{treaty?.title}
|
||||
</>
|
||||
);
|
||||
}, [treaty?.title]);
|
||||
|
@ -17,15 +17,6 @@ export const Grid: FunctionComponent<GridProps> = ({ 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 (
|
||||
<div className="flex flex-col">
|
||||
<header className="fixed sm:sticky bottom-0 sm:bottom-auto sm:top-0 left-0 z-30 flex justify-center w-full bg-white">
|
||||
|
92
pkg/grid/src/pages/PermalinkRoutes.tsx
Normal file
92
pkg/grid/src/pages/PermalinkRoutes.tsx
Normal file
@ -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 <AppLinkRedirect desk={ourDesk} link={link} />;
|
||||
}
|
||||
return <AppLinkNotFound {...props} />;
|
||||
}
|
||||
|
||||
function AppLinkNotFound(props: AppLinkProps) {
|
||||
const { ship, desk } = props.match.params;
|
||||
return (<Redirect to={`/leap/search/direct/apps/${ship}/${desk}`} />);
|
||||
}
|
||||
|
||||
function AppLinkInvalid(props: AppLinkProps) {
|
||||
return (
|
||||
<div>
|
||||
<h4>Link was malformed</h4>
|
||||
<p>The link you tried to follow was invalid</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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 <Redirect to="/" />;
|
||||
}
|
||||
|
||||
const LANDSCAPE_SHIP = '~zod';
|
||||
const LANDSCAPE_DESK = 'groups';
|
||||
|
||||
function LandscapeLink(props: RouteComponentProps<{ link: string }>) {
|
||||
const { link } = props.match.params;
|
||||
|
||||
return <Redirect to={`/perma/${LANDSCAPE_SHIP}/${LANDSCAPE_DESK}/${link}`} />;
|
||||
}
|
||||
|
||||
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 <Redirect to={url} />;
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route path="/perma/group/:link+" component={LandscapeLink} />
|
||||
<Route path="/perma/:ship/:desk/:link*" component={AppLink} />
|
||||
<Route path="/" component={AppLinkInvalid} />
|
||||
</Switch>
|
||||
);
|
||||
}
|
@ -87,7 +87,7 @@ const useDocketState = create<DocketState>((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 }
|
||||
|
@ -8,6 +8,7 @@ import { mockVats } from './mock-data';
|
||||
|
||||
interface KilnState {
|
||||
vats: Vats;
|
||||
loaded: boolean;
|
||||
fetchVats: () => Promise<void>;
|
||||
lag: boolean;
|
||||
fetchLag: () => Promise<void>;
|
||||
@ -16,13 +17,15 @@ interface KilnState {
|
||||
export const useKilnState = create<KilnState>((set) => ({
|
||||
vats: useMockData ? mockVats : {},
|
||||
lag: !!useMockData,
|
||||
loaded: false,
|
||||
fetchVats: async () => {
|
||||
if (useMockData) {
|
||||
await fakeRequest({}, 500);
|
||||
set({ loaded: true });
|
||||
return;
|
||||
}
|
||||
const vats = await api.scry<Vats>(getVats);
|
||||
set({ vats });
|
||||
set({ vats, loaded: true });
|
||||
},
|
||||
fetchLag: async () => {
|
||||
const lag = await api.scry<boolean>(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;
|
||||
|
Loading…
Reference in New Issue
Block a user