mirror of
https://github.com/urbit/shrub.git
synced 2024-12-21 01:41:37 +03:00
Merge pull request #5361 from urbit/lf/post-assembly-grabbag
garden/landscape: fixes
This commit is contained in:
commit
a3664da492
@ -232,8 +232,8 @@
|
||||
:: if the new chad is a site, we're instantly done
|
||||
::
|
||||
?: ?=(%site -.href.docket)
|
||||
:- ~[add-fact:cha]
|
||||
=. charges (new-chad:cha %site ~)
|
||||
:- ~[add-fact:cha]
|
||||
state
|
||||
::
|
||||
=. by-base (~(put by by-base) base.href.docket desk)
|
||||
|
@ -10,7 +10,7 @@
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint --cache \"**/*.{js,jsx,ts,tsx}\"",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"test": "echo \"No test yet\"",
|
||||
"test": "tsc --noEmit",
|
||||
"tsc": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -8,7 +8,7 @@ import { Dialog, DialogClose, DialogContent, DialogTrigger } from './Dialog';
|
||||
import { DocketHeader } from './DocketHeader';
|
||||
import { Spinner } from './Spinner';
|
||||
import { VatMeta } from './VatMeta';
|
||||
import useDocketState, { ChargeWithDesk } from '../state/docket';
|
||||
import useDocketState, { ChargeWithDesk, useTreaty } from '../state/docket';
|
||||
import { getAppHref, getAppName } from '../state/util';
|
||||
import { addRecentApp } from '../nav/search/Home';
|
||||
import { TreatyMeta } from './TreatyMeta';
|
||||
@ -52,6 +52,7 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
|
||||
const [ship, desk] = getRemoteDesk(docket, vat);
|
||||
const publisher = vat?.arak?.rail?.publisher ?? ship;
|
||||
const [copied, setCopied] = useState(false);
|
||||
const treaty = useTreaty(ship, desk);
|
||||
|
||||
const installApp = async () => {
|
||||
if (installStatus === 'installed') {
|
||||
@ -135,18 +136,20 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
|
||||
</PillButton>
|
||||
</div>
|
||||
</DocketHeader>
|
||||
<div className="space-y-6">
|
||||
{vat ? (
|
||||
<>
|
||||
<hr className="-mx-5 sm:-mx-8 border-gray-50" />
|
||||
<VatMeta vat={vat} />
|
||||
</>
|
||||
) : null}
|
||||
{'chad' in docket ? null : (
|
||||
{!treaty ? null : (
|
||||
<>
|
||||
<hr className="-mx-5 sm:-mx-8 border-gray-50" />
|
||||
<TreatyMeta treaty={docket} />
|
||||
<TreatyMeta treaty={treaty} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { Vat } from '@urbit/api/hood';
|
||||
|
||||
import { Attribute } from './Attribute';
|
||||
@ -12,12 +11,6 @@ export function VatMeta(props: { vat: Vat }) {
|
||||
const pluralUpdates = next?.length !== 1;
|
||||
return (
|
||||
<div className="mt-5 sm:mt-8 space-y-5 sm:space-y-8">
|
||||
<Attribute title="Developer Desk" attr="desk">
|
||||
{ship}/{foreignDesk}
|
||||
</Attribute>
|
||||
<Attribute title="Last Software Update" attr="case">
|
||||
{moment(cass.da).format('YYYY.MM.DD')}
|
||||
</Attribute>
|
||||
<Attribute title="Desk Hash" attr="hash">
|
||||
{hash}
|
||||
</Attribute>
|
||||
|
@ -1,18 +1,12 @@
|
||||
import {
|
||||
cite,
|
||||
GraphNotifIndex,
|
||||
GroupNotifIndex,
|
||||
IndexedNotification,
|
||||
NotificationGraphConfig,
|
||||
Post,
|
||||
Unreads
|
||||
} from '@urbit/api';
|
||||
import { patp } from 'urbit-ob';
|
||||
import bigInt, { BigInteger } from 'big-integer';
|
||||
import _ from 'lodash';
|
||||
import f from 'lodash/fp';
|
||||
import { pluralize } from './util';
|
||||
import useMetadataState from '../state/metadata';
|
||||
import { emptyHarkStats } from '../state/hark';
|
||||
|
||||
export function getLastSeen(
|
||||
@ -60,94 +54,3 @@ export function isWatching(
|
||||
);
|
||||
}
|
||||
|
||||
export function getNotificationKey(
|
||||
time: BigInteger,
|
||||
notification: IndexedNotification
|
||||
): string {
|
||||
const base = time.toString();
|
||||
if ('graph' in notification.index) {
|
||||
const { graph, index, description } = notification.index.graph;
|
||||
return `${base}-${graph}-${index}-${description}`;
|
||||
} else if ('group' in notification.index) {
|
||||
const { group } = notification.index.group;
|
||||
return `${base}-${group}`;
|
||||
}
|
||||
return `${base}-unknown`;
|
||||
}
|
||||
|
||||
export function notificationReferent(not: IndexedNotification) {
|
||||
if ('graph' in not.index) {
|
||||
return not.index.graph.graph;
|
||||
} else {
|
||||
return not.index.group.group;
|
||||
}
|
||||
}
|
||||
export function describeNotification(notification: IndexedNotification) {
|
||||
function group(idx: GroupNotifIndex) {
|
||||
switch (idx.description) {
|
||||
case 'add-members':
|
||||
return 'joined';
|
||||
case 'remove-members':
|
||||
return 'left';
|
||||
default:
|
||||
return idx.description;
|
||||
}
|
||||
}
|
||||
function graph(idx: GraphNotifIndex, plural: boolean, singleAuthor: boolean) {
|
||||
const isDm = idx.graph === `/ship/~${window.ship}/dm-inbox`;
|
||||
if (isDm) {
|
||||
return 'New DM from ';
|
||||
}
|
||||
switch (idx.description) {
|
||||
case 'post':
|
||||
return 'Your post received replies in';
|
||||
case 'link':
|
||||
return `New link${plural ? 's' : ''} in`;
|
||||
case 'comment':
|
||||
return `New comment${plural ? 's' : ''} on`;
|
||||
case 'note':
|
||||
return `New Note${plural ? 's' : ''} in`;
|
||||
// @ts-ignore need better types
|
||||
case 'edit-note':
|
||||
return `updated ${pluralize('note', plural)} in`;
|
||||
case 'mention':
|
||||
return 'You were mentioned in';
|
||||
case 'message':
|
||||
if (isDm) {
|
||||
return 'messaged you';
|
||||
}
|
||||
return `New message${plural ? 's' : ''} in`;
|
||||
default: return idx.description;
|
||||
}
|
||||
}
|
||||
if ('group' in notification.index) {
|
||||
return group(notification.index.group);
|
||||
} else if ('graph' in notification.index) {
|
||||
// @ts-ignore needs better type guard
|
||||
const contents = notification.notification?.contents?.graph ?? ([] as Post[]);
|
||||
return graph(
|
||||
notification.index.graph,
|
||||
contents.length > 1,
|
||||
_.uniq(_.map(contents, 'author')).length === 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getReferent(notification: IndexedNotification) {
|
||||
const meta = useMetadataState.getState();
|
||||
if ('graph' in notification.index) {
|
||||
if (notification.index.graph.graph === `/ship/~${window.ship}/dm-inbox`) {
|
||||
const [, ship] = notification.index.graph.index.split('/');
|
||||
return cite(patp(ship));
|
||||
}
|
||||
return (
|
||||
meta.associations.graph[notification.index.graph.graph]?.metadata
|
||||
?.title ?? notification.index.graph
|
||||
);
|
||||
} else if ('group' in notification.index) {
|
||||
return (
|
||||
meta.associations.groups[notification.index.group.group]?.metadata?.title ??
|
||||
notification.index.group.group
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
import { GraphNotificationContents, GraphNotifIndex } from '@urbit/api';
|
||||
|
||||
export function getParentIndex(
|
||||
idx: GraphNotifIndex,
|
||||
contents: GraphNotificationContents
|
||||
) {
|
||||
const origIndex = contents[0].index.slice(1).split('/');
|
||||
const ret = (i: string[]) => `/${i.join('/')}`;
|
||||
switch (idx.description) {
|
||||
case 'link':
|
||||
return '/';
|
||||
case 'comment':
|
||||
return ret(origIndex.slice(0, 1));
|
||||
case 'note':
|
||||
return '/';
|
||||
case 'mention':
|
||||
return undefined;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
@ -42,6 +42,7 @@ export interface HarkState {
|
||||
notificationsGroupConfig: string[];
|
||||
unreads: Unreads;
|
||||
archiveNote: (bin: HarkBin, lid: HarkLid) => Promise<void>;
|
||||
readCount: (path: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const useHarkState = createState<HarkState>(
|
||||
@ -171,5 +172,4 @@ export function useHarkGraphIndex(graph: string, index: string) {
|
||||
);
|
||||
}
|
||||
|
||||
window.hark = useHarkState.getState;
|
||||
export default useHarkState;
|
||||
|
@ -1,302 +0,0 @@
|
||||
import { Box, Col, Icon, Row, Text } from '@tlon/indigo-react';
|
||||
import { Association, GraphNotificationContents, GraphNotifIndex, Post } from '@urbit/api';
|
||||
import { BigInteger } from 'big-integer';
|
||||
import { patp } from 'urbit-ob';
|
||||
import _ from 'lodash';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { pluralize } from '~/logic/lib/util';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import {
|
||||
useAssocForGraph,
|
||||
useAssocForGroup
|
||||
} from '~/logic/state/metadata';
|
||||
import Author from '~/views/components/Author';
|
||||
import { GraphContent } from '~/views/landscape/components/Graph/GraphContent';
|
||||
import { Header } from './header';
|
||||
|
||||
const TruncBox = styled(Box)<{ truncate?: number }>`
|
||||
-webkit-line-clamp: ${p => p.truncate ?? 'unset'};
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
color: ${p => p.theme.colors.black};
|
||||
`;
|
||||
|
||||
function describeNotification(
|
||||
description: string,
|
||||
plural: boolean,
|
||||
isDm: boolean,
|
||||
singleAuthor: boolean
|
||||
): string {
|
||||
switch (description) {
|
||||
case 'post':
|
||||
return singleAuthor ? 'replied to you' : 'Your post received replies';
|
||||
case 'link':
|
||||
return `New link${plural ? 's' : ''} in`;
|
||||
case 'comment':
|
||||
return `New comment${plural ? 's' : ''} on`;
|
||||
case 'note':
|
||||
return `New Note${plural ? 's' : ''} in`;
|
||||
case 'edit-note':
|
||||
return `updated ${pluralize('note', plural)} in`;
|
||||
case 'mention':
|
||||
return singleAuthor ? 'mentioned you in' : 'You were mentioned in';
|
||||
case 'message':
|
||||
if (isDm) {
|
||||
return 'messaged you';
|
||||
}
|
||||
return `New message${plural ? 's' : ''} in`;
|
||||
default:
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
function ContentSummary({ icon, name, author, to }) {
|
||||
return (
|
||||
<Link to={to}>
|
||||
<Col
|
||||
gapY={1}
|
||||
flexDirection={['column', 'row']}
|
||||
alignItems={['flex-start', 'center']}
|
||||
>
|
||||
<Row
|
||||
alignItems="center"
|
||||
gapX={2}
|
||||
p={1}
|
||||
width="fit-content"
|
||||
borderRadius={2}
|
||||
border={1}
|
||||
borderColor="lightGray"
|
||||
>
|
||||
<Icon display="block" icon={icon} />
|
||||
<Text verticalAlign="baseline" fontWeight="medium">
|
||||
{name}
|
||||
</Text>
|
||||
</Row>
|
||||
<Row ml={[0, 1]} alignItems="center">
|
||||
<Text lineHeight={1} fontWeight="medium" mr={1}>
|
||||
by
|
||||
</Text>
|
||||
<Author
|
||||
sigilPadding={6}
|
||||
size={24}
|
||||
dontShowTime
|
||||
ship={author}
|
||||
showImage
|
||||
/>
|
||||
</Row>
|
||||
</Col>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export const GraphNodeContent = ({ post, mod, index, hidden, association }) => {
|
||||
const { contents } = post;
|
||||
const idx = index.slice(1).split('/');
|
||||
const url = getNodeUrl(mod, hidden, association?.group, association?.resource, index);
|
||||
if (mod === 'graph-validator-link' && idx.length === 1) {
|
||||
const [{ text: title }] = contents;
|
||||
return (
|
||||
<ContentSummary to={url} icon="Links" name={title} author={post.author} />
|
||||
);
|
||||
}
|
||||
if (mod === 'graph-validator-publish' && idx[1] === '1') {
|
||||
const [{ text: title }] = contents;
|
||||
return (
|
||||
<ContentSummary to={url} icon="Note" name={title} author={post.author} />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<TruncBox truncate={8}>
|
||||
<GraphContent contents={post.contents} showOurContact />
|
||||
</TruncBox>
|
||||
);
|
||||
};
|
||||
|
||||
function getNodeUrl(
|
||||
mod: string,
|
||||
hidden: boolean,
|
||||
groupPath: string,
|
||||
graph: string,
|
||||
index: string
|
||||
) {
|
||||
const graphValidator = 'graph-validator-';
|
||||
const rmValidator = mod.slice(graphValidator.length);
|
||||
if (hidden && mod === 'graph-validator-chat') {
|
||||
groupPath = '/messages';
|
||||
} else if (hidden) {
|
||||
groupPath = '/home';
|
||||
}
|
||||
const graphUrl = `/~landscape${groupPath}/resource/${rmValidator}${graph}`;
|
||||
const idx = index.slice(1).split('/');
|
||||
if (mod === 'graph-validator-publish') {
|
||||
const [noteId, kind, commId] = idx;
|
||||
const selected = kind === '2' ? `?selected=${commId}` : '';
|
||||
return `${graphUrl}/note/${noteId}${selected}`;
|
||||
} else if (mod === 'graph-validator-link') {
|
||||
const [linkId, commId] = idx;
|
||||
return `${graphUrl}/index/${linkId}${commId ? `?selected=${commId}` : ''}`;
|
||||
} else if (mod === 'graph-validator-chat') {
|
||||
if (idx.length > 0) {
|
||||
return `${graphUrl}?msg=${idx[0]}`;
|
||||
}
|
||||
return graphUrl;
|
||||
} else if (mod === 'graph-validator-post') {
|
||||
return `/~landscape${groupPath}/feed/thread${index}`;
|
||||
} else if (mod === 'graph-validator-dm') {
|
||||
return `/~landscape${groupPath}/dm/${patp(idx[0])}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
interface PostsByAuthor {
|
||||
author: string;
|
||||
posts: Post[];
|
||||
}
|
||||
const GraphNodes = (props: {
|
||||
posts: Post[];
|
||||
hideAuthors?: boolean;
|
||||
index: string;
|
||||
mod: string;
|
||||
association: Association;
|
||||
hidden: boolean;
|
||||
}) => {
|
||||
const {
|
||||
posts,
|
||||
mod,
|
||||
hidden,
|
||||
index,
|
||||
hideAuthors = false,
|
||||
association
|
||||
} = props;
|
||||
|
||||
const postsByConsecAuthor = _.reduce(
|
||||
posts,
|
||||
(acc: PostsByAuthor[], val: Post, key: number) => {
|
||||
const lent = acc.length;
|
||||
if (lent > 0 && acc?.[lent - 1]?.author === val.author) {
|
||||
const last = acc[lent - 1];
|
||||
const rest = acc.slice(0, -1);
|
||||
return [...rest, { ...last, posts: [...last.posts, val] }];
|
||||
}
|
||||
return [...acc, { author: val.author, posts: [val] }];
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{_.map(postsByConsecAuthor, ({ posts, author }, idx) => {
|
||||
const time = posts[0]?.['time-sent'];
|
||||
return (
|
||||
<Col key={idx} flexGrow={1} alignItems="flex-start">
|
||||
{!hideAuthors && (
|
||||
<Author
|
||||
size={24}
|
||||
sigilPadding={6}
|
||||
showImage
|
||||
ship={author}
|
||||
date={time}
|
||||
/>
|
||||
)}
|
||||
<Col gapY={2} py={hideAuthors ? 0 : 2} width="100%">
|
||||
{_.map(posts, post => (
|
||||
<GraphNodeContent
|
||||
key={post.index}
|
||||
post={post}
|
||||
mod={mod}
|
||||
index={index}
|
||||
association={association}
|
||||
hidden={hidden}
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export function GraphNotification(props: {
|
||||
index: GraphNotifIndex;
|
||||
contents: GraphNotificationContents;
|
||||
read: boolean;
|
||||
time: number;
|
||||
timebox: BigInteger;
|
||||
}) {
|
||||
const { contents, index, read, time, timebox } = props;
|
||||
const history = useHistory();
|
||||
|
||||
const authors = _.uniq(_.map(contents, 'author'));
|
||||
const singleAuthor = authors.length === 1;
|
||||
const { graph, mark } = index;
|
||||
const association = useAssocForGraph(graph);
|
||||
const dm = mark === 'graph-validator-dm';
|
||||
const desc = describeNotification(
|
||||
index.description,
|
||||
contents.length !== 1,
|
||||
dm,
|
||||
singleAuthor
|
||||
);
|
||||
const groupAssociation = useAssocForGroup(association?.group ?? '');
|
||||
const groups = useGroupState(state => state.groups);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if(dm) {
|
||||
history.push(`/~landscape/messages/dm/~${authors[0]}`);
|
||||
return;
|
||||
}
|
||||
const first = contents[0];
|
||||
history.push(
|
||||
getNodeUrl(
|
||||
index.mark,
|
||||
groups[association?.group]?.hidden,
|
||||
association?.group,
|
||||
association?.resource,
|
||||
first.index
|
||||
)
|
||||
);
|
||||
}, [timebox, index, read, history.push, authors, dm]);
|
||||
|
||||
const authorsInHeader =
|
||||
dm ||
|
||||
((index.description === 'mention' || index.description === 'post') &&
|
||||
singleAuthor);
|
||||
const hideAuthors =
|
||||
authorsInHeader ||
|
||||
index.description === 'note' ||
|
||||
index.description === 'link';
|
||||
const channelTitle = dm ? undefined : association?.metadata?.title ?? graph;
|
||||
const groupTitle = groupAssociation?.metadata?.title;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
time={time}
|
||||
authors={authorsInHeader ? authors : []}
|
||||
channelTitle={channelTitle}
|
||||
description={desc}
|
||||
groupTitle={groupTitle}
|
||||
content
|
||||
/>
|
||||
<Col onClick={onClick} gapY={2} flexGrow={1} width="100%" gridArea="main">
|
||||
<GraphNodes
|
||||
hideAuthors={hideAuthors}
|
||||
posts={contents.slice(0, 4)}
|
||||
mod={index.mark}
|
||||
index={contents?.[0].index}
|
||||
association={association}
|
||||
hidden={groups[association?.group]?.hidden}
|
||||
/>
|
||||
{contents.length > 4 && (
|
||||
<Text mb={2} gray>
|
||||
+ {contents.length - 4} more
|
||||
</Text>
|
||||
)}
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import { Col } from '@tlon/indigo-react';
|
||||
import {
|
||||
GroupNotificationContents,
|
||||
GroupNotifIndex,
|
||||
GroupUpdate
|
||||
} from '@urbit/api';
|
||||
import _ from 'lodash';
|
||||
import React, { ReactElement } from 'react';
|
||||
import { useAssocForGroup } from '~/logic/state/metadata';
|
||||
import { Header } from './header';
|
||||
|
||||
function describeNotification(description: string, plural: boolean) {
|
||||
switch (description) {
|
||||
case 'add-members':
|
||||
return 'joined';
|
||||
case 'remove-members':
|
||||
return 'left';
|
||||
default:
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
function getGroupUpdateParticipants(update: GroupUpdate): string[] {
|
||||
if ('addMembers' in update) {
|
||||
return update.addMembers.ships;
|
||||
}
|
||||
if ('removeMembers' in update) {
|
||||
return update.removeMembers.ships;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
interface GroupNotificationProps {
|
||||
index: GroupNotifIndex;
|
||||
contents: GroupNotificationContents;
|
||||
time: number;
|
||||
}
|
||||
|
||||
export function GroupNotification(props: GroupNotificationProps): ReactElement {
|
||||
const { contents, index, time } = props;
|
||||
|
||||
const authors = _.flatten(_.map(contents, getGroupUpdateParticipants));
|
||||
|
||||
const { group } = index;
|
||||
const desc = describeNotification(index.description, contents.length !== 1);
|
||||
|
||||
const association = useAssocForGroup(group);
|
||||
const groupTitle = association?.metadata?.title ?? group;
|
||||
|
||||
return (
|
||||
<Col>
|
||||
<Header
|
||||
time={time}
|
||||
authors={authors}
|
||||
description={desc}
|
||||
groupTitle={groupTitle}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export interface AuthorProps {
|
||||
size?: number;
|
||||
lineHeight?: string | number;
|
||||
isRelativeTime?: boolean;
|
||||
dontShowTime: boolean;
|
||||
dontShowTime?: boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
|
@ -25,7 +25,7 @@ import { CommentItem } from './CommentItem';
|
||||
import airlock from '~/logic/api';
|
||||
import useGraphState from '~/logic/state/graph';
|
||||
import { useHistory } from 'react-router';
|
||||
import { toHarkPlace } from '~/logic/lib/util';
|
||||
import { toHarkPath, toHarkPlace } from '~/logic/lib/util';
|
||||
|
||||
interface CommentsProps {
|
||||
comments: GraphNode;
|
||||
@ -134,7 +134,8 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
|
||||
}, [comments.post?.index, association.resource]);
|
||||
|
||||
const unreads = useHarkState(state => state.unreads);
|
||||
const readCount = children.length - getUnreadCount(unreads, association.resource, parentIndex);
|
||||
const harkPath = toHarkPath(association.resource, parentIndex);
|
||||
const readCount = children.length - getUnreadCount(unreads, harkPath);
|
||||
|
||||
const canComment = isWriter(group, association.resource) || association.metadata.vip === 'reader-comments';
|
||||
|
||||
|
@ -26,7 +26,6 @@ import useGroupState from '~/logic/state/group';
|
||||
import useMetadataState, { useAssocForGraph } from '~/logic/state/metadata';
|
||||
import { PropFunc } from '~/types';
|
||||
import { Header } from '~/views/apps/notifications/header';
|
||||
import { NotificationWrapper } from '~/views/apps/notifications/notification';
|
||||
import { MetadataIcon } from '~/views/landscape/components/MetadataIcon';
|
||||
import { StatelessAsyncButton } from '../StatelessAsyncButton';
|
||||
import airlock from '~/logic/api';
|
||||
@ -297,7 +296,7 @@ export function GroupInvite(props: GroupInviteProps): ReactElement {
|
||||
};
|
||||
|
||||
return (
|
||||
<NotificationWrapper>
|
||||
<>
|
||||
<Header content {...headerProps} />
|
||||
<Row
|
||||
onClick={onClick}
|
||||
@ -321,6 +320,6 @@ export function GroupInvite(props: GroupInviteProps): ReactElement {
|
||||
/>
|
||||
</ResponsiveRow>
|
||||
</Row>
|
||||
</NotificationWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { deSig, Graphs, UnreadStats } from '@urbit/api';
|
||||
import { useCallback } from 'react';
|
||||
import { SidebarAppConfig } from './types';
|
||||
|
||||
export function useGraphModule(
|
||||
graphKeys: Set<string>,
|
||||
graphs: Graphs,
|
||||
graphUnreads: Record<string, Record<string, UnreadStats>>
|
||||
): SidebarAppConfig {
|
||||
const getStatus = useCallback(
|
||||
(s: string) => {
|
||||
const [, , host, name] = s.split('/');
|
||||
const graphKey = `${deSig(host)}/${name}`;
|
||||
if (!graphKeys.has(graphKey)) {
|
||||
return 'unsubscribed';
|
||||
}
|
||||
|
||||
const notifications = graphUnreads?.[s]?.['/']?.notifications;
|
||||
if (
|
||||
notifications &&
|
||||
((typeof notifications === 'number' && notifications > 0)
|
||||
|| typeof notifications === 'object' && notifications.length)
|
||||
) {
|
||||
return 'notification';
|
||||
}
|
||||
|
||||
const unreads = graphUnreads?.[s]?.['/']?.unreads;
|
||||
if (typeof unreads === 'number' ? unreads > 0 : unreads?.size ?? 0 > 0) {
|
||||
return 'unread';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
[graphs, graphKeys, graphUnreads]
|
||||
);
|
||||
|
||||
const lastUpdated = useCallback((s: string) => {
|
||||
// cant get link timestamps without loading posts
|
||||
const last = graphUnreads?.[s]?.['/']?.last;
|
||||
if(last) {
|
||||
return last;
|
||||
}
|
||||
const stat = getStatus(s);
|
||||
if(stat === 'unsubscribed') {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}, [getStatus, graphUnreads]);
|
||||
|
||||
return { getStatus, lastUpdated };
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import React, { ReactElement, useCallback } from 'react';
|
||||
import { Associations, Graph } from '@urbit/api';
|
||||
import { Associations, Graph, Unreads } from '@urbit/api';
|
||||
import { patp, patp2dec } from 'urbit-ob';
|
||||
import _ from 'lodash';
|
||||
|
||||
@ -13,9 +13,8 @@ import useMetadataState from '~/logic/state/metadata';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useShortcut } from '~/logic/state/settings';
|
||||
|
||||
function sidebarSort(pending: Set<string>): Record<SidebarSort, (a: string, b: string) => number> {
|
||||
function sidebarSort(unreads: Unreads, pending: Set<string>): Record<SidebarSort, (a: string, b: string) => number> {
|
||||
const { associations } = useMetadataState.getState();
|
||||
const { unreads } = useHarkState.getState();
|
||||
const alphabetical = (a: string, b: string) => {
|
||||
const aAssoc = associations[a];
|
||||
const bAssoc = associations[b];
|
||||
@ -102,9 +101,10 @@ export function SidebarList(props: {
|
||||
const inbox = useInbox();
|
||||
const graphKeys = useGraphState(s => s.graphKeys);
|
||||
const pending = useGraphState(s => s.pendingDms);
|
||||
const unreads = useHarkState(s => s.unreads);
|
||||
|
||||
const ordered = getItems(associations, workspace, inbox, pending)
|
||||
.sort(sidebarSort(pending)[config.sortBy]);
|
||||
.sort(sidebarSort(unreads, pending)[config.sortBy]);
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user