mirror of
https://github.com/urbit/shrub.git
synced 2024-12-11 11:02:25 +03:00
Merge pull request #5209 from urbit/lf/app-linking
grid/treaty: link to apps
This commit is contained in:
commit
caea7a7f1f
@ -7,6 +7,7 @@
|
|||||||
sovereign=(map desk treaty)
|
sovereign=(map desk treaty)
|
||||||
entente=alliance
|
entente=alliance
|
||||||
=allies:ally
|
=allies:ally
|
||||||
|
direct=(set [=ship =desk])
|
||||||
==
|
==
|
||||||
--
|
--
|
||||||
^- agent:gall
|
^- agent:gall
|
||||||
@ -95,16 +96,18 @@
|
|||||||
[%treaty @ @ ~]
|
[%treaty @ @ ~]
|
||||||
=/ =ship (slav %p i.t.path)
|
=/ =ship (slav %p i.t.path)
|
||||||
=* desk i.t.t.path
|
=* desk i.t.t.path
|
||||||
=/ =treaty
|
?: =(our.bowl ship)
|
||||||
?: =(our.bowl ship) (~(got by sovereign) desk)
|
:_(this (fact-init:io treaty+!>((~(got by sovereign) desk)))^~)
|
||||||
(~(got by treaties) [ship desk])
|
?^ treat=(~(get by treaties) [ship desk])
|
||||||
:_ this
|
:_ this
|
||||||
(fact-init:io treaty+!>(treaty))^~
|
(fact-init:io treaty+!>(u.treat))^~
|
||||||
|
?> =(our.bowl src.bowl)
|
||||||
|
=. direct (~(put in direct) [ship desk])
|
||||||
|
:_(this (drop ~(safe-watch tr:cc [ship desk])))
|
||||||
::
|
::
|
||||||
[%alliance ~]
|
[%alliance ~]
|
||||||
:_ this
|
:_ this
|
||||||
(fact-init:io (alliance-update:cg:cc %ini entente))^~
|
(fact-init:io (alliance-update:cg:cc %ini entente))^~
|
||||||
|
|
||||||
:: local
|
:: local
|
||||||
::
|
::
|
||||||
[%allies ~]
|
[%allies ~]
|
||||||
@ -196,7 +199,9 @@
|
|||||||
::
|
::
|
||||||
%watch-ack
|
%watch-ack
|
||||||
?~ p.sign `this
|
?~ p.sign `this
|
||||||
=. treaties (~(del by treaties) ship desk)
|
=: treaties (~(del by treaties) ship desk)
|
||||||
|
direct (~(del in direct) ship desk)
|
||||||
|
==
|
||||||
%- (slog leaf+"Withdrew from treaty {<ship>}/{<desk>}" u.p.sign)
|
%- (slog leaf+"Withdrew from treaty {<ship>}/{<desk>}" u.p.sign)
|
||||||
`this
|
`this
|
||||||
::
|
::
|
||||||
|
@ -3,6 +3,8 @@ import Mousetrap from 'mousetrap';
|
|||||||
import { BrowserRouter, Switch, Route, useHistory } from 'react-router-dom';
|
import { BrowserRouter, Switch, Route, useHistory } from 'react-router-dom';
|
||||||
import { Grid } from './pages/Grid';
|
import { Grid } from './pages/Grid';
|
||||||
import useDocketState from './state/docket';
|
import useDocketState from './state/docket';
|
||||||
|
import { PermalinkRoutes } from './pages/PermalinkRoutes';
|
||||||
|
import useKilnState from './state/kiln';
|
||||||
import { usePreferencesStore } from './nav/preferences/usePreferencesStore';
|
import { usePreferencesStore } from './nav/preferences/usePreferencesStore';
|
||||||
import useContactState from './state/contact';
|
import useContactState from './state/contact';
|
||||||
import api from './state/api';
|
import api from './state/api';
|
||||||
@ -40,6 +42,9 @@ const AppRoutes = () => {
|
|||||||
const { fetchAllies, fetchCharges } = useDocketState.getState();
|
const { fetchAllies, fetchCharges } = useDocketState.getState();
|
||||||
fetchCharges();
|
fetchCharges();
|
||||||
fetchAllies();
|
fetchAllies();
|
||||||
|
const { fetchVats, fetchLag } = useKilnState.getState();
|
||||||
|
fetchVats();
|
||||||
|
fetchLag();
|
||||||
useContactState.getState().initialize(api);
|
useContactState.getState().initialize(api);
|
||||||
|
|
||||||
Mousetrap.bind(['command+/', 'ctrl+/'], () => {
|
Mousetrap.bind(['command+/', 'ctrl+/'], () => {
|
||||||
@ -49,6 +54,7 @@ const AppRoutes = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route path="/perma" component={PermalinkRoutes} />
|
||||||
<Route path={['/leap/:menu', '/']} component={Grid} />
|
<Route path={['/leap/:menu', '/']} component={Grid} />
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
|
@ -20,15 +20,13 @@ export const Setting: FC<SettingsProps> = ({ name, on, toggle, className, childr
|
|||||||
<h3 id={id} className="flex items-center h4 mb-2">
|
<h3 id={id} className="flex items-center h4 mb-2">
|
||||||
{name} {status === 'loading' && <Spinner className="ml-2" />}
|
{name} {status === 'loading' && <Spinner className="ml-2" />}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex-none mr-2">
|
<Toggle
|
||||||
<Toggle
|
aria-labelledby={id}
|
||||||
aria-labelledby={id}
|
pressed={on}
|
||||||
pressed={on}
|
onPressedChange={call}
|
||||||
onPressedChange={call}
|
className="text-blue-400"
|
||||||
className="text-blue-400"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 space-y-6">{children}</div>
|
<div className="flex-1 space-y-6">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
@ -5,8 +5,40 @@ import { NotificationPrefs } from './preferences/NotificationPrefs';
|
|||||||
import { SystemUpdatePrefs } from './preferences/SystemUpdatePrefs';
|
import { SystemUpdatePrefs } from './preferences/SystemUpdatePrefs';
|
||||||
import notificationsSVG from '../assets/notifications.svg';
|
import notificationsSVG from '../assets/notifications.svg';
|
||||||
import systemUpdatesSVG from '../assets/system-updates.svg';
|
import systemUpdatesSVG from '../assets/system-updates.svg';
|
||||||
|
import { InterfacePrefs } from './preferences/InterfacePrefs';
|
||||||
|
|
||||||
export const SystemPreferences = ({ match }: RouteComponentProps<{ submenu: string }>) => {
|
interface SystemPreferencesSectionProps extends RouteComponentProps<{ submenu: string }> {
|
||||||
|
submenu: string;
|
||||||
|
active: boolean;
|
||||||
|
text: string;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SystemPreferencesSection({
|
||||||
|
match,
|
||||||
|
submenu,
|
||||||
|
active,
|
||||||
|
icon,
|
||||||
|
text
|
||||||
|
}: SystemPreferencesSectionProps) {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
to={`${match.url}/${submenu}`}
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center px-5 py-3 hover:text-black hover:bg-gray-100',
|
||||||
|
active && 'text-black bg-gray-100'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{icon ? <img className="w-8 h-8 mr-3" src={icon} alt="" /> : null}
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SystemPreferences = (props: RouteComponentProps<{ submenu: string }>) => {
|
||||||
|
const { match } = props;
|
||||||
const subMatch = useRouteMatch<{ submenu: string }>(`${match.url}/:submenu`);
|
const subMatch = useRouteMatch<{ submenu: string }>(`${match.url}/:submenu`);
|
||||||
|
|
||||||
const matchSub = useCallback(
|
const matchSub = useCallback(
|
||||||
@ -28,36 +60,34 @@ export const SystemPreferences = ({ match }: RouteComponentProps<{ submenu: stri
|
|||||||
</div>
|
</div>
|
||||||
<nav className="border-b-2 border-gray-50">
|
<nav className="border-b-2 border-gray-50">
|
||||||
<ul className="font-semibold">
|
<ul className="font-semibold">
|
||||||
<li>
|
<SystemPreferencesSection
|
||||||
<Link
|
{...props}
|
||||||
to={`${match.url}/notifications`}
|
text="Notifications"
|
||||||
className={classNames(
|
icon={notificationsSVG}
|
||||||
'flex items-center px-8 py-3 hover:text-black hover:bg-gray-50',
|
submenu="notifications"
|
||||||
matchSub('notifications') && 'text-black bg-gray-50'
|
active={matchSub('notifications')}
|
||||||
)}
|
/>
|
||||||
>
|
<SystemPreferencesSection
|
||||||
<img className="w-8 h-8 mr-3" src={notificationsSVG} alt="" />
|
{...props}
|
||||||
Notifications
|
text="System Updates"
|
||||||
</Link>
|
icon={systemUpdatesSVG}
|
||||||
</li>
|
submenu="system-updates"
|
||||||
<li>
|
active={matchSub('system-updates')}
|
||||||
<Link
|
/>
|
||||||
to={`${match.url}/system-updates`}
|
<SystemPreferencesSection
|
||||||
className={classNames(
|
{...props}
|
||||||
'flex items-center px-8 py-3 hover:text-black hover:bg-gray-50',
|
text="Interface Settings"
|
||||||
matchSub('system-updates') && 'text-black bg-gray-50'
|
icon={systemUpdatesSVG}
|
||||||
)}
|
submenu="interface"
|
||||||
>
|
active={matchSub('interface')}
|
||||||
<img className="w-8 h-8 mr-3" src={systemUpdatesSVG} alt="" />
|
/>
|
||||||
System Updates
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
<section className="flex-1 p-8 text-black">
|
<section className="flex-1 p-8 text-black">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={`${match.url}/system-updates`} component={SystemUpdatePrefs} />
|
<Route path={`${match.url}/system-updates`} component={SystemUpdatePrefs} />
|
||||||
|
<Route path={`${match.url}/interface`} component={InterfacePrefs} />
|
||||||
<Route path={[`${match.url}/notifications`, match.url]} component={NotificationPrefs} />
|
<Route path={[`${match.url}/notifications`, match.url]} component={NotificationPrefs} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</section>
|
</section>
|
||||||
|
40
pkg/grid/src/nav/preferences/InterfacePrefs.tsx
Normal file
40
pkg/grid/src/nav/preferences/InterfacePrefs.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Setting } from '../../components/Setting';
|
||||||
|
import { ShipName } from '../../components/ShipName';
|
||||||
|
import { useProtocolHandling, setLocalState } from '../../state/local';
|
||||||
|
|
||||||
|
export function InterfacePrefs() {
|
||||||
|
const protocolHandling = useProtocolHandling();
|
||||||
|
const toggleProtoHandling = async () => {
|
||||||
|
if (!protocolHandling && window?.navigator?.registerProtocolHandler) {
|
||||||
|
try {
|
||||||
|
window.navigator.registerProtocolHandler('web+urbitgraph', '/apps/grid/perma?ext=%s', 'Urbit Links');
|
||||||
|
setLocalState((s) => {
|
||||||
|
s.protocolHandling = true;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
} else if (protocolHandling && window.navigator?.unregisterProtocolHandler) {
|
||||||
|
try {
|
||||||
|
window.navigator.unregisterProtocolHandler('web+urbitgraph', '/apps/grid/perma?ext=%s');
|
||||||
|
setLocalState((s) => {
|
||||||
|
s.protocolHandling = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h2 className="h3 mb-7">Interface Settings</h2>
|
||||||
|
<Setting on={protocolHandling} toggle={toggleProtoHandling} name="Handle Urbit links">
|
||||||
|
<p>Automatically open urbit links with this urbit</p>
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<div className="space-y-3"> </div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { AppInfo } from '../../components/AppInfo';
|
import { AppInfo } from '../../components/AppInfo';
|
||||||
import { ShipName } from '../../components/ShipName';
|
import { ShipName } from '../../components/ShipName';
|
||||||
import { useCharge, useTreaty } from '../../state/docket';
|
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
|
||||||
import { useVat } from '../../state/kiln';
|
import { useVat } from '../../state/kiln';
|
||||||
import { useLeapStore } from '../Nav';
|
import { useLeapStore } from '../Nav';
|
||||||
|
|
||||||
@ -13,10 +13,16 @@ export const TreatyInfo = () => {
|
|||||||
const vat = useVat(desk);
|
const vat = useVat(desk);
|
||||||
const charge = useCharge(desk);
|
const charge = useCharge(desk);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(!charge) {
|
||||||
|
useDocketState.getState().requestTreaty(host, desk);
|
||||||
|
}
|
||||||
|
}, [host, desk]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
select(
|
select(
|
||||||
<>
|
<>
|
||||||
Apps by <ShipName name={ship} className="font-mono" />: {treaty?.title}
|
{treaty?.title}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}, [treaty?.title]);
|
}, [treaty?.title]);
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { map, omit } from 'lodash';
|
import { map, omit } from 'lodash';
|
||||||
import React, { FunctionComponent, useEffect } from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
import { Route, RouteComponentProps } from 'react-router-dom';
|
import { Route, RouteComponentProps } from 'react-router-dom';
|
||||||
import { MenuState, Nav } from '../nav/Nav';
|
import { MenuState, Nav } from '../nav/Nav';
|
||||||
import useDocketState, { useCharges } from '../state/docket';
|
import { useCharges } from '../state/docket';
|
||||||
import { useKilnState } from '../state/kiln';
|
|
||||||
import { RemoveApp } from '../tiles/RemoveApp';
|
import { RemoveApp } from '../tiles/RemoveApp';
|
||||||
import { SuspendApp } from '../tiles/SuspendApp';
|
import { SuspendApp } from '../tiles/SuspendApp';
|
||||||
import { Tile } from '../tiles/Tile';
|
import { Tile } from '../tiles/Tile';
|
||||||
@ -17,15 +16,6 @@ export const Grid: FunctionComponent<GridProps> = ({ match }) => {
|
|||||||
const charges = useCharges();
|
const charges = useCharges();
|
||||||
const chargesLoaded = Object.keys(charges).length > 0;
|
const chargesLoaded = Object.keys(charges).length > 0;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const { fetchCharges, fetchAllies } = useDocketState.getState();
|
|
||||||
const { fetchVats, fetchLag } = useKilnState.getState();
|
|
||||||
fetchCharges();
|
|
||||||
fetchAllies();
|
|
||||||
fetchVats();
|
|
||||||
fetchLag();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<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 px-4 bg-white">
|
<header className="fixed sm:sticky bottom-0 sm:bottom-auto sm:top-0 left-0 z-30 flex justify-center w-full px-4 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];
|
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 };
|
const treaty = { ...normalizeDocket(result, desk), ship };
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
treaties: { ...state.treaties, [key]: treaty }
|
treaties: { ...state.treaties, [key]: treaty }
|
||||||
|
@ -8,21 +8,24 @@ import { mockVats } from './mock-data';
|
|||||||
|
|
||||||
interface KilnState {
|
interface KilnState {
|
||||||
vats: Vats;
|
vats: Vats;
|
||||||
|
loaded: boolean;
|
||||||
fetchVats: () => Promise<void>;
|
fetchVats: () => Promise<void>;
|
||||||
lag: boolean;
|
lag: boolean;
|
||||||
fetchLag: () => Promise<void>;
|
fetchLag: () => Promise<void>;
|
||||||
set: (s: KilnState) => void;
|
set: (s: KilnState) => void;
|
||||||
}
|
}
|
||||||
export const useKilnState = create<KilnState>((set) => ({
|
const useKilnState = create<KilnState>((set) => ({
|
||||||
vats: useMockData ? mockVats : {},
|
vats: useMockData ? mockVats : {},
|
||||||
lag: !!useMockData,
|
lag: !!useMockData,
|
||||||
|
loaded: false,
|
||||||
fetchVats: async () => {
|
fetchVats: async () => {
|
||||||
if (useMockData) {
|
if (useMockData) {
|
||||||
await fakeRequest({}, 500);
|
await fakeRequest({}, 500);
|
||||||
|
set({ loaded: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const vats = await api.scry<Vats>(getVats);
|
const vats = await api.scry<Vats>(getVats);
|
||||||
set({ vats });
|
set({ vats, loaded: true });
|
||||||
},
|
},
|
||||||
fetchLag: async () => {
|
fetchLag: async () => {
|
||||||
const lag = await api.scry<boolean>(scryLag);
|
const lag = await api.scry<boolean>(scryLag);
|
||||||
@ -53,4 +56,9 @@ export function useLag() {
|
|||||||
return useKilnState(selLag);
|
return useKilnState(selLag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selLoaded = (s: KilnState) => s.loaded;
|
||||||
|
export function useKilnLoaded() {
|
||||||
|
return useKilnState(selLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
export default useKilnState;
|
export default useKilnState;
|
||||||
|
27
pkg/grid/src/state/local.ts
Normal file
27
pkg/grid/src/state/local.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import create from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import produce from 'immer';
|
||||||
|
|
||||||
|
interface LocalState {
|
||||||
|
protocolHandling: boolean;
|
||||||
|
set: (f: (s: LocalState) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLocalState = create<LocalState>(
|
||||||
|
persist(
|
||||||
|
(set, get) => ({
|
||||||
|
set: (f) => set(produce(get(), f)),
|
||||||
|
protocolHandling: false
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'grid-local'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const selProtocolHandling = (s: LocalState) => s.protocolHandling;
|
||||||
|
export function useProtocolHandling() {
|
||||||
|
return useLocalState(selProtocolHandling);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setLocalState = (f: (s: LocalState) => void) => useLocalState.getState().set(f);
|
@ -230,7 +230,7 @@ export const mockVat = (desk: string, blockers?: boolean): Vat => ({
|
|||||||
aeon: 3,
|
aeon: 3,
|
||||||
desk,
|
desk,
|
||||||
next: blockers ? [{ aeon: 3, weft: { name: 'zuse', kelvin: 419 } }] : [],
|
next: blockers ? [{ aeon: 3, weft: { name: 'zuse', kelvin: 419 } }] : [],
|
||||||
ship: '~dopzod'
|
ship: '~zod'
|
||||||
},
|
},
|
||||||
hash: '0vh.lhfn6.julg1.fs52d.g2lqj.q5kp0.2o7j3.2bljl.jdm34.hd46v.9uv5v'
|
hash: '0vh.lhfn6.julg1.fs52d.g2lqj.q5kp0.2o7j3.2bljl.jdm34.hd46v.9uv5v'
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user