mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-30 18:31:36 +03:00
notifications: adding initial structure
This commit is contained in:
parent
aaaee41e95
commit
b4bd2c3bea
@ -5,11 +5,14 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Landscape • Home</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body class="text-sm font-sans text-gray-900 bg-white antialiased">
|
||||
<body class="text-sm leading-6 font-sans text-gray-900 bg-white antialiased">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
14
pkg/grid/src/components/icons/Elbow.tsx
Normal file
14
pkg/grid/src/components/icons/Elbow.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React, { HTMLAttributes } from 'react';
|
||||
|
||||
type ElbowProps = HTMLAttributes<SVGSVGElement>;
|
||||
|
||||
export const Elbow = (props: ElbowProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M11 1V5C11 9.41828 14.5817 13 19 13H23"
|
||||
className="stroke-current"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
@ -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 <SystemNotification notification={notification} />;
|
||||
}
|
||||
|
||||
return <BasicNotification notification={notification} />;
|
||||
}
|
||||
|
||||
const Empty = () => (
|
||||
<section className="flex justify-center items-center min-h-[480px] text-gray-400 space-y-2">
|
||||
<span className="h4">All clear!</span>
|
||||
</section>
|
||||
);
|
||||
|
||||
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 (
|
||||
<div className="p-4 md:p-8 space-y-8">
|
||||
<h2 className="h4 text-gray-500">Recent Apps</h2>
|
||||
<div className="min-h-[150px] rounded-xl bg-gray-100" />
|
||||
<hr className="-mx-4 md:-mx-8" />
|
||||
<h2 className="h4 text-gray-500">Recent Developers</h2>
|
||||
<div className="min-h-[150px] rounded-xl bg-gray-100" />
|
||||
<div className="p-4 md:p-8">
|
||||
<header className="space-x-2 mb-8">
|
||||
<Button variant="secondary" className="py-1.5 px-6 rounded-full">
|
||||
Mark All as Read
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
variant="secondary"
|
||||
to="/leap/system-preferences/notifications"
|
||||
className="py-1.5 px-6 rounded-full"
|
||||
>
|
||||
Notification Settings
|
||||
</Button>
|
||||
</header>
|
||||
|
||||
{!hasNotifications && <Empty />}
|
||||
{hasNotifications && (
|
||||
<section className="min-h-[480px] text-gray-400 space-y-2">
|
||||
{notifications.map((n) => renderNotification(n))}
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
10
pkg/grid/src/nav/notifications/BasicNotification.tsx
Normal file
10
pkg/grid/src/nav/notifications/BasicNotification.tsx
Normal file
@ -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) => (
|
||||
<div>{notification.message}</div>
|
||||
);
|
37
pkg/grid/src/nav/notifications/SystemNotification.tsx
Normal file
37
pkg/grid/src/nav/notifications/SystemNotification.tsx
Normal file
@ -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 (
|
||||
<section
|
||||
className="notification pl-12 text-black bg-orange-100"
|
||||
aria-labelledby="system-updates-blocked"
|
||||
>
|
||||
<header id="system-updates-blocked" className="relative -left-8 space-y-2">
|
||||
<div className="flex space-x-2">
|
||||
<span className="inline-block w-6 h-6 bg-orange-500 rounded-full" />
|
||||
<span className="font-medium">Landscape</span>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Elbow className="w-6 h-6 text-gray-300" />
|
||||
<h2 id="blocked-apps">
|
||||
The following ({blockedCharges.length}) apps blocked a System Update:
|
||||
</h2>
|
||||
<AppList apps={blockedCharges} labelledBy="blocked-apps" />
|
||||
</div>
|
||||
</header>
|
||||
</section>
|
||||
);
|
||||
};
|
@ -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<number | void>;
|
||||
}
|
||||
|
||||
async function fakeRequest<T>(data: T, time = 300): Promise<T> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(data);
|
||||
}, time);
|
||||
});
|
||||
}
|
||||
|
||||
const useDocketState = create<DocketState>((set, get) => ({
|
||||
fetchCharges: async () => {
|
||||
const charg = useMockData
|
||||
|
18
pkg/grid/src/state/hark-types.ts
Normal file
18
pkg/grid/src/state/hark-types.ts
Normal file
@ -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;
|
12
pkg/grid/src/state/hark.ts
Normal file
12
pkg/grid/src/state/hark.ts
Normal file
@ -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<HarkStore>(() => ({
|
||||
notifications: useMockData ? [mockBlockedChargeNotification] : []
|
||||
}));
|
@ -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<Treaty, 'cass' | 'hash' | 'website' | 'license' | 'version'> = {
|
||||
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']
|
||||
};
|
||||
|
@ -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<T>(data: T, time = 300): Promise<T> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function getAppHref(href: DocketHref) {
|
||||
return 'site' in href ? href.site : `/apps/${href.glob.base}`;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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')()]
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user