From 85a23dc5ff66e1a0d5b665fcdc0d2c717685b9df Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 1 Dec 2022 08:10:32 -0800 Subject: [PATCH 01/29] grid: import utils from upstream --- ui/src/state/util.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/src/state/util.ts b/ui/src/state/util.ts index 306b0ab..ae5a9ea 100644 --- a/ui/src/state/util.ts +++ b/ui/src/state/util.ts @@ -2,6 +2,16 @@ import { Docket, DocketHref, Treaty } from '@urbit/api'; import { hsla, parseToHsla } from 'color2k'; import _ from 'lodash'; +export const useMockData = import.meta.env.MODE === 'mock'; + +export async function fakeRequest(data: T, time = 300): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(data); + }, time); + }); +} + export function getAppHref(href: DocketHref) { return 'site' in href ? href.site : `/apps/${href.glob.base}/`; } From 29fff1d8421c372ea9fc93c9aecb7d54391119c8 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 3 Nov 2022 22:19:36 -0700 Subject: [PATCH 02/29] grid: consume Pike interface and scry --- ui/src/app.tsx | 3 ++- ui/src/state/kiln.ts | 18 +++++++++++++++++- ui/src/state/mock-data.ts | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/ui/src/app.tsx b/ui/src/app.tsx index 7b02839..41d5f94 100644 --- a/ui/src/app.tsx +++ b/ui/src/app.tsx @@ -86,9 +86,10 @@ const AppRoutes = () => { fetchCharges(); fetchAllies(); - const { fetchVats, fetchLag } = useKilnState.getState(); + const { fetchVats, fetchLag, fetchPikes } = useKilnState.getState(); fetchVats(); fetchLag(); + fetchPikes(); useContactState.getState().initialize(api); useHarkState.getState().start(); diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 4237536..2d65044 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -6,31 +6,47 @@ import { Vat, kilnInstall, kilnPause, - kilnResume + kilnResume, + getPikes, + Pikes } from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; import { useCallback } from 'react'; import api from './api'; +import { fakeRequest, useMockData } from './util'; +import { mockPikes, mockVats } from './mock-data'; interface KilnState { vats: Vats; + pikes: Pikes; loaded: boolean; fetchVats: () => Promise; lag: boolean; fetchLag: () => Promise; + fetchPikes: () => Promise; changeOTASource: (ship: string) => Promise; toggleOTAs: (desk: string, on: boolean) => Promise; set: (s: KilnState) => void; } const useKilnState = create((set, get) => ({ vats: {}, + pikes: useMockData ? mockPikes : {}, lag: false, loaded: false, fetchVats: async () => { const vats = await api.scry(getVats); set({ vats, loaded: true }); }, + fetchPikes: async () => { + if (useMockData) { + await fakeRequest({}, 500); + set({ loaded: true }); + return; + } + const pikes = await api.scry(getPikes); + set({ pikes, loaded: true }); + }, fetchLag: async () => { const lag = await api.scry(scryLag); set({ lag }); diff --git a/ui/src/state/mock-data.ts b/ui/src/state/mock-data.ts index 967f07d..ab62028 100644 --- a/ui/src/state/mock-data.ts +++ b/ui/src/state/mock-data.ts @@ -13,7 +13,8 @@ import { Contact, Contacts, Timebox, - harkBinToId + harkBinToId, + Pikes } from '@urbit/api'; import _ from 'lodash'; import systemUrl from '../assets/system.png'; @@ -375,3 +376,36 @@ export const mockVats = _.reduce( }, { base: mockVat('base', true) } as Vats ); + +export const mockPikes: Pikes = { + kids: { + sync: null, + zest: 'dead', + wefts: [], + hash: '0v19.q7u27.omps3.fbhf4.53rai.co157.pben7.pu94n.63v4p.3kcb7.iafj0' + }, + garden: { + sync: { + desk: 'garden', + ship: '~mister-dister-dozzod-dozzod' + }, + zest: 'live', + wefts: [], + hash: '0v18.hbbs6.onu15.skjkv.qrfgl.vf4oo.0igo5.2q0d3.6r3r8.2dkmo.oa04m' + }, + landscape: { + sync: { + desk: 'landscape', + ship: '~lander-dister-dozzod-dozzod' + }, + zest: 'live', + wefts: [], + hash: '0v1t.qln8k.cskmt.cn6lv.gu335.jfba6.kte90.iqqn3.aj67b.t389a.8imuo' + }, + base: { + sync: null, + zest: 'live', + wefts: [], + hash: '0v1e.b5auh.6u82i.hqk1r.22kli.4ubef.a1cbo.3g532.6l49k.g0i8e.t6eid' + } +}; From a92fb139f014f221c36823956b1804e144822319 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 3 Nov 2022 22:35:41 -0700 Subject: [PATCH 03/29] grid: kiln-bump poke no longer takes args --- ui/src/logic/useSystemUpdate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/logic/useSystemUpdate.tsx b/ui/src/logic/useSystemUpdate.tsx index e6aee48..94bcdd1 100644 --- a/ui/src/logic/useSystemUpdate.tsx +++ b/ui/src/logic/useSystemUpdate.tsx @@ -30,7 +30,7 @@ export function useSystemUpdate() { const blockedCount = blockedCharges.length; const freezeApps = useCallback(async () => { - api.poke(kilnBump(true)); + api.poke(kilnBump()); push('/leap/upgrading'); }, []); From b23293228d052d7c603417e308b2267726fbedb7 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 3 Nov 2022 22:58:12 -0700 Subject: [PATCH 04/29] grid: AppPrefs consumes pike --- ui/src/preferences/AppPrefs.tsx | 10 +++++----- ui/src/state/kiln.ts | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index 9ce6216..763d493 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -3,16 +3,16 @@ import { RouteComponentProps } from 'react-router-dom'; import { Setting } from '../components/Setting'; import { ShipName } from '../components/ShipName'; import { useCharge } from '../state/docket'; -import useKilnState, { useVat } from '../state/kiln'; +import useKilnState, { usePike, useVat } from '../../state/kiln'; import { getAppName } from '../state/util'; export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const { desk } = match.params; const charge = useCharge(desk); - const vat = useVat(desk); - const tracking = !!vat?.arak.rail; - const otasEnabled = !vat?.arak.rail?.paused; - const otaSource = vat?.arak.rail?.ship; + const pike = usePike(desk); + const tracking = !!pike?.sync; + const otasEnabled = pike?.zest === 'live'; + const otaSource = pike?.sync?.ship; const toggleOTAs = useKilnState((s) => s.toggleOTAs); const toggleUpdates = useCallback((on: boolean) => toggleOTAs(desk, on), [desk, toggleOTAs]); diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 2d65044..36c4089 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -8,7 +8,8 @@ import { kilnPause, kilnResume, getPikes, - Pikes + Pikes, + Pike } from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; @@ -92,6 +93,10 @@ export function useVat(desk: string): Vat | undefined { return useKilnState(useCallback((s) => s.vats[desk], [desk])); } +export function usePike(desk: string): Pike | undefined { + return useKilnState(useCallback((s) => s.pikes[desk], [desk])); +} + const selLag = (s: KilnState) => s.lag; export function useLag() { return useKilnState(selLag); From 5eaed938681b70b9ceedc3ddba64f988e9563896 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 3 Nov 2022 23:21:04 -0700 Subject: [PATCH 05/29] grid: SystemNotifications consumes pike --- ui/src/logic/useSystemUpdate.tsx | 18 +++++++----------- ui/src/preferences/AppPrefs.tsx | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ui/src/logic/useSystemUpdate.tsx b/ui/src/logic/useSystemUpdate.tsx index 94bcdd1..e8834ac 100644 --- a/ui/src/logic/useSystemUpdate.tsx +++ b/ui/src/logic/useSystemUpdate.tsx @@ -1,27 +1,23 @@ -import { kilnBump, Vat } from '@urbit/api'; +import { kilnBump, Pike } from '@urbit/api'; import { partition, pick } from 'lodash'; import { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import api from '../state/api'; import { useCharges } from '../state/docket'; -import useKilnState, { useVat } from '../state/kiln'; +import useKilnState, { usePike } from '../../state/kiln'; -export function vatIsBlocked(newKelvin: number | undefined, vat: Vat) { - if (!newKelvin) { - return false; - } - - return !(vat.arak?.rail?.next || []).find(({ weft }) => weft.kelvin === newKelvin); +function pikeIsBlocked(newKelvin: number, pike: Pike) { + return !pike.wefts?.find(({ kelvin }) => kelvin === newKelvin); } export function useSystemUpdate() { const { push } = useHistory(); - const base = useVat('base'); + const basePike = usePike('base'); const update = base?.arak?.rail?.next?.[0]; - const newKelvin = update?.weft?.kelvin; + const newKelvin = basePike?.wefts[0]?.kelvin ?? 418; const charges = useCharges(); const [blocked] = useKilnState((s) => { - const [b, u] = partition(Object.entries(s.vats), ([, vat]) => vatIsBlocked(newKelvin, vat)); + const [b, u] = partition(Object.entries(s.pikes), ([, pike]) => pikeIsBlocked(newKelvin, pike)); return [b.map(([d]) => d), u.map(([d]) => d)] as const; }); diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index 763d493..4df62d2 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -3,7 +3,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { Setting } from '../components/Setting'; import { ShipName } from '../components/ShipName'; import { useCharge } from '../state/docket'; -import useKilnState, { usePike, useVat } from '../../state/kiln'; +import useKilnState, { usePike } from '../../state/kiln'; import { getAppName } from '../state/util'; export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { From 1826e0fa3f882d0cfb565b5e45eae8dd8c105aef Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 3 Nov 2022 23:37:19 -0700 Subject: [PATCH 06/29] grid: AppInfo consumes pike --- ui/src/components/AppInfo.tsx | 23 +++++++++++------------ ui/src/components/PikeMeta.tsx | 25 +++++++++++++++++++++++++ ui/src/components/VatMeta.tsx | 27 --------------------------- ui/src/tiles/TileInfo.tsx | 6 +++--- 4 files changed, 39 insertions(+), 42 deletions(-) create mode 100644 ui/src/components/PikeMeta.tsx delete mode 100644 ui/src/components/VatMeta.tsx diff --git a/ui/src/components/AppInfo.tsx b/ui/src/components/AppInfo.tsx index c2a8a6d..b2e3d76 100644 --- a/ui/src/components/AppInfo.tsx +++ b/ui/src/components/AppInfo.tsx @@ -1,4 +1,4 @@ -import { chadIsRunning, Treaty, Vat } from '@urbit/api'; +import { chadIsRunning, Pike, Treaty } from '@urbit/api'; import clipboardCopy from 'clipboard-copy'; import React, { FC, useCallback, useState } from 'react'; import cn from 'classnames'; @@ -6,7 +6,7 @@ import { Button, PillButton } from './Button'; import { Dialog, DialogClose, DialogContent, DialogTrigger } from './Dialog'; import { DocketHeader } from './DocketHeader'; import { Spinner } from './Spinner'; -import { VatMeta } from './VatMeta'; +import { PikeMeta } from './PikeMeta'; import useDocketState, { ChargeWithDesk, useTreaty } from '../state/docket'; import { getAppHref, getAppName } from '../state/util'; import { addRecentApp } from '../nav/search/Home'; @@ -17,7 +17,7 @@ type InstallStatus = 'uninstalled' | 'installing' | 'installed'; type App = ChargeWithDesk | Treaty; interface AppInfoProps { docket: App; - vat?: Vat; + pike?: Pike; className?: string; } @@ -34,10 +34,9 @@ function getInstallStatus(docket: App): InstallStatus { return 'uninstalled'; } -function getRemoteDesk(docket: App, vat?: Vat) { - if (vat && vat.arak.rail) { - const { ship, desk } = vat.arak.rail; - return [ship, desk]; +function getRemoteDesk(docket: App, pike?: Pike) { + if (pike && pike.sync) { + return [pike.sync.ship, pike.sync.desk]; } if ('chad' in docket) { return ['', docket.desk]; @@ -46,10 +45,10 @@ function getRemoteDesk(docket: App, vat?: Vat) { return [ship, desk]; } -export const AppInfo: FC = ({ docket, vat, className }) => { +export const AppInfo: FC = ({ docket, pike, className }) => { const installStatus = getInstallStatus(docket); - const [ship, desk] = getRemoteDesk(docket, vat); - const publisher = vat?.arak?.rail?.publisher ?? ship; + const [ship, desk] = getRemoteDesk(docket, pike); + const publisher = pike?.sync?.ship ?? ship; const [copied, setCopied] = useState(false); const treaty = useTreaty(ship, desk); @@ -136,10 +135,10 @@ export const AppInfo: FC = ({ docket, vat, className }) => {
- {vat ? ( + {pike ? ( <>
- + ) : null} {!treaty ? null : ( diff --git a/ui/src/components/PikeMeta.tsx b/ui/src/components/PikeMeta.tsx new file mode 100644 index 0000000..9349eeb --- /dev/null +++ b/ui/src/components/PikeMeta.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Pike } from '@urbit/api'; + +import { Attribute } from './Attribute'; + +export function PikeMeta(props: { pike: Pike }) { + const { pike } = props; + + const pluralUpdates = pike.wefts?.length !== 1; + return ( +
+ + {pike.hash} + + + %{pike.sync?.desk} + + {pike.wefts && pike.wefts.length > 0 ? ( + + {pike.wefts.length} update{pluralUpdates ? 's are' : ' is'} pending a System Update + + ) : null} +
+ ); +} diff --git a/ui/src/components/VatMeta.tsx b/ui/src/components/VatMeta.tsx deleted file mode 100644 index f55351e..0000000 --- a/ui/src/components/VatMeta.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { Vat } from '@urbit/api'; - -import { Attribute } from './Attribute'; - -export function VatMeta(props: { vat: Vat }) { - const { vat } = props; - const { desk, arak, cass, hash } = vat; - - const { desk: foreignDesk, ship, next } = arak.rail || {}; - const pluralUpdates = next?.length !== 1; - return ( -
- - {hash} - - - %{desk} - - {next && next.length > 0 ? ( - - {next.length} update{pluralUpdates ? 's are' : ' is'} pending a System Update - - ) : null} -
- ); -} diff --git a/ui/src/tiles/TileInfo.tsx b/ui/src/tiles/TileInfo.tsx index f95c046..0ff2a4e 100644 --- a/ui/src/tiles/TileInfo.tsx +++ b/ui/src/tiles/TileInfo.tsx @@ -3,13 +3,13 @@ import { useHistory, useParams } from 'react-router-dom'; import { Dialog, DialogContent } from '../components/Dialog'; import { AppInfo } from '../components/AppInfo'; import { useCharge } from '../state/docket'; -import { useVat } from '../state/kiln'; +import { usePike } from '../state/kiln'; export const TileInfo = () => { const { desk } = useParams<{ desk: string }>(); const { push } = useHistory(); const charge = useCharge(desk); - const vat = useVat(desk); + const pike = usePike(desk); if (!charge) { return null; @@ -18,7 +18,7 @@ export const TileInfo = () => { return ( !open && push('/')}> - + ); From ac3dc729837b7c7d89029459c3e6d708e8f23d13 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 3 Nov 2022 23:46:55 -0700 Subject: [PATCH 07/29] grid: OnboardingNotification consumes pike --- ui/src/state/kiln.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 36c4089..8849e23 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -93,6 +93,11 @@ export function useVat(desk: string): Vat | undefined { return useKilnState(useCallback((s) => s.vats[desk], [desk])); } +const selPikes = (s: KilnState) => s.pikes; +export function usePikes(): Pikes { + return useKilnState(selPikes); +} + export function usePike(desk: string): Pike | undefined { return useKilnState(useCallback((s) => s.pikes[desk], [desk])); } From 29f42aff1f364304d025d4dc91e2230b42f21ef8 Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 4 Nov 2022 00:05:43 -0700 Subject: [PATCH 08/29] grid: clean up kiln state initialization --- ui/src/app.tsx | 6 ++---- ui/src/state/kiln.ts | 33 +++++++++++---------------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/ui/src/app.tsx b/ui/src/app.tsx index 41d5f94..8c8bb0d 100644 --- a/ui/src/app.tsx +++ b/ui/src/app.tsx @@ -86,10 +86,8 @@ const AppRoutes = () => { fetchCharges(); fetchAllies(); - const { fetchVats, fetchLag, fetchPikes } = useKilnState.getState(); - fetchVats(); - fetchLag(); - fetchPikes(); + const { initializeKiln } = useKilnState.getState(); + initializeKiln(); useContactState.getState().initialize(api); useHarkState.getState().start(); diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 8849e23..89e3c0d 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -22,23 +22,19 @@ interface KilnState { vats: Vats; pikes: Pikes; loaded: boolean; - fetchVats: () => Promise; lag: boolean; fetchLag: () => Promise; fetchPikes: () => Promise; changeOTASource: (ship: string) => Promise; toggleOTAs: (desk: string, on: boolean) => Promise; set: (s: KilnState) => void; + initializeKiln: () => Promise; } const useKilnState = create((set, get) => ({ vats: {}, pikes: useMockData ? mockPikes : {}, lag: false, loaded: false, - fetchVats: async () => { - const vats = await api.scry(getVats); - set({ vats, loaded: true }); - }, fetchPikes: async () => { if (useMockData) { await fakeRequest({}, 500); @@ -58,31 +54,24 @@ const useKilnState = create((set, get) => ({ toggleOTAs: async (desk: string, on: boolean) => { set( produce((draft: KilnState) => { - const { arak } = draft.vats[desk]; - if (!arak.rail) { + const pike = draft.pikes[desk]; + if (!pike) { return; } - if (on) { - arak.rail.paused = false; - } else { - arak.rail.paused = true; - } + + pike.zest = on ? 'live' : 'held'; }) ); await api.poke(on ? kilnResume(desk) : kilnPause(desk)); - await get().fetchVats(); // refresh vat state + await get().fetchPikes(); // refresh pikes state }, - set: produce(set) -})); - -api.subscribe({ - app: 'hood', - path: '/kiln/vats', - event: () => { - useKilnState.getState().fetchVats(); + set: produce(set), + initializeKiln: async () => { + await get().fetchLag(); + await get().fetchPikes(); } -}); +})); const selBlockers = (s: KilnState) => getBlockers(s.vats); export function useBlockers() { From 1c72528bcc50723cb86df0accea1baf727ec25be Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 4 Nov 2022 00:07:25 -0700 Subject: [PATCH 09/29] grid: Tile consumes pike --- ui/src/tiles/Tile.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/tiles/Tile.tsx b/ui/src/tiles/Tile.tsx index 4ffb59c..52fd1b9 100644 --- a/ui/src/tiles/Tile.tsx +++ b/ui/src/tiles/Tile.tsx @@ -8,7 +8,7 @@ import { getAppHref } from '../state/util'; import { useRecentsStore } from '../nav/search/Home'; import { ChargeWithDesk } from '../state/docket'; import { useTileColor } from './useTileColor'; -import { useVat } from '../state/kiln'; +import { usePike } from '../state/kiln'; import { Bullet } from '../components/icons/Bullet'; import { dragTypes } from './TileGrid'; @@ -21,7 +21,7 @@ type TileProps = { export const Tile: FunctionComponent = ({ charge, desk, disabled = false }) => { const addRecentApp = useRecentsStore((state) => state.addRecentApp); const { title, image, color, chad, href } = charge; - const vat = useVat(desk); + const pike = usePike(desk); const { lightText, tileColor, menuColor, suspendColor, suspendMenuColor } = useTileColor(color); const loading = !disabled && 'install' in chad; const suspended = disabled || 'suspend' in chad; @@ -65,7 +65,7 @@ export const Tile: FunctionComponent = ({ charge, desk, disabled = fa )}
- {vat?.arak.rail?.paused && !disabled && ( + {pike?.zest === 'held' && !disabled && ( )} Date: Fri, 4 Nov 2022 00:09:12 -0700 Subject: [PATCH 10/29] grid: SystemUpdatePrefs consumes pike --- ui/src/preferences/about-system/UpdatePreferences.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/src/preferences/about-system/UpdatePreferences.tsx b/ui/src/preferences/about-system/UpdatePreferences.tsx index ccdced0..0071b9f 100644 --- a/ui/src/preferences/about-system/UpdatePreferences.tsx +++ b/ui/src/preferences/about-system/UpdatePreferences.tsx @@ -5,7 +5,7 @@ import { Button } from '../../components/Button'; import { Setting } from '../../components/Setting'; import { Spinner } from '../../components/Spinner'; import { useAsyncCall } from '../../logic/useAsyncCall'; -import useKilnState from '../../state/kiln'; +import useKilnState, { usePike } from '../../state/kiln'; interface UpdatePreferencesProps { base: Vat | undefined; @@ -15,8 +15,9 @@ export const UpdatePreferences = ({ base }: UpdatePreferencesProps) => { const { changeOTASource, toggleOTAs } = useKilnState((s) => _.pick(s, ['toggleOTAs', 'changeOTASource']) ); - const otasEnabled = base && !(base.arak?.rail?.paused ?? true); - const otaSource = base && base.arak.rail?.ship; + const pike = usePike('base'); + const otasEnabled = pike?.zest === 'live'; + const otaSource = pike?.sync?.ship; const toggleBase = useCallback((on: boolean) => toggleOTAs('base', on), [toggleOTAs]); From 857688d0687a6f6842a35fd51a838b222c3c4706 Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 4 Nov 2022 00:11:06 -0700 Subject: [PATCH 11/29] grid: PermalinkRoutes consumes pike --- ui/src/pages/PermalinkRoutes.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/src/pages/PermalinkRoutes.tsx b/ui/src/pages/PermalinkRoutes.tsx index c1fefda..5a412c1 100644 --- a/ui/src/pages/PermalinkRoutes.tsx +++ b/ui/src/pages/PermalinkRoutes.tsx @@ -1,15 +1,15 @@ +import { Pikes } from '@urbit/api'; 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 } from '../state/kiln'; +import { useKilnLoaded, usePikes } from '../state/kiln'; import { getAppHref } from '../state/util'; -function getDeskByForeignRef(ship: string, desk: string): string | undefined { - const { vats } = useKilnState.getState(); - const found = Object.entries(vats).find( - ([, vat]) => vat.arak.rail?.ship === ship && vat.arak.rail?.desk === desk +function getDeskByForeignRef(pikes: Pikes, ship: string, desk: string): string | undefined { + const found = Object.entries(pikes).find( + ([, pike]) => pike.sync?.ship === ship && pike.sync?.desk === desk ); return found ? found[0] : undefined; } @@ -22,8 +22,8 @@ type AppLinkProps = RouteComponentProps<{ function AppLink({ match, history, location }: AppLinkProps) { const { ship, desk, link = '' } = match.params; - const ourDesk = getDeskByForeignRef(ship, desk); - console.log(ourDesk); + const pikes = usePikes(); + const ourDesk = getDeskByForeignRef(pikes, ship, desk); if (ourDesk) { return ; From a257aa89f8e257f401219ebeaca0796bbf579812 Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 4 Nov 2022 00:20:16 -0700 Subject: [PATCH 12/29] grid: TreatyInfo consumes pike --- ui/src/nav/search/TreatyInfo.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/nav/search/TreatyInfo.tsx b/ui/src/nav/search/TreatyInfo.tsx index a6c78fe..ac305e0 100644 --- a/ui/src/nav/search/TreatyInfo.tsx +++ b/ui/src/nav/search/TreatyInfo.tsx @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom'; import { AppInfo } from '../../components/AppInfo'; import { Spinner } from '../../components/Spinner'; import useDocketState, { useCharge, useTreaty } from '../../state/docket'; -import { useVat } from '../../state/kiln'; +import { usePike } from '../../state/kiln'; import { getAppName } from '../../state/util'; import { useLeapStore } from '../Nav'; @@ -11,7 +11,7 @@ export const TreatyInfo = () => { const select = useLeapStore((state) => state.select); const { host, desk } = useParams<{ host: string; desk: string }>(); const treaty = useTreaty(host, desk); - const vat = useVat(desk); + const pike = usePike(desk); const charge = useCharge(desk); const name = getAppName(treaty); @@ -34,5 +34,5 @@ export const TreatyInfo = () => { ); } - return ; + return ; }; From 231d87b4ac7db1eed4316374418f0f25377ae40e Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 4 Nov 2022 00:20:33 -0700 Subject: [PATCH 13/29] grid: SystemMenu consumes pike --- ui/src/nav/SystemMenu.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/nav/SystemMenu.tsx b/ui/src/nav/SystemMenu.tsx index 44f8379..fb2f0c5 100644 --- a/ui/src/nav/SystemMenu.tsx +++ b/ui/src/nav/SystemMenu.tsx @@ -3,9 +3,9 @@ import classNames from 'classnames'; import clipboardCopy from 'clipboard-copy'; import React, { HTMLAttributes, useCallback, useState } from 'react'; import { Link, Route, useHistory } from 'react-router-dom'; -import { Vat } from '@urbit/api'; +import { Pike } from '@urbit/api'; import { Adjust } from '../components/icons/Adjust'; -import { useVat } from '../state/kiln'; +import { usePike } from '../state/kiln'; import { disableDefault, handleDropdownLink } from '../state/util'; import { useMedia } from '../logic/useMedia'; import { Cross } from '../components/icons/Cross'; @@ -17,15 +17,15 @@ type SystemMenuProps = HTMLAttributes & { shouldDim: boolean; }; -function getHash(vat: Vat): string { - const parts = vat.hash.split('.'); +function getHash(pike: Pike): string { + const parts = pike.hash.split('.'); return parts[parts.length - 1]; } export const SystemMenu = ({ className, open, subMenuOpen, shouldDim }: SystemMenuProps) => { const { push } = useHistory(); const [copied, setCopied] = useState(false); - const garden = useVat(window.desk); + const garden = usePike(window.desk); const hash = garden ? getHash(garden) : null; const isMobile = useMedia('(max-width: 639px)'); const select = useLeapStore((s) => s.select); From 089d67eb3143d069b9915f2cd698f422ba988968 Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 4 Nov 2022 00:21:03 -0700 Subject: [PATCH 14/29] grid: remove deprecated Vats state + helpers --- ui/src/state/kiln.ts | 27 ++------------------------- ui/src/state/mock-data.ts | 37 ------------------------------------- 2 files changed, 2 insertions(+), 62 deletions(-) diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 89e3c0d..ed2ff53 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -1,25 +1,12 @@ -import { - getVats, - Vats, - scryLag, - getBlockers, - Vat, - kilnInstall, - kilnPause, - kilnResume, - getPikes, - Pikes, - Pike -} from '@urbit/api'; +import { scryLag, kilnInstall, kilnPause, kilnResume, getPikes, Pikes, Pike } from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; import { useCallback } from 'react'; import api from './api'; import { fakeRequest, useMockData } from './util'; -import { mockPikes, mockVats } from './mock-data'; +import { mockPikes } from './mock-data'; interface KilnState { - vats: Vats; pikes: Pikes; loaded: boolean; lag: boolean; @@ -31,7 +18,6 @@ interface KilnState { initializeKiln: () => Promise; } const useKilnState = create((set, get) => ({ - vats: {}, pikes: useMockData ? mockPikes : {}, lag: false, loaded: false, @@ -73,15 +59,6 @@ const useKilnState = create((set, get) => ({ } })); -const selBlockers = (s: KilnState) => getBlockers(s.vats); -export function useBlockers() { - return useKilnState(selBlockers); -} - -export function useVat(desk: string): Vat | undefined { - return useKilnState(useCallback((s) => s.vats[desk], [desk])); -} - const selPikes = (s: KilnState) => s.pikes; export function usePikes(): Pikes { return useKilnState(selPikes); diff --git a/ui/src/state/mock-data.ts b/ui/src/state/mock-data.ts index ab62028..f360483 100644 --- a/ui/src/state/mock-data.ts +++ b/ui/src/state/mock-data.ts @@ -1,6 +1,4 @@ import { - Vat, - Vats, Allies, Charges, DocketHrefGlob, @@ -342,41 +340,6 @@ export const mockContacts: Contacts = { } }; -export const mockVat = (desk: string, blockers?: boolean): Vat => ({ - cass: { - da: '~2021.9.13..05.41.04..ae65', - ud: 1 - }, - desk, - arak: { - rein: { - sub: [], - add: [] - }, - rail: - desk === 'uniswap' - ? null - : { - aeon: 3, - desk, - publisher: '~zod', - next: blockers ? [{ aeon: 3, weft: { name: 'zuse', kelvin: 419 } }] : [], - ship: '~zod', - paused: desk === 'groups' - } - }, - hash: '0vh.lhfn6.julg1.fs52d.g2lqj.q5kp0.2o7j3.2bljl.jdm34.hd46v.9uv5v' -}); - -const badVats = ['inbox', 'system', 'terminal', 'base']; -export const mockVats = _.reduce( - mockCharges, - (vats, charge, desk) => { - return { ...vats, [desk]: mockVat(desk, !badVats.includes(desk)) }; - }, - { base: mockVat('base', true) } as Vats -); - export const mockPikes: Pikes = { kids: { sync: null, From b15b2dd3fdad0c9fcbef741b468bbc62fe6f8192 Mon Sep 17 00:00:00 2001 From: tomholford Date: Mon, 7 Nov 2022 17:52:13 -0800 Subject: [PATCH 15/29] prefs: use sync.pike to discern if OTAs enabled Also, remove the conditional rendering logic for AppPrefs. With the new logic (and before this change) if the User disables OTAs, the toggle would disappear, which feels like an antipattern. --- ui/src/preferences/AppPrefs.tsx | 32 +++++-------------- .../about-system/UpdatePreferences.tsx | 2 +- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index 4df62d2..0bf9ca8 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -10,8 +10,7 @@ export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const { desk } = match.params; const charge = useCharge(desk); const pike = usePike(desk); - const tracking = !!pike?.sync; - const otasEnabled = pike?.zest === 'live'; + const otasEnabled = !!pike?.sync; const otaSource = pike?.sync?.ship; const toggleOTAs = useKilnState((s) => s.toggleOTAs); @@ -21,29 +20,14 @@ export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { <>

{getAppName(charge)} Settings

- {tracking ? ( - -

- Automatically download and apply updates to keep{' '} - {getAppName(charge)} up to date. + +

Automatically download and apply updates to keep {getAppName(charge)} up to date.

+ {otaSource && ( +

+ OTA Source:

- {otaSource && ( -

- OTA Source:{' '} - -

- )} -
- ) : ( -

No settings

- )} + )} +
); diff --git a/ui/src/preferences/about-system/UpdatePreferences.tsx b/ui/src/preferences/about-system/UpdatePreferences.tsx index 0071b9f..fa14dc2 100644 --- a/ui/src/preferences/about-system/UpdatePreferences.tsx +++ b/ui/src/preferences/about-system/UpdatePreferences.tsx @@ -16,7 +16,7 @@ export const UpdatePreferences = ({ base }: UpdatePreferencesProps) => { _.pick(s, ['toggleOTAs', 'changeOTASource']) ); const pike = usePike('base'); - const otasEnabled = pike?.zest === 'live'; + const otasEnabled = !!pike?.sync; const otaSource = pike?.sync?.ship; const toggleBase = useCallback((on: boolean) => toggleOTAs('base', on), [toggleOTAs]); From dd2d1b03507cb3607318d0acb87062f61315436c Mon Sep 17 00:00:00 2001 From: tomholford Date: Mon, 7 Nov 2022 23:23:08 -0800 Subject: [PATCH 16/29] grid: sync / unsync App Preferences --- ui/src/preferences/AppPrefs.tsx | 92 ++++++++++++++++++++++++++++----- ui/src/state/kiln.ts | 10 +++- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index 0bf9ca8..2f1c616 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -1,33 +1,99 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { Setting } from '../components/Setting'; import { ShipName } from '../components/ShipName'; +import { Button } from '../../components/Button'; import { useCharge } from '../state/docket'; import useKilnState, { usePike } from '../../state/kiln'; import { getAppName } from '../state/util'; +import { useAsyncCall } from '../../logic/useAsyncCall'; +import { Spinner } from '../../components/Spinner'; export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const { desk } = match.params; const charge = useCharge(desk); + const appName = getAppName(charge); const pike = usePike(desk); - const otasEnabled = !!pike?.sync; - const otaSource = pike?.sync?.ship; - const toggleOTAs = useKilnState((s) => s.toggleOTAs); + const syncShip = pike?.sync?.ship; + const [newSyncShip, setNewSyncShip] = useState(syncShip ?? ''); + const syncDirty = newSyncShip !== syncShip; - const toggleUpdates = useCallback((on: boolean) => toggleOTAs(desk, on), [desk, toggleOTAs]); + const handleSourceChange = useCallback((e: React.ChangeEvent) => { + const { target } = e; + const value = target.value.trim(); + setNewSyncShip(value.startsWith('~') ? value : `~${value}`); + }, []); + const { toggleSync } = useKilnState(); + + const onUnsync = useCallback(() => { + if (!syncShip) { + return; + } + if ( + // eslint-disable-next-line no-alert, no-restricted-globals + confirm(`Are you sure you want to unsync ${appName}? You will no longer receive updates.`) + ) { + toggleSync(desk, syncShip); + } + }, [syncShip, desk, toggleSync]); + + const { status: requestStatus, call: setSync } = useAsyncCall(toggleSync); + + const onSubmit = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + await setSync(desk, newSyncShip); + }, + [desk, newSyncShip, toggleSync] + ); + return ( <> -

{getAppName(charge)} Settings

+

{appName} Settings

- -

Automatically download and apply updates to keep {getAppName(charge)} up to date.

- {otaSource && ( + {syncShip ? ( + <> +

Automatic Updates

+

Automatically download and apply updates to keep {appName} up to date.

+

- OTA Source: + OTA Source:

- )} - +
+
+ +
+ + ) : ( +
+ +

Enter a valid urbit name to receive updates for {appName}.

+
+ + {syncDirty && ( + + )} +
+
+ )}
); diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index ed2ff53..c198ee4 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -1,4 +1,4 @@ -import { scryLag, kilnInstall, kilnPause, kilnResume, getPikes, Pikes, Pike } from '@urbit/api'; +import { scryLag, kilnInstall, kilnPause, kilnResume, getPikes, Pikes, Pike, kilnUnsync, kilnSync } from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; import { useCallback } from 'react'; @@ -14,6 +14,7 @@ interface KilnState { fetchPikes: () => Promise; changeOTASource: (ship: string) => Promise; toggleOTAs: (desk: string, on: boolean) => Promise; + toggleSync: (desk: string, ship: string) => Promise; set: (s: KilnState) => void; initializeKiln: () => Promise; } @@ -52,6 +53,13 @@ const useKilnState = create((set, get) => ({ await api.poke(on ? kilnResume(desk) : kilnPause(desk)); await get().fetchPikes(); // refresh pikes state }, + toggleSync: async (desk: string, ship: string) => { + const synced = !!get().pikes[desk].sync; + await (useMockData + ? fakeRequest('') + : api.poke(synced ? kilnUnsync(ship, desk) : kilnSync(ship, desk))); + await get().fetchPikes(); + }, set: produce(set), initializeKiln: async () => { await get().fetchLag(); From 2c4d8b59bc84b02b60640ab584dd3f0cb3150dca Mon Sep 17 00:00:00 2001 From: tomholford Date: Mon, 7 Nov 2022 23:56:49 -0800 Subject: [PATCH 17/29] grid: sync / unsync OTA source in System Prefs --- ui/src/components/SourceSyncer.tsx | 97 +++++++++++++++++++ ui/src/logic/useSystemUpdate.tsx | 4 +- ui/src/preferences/AppPrefs.tsx | 95 ++---------------- .../preferences/about-system/AboutSystem.tsx | 1 + .../about-system/UpdatePreferences.tsx | 78 ++------------- 5 files changed, 117 insertions(+), 158 deletions(-) create mode 100644 ui/src/components/SourceSyncer.tsx diff --git a/ui/src/components/SourceSyncer.tsx b/ui/src/components/SourceSyncer.tsx new file mode 100644 index 0000000..b604c3a --- /dev/null +++ b/ui/src/components/SourceSyncer.tsx @@ -0,0 +1,97 @@ +import React, { useCallback, useState } from 'react'; +import { useAsyncCall } from '../logic/useAsyncCall'; +import useKilnState from '../state/kiln'; +import { Button } from './Button'; +import { ShipName } from './ShipName'; +import { Spinner } from './Spinner'; + +interface SourceSyncerProps { + appName: string; + title: string; + syncDesk: string; + syncShip?: string; +} + +export default function SourceSyncer({ appName, title, syncDesk, syncShip }: SourceSyncerProps) { + const [newSyncShip, setNewSyncShip] = useState(syncShip ?? ''); + const { toggleSync } = useKilnState(); + const { status: requestStatus, call: setSync } = useAsyncCall(toggleSync); + const syncDirty = newSyncShip !== syncShip; + + const onUnsync = useCallback(() => { + if (!syncShip) { + return; + } + if ( + // eslint-disable-next-line no-alert, no-restricted-globals + confirm(`Are you sure you want to unsync ${appName}? You will no longer receive updates.`) + ) { + toggleSync(syncDesk, syncShip); + } + }, [syncShip, syncDesk, toggleSync]); + + const handleSourceChange = useCallback((e: React.ChangeEvent) => { + const { target } = e; + const value = target.value.trim(); + setNewSyncShip(value.startsWith('~') ? value : `~${value}`); + }, []); + + const onSubmit = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + await setSync(syncDesk, newSyncShip); + }, + [syncDesk, newSyncShip, toggleSync] + ); + + return ( + <> +

{title}

+
+ {syncShip ? ( + <> +

Automatic Updates

+

Automatically download and apply updates to keep {appName} up to date.

+
+

+ OTA Source: +

+
+
+ +
+ + ) : ( +
+ +

Enter a valid urbit name to receive updates for {appName}.

+
+ + {syncDirty && ( + + )} +
+
+ )} +
+ + ); +} diff --git a/ui/src/logic/useSystemUpdate.tsx b/ui/src/logic/useSystemUpdate.tsx index e8834ac..8f5f9a7 100644 --- a/ui/src/logic/useSystemUpdate.tsx +++ b/ui/src/logic/useSystemUpdate.tsx @@ -12,9 +12,9 @@ function pikeIsBlocked(newKelvin: number, pike: Pike) { export function useSystemUpdate() { const { push } = useHistory(); - const basePike = usePike('base'); + const base = usePike('base'); const update = base?.arak?.rail?.next?.[0]; - const newKelvin = basePike?.wefts[0]?.kelvin ?? 418; + const newKelvin = base?.wefts[0]?.kelvin ?? 418; const charges = useCharges(); const [blocked] = useKilnState((s) => { const [b, u] = partition(Object.entries(s.pikes), ([, pike]) => pikeIsBlocked(newKelvin, pike)); diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index 2f1c616..1f40db8 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -1,12 +1,9 @@ -import React, { useCallback, useState } from 'react'; +import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { ShipName } from '../components/ShipName'; -import { Button } from '../../components/Button'; import { useCharge } from '../state/docket'; -import useKilnState, { usePike } from '../../state/kiln'; +import { usePike } from '../../state/kiln'; import { getAppName } from '../state/util'; -import { useAsyncCall } from '../../logic/useAsyncCall'; -import { Spinner } from '../../components/Spinner'; +import SourceSyncer from '../components/SourceSyncer'; export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const { desk } = match.params; @@ -14,87 +11,13 @@ export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const appName = getAppName(charge); const pike = usePike(desk); const syncShip = pike?.sync?.ship; - const [newSyncShip, setNewSyncShip] = useState(syncShip ?? ''); - const syncDirty = newSyncShip !== syncShip; - - const handleSourceChange = useCallback((e: React.ChangeEvent) => { - const { target } = e; - const value = target.value.trim(); - setNewSyncShip(value.startsWith('~') ? value : `~${value}`); - }, []); - - const { toggleSync } = useKilnState(); - - const onUnsync = useCallback(() => { - if (!syncShip) { - return; - } - if ( - // eslint-disable-next-line no-alert, no-restricted-globals - confirm(`Are you sure you want to unsync ${appName}? You will no longer receive updates.`) - ) { - toggleSync(desk, syncShip); - } - }, [syncShip, desk, toggleSync]); - - const { status: requestStatus, call: setSync } = useAsyncCall(toggleSync); - - const onSubmit = useCallback( - async (e: React.FormEvent) => { - e.preventDefault(); - await setSync(desk, newSyncShip); - }, - [desk, newSyncShip, toggleSync] - ); return ( - <> -

{appName} Settings

-
- {syncShip ? ( - <> -

Automatic Updates

-

Automatically download and apply updates to keep {appName} up to date.

-
-

- OTA Source: -

-
-
- -
- - ) : ( -
- -

Enter a valid urbit name to receive updates for {appName}.

-
- - {syncDirty && ( - - )} -
-
- )} -
- + ); }; diff --git a/ui/src/preferences/about-system/AboutSystem.tsx b/ui/src/preferences/about-system/AboutSystem.tsx index 3dc39dc..2809a57 100644 --- a/ui/src/preferences/about-system/AboutSystem.tsx +++ b/ui/src/preferences/about-system/AboutSystem.tsx @@ -18,6 +18,7 @@ function getHash(vat: Vat): string { export const AboutSystem = () => { const garden = useVat('garden'); const gardenCharge = useCharge('garden'); + // TODO const { base, update, systemBlocked, blockedCharges, blockedCount, freezeApps } = useSystemUpdate(); const hash = base && getHash(base); diff --git a/ui/src/preferences/about-system/UpdatePreferences.tsx b/ui/src/preferences/about-system/UpdatePreferences.tsx index fa14dc2..65f1dad 100644 --- a/ui/src/preferences/about-system/UpdatePreferences.tsx +++ b/ui/src/preferences/about-system/UpdatePreferences.tsx @@ -1,82 +1,20 @@ import { Vat } from '@urbit/api'; import _ from 'lodash'; -import React, { ChangeEvent, FormEvent, useCallback, useEffect, useState } from 'react'; -import { Button } from '../../components/Button'; -import { Setting } from '../../components/Setting'; -import { Spinner } from '../../components/Spinner'; -import { useAsyncCall } from '../../logic/useAsyncCall'; -import useKilnState, { usePike } from '../../state/kiln'; +import React from 'react'; +import SourceSyncer from '../../components/SourceSyncer'; +import { usePike } from '../../state/kiln'; interface UpdatePreferencesProps { base: Vat | undefined; } export const UpdatePreferences = ({ base }: UpdatePreferencesProps) => { - const { changeOTASource, toggleOTAs } = useKilnState((s) => - _.pick(s, ['toggleOTAs', 'changeOTASource']) - ); - const pike = usePike('base'); - const otasEnabled = !!pike?.sync; - const otaSource = pike?.sync?.ship; - - const toggleBase = useCallback((on: boolean) => toggleOTAs('base', on), [toggleOTAs]); - - const [source, setSource] = useState(''); - const sourceDirty = source !== otaSource; - const { status: sourceStatus, call: setOTA } = useAsyncCall(changeOTASource); - - useEffect(() => { - if (otaSource) { - setSource(otaSource); - } - }, [otaSource]); - - const handleSourceChange = useCallback((e: ChangeEvent) => { - const { target } = e; - const value = target.value.trim(); - setSource(value.startsWith('~') ? value : `~${value}`); - }, []); - - const onSubmit = useCallback( - (e: FormEvent) => { - e.preventDefault(); - setOTA(source); - }, - [source] - ); + const desk = 'base'; + const appName = 'your Urbit'; + const pike = usePike(desk); + const syncShip = pike?.sync?.ship; return ( -
-

Update Preferences

-
- -
- - -
-
- -

- Ensure that system updates are downloaded and applied as soon as my update provider has an - update readied -

-
-
+ ); }; From 59562c54b80b799bdd7e398f2ce2561c05c60d1a Mon Sep 17 00:00:00 2001 From: tomholford Date: Mon, 7 Nov 2022 23:58:03 -0800 Subject: [PATCH 18/29] grid: remove unused kiln/state logic --- ui/src/state/kiln.ts | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index c198ee4..477593f 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -1,4 +1,4 @@ -import { scryLag, kilnInstall, kilnPause, kilnResume, getPikes, Pikes, Pike, kilnUnsync, kilnSync } from '@urbit/api'; +import { scryLag, getPikes, Pikes, Pike, kilnUnsync, kilnSync } from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; import { useCallback } from 'react'; @@ -12,8 +12,6 @@ interface KilnState { lag: boolean; fetchLag: () => Promise; fetchPikes: () => Promise; - changeOTASource: (ship: string) => Promise; - toggleOTAs: (desk: string, on: boolean) => Promise; toggleSync: (desk: string, ship: string) => Promise; set: (s: KilnState) => void; initializeKiln: () => Promise; @@ -35,24 +33,6 @@ const useKilnState = create((set, get) => ({ const lag = await api.scry(scryLag); set({ lag }); }, - changeOTASource: async (ship: string) => { - await api.poke(kilnInstall(ship, 'kids', 'base')); - }, - toggleOTAs: async (desk: string, on: boolean) => { - set( - produce((draft: KilnState) => { - const pike = draft.pikes[desk]; - if (!pike) { - return; - } - - pike.zest = on ? 'live' : 'held'; - }) - ); - - await api.poke(on ? kilnResume(desk) : kilnPause(desk)); - await get().fetchPikes(); // refresh pikes state - }, toggleSync: async (desk: string, ship: string) => { const synced = !!get().pikes[desk].sync; await (useMockData From a5cfafa1fc19c0f02f43d72944b80fa91eb90934 Mon Sep 17 00:00:00 2001 From: tomholford Date: Tue, 8 Nov 2022 03:32:32 -0800 Subject: [PATCH 19/29] kiln: update %kiln-bump poke and mark --- ui/src/pages/Grid.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/pages/Grid.tsx b/ui/src/pages/Grid.tsx index b1d9682..858cae3 100644 --- a/ui/src/pages/Grid.tsx +++ b/ui/src/pages/Grid.tsx @@ -26,8 +26,8 @@ export const Grid: FunctionComponent = () => { window.location.reload(); } const start = performance.now(); - await useKilnState.getState().fetchVats(); - await useKilnState.getState().fetchVats(); + await useKilnState.getState().fetchPikes(); + await useKilnState.getState().fetchPikes(); if (performance.now() - start > 5000) { attempt(count + 1); } else { From 8766fd05cc3ae5c9e58c2fbfb7df1598f77b3c5d Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 11 Nov 2022 00:54:23 -0800 Subject: [PATCH 20/29] ux: clearer button label This addresses feedback from the Design team: what is being Unsynced? Thanks @urcades! --- ui/src/components/SourceSyncer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/SourceSyncer.tsx b/ui/src/components/SourceSyncer.tsx index b604c3a..69c63e6 100644 --- a/ui/src/components/SourceSyncer.tsx +++ b/ui/src/components/SourceSyncer.tsx @@ -59,7 +59,7 @@ export default function SourceSyncer({ appName, title, syncDesk, syncShip }: Sou
From 714f50ed4f45c5fd562770306561fee6899fb1e5 Mon Sep 17 00:00:00 2001 From: tomholford Date: Mon, 21 Nov 2022 16:10:59 -0800 Subject: [PATCH 21/29] grid: distinguish app sync from system install By using `%kiln-install` instead of `%kiln-sync` for System Updates, this ensures that the `%kids` desk is also updated. Also, address UX feedback: render the entire source ship's patp to avoid ambiguity. (as opposed to truncating a moon's name). --- ui/src/components/ShipName.tsx | 5 ++- .../{SourceSyncer.tsx => SourceSetter.tsx} | 42 +++++++++++-------- ui/src/preferences/AppPrefs.tsx | 16 +++---- .../about-system/UpdatePreferences.tsx | 18 +++++--- ui/src/state/kiln.ts | 19 ++++++++- 5 files changed, 67 insertions(+), 33 deletions(-) rename ui/src/components/{SourceSyncer.tsx => SourceSetter.tsx} (74%) diff --git a/ui/src/components/ShipName.tsx b/ui/src/components/ShipName.tsx index 49a5441..ef16071 100644 --- a/ui/src/components/ShipName.tsx +++ b/ui/src/components/ShipName.tsx @@ -3,11 +3,12 @@ import React, { HTMLAttributes } from 'react'; type ShipNameProps = { name: string; + truncate?: boolean; } & HTMLAttributes; -export const ShipName = ({ name, ...props }: ShipNameProps) => { +export const ShipName = ({ name, truncate = true, ...props }: ShipNameProps) => { const separator = /([_^-])/; - const citedName = cite(name); + const citedName = truncate ? cite(name) : name; if (!citedName) { return null; diff --git a/ui/src/components/SourceSyncer.tsx b/ui/src/components/SourceSetter.tsx similarity index 74% rename from ui/src/components/SourceSyncer.tsx rename to ui/src/components/SourceSetter.tsx index 69c63e6..b37936f 100644 --- a/ui/src/components/SourceSyncer.tsx +++ b/ui/src/components/SourceSetter.tsx @@ -1,34 +1,39 @@ import React, { useCallback, useState } from 'react'; import { useAsyncCall } from '../logic/useAsyncCall'; -import useKilnState from '../state/kiln'; import { Button } from './Button'; import { ShipName } from './ShipName'; import { Spinner } from './Spinner'; -interface SourceSyncerProps { +interface SourceSetterProps { appName: string; + srcDesk: string; + srcShip?: string; title: string; - syncDesk: string; - syncShip?: string; + toggleSrc: (desk: string, ship: string) => Promise; } -export default function SourceSyncer({ appName, title, syncDesk, syncShip }: SourceSyncerProps) { - const [newSyncShip, setNewSyncShip] = useState(syncShip ?? ''); - const { toggleSync } = useKilnState(); - const { status: requestStatus, call: setSync } = useAsyncCall(toggleSync); - const syncDirty = newSyncShip !== syncShip; +export default function SourceSetter({ + appName, + srcDesk, + srcShip, + title, + toggleSrc +}: SourceSetterProps) { + const [newSyncShip, setNewSyncShip] = useState(srcShip ?? ''); + const { status: requestStatus, call: handleSubmit } = useAsyncCall(toggleSrc); + const syncDirty = newSyncShip !== srcShip; - const onUnsync = useCallback(() => { - if (!syncShip) { + const onUnset = useCallback(() => { + if (!srcShip) { return; } if ( // eslint-disable-next-line no-alert, no-restricted-globals confirm(`Are you sure you want to unsync ${appName}? You will no longer receive updates.`) ) { - toggleSync(syncDesk, syncShip); + toggleSrc(srcDesk, srcShip); } - }, [syncShip, syncDesk, toggleSync]); + }, [srcShip, srcDesk]); const handleSourceChange = useCallback((e: React.ChangeEvent) => { const { target } = e; @@ -39,26 +44,27 @@ export default function SourceSyncer({ appName, title, syncDesk, syncShip }: Sou const onSubmit = useCallback( async (e: React.FormEvent) => { e.preventDefault(); - await setSync(syncDesk, newSyncShip); + await handleSubmit(srcDesk, newSyncShip); }, - [syncDesk, newSyncShip, toggleSync] + [srcDesk, newSyncShip] ); return ( <>

{title}

- {syncShip ? ( + {srcShip ? ( <>

Automatic Updates

Automatically download and apply updates to keep {appName} up to date.

- OTA Source: + OTA Source:{' '} +

-
diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index 1f40db8..dd79709 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -1,23 +1,25 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { useCharge } from '../state/docket'; -import { usePike } from '../../state/kiln'; +import useKilnState, { usePike } from '../state/kiln'; import { getAppName } from '../state/util'; -import SourceSyncer from '../components/SourceSyncer'; +import SourceSetter from '../components/SourceSetter'; export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const { desk } = match.params; const charge = useCharge(desk); const appName = getAppName(charge); const pike = usePike(desk); - const syncShip = pike?.sync?.ship; - + const srcShip = pike?.sync?.ship; + const { toggleSync } = useKilnState(); + return ( - ); }; diff --git a/ui/src/preferences/about-system/UpdatePreferences.tsx b/ui/src/preferences/about-system/UpdatePreferences.tsx index 65f1dad..13e48c7 100644 --- a/ui/src/preferences/about-system/UpdatePreferences.tsx +++ b/ui/src/preferences/about-system/UpdatePreferences.tsx @@ -1,9 +1,10 @@ import { Vat } from '@urbit/api'; import _ from 'lodash'; import React from 'react'; -import SourceSyncer from '../../components/SourceSyncer'; -import { usePike } from '../../state/kiln'; +import SourceSetter from '../../components/SourceSetter'; +import useKilnState, { usePike } from '../../state/kiln'; +// TODO interface UpdatePreferencesProps { base: Vat | undefined; } @@ -12,9 +13,16 @@ export const UpdatePreferences = ({ base }: UpdatePreferencesProps) => { const desk = 'base'; const appName = 'your Urbit'; const pike = usePike(desk); - const syncShip = pike?.sync?.ship; - + const srcShip = pike?.sync?.ship; + const { toggleInstall } = useKilnState(); + return ( - + ); }; diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 477593f..2590d51 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -1,4 +1,13 @@ -import { scryLag, getPikes, Pikes, Pike, kilnUnsync, kilnSync } from '@urbit/api'; +import { + scryLag, + getPikes, + Pikes, + Pike, + kilnUnsync, + kilnSync, + kilnUninstall, + kilnInstall +} from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; import { useCallback } from 'react'; @@ -12,6 +21,7 @@ interface KilnState { lag: boolean; fetchLag: () => Promise; fetchPikes: () => Promise; + toggleInstall: (desk: string, ship: string) => Promise; toggleSync: (desk: string, ship: string) => Promise; set: (s: KilnState) => void; initializeKiln: () => Promise; @@ -33,6 +43,13 @@ const useKilnState = create((set, get) => ({ const lag = await api.scry(scryLag); set({ lag }); }, + toggleInstall: async (desk: string, ship: string) => { + const synced = !!get().pikes[desk].sync; + await (useMockData + ? fakeRequest('') + : api.poke(synced ? kilnUninstall(desk) : kilnInstall(ship, desk))); + await get().fetchPikes(); + }, toggleSync: async (desk: string, ship: string) => { const synced = !!get().pikes[desk].sync; await (useMockData From b8bf4dc431c8c77b56d3c40b0142e9ec534f6984 Mon Sep 17 00:00:00 2001 From: tomholford Date: Mon, 21 Nov 2022 21:52:11 -0800 Subject: [PATCH 22/29] grid: install foreign system updates to local desk This addresses PR feedback: https://github.com/urbit/urbit/pull/6093#discussion_r1028798146 --- ui/src/state/kiln.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/state/kiln.ts b/ui/src/state/kiln.ts index 2590d51..5abd1f8 100644 --- a/ui/src/state/kiln.ts +++ b/ui/src/state/kiln.ts @@ -47,7 +47,7 @@ const useKilnState = create((set, get) => ({ const synced = !!get().pikes[desk].sync; await (useMockData ? fakeRequest('') - : api.poke(synced ? kilnUninstall(desk) : kilnInstall(ship, desk))); + : api.poke(synced ? kilnUninstall(desk) : kilnInstall(ship, 'kids', desk))); await get().fetchPikes(); }, toggleSync: async (desk: string, ship: string) => { From 67f90912128fdbc460b01573c5005d75578846ab Mon Sep 17 00:00:00 2001 From: tomholford Date: Tue, 29 Nov 2022 05:31:16 -0800 Subject: [PATCH 23/29] grid: fix tile bullet + suspend notice layout --- ui/src/tiles/Tile.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/src/tiles/Tile.tsx b/ui/src/tiles/Tile.tsx index 52fd1b9..bc2bfe8 100644 --- a/ui/src/tiles/Tile.tsx +++ b/ui/src/tiles/Tile.tsx @@ -26,6 +26,7 @@ export const Tile: FunctionComponent = ({ charge, desk, disabled = fa const loading = !disabled && 'install' in chad; const suspended = disabled || 'suspend' in chad; const hung = 'hung' in chad; + // TODO should held zest be considered inactive? suspended? also, null sync? const active = !disabled && chadIsRunning(chad); const link = getAppHref(href); const backgroundColor = suspended ? suspendColor : active ? tileColor || 'purple' : suspendColor; @@ -56,6 +57,9 @@ export const Tile: FunctionComponent = ({ charge, desk, disabled = fa >
+ {pike?.zest === 'held' && !disabled && ( + + )} {!active && ( <> {loading && } @@ -65,9 +69,6 @@ export const Tile: FunctionComponent = ({ charge, desk, disabled = fa )}
- {pike?.zest === 'held' && !disabled && ( - - )} Date: Tue, 29 Nov 2022 06:31:56 -0800 Subject: [PATCH 24/29] grid: can unsync system OTAs from notifications When shown the Base Blocked notification, the user can opt to disable updates so as to continue using out-of-date apps. --- ui/src/logic/useSystemUpdate.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/logic/useSystemUpdate.tsx b/ui/src/logic/useSystemUpdate.tsx index 8f5f9a7..5bc3391 100644 --- a/ui/src/logic/useSystemUpdate.tsx +++ b/ui/src/logic/useSystemUpdate.tsx @@ -12,6 +12,7 @@ function pikeIsBlocked(newKelvin: number, pike: Pike) { export function useSystemUpdate() { const { push } = useHistory(); + // TODO: fix update const base = usePike('base'); const update = base?.arak?.rail?.next?.[0]; const newKelvin = base?.wefts[0]?.kelvin ?? 418; @@ -26,7 +27,7 @@ export function useSystemUpdate() { const blockedCount = blockedCharges.length; const freezeApps = useCallback(async () => { - api.poke(kilnBump()); + await api.poke(kilnBump()); push('/leap/upgrading'); }, []); From 2e0dc2dbc3eb9923bf98766467232e5d209be863 Mon Sep 17 00:00:00 2001 From: tomholford Date: Tue, 29 Nov 2022 07:51:57 -0800 Subject: [PATCH 25/29] grid: dismiss Base Blocked notice on Archive --- ui/src/preferences/about-system/AboutSystem.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/preferences/about-system/AboutSystem.tsx b/ui/src/preferences/about-system/AboutSystem.tsx index 2809a57..93dbbf3 100644 --- a/ui/src/preferences/about-system/AboutSystem.tsx +++ b/ui/src/preferences/about-system/AboutSystem.tsx @@ -6,7 +6,6 @@ import { Dialog, DialogClose, DialogContent, DialogTrigger } from '../../compone import { FullTlon16Icon } from '../../components/icons/FullTlon16Icon'; import { useSystemUpdate } from '../../logic/useSystemUpdate'; import { useCharge } from '../../state/docket'; -import { useVat } from '../../state/kiln'; import { disableDefault, pluralize } from '../../state/util'; import { UpdatePreferences } from './UpdatePreferences'; @@ -16,11 +15,11 @@ function getHash(vat: Vat): string { } export const AboutSystem = () => { - const garden = useVat('garden'); const gardenCharge = useCharge('garden'); // TODO const { base, update, systemBlocked, blockedCharges, blockedCount, freezeApps } = useSystemUpdate(); + // TODO: should aeon come from chad, or from the base weft? or elsewhere? const hash = base && getHash(base); const aeon = base ? base.arak.rail?.aeon : ''; const nextAeon = update?.aeon; From f0b6568ad9dd8592f1b44988c107f02687e6a691 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 1 Dec 2022 14:23:03 -0800 Subject: [PATCH 26/29] deps: bump @urbit packages --- ui/package-lock.json | 16 ++++++++-------- ui/package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index b6aa9a3..254e81f 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -20,8 +20,8 @@ "@tlon/sigil-js": "^1.4.4", "@tloncorp/mock-http-api": "^1.2.0", "@types/lodash": "^4.14.172", - "@urbit/api": "^2.1.1", - "@urbit/http-api": "^2.2.0", + "@urbit/api": "^2.2.0", + "@urbit/http-api": "^2.3.0", "big-integer": "^1.6.48", "browser-cookies": "^1.2.0", "classnames": "^2.3.1", @@ -1695,9 +1695,9 @@ } }, "node_modules/@urbit/api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.1.1.tgz", - "integrity": "sha512-QRlqhtJ73q+pgMdSwuOO62HlxA7/2c5ylCcOUT01LXkJ2LTVCl5u+QnejdDvUmqjOuN2PyZk7df30xJVg6rC2A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.2.0.tgz", + "integrity": "sha512-W8kP9OT6yOK62n+4yCPO3i9QqU5xriLvZQ9WYW4SAV7ktbSrGuf2kmYbnoqfA/NybIs9Q/MbFkPewrz4XJ96Ag==", "dependencies": { "@babel/runtime": "^7.16.0", "big-integer": "^1.6.48", @@ -8888,9 +8888,9 @@ } }, "@urbit/api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.1.1.tgz", - "integrity": "sha512-QRlqhtJ73q+pgMdSwuOO62HlxA7/2c5ylCcOUT01LXkJ2LTVCl5u+QnejdDvUmqjOuN2PyZk7df30xJVg6rC2A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.2.0.tgz", + "integrity": "sha512-W8kP9OT6yOK62n+4yCPO3i9QqU5xriLvZQ9WYW4SAV7ktbSrGuf2kmYbnoqfA/NybIs9Q/MbFkPewrz4XJ96Ag==", "requires": { "@babel/runtime": "^7.16.0", "big-integer": "^1.6.48", diff --git a/ui/package.json b/ui/package.json index 0496eb6..3e2ad47 100644 --- a/ui/package.json +++ b/ui/package.json @@ -27,8 +27,8 @@ "@tlon/sigil-js": "^1.4.4", "@tloncorp/mock-http-api": "^1.2.0", "@types/lodash": "^4.14.172", - "@urbit/api": "^2.1.1", - "@urbit/http-api": "^2.2.0", + "@urbit/api": "^2.2.0", + "@urbit/http-api": "^2.3.0", "big-integer": "^1.6.48", "browser-cookies": "^1.2.0", "classnames": "^2.3.1", From 8f929eb066c6363e13da924fe800782386d1aac9 Mon Sep 17 00:00:00 2001 From: tomholford Date: Thu, 1 Dec 2022 17:22:45 -0800 Subject: [PATCH 27/29] grid: resolve diff in Preferences This change reconciles the drift in System Preferences between urbit/urbit/pkg/grid and urbit/garden --- ui/src/logic/useSystemUpdate.tsx | 12 ++++---- ui/src/mocks/handlers.ts | 10 +------ .../preferences/about-system/AboutSystem.tsx | 29 ++++++------------- .../about-system/UpdatePreferences.tsx | 7 +---- 4 files changed, 16 insertions(+), 42 deletions(-) diff --git a/ui/src/logic/useSystemUpdate.tsx b/ui/src/logic/useSystemUpdate.tsx index 5bc3391..a532080 100644 --- a/ui/src/logic/useSystemUpdate.tsx +++ b/ui/src/logic/useSystemUpdate.tsx @@ -4,7 +4,7 @@ import { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import api from '../state/api'; import { useCharges } from '../state/docket'; -import useKilnState, { usePike } from '../../state/kiln'; +import useKilnState, { usePike } from '../state/kiln'; function pikeIsBlocked(newKelvin: number, pike: Pike) { return !pike.wefts?.find(({ kelvin }) => kelvin === newKelvin); @@ -12,17 +12,16 @@ function pikeIsBlocked(newKelvin: number, pike: Pike) { export function useSystemUpdate() { const { push } = useHistory(); - // TODO: fix update const base = usePike('base'); - const update = base?.arak?.rail?.next?.[0]; - const newKelvin = base?.wefts[0]?.kelvin ?? 418; + const nextUpdate = base?.wefts[0]; + const newKelvin = base?.wefts[0]?.kelvin ?? 417; const charges = useCharges(); const [blocked] = useKilnState((s) => { const [b, u] = partition(Object.entries(s.pikes), ([, pike]) => pikeIsBlocked(newKelvin, pike)); return [b.map(([d]) => d), u.map(([d]) => d)] as const; }); - const systemBlocked = update && blocked; + const systemBlocked = nextUpdate && blocked; const blockedCharges = Object.values(pick(charges, blocked)); const blockedCount = blockedCharges.length; @@ -32,8 +31,7 @@ export function useSystemUpdate() { }, []); return { - base, - update, + nextUpdate, systemBlocked, blockedCharges, blockedCount, diff --git a/ui/src/mocks/handlers.ts b/ui/src/mocks/handlers.ts index 42fd629..ab71796 100644 --- a/ui/src/mocks/handlers.ts +++ b/ui/src/mocks/handlers.ts @@ -1,6 +1,6 @@ import { Handler, SubscriptionHandler, createResponse } from '@tloncorp/mock-http-api'; import mockContacts from './mockContacts'; -import { mockAllies, mockCharges, mockTreaties, mockVats } from '../state/mock-data'; +import { mockAllies, mockCharges, mockTreaties } from '../state/mock-data'; const settingsSub = { action: 'subscribe', @@ -45,14 +45,6 @@ const mockHandlers: Handler[] = [ } }) }, - { - action: 'scry', - app: 'hood', - path: '/kiln/vats', - func: () => ({ - mockVats - }) - }, { action: 'scry', app: 'hood', diff --git a/ui/src/preferences/about-system/AboutSystem.tsx b/ui/src/preferences/about-system/AboutSystem.tsx index 93dbbf3..9c913d3 100644 --- a/ui/src/preferences/about-system/AboutSystem.tsx +++ b/ui/src/preferences/about-system/AboutSystem.tsx @@ -1,4 +1,4 @@ -import { Vat } from '@urbit/api'; +import { Pike } from '@urbit/api'; import React from 'react'; import { AppList } from '../../components/AppList'; import { Button } from '../../components/Button'; @@ -6,24 +6,22 @@ import { Dialog, DialogClose, DialogContent, DialogTrigger } from '../../compone import { FullTlon16Icon } from '../../components/icons/FullTlon16Icon'; import { useSystemUpdate } from '../../logic/useSystemUpdate'; import { useCharge } from '../../state/docket'; +import { usePike } from '../../state/kiln'; import { disableDefault, pluralize } from '../../state/util'; import { UpdatePreferences } from './UpdatePreferences'; -function getHash(vat: Vat): string { - const parts = vat.hash.split('.'); +function getHash(pike: Pike): string { + const parts = pike.hash.split('.'); return parts[parts.length - 1]; } export const AboutSystem = () => { const gardenCharge = useCharge('garden'); - // TODO - const { base, update, systemBlocked, blockedCharges, blockedCount, freezeApps } = + const gardenPike = usePike(window.desk); + const { systemBlocked, blockedCharges, blockedCount, freezeApps } = useSystemUpdate(); - // TODO: should aeon come from chad, or from the base weft? or elsewhere? - const hash = base && getHash(base); - const aeon = base ? base.arak.rail?.aeon : ''; - const nextAeon = update?.aeon; - + const hash = gardenPike && getHash(gardenPike); + return ( <>
@@ -45,15 +43,6 @@ export const AboutSystem = () => {

Version {gardenCharge?.version} ({hash})

- {systemBlocked && ( -

- Aeon {aeon}{' '} - - —> / —> - {' '} - Aeon {nextAeon} -

- )}
{systemBlocked ? ( <> @@ -98,7 +87,7 @@ export const AboutSystem = () => { )}
- + ); }; diff --git a/ui/src/preferences/about-system/UpdatePreferences.tsx b/ui/src/preferences/about-system/UpdatePreferences.tsx index 13e48c7..f0df061 100644 --- a/ui/src/preferences/about-system/UpdatePreferences.tsx +++ b/ui/src/preferences/about-system/UpdatePreferences.tsx @@ -4,12 +4,7 @@ import React from 'react'; import SourceSetter from '../../components/SourceSetter'; import useKilnState, { usePike } from '../../state/kiln'; -// TODO -interface UpdatePreferencesProps { - base: Vat | undefined; -} - -export const UpdatePreferences = ({ base }: UpdatePreferencesProps) => { +export const UpdatePreferences = () => { const desk = 'base'; const appName = 'your Urbit'; const pike = usePike(desk); From 015d46afc66ee98dd20d8d6e62078e01f2025068 Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 2 Dec 2022 10:22:52 -0800 Subject: [PATCH 28/29] garden: import clay + kiln changes Co-authored-by: liam-fitzgerald --- desk/app/docket.hoon | 251 +++++++++++++++++---------------- desk/app/hark-system-hook.hoon | 132 +++++++++-------- desk/lib/treaty.hoon | 1 + desk/sys.kelvin | 2 +- desk/ted/get-dudes.hoon | 53 +++++++ 5 files changed, 265 insertions(+), 174 deletions(-) create mode 100644 desk/ted/get-dudes.hoon diff --git a/desk/app/docket.hoon b/desk/app/docket.hoon index 6dec08c..671834c 100644 --- a/desk/app/docket.hoon +++ b/desk/app/docket.hoon @@ -3,7 +3,7 @@ |% +$ card card:agent:gall +$ app-state - $: %2 + $: %3 :: local charges=(map desk charge) == @@ -42,7 +42,7 @@ ++ on-init ^- (quip card _this) :_ this - :~ (~(watch-our pass /kiln) %hood /kiln/vats) + :~ ~(tire pass /tire) (~(connect pass /eyre) [~ /] %docket) (~(wait pass /init) (add 1 now.bowl)) (~(connect pass /eyre) [~ /apps] %docket) @@ -54,20 +54,21 @@ |^ =+ !<(old=app-states vase) =? old ?=(?(~ ^) -.old) [%1 old] - =^ cards old + =^ cards-1 old ?. ?=(%1 -.old) `old - =/ rein=cage kiln-rein+!>([%base %.y ~ ~]) - =/ nuke=cage kiln-uninstall+!>(%hodl) - :_ old(- %2) - :~ [%pass /rein %agent [our.bowl %hood] %poke rein] - [%pass /nuke %agent [our.bowl %hood] %poke nuke] - == - ?> ?=(%2 -.old) + `old(- %2) + =^ cards-2 old + ?. ?=(%2 -.old) `old + :_ old(- %3) :_ ~ + ~(tire pass /tire) + ?> ?=(%3 -.old) + =/ cards-tire [~(tire pass /tire) ~] =. -.state old :: inflate-cache needs to be called after the state is set :: =. +.state inflate-cache - [cards this] + [:(weld cards-1 cards-2 cards-tire) this] + :: ++ inflate-cache ^- cache @@ -81,9 +82,11 @@ $^ state-0-ket $% state-0-sig state-1 + state-2 app-state == :: + +$ state-2 [%2 (map desk charge)] +$ state-1 [%1 (map desk charge)] +$ state-0-sig $: ~ @@ -94,6 +97,7 @@ == -- :: + ++ on-save !>(-.state) ++ on-poke |= [=mark =vase] @@ -189,13 +193,21 @@ == :: [%x %charges ~] + =/ tyr + .^(rock:tire:clay %cx /(scot %p our.bowl)//(scot %da now.bowl)/tire) :- ~ :- ~ %- charge-update:cg :- %initial %- ~(gas by *(map desk charge)) - %+ turn ~(tap by charges) + %+ murn ~(tap by charges) |= [=desk =charge] - [desk (get-light-charge charge)] + ?~ got=(~(get by tyr) desk) + ~ + ?: ?& ?=(%dead zest.u.got) + ?=(~ (get-apps-have:hood our.bowl desk now.bowl)) + == + ~ + `u=[desk (get-light-charge charge)] :: [%x %charges @ %version ~] ?~ charge=(~(get by charges) i.t.t.path) @@ -212,114 +224,11 @@ ~ `state [%rein ~] ~&(%reined `state) [%nuke ~] ~&(%nuked `state) - [%kiln ~] take-kiln + [%kiln ~] `state [%charge @ *] (take-charge i.t.wire t.t.wire) == [cards this] :: - ++ take-kiln - ^- (quip card _state) - ?+ -.sign (on-agent:def:cc wire sign) - %kick [(~(watch-our pass /kiln) %hood /kiln/vats)^~ state] - %fact - |^ ^- (quip card _state) - ?+ p.cage.sign ~|(take-kiln-mark/p.cage.sign !!) - %kiln-vats-snap-0 (on-snap !<(snap:hood q.cage.sign)) - %kiln-vats-diff-0 (on-diff !<(diff:hood q.cage.sign)) - == - :: - ++ on-snap - |= =snap:hood - ^- (quip card _state) - =| fex=(list card) - =/ ark ~(tap by snap) - |- ^- (quip card _state) - ?~ ark [(flop fex) state] - =^ caz state (on-commit i.ark) - $(ark t.ark, fex (weld (flop caz) fex)) - :: - ++ on-diff - |= =diff:hood - =+ !<(=diff:hood q.cage.sign) - ?- -.diff - %commit (on-commit [desk arak]:diff) - %suspend (on-suspend [desk arak]:diff) - %revive (on-revive [desk arak]:diff) - ?(%block %reset %merge-sunk %merge-fail) - `state - == - :: - ++ on-commit - |= [=desk =arak:hood] - ^- (quip card _state) - =* cha ~(. ch desk) - ?. docket-exists:cha - ~? ?& !=(%base desk) - !=(%kids desk) - == - [dap.bowl %no-docket-file-for desk] - `state - :: always update the docket in state to match clay's - :: - =/ =docket docket:cha - =/ pre=(unit charge) (~(get by charges) desk) - =. charges (new-docket:cha docket) - :: if the new chad is a site, we're instantly done - :: - ?: ?=(%site -.href.docket) - =. charges (new-chad:cha %site ~) - :- ~[add-fact:cha] - state - :: - =. by-base (~(put by by-base) base.href.docket desk) - :: if the glob specification is unchanged, keep it - :: - ?: &(?=(^ pre) =(href.docket.u.pre href.docket) ?=(%glob -.chad.u.pre)) - [~[add-fact:cha] state] - :: if the glob spec changed, but we already host it, keep it - :: (this is the "just locally uploaded" case) - :: - ?: ?& ?=(^ pre) - ?=(%glob -.chad.u.pre) - :: - .= [(sham glob.chad.u.pre) %ames our.bowl] - glob-reference.href.docket - == - [~[add-fact:cha] state] - :: if the glob changed, forget the old and fetch the new - :: - =. charges (new-chad:cha %install ~) - [[add-fact:cha fetch-glob:cha] state] - :: - ++ on-suspend - |= [=desk =arak:hood] - ^- (quip card _state) - =* cha ~(. ch desk) - ?. (~(has by charges) desk) `state - =/ glob=(unit glob) - =/ =chad - chad:(~(got by charges) desk) - ?:(?=(%glob -.chad) `glob.chad ~) - =. charges (new-chad:cha %suspend glob) - :_(state ~[add-fact:cha]) - :: - ++ on-revive - |= [=desk =arak:hood] - ^- (quip card _state) - =* cha ~(. ch desk) - ?. (~(has by charges) desk) `state - =/ =charge (~(got by charges) desk) - ?. ?=(%glob -.href.docket.charge) - =. charges (new-chad:cha %site ~) - :_(state ~[add-fact:cha]) - =. charges - %- new-chad:cha - ?. ?=([%suspend ~ *] chad.charge) - [%install ~] - [%glob u.glob.chad.charge] - :_(state [add-fact fetch-glob]:cha) - -- - == ++ take-charge |= [=desk =^wire] ^- (quip card _state) @@ -398,6 +307,7 @@ :: ++ on-arvo |= [=wire sign=sign-arvo] + |^ =^ cards state ?+ wire (on-arvo:def wire sign) [%init ~] @@ -410,8 +320,114 @@ ?: accepted.sign `state ~& [dap.bowl %failed-to-bind path.binding.sign] `state + :: + [%tire ~] + ?> ?=([%clay %tire *] sign) + ?- -.p.sign + %& (on-rock p.p.sign) + %| (on-wave p.p.sign) + == + :: + [%warp * ~] + ?> ?=(%writ +<.sign) + (on-writ i.t.wire p.sign) == [cards this] + :: + ++ on-rock + |= tyr=rock:tire:clay + ^- (quip card _state) + =| fex=(list card) + =/ ark ~(tap by tyr) + |- ^- (quip card _state) + ?~ ark [(flop fex) state] + =^ caz state (on-zest [p zest.q]:i.ark) + $(ark t.ark, fex (weld (flop caz) fex)) + :: + ++ on-wave + |= =wave:tire:clay + ^- (quip card _state) + ?- -.wave + %wait `state + %warp `state + %zest (on-zest +.wave) + == + :: + ++ on-zest + |= [=desk =zest:clay] + ^- (quip card _state) + =* cha ~(. ch desk) + =/ card-1 + (~(warp-our pass /warp/[desk]) desk ~ %sing %z da+now.bowl /desk/docket-0) + =^ cards-2 state + ?. (~(has by charges) desk) + `state + =/ =charge (~(got by charges) desk) + ?- zest + %live + ?. ?=(%glob -.href.docket.charge) + =. charges (new-chad:cha %site ~) + :_(state ~[add-fact:cha]) + :_(state ~[add-fact:cha]) + :: + ?(%held %dead) + =/ glob=(unit glob) + ?:(?=(%glob -.chad.charge) `glob.chad.charge ~) + =. charges (new-chad:cha %suspend glob) + :_(state ~[add-fact:cha]) + == + [[card-1 cards-2] state] + :: + ++ on-writ + |= [=desk =riot:clay] + ^- (quip card _state) + =/ card-1 + (~(warp-our pass /warp/[desk]) desk ~ %next %z da+now.bowl /desk/docket-0) + =^ cards-2 state + =* cha ~(. ch desk) + =/ tyr + .^(rock:tire:clay %cx /(scot %p our.bowl)//(scot %da now.bowl)/tire) + ?. =(%live zest:(~(got by tyr) desk)) + `state + ?. docket-exists:cha + :: ~? ?& !=(%base desk) + :: !=(%kids desk) + :: == + :: [dap.bowl %no-docket-file-for desk] + `state + :: always update the docket in state to match clay's + :: + =/ =docket docket:cha + =/ pre=(unit charge) (~(get by charges) desk) + =. charges (new-docket:cha docket) + :: if the new chad is a site, we're instantly done + :: + ?: ?=(%site -.href.docket) + =. charges (new-chad:cha %site ~) + :- ~[add-fact:cha] + state + :: + =. by-base (~(put by by-base) base.href.docket desk) + :: if the glob specification is unchanged, keep it + :: + ?: &(?=(^ pre) =(href.docket.u.pre href.docket) ?=(%glob -.chad.u.pre)) + [~[add-fact:cha] state] + :: if the glob spec changed, but we already host it, keep it + :: (this is the "just locally uploaded" case) + :: + ?: ?& ?=(^ pre) + ?=(%glob -.chad.u.pre) + :: + .= [(sham glob.chad.u.pre) %ames our.bowl] + glob-reference.href.docket + == + [~[add-fact:cha] state] + :: if the glob changed, forget the old and fetch the new + :: + =. charges (new-chad:cha %install ~) + [[add-fact:cha fetch-glob:cha] state] + [[card-1 cards-2] state] + -- :: ++ on-fail on-fail:def ++ on-leave on-leave:def @@ -728,4 +744,3 @@ ++ docket .^(^docket %cx (scry:io desk docket-loc)) -- -- - diff --git a/desk/app/hark-system-hook.hoon b/desk/app/hark-system-hook.hoon index e7cd713..e4c4994 100644 --- a/desk/app/hark-system-hook.hoon +++ b/desk/app/hark-system-hook.hoon @@ -2,16 +2,16 @@ /+ verb, dbug, default-agent, agentio |% +$ card card:agent:gall -+$ state-0 [%0 lagging=_|] ++$ state-1 [%1 lagging=_|] :: ++ lag-interval ~m10 -- %+ verb | %- agent:dbug ^- agent:gall -=| state-0 +=| state-1 =* state - -=< +=< |_ =bowl:gall +* this . def ~(. (default-agent this %|) bowl) @@ -21,12 +21,23 @@ ++ on-init ^- (quip card _this) :_ this - [onboard watch:kiln check:lag ~]:cc + [onboard tire:cy check:lag ~]:cc :: ++ on-load |= =vase - =+ !<(old=state-0 vase) - `this(state old) + ^- (quip card _this) + |^ + =+ !<(old=app-states vase) + =^ cards-1 old + ?. ?=(%0 -.old) `old + [[tire:cy:cc]~ old(- %1)] + ?> ?=(%1 -.old) + =/ cards-tire [tire:cy:cc ~] + [(weld cards-1 cards-tire) this(state old)] + :: + +$ app-states $%(state-0 state-1) + +$ state-0 [%0 lagging=_|] + -- :: ++ on-save !>(state) ++ on-poke on-poke:def @@ -34,45 +45,57 @@ ++ on-watch on-watch:def ++ on-agent |= [=wire =sign:agent:gall] - |^ + ^- (quip card _this) ?+ wire (on-agent:def wire sign) - [%kiln %vats ~] take-kiln-vats + [%kiln %vats ~] `this == - ++ take-kiln-vats - ?- -.sign - ?(%poke-ack %watch-ack) (on-agent:def wire sign) - %kick :_(this (drop safe-watch:kiln:cc)) - :: - %fact - ?. ?=(%kiln-vats-diff-0 p.cage.sign) `this - =+ !<(=diff:hood q.cage.sign) - ?+ -.diff `this - :: - %commit - ?. |(=(desk.diff %base) ~(has-docket de:cc desk.diff)) `this - =/ =action:hark ~(commit de:cc desk.diff) +:: +++ on-arvo + |= [=wire sign=sign-arvo] + ^- (quip card _this) + |^ + ?+ wire (on-arvo:def wire sign) + [%clay %tire ~] take-clay-tire + [%clay %warp * ~] (take-clay-warp i.t.t.wire) + [%check-lag ~] take-check-lag + == + :: + ++ take-check-lag + ^- (quip card _this) + ?> ?=([%behn %wake *] sign) + =+ .^(lag=? %$ (scry:io %$ /zen/lag)) + ?: =(lagging lag) :_(this ~[check:lag:cc]) + :_ this(lagging lag) + :_ ~[check:lag:cc] + ?:(lagging start:lag:cc stop:lag:cc) + :: + ++ take-clay-tire + ^- (quip card _this) + ?> ?=(%tire +<.sign) + ?- -.p.sign + %& [(turn ~(tap in ~(key by p.p.sign)) warp:cy:cc) this] + %| + ?- -.p.p.sign + %zest `this + %warp `this + %wait + =/ =action:hark (~(blocked de:cc desk.p.p.sign) weft.p.p.sign) :_ this ~[(poke:ha:cc action)] - :: - %block - =/ =action:hark (~(blocked de:cc desk.diff) blockers.diff) - :_ this - ~[(poke:ha:cc action)] == == + :: + ++ take-clay-warp + |= =desk + ^- (quip card _this) + ?> ?=(%writ +<.sign) + =/ cards + ?. |(=(desk %base) ~(has-docket de:cc desk)) ~ + =/ =action:hark ~(commit de:cc desk) + ~[(poke:ha:cc action)] + [[(warp:cy:cc desk) cards] this] -- :: -++ on-arvo - |= [=wire sign=sign-arvo] - ^- (quip card _this) - ?. ?=([%check-lag ~] wire) (on-arvo:def wire sign) - ?> ?=([%behn %wake *] sign) - =+ .^(lag=? %$ (scry:io %$ /zen/lag)) - ?: =(lagging lag) :_(this ~[check:lag:cc]) - :_ this(lagging lag) - :_ ~[check:lag:cc] - ?:(lagging start:lag:cc stop:lag:cc) -:: ++ on-fail on-fail:def ++ on-leave on-leave:def -- @@ -89,7 +112,7 @@ [~[text+'Welcome to urbit'] ~ now.bowl / /] :: ++ lag - |% + |% ++ check (~(wait pass /check-lag) (add now.bowl lag-interval)) ++ place [q.byk.bowl /lag] ++ body `body:hark`[~[text/'Runtime lagging'] ~ now.bowl / /] @@ -102,24 +125,23 @@ ++ poke |=(=action:hark (poke-our:pass %hark-store hark-action+!>(action))) -- -++ kiln +:: +++ cy |% - ++ path /kiln/vats - ++ pass ~(. ^pass path) - ++ watch (watch-our:pass %hood path) - ++ watching (~(has by wex.bowl) [path our.bowl %hood]) - ++ safe-watch `(unit card)`?:(watching ~ `watch) + ++ tire ~(tire pass /clay/tire) + ++ warp + |= =desk + (~(warp-our pass /clay/warp/[desk]) desk ~ %next %z da+now.bowl /) -- :: ++ de |_ =desk - ++ scry-path (scry:io desk /desk/docket-0) + ++ scry-path (scry:io desk /desk/docket-0) ++ has-docket .^(? %cu scry-path) - ++ docket .^(docket:^docket %cx scry-path) - ++ hash .^(@uv %cz (scry:io desk ~)) - ++ place `place:hark`[q.byk.bowl /desk/[desk]] - ++ vat - .^(vat:hood %gx (scry:io %hood /kiln/vat/[desk]/noun)) + ++ docket .^(docket:^docket %cx scry-path) + ++ hash .^(@uv %cz (scry:io desk ~)) + ++ place `place:hark`[q.byk.bowl /desk/[desk]] + ++ version ud:.^(cass:clay %cw (scry:io desk /)) ++ body |= [=path title=cord content=(unit cord)] ^- body:hark @@ -131,7 +153,7 @@ %+ rap 3 ?: =(desk %base) ['System software' cord ~] - ?: has-docket + ?: has-docket ['App: "' title:docket '"' cord ~] ['Desk: ' desk cord ~] :: @@ -142,7 +164,7 @@ :: ++ commit ^- action:hark - ?:(=(1 ud.cass:vat) created updated) + ?:(=(1 version) created updated) :: ++ created ^- action:hark @@ -155,11 +177,11 @@ (body /desk/[desk] (title-prefix (rap 3 ' has been updated to ' get-version ~)) ~) :: ++ blocked - |= blockers=(set ^desk) + |= =weft ^- action:hark :+ %add-note [/blocked place] %^ body /blocked (title-prefix ' is blocked from upgrading') - `(rap 3 'Blocking desks: ' (join ', ' ~(tap in blockers))) + `(rap 3 'Blocked waiting for system version: ' (scot %ud num.weft) 'K' ~) :: ++ ver |= =version:^docket @@ -172,7 +194,7 @@ -- ++ note |% - ++ merge + ++ merge |= [=desk hash=@uv] ^- (list body:hark) :_ ~ diff --git a/desk/lib/treaty.hoon b/desk/lib/treaty.hoon index bea8fc7..2a3205a 100644 --- a/desk/lib/treaty.hoon +++ b/desk/lib/treaty.hoon @@ -28,6 +28,7 @@ %tas s+(scot %tas p.c) %ud (numb p.c) == + :: ++ foreign-desk |= [s=^ship =desk] ^- cord diff --git a/desk/sys.kelvin b/desk/sys.kelvin index e77a3de..b7bcb9e 100644 --- a/desk/sys.kelvin +++ b/desk/sys.kelvin @@ -1 +1 @@ -[%zuse 418] +[%zuse 417] diff --git a/desk/ted/get-dudes.hoon b/desk/ted/get-dudes.hoon new file mode 100644 index 0000000..d1b97cd --- /dev/null +++ b/desk/ted/get-dudes.hoon @@ -0,0 +1,53 @@ +/- spider +/+ *strandio +:: +=, strand=strand:spider +:: +:: send on /spider/garden/json/get-dudes/json +:: +|% +++ buds :: get agents currently running + |= p=desk + =/ m (strand ,(list dude:gall)) + ^- form:m + ?. =(%$ p) + ;< q=(list dude:gall) bind:m (suds p) + (pure:m q) + ;< q=(list desk) bind:m duds + =| r=(list (list dude:gall)) + |- ^- form:m + =* s $ + ?~ q (pure:m (zing r)) + ;< t=(list dude:gall) bind:m (suds i.q) + s(q t.q, r [t r]) +:: +++ suds :: clean %ge scry + |= p=desk + =/ m (strand ,(list dude:gall)) + ^- form:m + ;< q=(set [dude:gall ?]) bind:m + (scry (set ,[dude:gall ?]) /ge/(scot %tas p)) + %- pure:m + (murn ~(tap in q) |=([dude:gall ?] ?.(+.+< ~ `-.+<))) +:: +++ duds :: get desks + =/ m (strand ,(list desk)) + ^- form:m + ;< p=(set desk) bind:m (scry (set ,desk) /cd/base) + (pure:m ~(tap in p)) +-- +:: +^- thread:spider +|= jon=vase +=/ m (strand ,vase) +^- form:m +;< =bowl:spider bind:m get-bowl +=, bowl +?~ know=!<((unit json) jon) + (pure:m !>(`json`[%s 'invalid-request'])) +?. ?=([%s @] u.know) + (pure:m !>(`json`[%s 'invalid-request'])) +=, format +;< breh=(list @tas) bind:m (buds (so:dejs u.know)) +%- pure:m +!>(`json`(frond:enjs 'buds' a+(turn breh |=(@tas s+[+<])))) From 235f7ca0db7dbd633fee4372235aca536c77cecd Mon Sep 17 00:00:00 2001 From: tomholford Date: Fri, 2 Dec 2022 14:31:03 -0800 Subject: [PATCH 29/29] ui: address PR feedback - wrap the SourceSetter with a black card backdrop --- ui/src/preferences/AppPrefs.tsx | 16 +++++++++------- .../about-system/UpdatePreferences.tsx | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/ui/src/preferences/AppPrefs.tsx b/ui/src/preferences/AppPrefs.tsx index dd79709..ad53136 100644 --- a/ui/src/preferences/AppPrefs.tsx +++ b/ui/src/preferences/AppPrefs.tsx @@ -14,12 +14,14 @@ export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => { const { toggleSync } = useKilnState(); return ( - +
+ +
); }; diff --git a/ui/src/preferences/about-system/UpdatePreferences.tsx b/ui/src/preferences/about-system/UpdatePreferences.tsx index f0df061..bf63ec9 100644 --- a/ui/src/preferences/about-system/UpdatePreferences.tsx +++ b/ui/src/preferences/about-system/UpdatePreferences.tsx @@ -12,12 +12,14 @@ export const UpdatePreferences = () => { const { toggleInstall } = useKilnState(); return ( - +
+ +
); };