diff --git a/pkg/grid/index.html b/pkg/grid/index.html index 3d2721deb..ee7d7eeed 100644 --- a/pkg/grid/index.html +++ b/pkg/grid/index.html @@ -5,11 +5,14 @@ Landscape • Home - - - + + + - +
diff --git a/pkg/grid/src/components/icons/Elbow.tsx b/pkg/grid/src/components/icons/Elbow.tsx new file mode 100644 index 000000000..6b50598f2 --- /dev/null +++ b/pkg/grid/src/components/icons/Elbow.tsx @@ -0,0 +1,14 @@ +import React, { HTMLAttributes } from 'react'; + +type ElbowProps = HTMLAttributes; + +export const Elbow = (props: ElbowProps) => ( + + + +); diff --git a/pkg/grid/src/nav/Notifications.tsx b/pkg/grid/src/nav/Notifications.tsx index faee3838c..e99fafffd 100644 --- a/pkg/grid/src/nav/Notifications.tsx +++ b/pkg/grid/src/nav/Notifications.tsx @@ -1,20 +1,57 @@ import React, { useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { Button } from '../components/Button'; +import { useHarkStore } from '../state/hark'; +import { Notification } from '../state/hark-types'; import { useLeapStore } from './Nav'; +import { BasicNotification } from './notifications/BasicNotification'; +import { SystemNotification } from './notifications/SystemNotification'; + +function renderNotification(notification: Notification) { + if (notification.type === 'system-updates-blocked') { + return ; + } + + return ; +} + +const Empty = () => ( +
+ All clear! +
+); export const Notifications = () => { - const select = useLeapStore((state) => state.select); + const select = useLeapStore((s) => s.select); + const notifications = useHarkStore((s) => s.notifications); + const hasNotifications = notifications.length > 0; useEffect(() => { select('Notifications'); }, []); return ( -
-

Recent Apps

-
-
-

Recent Developers

-
+
+
+ + +
+ + {!hasNotifications && } + {hasNotifications && ( +
+ {notifications.map((n) => renderNotification(n))} +
+ )}
); }; diff --git a/pkg/grid/src/nav/notifications/BasicNotification.tsx b/pkg/grid/src/nav/notifications/BasicNotification.tsx new file mode 100644 index 000000000..64e9741c7 --- /dev/null +++ b/pkg/grid/src/nav/notifications/BasicNotification.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { BasicNotification as BasicNotificationType } from '../../state/hark-types'; + +interface BasicNotificationProps { + notification: BasicNotificationType; +} + +export const BasicNotification = ({ notification }: BasicNotificationProps) => ( +
{notification.message}
+); diff --git a/pkg/grid/src/nav/notifications/SystemNotification.tsx b/pkg/grid/src/nav/notifications/SystemNotification.tsx new file mode 100644 index 000000000..e4be4ece1 --- /dev/null +++ b/pkg/grid/src/nav/notifications/SystemNotification.tsx @@ -0,0 +1,37 @@ +import { pick } from 'lodash-es'; +import React from 'react'; +import { AppList } from '../../components/AppList'; +import { Elbow } from '../../components/icons/Elbow'; +import { useCharges } from '../../state/docket'; +import { SystemNotification as SystemNotificationType } from '../../state/hark-types'; + +interface SystemNotificationProps { + notification: SystemNotificationType; +} + +export const SystemNotification = ({ notification }: SystemNotificationProps) => { + const keys = notification.charges; + const charges = useCharges(); + const blockedCharges = Object.values(pick(charges, keys)); + + return ( +
+
+
+ + Landscape +
+
+ +

+ The following ({blockedCharges.length}) apps blocked a System Update: +

+ +
+
+
+ ); +}; diff --git a/pkg/grid/src/state/docket.ts b/pkg/grid/src/state/docket.ts index f60ff5faf..be035ea24 100644 --- a/pkg/grid/src/state/docket.ts +++ b/pkg/grid/src/state/docket.ts @@ -22,6 +22,7 @@ import { import { kilnRevive, kilnSuspend } from '@urbit/api/hood'; import api from './api'; import { mockAllies, mockCharges, mockTreaties } from './mock-data'; +import { fakeRequest } from './util'; const useMockData = import.meta.env.MODE === 'mock'; @@ -38,14 +39,6 @@ interface DocketState { uninstallDocket: (desk: string) => Promise; } -async function fakeRequest(data: T, time = 300): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(data); - }, time); - }); -} - const useDocketState = create((set, get) => ({ fetchCharges: async () => { const charg = useMockData diff --git a/pkg/grid/src/state/hark-types.ts b/pkg/grid/src/state/hark-types.ts new file mode 100644 index 000000000..d17c1f7d1 --- /dev/null +++ b/pkg/grid/src/state/hark-types.ts @@ -0,0 +1,18 @@ +/** + * I know this doesn't match our current hark type scheme, but since we're talking + * about changing that I decided to just throw something together to at least test + * this flow for updates. + */ + +export interface SystemNotification { + type: 'system-updates-blocked'; + charges: string[]; +} + +export interface BasicNotification { + type: 'basic'; + time: string; + message: string; +} + +export type Notification = BasicNotification | SystemNotification; diff --git a/pkg/grid/src/state/hark.ts b/pkg/grid/src/state/hark.ts new file mode 100644 index 000000000..b878cffba --- /dev/null +++ b/pkg/grid/src/state/hark.ts @@ -0,0 +1,12 @@ +import create from 'zustand'; +import { Notification } from './hark-types'; +import { mockBlockedChargeNotification } from './mock-data'; +import { useMockData } from './util'; + +interface HarkStore { + notifications: Notification[]; +} + +export const useHarkStore = create(() => ({ + notifications: useMockData ? [mockBlockedChargeNotification] : [] +})); diff --git a/pkg/grid/src/state/mock-data.ts b/pkg/grid/src/state/mock-data.ts index 6f6728eaf..185962a2e 100644 --- a/pkg/grid/src/state/mock-data.ts +++ b/pkg/grid/src/state/mock-data.ts @@ -1,6 +1,7 @@ import _ from 'lodash-es'; import { Allies, Charges, DocketHrefGlob, Treaties, Treaty } from '@urbit/api/docket'; import systemUrl from '../assets/system.png'; +import { SystemNotification } from './hark-types'; export const appMetaData: Pick = { cass: '~2021.8.11..05.11.10..b721', @@ -149,3 +150,8 @@ export const mockAllies: Allies = [ '~nalrex_bannus', '~nalrys' ].reduce((acc, val) => ({ ...acc, [val]: charter }), {}); + +export const mockBlockedChargeNotification: SystemNotification = { + type: 'system-updates-blocked', + charges: ['~zod/groups', '~zod/pomodoro'] +}; diff --git a/pkg/grid/src/state/util.ts b/pkg/grid/src/state/util.ts index 2ad28781f..1bfd52bdf 100644 --- a/pkg/grid/src/state/util.ts +++ b/pkg/grid/src/state/util.ts @@ -1,4 +1,4 @@ -import { DocketHref } from "@urbit/api/docket"; +import { DocketHref } from '@urbit/api/docket'; export function makeKeyFn(key: string) { return (childKeys: string[] = []) => { @@ -16,7 +16,6 @@ export async function fakeRequest(data: T, time = 300): Promise { }); } - export function getAppHref(href: DocketHref) { return 'site' in href ? href.site : `/apps/${href.glob.base}`; } diff --git a/pkg/grid/src/styles/components.css b/pkg/grid/src/styles/components.css index 7ad7a9e3f..7f62df764 100644 --- a/pkg/grid/src/styles/components.css +++ b/pkg/grid/src/styles/components.css @@ -22,6 +22,10 @@ @apply min-w-52 p-4 rounded-xl; } +.notification { + @apply p-4 bg-gray-100 rounded-xl; +} + .spinner { @apply inline-flex items-center w-6 h-6 animate-spin; } diff --git a/pkg/grid/tailwind.config.js b/pkg/grid/tailwind.config.js index 65122483b..555e92bcb 100644 --- a/pkg/grid/tailwind.config.js +++ b/pkg/grid/tailwind.config.js @@ -7,68 +7,61 @@ module.exports = { theme: { extend: { colors: { - transparent: "transparent", - white: "#FFFFFF", - black: "#000000", + transparent: 'transparent', + white: '#FFFFFF', + black: '#000000', gray: { ...colors.trueGray, - 100: "#F2F2F2", - 200: "#CCCCCC", - 300: "#B3B3B3", - 400: "#808080", - 500: "#666666", + 100: '#F2F2F2', + 200: '#CCCCCC', + 300: '#B3B3B3', + 400: '#808080', + 500: '#666666' }, blue: { - 100: "#E9F5FF", - 200: "#D3EBFF", - 300: "#BCE2FF", - 400: "#219DFF", + 100: '#E9F5FF', + 200: '#D3EBFF', + 300: '#BCE2FF', + 400: '#219DFF' }, red: { - 100: "#FFF6F5", - 200: "#FFC6C3", - 400: "#FF4136", + 100: '#FFF6F5', + 200: '#FFC6C3', + 400: '#FF4136' }, green: { - 100: "#E6F5F0", - 200: "#B3E2D1", - 400: "#009F65", + 100: '#E6F5F0', + 200: '#B3E2D1', + 400: '#009F65' }, yellow: { - 100: "#FFF9E6", - 200: "#FFEEB3", - 300: "#FFDD66", - 400: "#FFC700", + 100: '#FFF9E6', + 200: '#FFEEB3', + 300: '#FFDD66', + 400: '#FFC700' }, + orange: colors.orange }, fontFamily: { sans: [ '"Inter"', '"Inter UI"', - "-apple-system", - "BlinkMacSystemFont", + '-apple-system', + 'BlinkMacSystemFont', '"San Francisco"', '"Helvetica Neue"', - "Arial", - "sans-serif", - ], - mono: [ - '"Source Code Pro"', - '"Roboto mono"', - '"Courier New"', - "monospace", + 'Arial', + 'sans-serif' ], + mono: ['"Source Code Pro"', '"Roboto mono"', '"Courier New"', 'monospace'] }, - minWidth: theme => theme('spacing'), - }, + minWidth: (theme) => theme('spacing') + } }, variants: { extend: { opacity: ['hover-none'] - }, + } }, - plugins: [ - require('@tailwindcss/aspect-ratio'), - require('tailwindcss-touch')() - ], -} + plugins: [require('@tailwindcss/aspect-ratio'), require('tailwindcss-touch')()] +};