From 81ef91b24a564525827e30365ed78f2f1387021b Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 25 Aug 2021 09:17:12 +1000 Subject: [PATCH] grid: system notifications wip --- pkg/grid/src/components/DocketHeader.tsx | 14 +--- pkg/grid/src/components/DocketImage.tsx | 29 ++++++++ pkg/grid/src/nav/Notifications.tsx | 94 +++++++++++++++++++++++- pkg/grid/src/pages/Grid.tsx | 12 ++- pkg/grid/src/state/api.ts | 2 +- pkg/grid/src/state/kiln.ts | 24 +++++- pkg/grid/tailwind.config.js | 3 + pkg/grid/vite.config.ts | 2 +- pkg/npm/api/hood/lib.ts | 25 +++++++ 9 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 pkg/grid/src/components/DocketImage.tsx diff --git a/pkg/grid/src/components/DocketHeader.tsx b/pkg/grid/src/components/DocketHeader.tsx index 6e4000fdb..138b62626 100644 --- a/pkg/grid/src/components/DocketHeader.tsx +++ b/pkg/grid/src/components/DocketHeader.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Docket } from '@urbit/api/docket'; +import {DocketImage} from './DocketImage'; interface DocketHeaderProps { docket: Docket; @@ -12,18 +13,7 @@ export function DocketHeader(props: DocketHeaderProps) { return (
-
- {image && ( - - )} -
+

{title}

{info &&

{info}

} diff --git a/pkg/grid/src/components/DocketImage.tsx b/pkg/grid/src/components/DocketImage.tsx new file mode 100644 index 000000000..550fa1d3c --- /dev/null +++ b/pkg/grid/src/components/DocketImage.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Docket } from '@urbit/api/docket'; +import cn from 'classnames'; + +interface DocketImageProps extends Pick { + small?: boolean; + className?: string; +} + +export function DocketImage({ color, image, className = '', small = false }: DocketImageProps) { + const sizing = small + ? 'w-4 h-4 md:w-6 md:h-6 rounded-md' + : 'w-12 h-12 md:w-20 md:h-20 rounded-xl'; + + return ( +
+ {image && ( + + )} +
+ ); +} diff --git a/pkg/grid/src/nav/Notifications.tsx b/pkg/grid/src/nav/Notifications.tsx index faee3838c..8ef85d2f3 100644 --- a/pkg/grid/src/nav/Notifications.tsx +++ b/pkg/grid/src/nav/Notifications.tsx @@ -1,5 +1,89 @@ -import React, { useEffect } from 'react'; +import React, { FC, useEffect, useState } from 'react'; +import cn from 'classnames'; import { useLeapStore } from './Nav'; +import { useBlockers, useLag } from '../state/kiln'; +import { useCharge } from '../state/docket'; +import { DocketImage } from '../components/DocketImage'; +import api from '../state/api'; +import { kilnSuspend } from '../../../npm/api/hood'; + +interface INotification { + title: string; + body: string; + actions: { + title: string; + role: 'primary' | 'destructive' | 'none'; + link: string; + }[]; +} + +interface NotificationProps { + notification?: INotification; + read?: boolean; + className?: string; + children?: React.ReactNode; +} + +const Notification: FC = ({ notification, className, children }) => ( +
{children}
+); + +const LagNotification = () => ( + +

+ The runtime of this ship is out of date, and preventing a kernel upgrade. Please upgrade to + the latest runtime version. If you are hosted, please contact your hosting provider +

+
+); + +const DeskIcon = ({ desk }) => { + const { title, image, color } = useCharge(desk); + + return ( +
+ +

{title}

+
+ ); +}; + +interface BlockNotificationProps { + desks: string[]; +} +const BlockNotification: React.FC = ({ desks }) => { + const count = desks.length; + const [dismissed, setDismissed] = useState(false); + const onArchive = async () => { + await Promise.all(desks.map((d) => api.poke(kilnSuspend(d)))); + }; + + if (dismissed) { + return null; + } + return ( + +

The following {desks.length} apps blocked a System Update:

+
+ {desks.map((desk) => ( + + ))} +
+

+ In order to proceed with the System Update, you’ll need to temporarily archive these apps, + which will render them unusable, but with data intact. +
+
+ Archived apps will automatically un-archive and resume operation when their developer + provides an app update. +

+
+ + +
+
+ ); +}; export const Notifications = () => { const select = useLeapStore((state) => state.select); @@ -8,13 +92,19 @@ export const Notifications = () => { select('Notifications'); }, []); + const blockers = useBlockers(); + const lag = useLag(); + return (
-

Recent Apps

+ {lag && } + {blockers.length > 0 ? : null} + {/*

Recent Apps


Recent Developers

+ */}
); }; diff --git a/pkg/grid/src/pages/Grid.tsx b/pkg/grid/src/pages/Grid.tsx index 4d33cfd1d..1bbc592d1 100644 --- a/pkg/grid/src/pages/Grid.tsx +++ b/pkg/grid/src/pages/Grid.tsx @@ -1,12 +1,14 @@ import { map, omit } from 'lodash-es'; import React, { FunctionComponent, useEffect } from 'react'; import { Route, RouteComponentProps } from 'react-router-dom'; +import { KilnDiff } from '@urbit/api/hood'; import { MenuState, Nav } from '../nav/Nav'; import useDocketState, { useCharges } from '../state/docket'; import { useKilnState } from '../state/kiln'; import { RemoveApp } from '../tiles/RemoveApp'; import { SuspendApp } from '../tiles/SuspendApp'; import { Tile } from '../tiles/Tile'; +import api from '../state/api'; type GridProps = RouteComponentProps<{ menu?: MenuState; @@ -18,10 +20,18 @@ export const Grid: FunctionComponent = ({ match }) => { useEffect(() => { const { fetchCharges, fetchAllies } = useDocketState.getState(); - const { fetchVats } = useKilnState.getState(); + const { fetchVats, fetchLag } = useKilnState.getState(); fetchCharges(); fetchAllies(); fetchVats(); + fetchLag(); + api.subscribe({ app: 'hood', path: '/kiln/vats', event: (data: KilnDiff) => { + console.log(data); + }, err: () => { }, quit: () => {} }).catch(e => { + console.log(e); + + }).then(r => { console.log(r); }); + }, []); return ( diff --git a/pkg/grid/src/state/api.ts b/pkg/grid/src/state/api.ts index a3efeae7c..e14abc0ca 100644 --- a/pkg/grid/src/state/api.ts +++ b/pkg/grid/src/state/api.ts @@ -17,5 +17,5 @@ const api = } as unknown as Urbit : new Urbit('', ''); -api.ship = 'marzod'; +api.ship = 'dopmet'; export default api; diff --git a/pkg/grid/src/state/kiln.ts b/pkg/grid/src/state/kiln.ts index f2ab0c739..6aa7e5819 100644 --- a/pkg/grid/src/state/kiln.ts +++ b/pkg/grid/src/state/kiln.ts @@ -1,20 +1,42 @@ -import { getVats, Vats } from '@urbit/api'; +import { getVats, Vats, scryLag, KilnDiff } from '@urbit/api'; import create from 'zustand'; import produce from 'immer'; import api from './api'; +import {getBlockers} from '../../../npm/api/hood'; interface KilnState { vats: Vats; fetchVats: () => Promise; + lag: boolean; + fetchLag: () => Promise; set: (s: KilnState) => void; } export const useKilnState = create((set) => ({ vats: {}, + lag: true, fetchVats: async () => { const vats = await api.scry(getVats); + console.log(vats); set ({ vats }); }, + fetchLag: async () => { + const lag = await api.scry(scryLag); + set({ lag }); + }, set: produce(set) })); +const selBlockers = (s: KilnState) => getBlockers(s.vats) +export function useBlockers() { + return useKilnState(selBlockers); +} + +const selLag = (s: KilnState) => s.lag; +export function useLag() { + return useKilnState(selLag); +} + + +window.kiln = useKilnState; + export default useKilnState; diff --git a/pkg/grid/tailwind.config.js b/pkg/grid/tailwind.config.js index 65122483b..06d59d5e7 100644 --- a/pkg/grid/tailwind.config.js +++ b/pkg/grid/tailwind.config.js @@ -40,6 +40,9 @@ module.exports = { 300: "#FFDD66", 400: "#FFC700", }, + orange: { + 100: '#FFF2EB' + } }, fontFamily: { sans: [ diff --git a/pkg/grid/vite.config.ts b/pkg/grid/vite.config.ts index c151b2398..e6e7b05a9 100644 --- a/pkg/grid/vite.config.ts +++ b/pkg/grid/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig(({ mode }) => ({ : { proxy: { '^((?!/apps/grid).)*$': { - target: 'http://localhost:8080' + target: 'http://localhost:8083' } } }, diff --git a/pkg/npm/api/hood/lib.ts b/pkg/npm/api/hood/lib.ts index aad51187f..f19ee9026 100644 --- a/pkg/npm/api/hood/lib.ts +++ b/pkg/npm/api/hood/lib.ts @@ -1,4 +1,6 @@ import { Poke, Scry } from '../lib'; +import {Vats} from './types'; +import _ from 'lodash'; export const getVats: Scry = { app: 'hood', @@ -56,3 +58,26 @@ export function kilnRevive( json: desk }; } + +export const scryLag: Scry = ({ app: 'hood', path: '/kiln/lag' }); + +export function getBlockers(vats: Vats): string[] { + let blockers: string[] = []; + const base = vats.base; + if(!base) { + return blockers; + } + const blockedOn = base.arak.next?.[0]?.weft?.kelvin; + if(!blockedOn) { + return blockers; + } + _.forEach(_.omit(vats, 'base'), (vat, desk) => { + // assuming only %zuse + const kelvins = _.map(vat.arak.next, n => n.weft.kelvin); + if(!(kelvins.includes(blockedOn))) { + blockers.push(desk); + } + }); + + return blockers; +}