mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-01 11:33:41 +03:00
grid: upgrade for new hark store
This commit is contained in:
parent
76e033c649
commit
43093f811f
@ -46,6 +46,7 @@ module.exports = {
|
||||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
'react/no-array-index-key': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'react/jsx-props-no-spreading': 'off',
|
||||
|
48
pkg/grid/package-lock.json
generated
48
pkg/grid/package-lock.json
generated
@ -13,8 +13,10 @@
|
||||
"@radix-ui/react-polymorphic": "^0.0.13",
|
||||
"@radix-ui/react-portal": "^0.0.15",
|
||||
"@radix-ui/react-toggle": "^0.0.10",
|
||||
"@types/lodash": "^4.14.172",
|
||||
"@urbit/api": "^1.4.0",
|
||||
"@urbit/http-api": "^1.3.1",
|
||||
"big-integer": "^1.6.48",
|
||||
"classnames": "^2.3.1",
|
||||
"clipboard-copy": "^4.0.1",
|
||||
"color2k": "^1.2.4",
|
||||
@ -1435,7 +1437,21 @@
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/big-integer": {
|
||||
"version": "1.6.48",
|
||||
@ -1517,6 +1533,20 @@
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
@ -3598,7 +3628,21 @@
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "4.0.6",
|
||||
|
@ -19,8 +19,10 @@
|
||||
"@radix-ui/react-polymorphic": "^0.0.13",
|
||||
"@radix-ui/react-portal": "^0.0.15",
|
||||
"@radix-ui/react-toggle": "^0.0.10",
|
||||
"@types/lodash": "^4.14.172",
|
||||
"@urbit/api": "^1.4.0",
|
||||
"@urbit/http-api": "^1.3.1",
|
||||
"big-integer": "^1.6.48",
|
||||
"classnames": "^2.3.1",
|
||||
"clipboard-copy": "^4.0.1",
|
||||
"color2k": "^1.2.4",
|
||||
|
@ -1,23 +1,31 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Notification } from '@urbit/api';
|
||||
import { useLeapStore } from './Nav';
|
||||
import { Button } from '../components/Button';
|
||||
import { Notification } from '../state/hark-types';
|
||||
import { BasicNotification } from './notifications/BasicNotification';
|
||||
import {
|
||||
BaseBlockedNotification,
|
||||
RuntimeLagNotification
|
||||
} from './notifications/SystemNotification';
|
||||
import { useNotifications } from '../state/notifications';
|
||||
import { useHarkStore } from '../state/hark';
|
||||
import { OnboardingNotification } from './notifications/OnboardingNotification';
|
||||
|
||||
function renderNotification(notification: Notification, key: string) {
|
||||
if (notification.type === 'system-updates-blocked') {
|
||||
return <BaseBlockedNotification key={key} notification={notification} />;
|
||||
function renderNotification(notification: Notification, key: string, unread = false) {
|
||||
// Special casing
|
||||
if (notification.bin.place.desk === window.desk) {
|
||||
if (notification.bin.place.path === '/lag') {
|
||||
return <RuntimeLagNotification key={key} />;
|
||||
}
|
||||
if (notification.bin.place.path === '/blocked') {
|
||||
return <BaseBlockedNotification key={key} />;
|
||||
}
|
||||
if (notification.bin.place.path === '/onboard') {
|
||||
return <OnboardingNotification key={key} unread={unread} />;
|
||||
}
|
||||
}
|
||||
if (notification.type === 'runtime-lag') {
|
||||
return <RuntimeLagNotification key={key} />;
|
||||
}
|
||||
return <BasicNotification key={key} notification={notification} />;
|
||||
return <BasicNotification key={key} notification={notification} unread={unread} />;
|
||||
}
|
||||
|
||||
const Empty = () => (
|
||||
@ -28,16 +36,22 @@ const Empty = () => (
|
||||
|
||||
export const Notifications = () => {
|
||||
const select = useLeapStore((s) => s.select);
|
||||
const { notifications, systemNotifications, hasAnyNotifications } = useNotifications();
|
||||
const { unreads, reads, systemNotifications, hasAnyNotifications } = useNotifications();
|
||||
const markAllAsRead = () => {
|
||||
const { readAll } = useHarkStore.getState();
|
||||
readAll();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
select('Notifications');
|
||||
const { getMore } = useHarkStore.getState();
|
||||
getMore();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="grid grid-rows-[auto,1fr] h-full p-4 md:p-8 overflow-hidden">
|
||||
<header className="space-x-2 mb-8">
|
||||
<Button variant="secondary" className="py-1.5 px-6 rounded-full">
|
||||
<Button onClick={markAllAsRead} variant="secondary" className="py-1.5 px-6 rounded-full">
|
||||
Mark All as Read
|
||||
</Button>
|
||||
<Button
|
||||
@ -53,10 +67,16 @@ export const Notifications = () => {
|
||||
{!hasAnyNotifications && <Empty />}
|
||||
{hasAnyNotifications && (
|
||||
<section className="text-gray-400 space-y-2 overflow-y-auto">
|
||||
{notifications.map((n, index) => renderNotification(n, index.toString()))}
|
||||
{systemNotifications.map((n, index) =>
|
||||
renderNotification(n, (notifications.length + index).toString())
|
||||
renderNotification(n, (unreads.length + index).toString())
|
||||
)}
|
||||
{unreads.map((n, index) =>
|
||||
renderNotification(n, (systemNotifications.length + index).toString(), true)
|
||||
)}
|
||||
{Array.from(reads)
|
||||
.map(([, nots]) => nots)
|
||||
.flat()
|
||||
.map((n, index) => renderNotification(n, `reads-${index}`))}
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { Notification } from '@urbit/api';
|
||||
import { Link, LinkProps } from 'react-router-dom';
|
||||
import { Bullet } from '../components/icons/Bullet';
|
||||
import { Notification } from '../state/hark-types';
|
||||
import { useNotifications } from '../state/notifications';
|
||||
|
||||
type NotificationsState = 'empty' | 'unread' | 'attention-needed';
|
||||
@ -28,8 +28,8 @@ type NotificationsLinkProps = Omit<LinkProps<HTMLAnchorElement>, 'to'> & {
|
||||
};
|
||||
|
||||
export const NotificationsLink = ({ isOpen }: NotificationsLinkProps) => {
|
||||
const { notifications, systemNotifications } = useNotifications();
|
||||
const state = getNotificationsState(notifications, systemNotifications);
|
||||
const { unreads, systemNotifications } = useNotifications();
|
||||
const state = getNotificationsState(unreads, systemNotifications);
|
||||
|
||||
return (
|
||||
<Link
|
||||
@ -44,7 +44,7 @@ export const NotificationsLink = ({ isOpen }: NotificationsLinkProps) => {
|
||||
)}
|
||||
>
|
||||
{state === 'empty' && <Bullet className="w-6 h-6" />}
|
||||
{state === 'unread' && notifications.length}
|
||||
{state === 'unread' && unreads.length}
|
||||
{state === 'attention-needed' && (
|
||||
<span className="h2">
|
||||
! <span className="sr-only">Attention needed</span>
|
||||
|
@ -1,10 +1,80 @@
|
||||
import React from 'react';
|
||||
import { BasicNotification as BasicNotificationType } from '../../state/hark-types';
|
||||
import cn from 'classnames';
|
||||
import { Notification, harkBinToId, HarkContent } from '@urbit/api';
|
||||
import { map, take } from 'lodash-es';
|
||||
import { useCharge } from '../../state/docket';
|
||||
import { Elbow } from '../../components/icons/Elbow';
|
||||
import { ShipName } from '../../components/ShipName';
|
||||
import { getAppHref } from '../../state/util';
|
||||
|
||||
interface BasicNotificationProps {
|
||||
notification: BasicNotificationType;
|
||||
notification: Notification;
|
||||
unread?: boolean;
|
||||
}
|
||||
|
||||
export const BasicNotification = ({ notification }: BasicNotificationProps) => (
|
||||
<div>{notification.message}</div>
|
||||
);
|
||||
const MAX_CONTENTS = 20;
|
||||
|
||||
const NotificationText = ({ contents }: { contents: HarkContent[] }) => {
|
||||
return (
|
||||
<>
|
||||
{contents.map((content, idx) => {
|
||||
if ('ship' in content) {
|
||||
return <ShipName key={idx} name={content.ship} />;
|
||||
}
|
||||
return content.text;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const BasicNotification = ({ notification, unread = false }: BasicNotificationProps) => {
|
||||
const { desk } = notification.bin.place;
|
||||
const binId = harkBinToId(notification.bin);
|
||||
const id = `notif-${notification.time}-${binId}`;
|
||||
|
||||
const charge = useCharge(desk);
|
||||
const first = notification.body?.[0];
|
||||
if (!first) {
|
||||
return null;
|
||||
}
|
||||
const contents = map(notification.body, 'content').filter((c) => c.length > 0);
|
||||
const large = contents.length === 0;
|
||||
const link = `${getAppHref(charge.href)}?grid-note=${encodeURIComponent(first.link)}`;
|
||||
|
||||
return (
|
||||
<a
|
||||
href={link}
|
||||
target={desk}
|
||||
className={cn(
|
||||
'text-black rounded',
|
||||
unread ? 'bg-blue-100' : 'bg-gray-100',
|
||||
large ? 'note-grid-no-content' : 'note-grid-content'
|
||||
)}
|
||||
aria-labelledby={id}
|
||||
>
|
||||
<header id={id} className="contents">
|
||||
<div
|
||||
className="note-grid-icon rounded w-full h-full"
|
||||
style={{ backgroundColor: charge?.color ?? '#ee5432' }}
|
||||
/>
|
||||
<div className="note-grid-title font-semibold">{charge?.title || desk}</div>
|
||||
{!large ? <Elbow className="note-grid-arrow w-6 h-6 text-gray-300" /> : null}
|
||||
<h2 id={`${id}-title`} className="note-grid-head font-semibold text-gray-600">
|
||||
<NotificationText contents={first.title} />
|
||||
</h2>
|
||||
</header>
|
||||
{contents.length > 0 ? (
|
||||
<div className="note-grid-body space-y-2">
|
||||
{take(contents, MAX_CONTENTS).map((content) => (
|
||||
<p className="">
|
||||
<NotificationText contents={content} />
|
||||
</p>
|
||||
))}
|
||||
{contents.length > MAX_CONTENTS ? (
|
||||
<p className="text-gray-300">and {contents.length - MAX_CONTENTS} more</p>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
86
pkg/grid/src/nav/notifications/OnboardingNotification.tsx
Normal file
86
pkg/grid/src/nav/notifications/OnboardingNotification.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { Button } from '../../components/Button';
|
||||
|
||||
const cards: OnboardingCardProps[] = [
|
||||
{
|
||||
title: 'Terminal',
|
||||
body: "Install a web terminal to access your Urbit's command line",
|
||||
button: 'Install',
|
||||
color: '#9CA4B1'
|
||||
},
|
||||
{
|
||||
title: 'Groups',
|
||||
body: 'Install Groups, a suite of social software to communicate with other urbit users',
|
||||
button: 'Install',
|
||||
color: '#D1DDD3'
|
||||
},
|
||||
{
|
||||
title: 'Bitcoin',
|
||||
body: 'Install a bitcoin wallet. You can send bitcoin to any btc address, or even another ship',
|
||||
button: 'Install',
|
||||
color: '#F6EBDB'
|
||||
},
|
||||
{
|
||||
title: 'Debug',
|
||||
body: "Install a debugger. You can inspect your ship's internals using this interface",
|
||||
button: 'Install'
|
||||
},
|
||||
{
|
||||
title: 'Build an app',
|
||||
body: 'You can instantly get started building new things on Urbit. Just right click your Landscape and select “New App”',
|
||||
button: 'Learn more',
|
||||
color: '#82A6CA'
|
||||
}
|
||||
];
|
||||
|
||||
interface OnboardingCardProps {
|
||||
title: string;
|
||||
button: string;
|
||||
body: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
const OnboardingCard = ({ title, button, body, color }: OnboardingCardProps) => (
|
||||
<div
|
||||
className="p-4 flex flex-col space-y-2 text-black bg-gray-100 justify-between rounded-xl"
|
||||
style={color ? { backgroundColor: color } : {}}
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-semibold">{title}</h4>
|
||||
<p>{body}</p>
|
||||
</div>
|
||||
<Button variant="primary" className="bg-gray-500">
|
||||
{button}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface OnboardingNotificationProps {
|
||||
unread: boolean;
|
||||
}
|
||||
|
||||
export const OnboardingNotification = ({ unread }: OnboardingNotificationProps) => (
|
||||
<section
|
||||
className={cn('notification space-y-2 text-black', unread ? 'bg-blue-100' : 'bg-gray-100')}
|
||||
aria-labelledby=""
|
||||
>
|
||||
<header id="system-updates-blocked" className="relative 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">Grid</span>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<h2 id="runtime-lag">Hello there, welcome to Grid!</h2>
|
||||
</div>
|
||||
</header>
|
||||
<div className="grid grid-cols-3 grid-rows-2 gap-4">
|
||||
{
|
||||
/* eslint-disable-next-line react/no-array-index-key */
|
||||
cards.map((card, i) => (
|
||||
<OnboardingCard key={i} {...card} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
);
|
@ -1,21 +1,16 @@
|
||||
import { pick } from 'lodash-es';
|
||||
import React, { useCallback } from 'react';
|
||||
import { kilnSuspend } from '@urbit/api/hood';
|
||||
import { kilnSuspend } from '@urbit/api';
|
||||
import { AppList } from '../../components/AppList';
|
||||
import { Button } from '../../components/Button';
|
||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from '../../components/Dialog';
|
||||
import { Elbow } from '../../components/icons/Elbow';
|
||||
import api from '../../state/api';
|
||||
import { useCharges } from '../../state/docket';
|
||||
import { BaseBlockedNotification as BaseBlockedNotificationType } from '../../state/hark-types';
|
||||
|
||||
import { NotificationButton } from './NotificationButton';
|
||||
import { disableDefault } from '../../state/util';
|
||||
|
||||
interface BaseBlockedNotificationProps {
|
||||
notification: BaseBlockedNotificationType;
|
||||
}
|
||||
|
||||
export const RuntimeLagNotification = () => (
|
||||
<section
|
||||
className="notification pl-12 space-y-2 text-black bg-orange-50"
|
||||
@ -40,8 +35,8 @@ export const RuntimeLagNotification = () => (
|
||||
</section>
|
||||
);
|
||||
|
||||
export const BaseBlockedNotification = ({ notification }: BaseBlockedNotificationProps) => {
|
||||
const { desks } = notification;
|
||||
export const BaseBlockedNotification = () => {
|
||||
const desks: string[] = [];
|
||||
const charges = useCharges();
|
||||
const blockedCharges = Object.values(pick(charges, desks));
|
||||
const count = blockedCharges.length;
|
||||
|
@ -1,12 +1,95 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import create from 'zustand';
|
||||
import { Notification } from './hark-types';
|
||||
import { mockNotification } from './mock-data';
|
||||
import { Notification, harkBinEq, makePatDa, readAll, decToUd, unixToDa } from '@urbit/api';
|
||||
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||
/* eslint-disable-next-line camelcase */
|
||||
import { unstable_batchedUpdates } from 'react-dom';
|
||||
import produce from 'immer';
|
||||
import { map } from 'lodash';
|
||||
import bigInt from 'big-integer';
|
||||
import api from './api';
|
||||
import { useMockData } from './util';
|
||||
import { mockNotifications } from './mock-data';
|
||||
|
||||
interface HarkStore {
|
||||
notifications: Notification[];
|
||||
unreads: Notification[];
|
||||
reads: BigIntOrderedMap<Notification[]>;
|
||||
set: (f: (s: HarkStore) => void) => void;
|
||||
readAll: () => Promise<void>;
|
||||
getMore: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const useHarkStore = create<HarkStore>(() => ({
|
||||
notifications: useMockData ? [mockNotification] : []
|
||||
export const useHarkStore = create<HarkStore>((set, get) => ({
|
||||
unreads: useMockData ? mockNotifications : [],
|
||||
reads: useMockData
|
||||
? new BigIntOrderedMap<Notification[]>().gas([[bigInt.zero, mockNotifications]])
|
||||
: new BigIntOrderedMap<Notification[]>(),
|
||||
set: (f) => {
|
||||
const newState = produce(get(), f);
|
||||
set(newState);
|
||||
},
|
||||
readAll: async () => {
|
||||
const { set: innerSet } = get();
|
||||
innerSet((state) => {
|
||||
state.unreads.forEach((note) => {
|
||||
const time = makePatDa(note.time as any);
|
||||
const box = state.reads.get(time) || [];
|
||||
state.reads.set(time, [...box, note]);
|
||||
});
|
||||
state.unreads = [];
|
||||
});
|
||||
await api.poke(readAll);
|
||||
},
|
||||
getMore: async () => {
|
||||
const { reads } = get();
|
||||
const idx = decToUd((reads.peekSmallest()?.[0] || unixToDa(Date.now() * 1000)).toString());
|
||||
const update = await api.scry({
|
||||
app: 'hark-store',
|
||||
path: `/recent/inbox/${idx}/5`
|
||||
});
|
||||
reduceHark(update);
|
||||
}
|
||||
}));
|
||||
|
||||
function reduceHark(u: any) {
|
||||
const { set } = useHarkStore.getState();
|
||||
if ('more' in u) {
|
||||
u.more.forEach((upd) => {
|
||||
reduceHark(upd);
|
||||
});
|
||||
} else if ('all-stats' in u) {
|
||||
// TODO: probably ignore?
|
||||
} else if ('added' in u) {
|
||||
set((state) => {
|
||||
state.unreads = state.unreads.filter((unread) => !harkBinEq(unread.bin, u.added.bin));
|
||||
|
||||
state.unreads.push(u.added);
|
||||
});
|
||||
} else if ('timebox' in u) {
|
||||
const { timebox } = u;
|
||||
const notifications = map(timebox.notifications, 'notification');
|
||||
if (timebox.time) {
|
||||
console.log(notifications);
|
||||
set((state) => {
|
||||
state.reads = state.reads.set(makePatDa(timebox.time), notifications);
|
||||
});
|
||||
} else {
|
||||
set((state) => {
|
||||
state.unreads = [...state.unreads, ...notifications];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.subscribe({
|
||||
app: 'hark-store',
|
||||
path: '/updates',
|
||||
event: (u: any) => {
|
||||
/* eslint-ignore-next-line camelcase */
|
||||
unstable_batchedUpdates(() => {
|
||||
reduceHark(u);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.hark = useHarkStore.getState;
|
||||
|
@ -1,8 +1,16 @@
|
||||
import _ from 'lodash-es';
|
||||
import { Allies, Charges, DocketHrefGlob, Treaties, Treaty } from '@urbit/api/docket';
|
||||
import { Vat, Vats } from '@urbit/api/hood';
|
||||
import {
|
||||
Vat,
|
||||
Vats,
|
||||
Allies,
|
||||
Charges,
|
||||
DocketHrefGlob,
|
||||
Treaties,
|
||||
Treaty,
|
||||
Notification,
|
||||
HarkContent
|
||||
} from '@urbit/api';
|
||||
import systemUrl from '../assets/system.png';
|
||||
import { BasicNotification } from './hark-types';
|
||||
|
||||
export const appMetaData: Pick<Treaty, 'cass' | 'hash' | 'website' | 'license' | 'version'> = {
|
||||
cass: {
|
||||
@ -155,11 +163,58 @@ export const mockAllies: Allies = [
|
||||
'~nalrys'
|
||||
].reduce((acc, val) => ({ ...acc, [val]: charter }), {});
|
||||
|
||||
export const mockNotification: BasicNotification = {
|
||||
type: 'basic',
|
||||
time: '',
|
||||
message: 'test'
|
||||
};
|
||||
function createDmNotification(content: string): HarkContent {
|
||||
return {
|
||||
title: ' messaged you',
|
||||
author: '~hastuc-dibtux',
|
||||
time: Date.now() - 3_600,
|
||||
content,
|
||||
links: []
|
||||
};
|
||||
}
|
||||
|
||||
function createBitcoinNotif(amount: string) {
|
||||
return {
|
||||
title: ` sent you ${amount}`,
|
||||
author: '~silnem',
|
||||
time: Date.now() - 3_600,
|
||||
content: '',
|
||||
links: []
|
||||
};
|
||||
}
|
||||
|
||||
function createGroupNotif(to: string): HarkContent {
|
||||
return {
|
||||
title: ` invited you to ${to}`,
|
||||
author: '~ridlur-figbud',
|
||||
content: '',
|
||||
time: Date.now() - 3_600,
|
||||
links: []
|
||||
};
|
||||
}
|
||||
|
||||
export function createMockNotification(desk: string, contents: HarkContent[]) {
|
||||
return {
|
||||
bin: {
|
||||
place: {
|
||||
desk,
|
||||
path: '/'
|
||||
},
|
||||
path: '/'
|
||||
},
|
||||
time: Date.now() - 3_600,
|
||||
contents
|
||||
};
|
||||
}
|
||||
|
||||
export const mockNotifications: Notification[] = [
|
||||
createMockNotification('groups', [
|
||||
createDmNotification('ie the hook agent responsible for marking the notifications'),
|
||||
createDmNotification('~hastuc-dibtux sent a link')
|
||||
]),
|
||||
createMockNotification('bitcoin-wallet', [createBitcoinNotif('0.025 BTC')]),
|
||||
createMockNotification('groups', [createGroupNotif('a Group: Tlon Corporation')])
|
||||
];
|
||||
|
||||
export const mockVat = (desk: string, blockers?: boolean): Vat => ({
|
||||
cass: {
|
||||
|
@ -1,28 +1,13 @@
|
||||
import shallow from 'zustand/shallow';
|
||||
import { useHarkStore } from './hark';
|
||||
import { Notification } from './hark-types';
|
||||
import { useBlockers, useLag } from './kiln';
|
||||
|
||||
function getSystemNotifications(lag: boolean, blockers: string[]) {
|
||||
const nots = [] as Notification[];
|
||||
if (lag) {
|
||||
nots.push({ type: 'runtime-lag' });
|
||||
}
|
||||
if (blockers.length > 0) {
|
||||
nots.push({ type: 'system-updates-blocked', desks: blockers });
|
||||
}
|
||||
return nots;
|
||||
}
|
||||
|
||||
export const useNotifications = () => {
|
||||
const notifications = useHarkStore((s) => s.notifications);
|
||||
const blockers = useBlockers();
|
||||
const lag = useLag();
|
||||
const systemNotifications = getSystemNotifications(lag, blockers);
|
||||
const hasAnyNotifications = notifications.length > 0 || systemNotifications.length > 0;
|
||||
const [unreads, reads] = useHarkStore((s) => [s.unreads, s.reads], shallow);
|
||||
const hasAnyNotifications = unreads.length > 0 || reads.size > 0;
|
||||
|
||||
return {
|
||||
notifications,
|
||||
systemNotifications,
|
||||
unreads,
|
||||
reads,
|
||||
hasAnyNotifications
|
||||
};
|
||||
};
|
||||
|
44
pkg/grid/src/styles/grids.css
Normal file
44
pkg/grid/src/styles/grids.css
Normal file
@ -0,0 +1,44 @@
|
||||
.note-grid-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5rem 1fr;
|
||||
grid-template-rows: 1.5rem 1.5rem;
|
||||
grid-gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
grid-template-areas:
|
||||
'icon title'
|
||||
'arrow head'
|
||||
'. body';
|
||||
}
|
||||
|
||||
.note-grid-no-content {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-rows: 1.75rem 1.75rem;
|
||||
grid-template-columns: 3.5rem 1fr;
|
||||
grid-column-gap: 0.75rem;
|
||||
align-items: center;
|
||||
grid-template-areas:
|
||||
'icon title'
|
||||
'icon head';
|
||||
}
|
||||
.note-grid-title {
|
||||
grid-area: title;
|
||||
}
|
||||
|
||||
.note-grid-icon {
|
||||
grid-area: icon;
|
||||
}
|
||||
|
||||
.note-grid-body {
|
||||
grid-area: body;
|
||||
}
|
||||
|
||||
.note-grid-arrow {
|
||||
grid-area: arrow;
|
||||
}
|
||||
|
||||
.note-grid-head {
|
||||
grid-area: head;
|
||||
}
|
||||
|
||||
|
@ -5,4 +5,6 @@
|
||||
@import "./components.css";
|
||||
|
||||
@import "tailwindcss/utilities";
|
||||
@import "./utilities.css";
|
||||
@import "./utilities.css";
|
||||
|
||||
@import "./grids.css";
|
||||
|
Loading…
Reference in New Issue
Block a user