mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-13 20:04:35 +03:00
hark: display chat notifications
This commit is contained in:
parent
e1b11d610d
commit
a0ea86098b
93
pkg/interface/src/views/apps/notifications/chat.tsx
Normal file
93
pkg/interface/src/views/apps/notifications/chat.tsx
Normal file
@ -0,0 +1,93 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import {
|
||||
Rolodex,
|
||||
Associations,
|
||||
ChatNotifIndex,
|
||||
ChatNotificationContents,
|
||||
Groups,
|
||||
} from "~/types";
|
||||
import { BigInteger } from "big-integer";
|
||||
import { Box, Col } from "@tlon/indigo-react";
|
||||
import { Header } from "./header";
|
||||
import { pluralize } from "~/logic/lib/util";
|
||||
import ChatMessage from "../chat/components/ChatMessage";
|
||||
|
||||
function describeNotification(lent: number) {
|
||||
return `sent ${pluralize("message", lent !== 1)} in`;
|
||||
}
|
||||
|
||||
export function ChatNotification(props: {
|
||||
index: ChatNotifIndex;
|
||||
contents: ChatNotificationContents;
|
||||
archived: boolean;
|
||||
read: boolean;
|
||||
time: number;
|
||||
timebox: BigInteger;
|
||||
associations: Associations;
|
||||
contacts: Rolodex;
|
||||
groups: Groups;
|
||||
api: GlobalApi;
|
||||
}) {
|
||||
const { index, contents, read, time, api, timebox } = props;
|
||||
const authors = _.map(contents, "author");
|
||||
|
||||
const association = props.associations.chat[index];
|
||||
const groupPath = association["group-path"];
|
||||
const appPath = association["app-path"];
|
||||
|
||||
const group = props.groups[groupPath];
|
||||
|
||||
const desc = describeNotification(contents.length);
|
||||
const groupContacts = props.contacts[groupPath];
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (props.archived) {
|
||||
return;
|
||||
}
|
||||
|
||||
const func = read ? "unread" : "read";
|
||||
return api.hark[func](timebox, { chat: index });
|
||||
}, [api, timebox, index, read]);
|
||||
|
||||
return (
|
||||
<Col onClick={onClick} flexGrow="1" p="2">
|
||||
<Header
|
||||
chat
|
||||
associations={props.associations}
|
||||
read={read}
|
||||
archived={props.archived}
|
||||
time={time}
|
||||
authors={authors}
|
||||
moduleIcon="Chat"
|
||||
channel={index}
|
||||
contacts={props.contacts}
|
||||
group={groupPath}
|
||||
description={desc}
|
||||
/>
|
||||
<Col pb="3" pl="5">
|
||||
{_.map(_.take(contents, 5), (content, idx) => {
|
||||
const to = `/~landscape${groupPath}/resource/chat${appPath}?msg=${content.number}`;
|
||||
return (
|
||||
<Link key={idx} to={to}>
|
||||
<ChatMessage
|
||||
measure={() => {}}
|
||||
msg={content}
|
||||
isLastRead={false}
|
||||
group={group}
|
||||
contacts={groupContacts}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
{contents.length > 5 && (
|
||||
<Box ml="4" mt="3" mb="2" color="gray" fontSize="14px">
|
||||
and {contents.length - 5} other message
|
||||
{contents.length > 6 ? "s" : ""}
|
||||
</Box>
|
||||
)}
|
||||
</Col>
|
||||
</Col>
|
||||
);
|
||||
}
|
@ -42,6 +42,7 @@ export function Header(props: {
|
||||
time: number;
|
||||
read: boolean;
|
||||
associations: Associations;
|
||||
chat?: boolean;
|
||||
}) {
|
||||
const { description, channel, group, moduleIcon, read } = props;
|
||||
const contacts = props.contacts[group] || {};
|
||||
@ -70,8 +71,9 @@ export function Header(props: {
|
||||
const groupTitle =
|
||||
props.associations.contacts?.[props.group]?.metadata?.title || props.group;
|
||||
|
||||
const app = props.chat ? 'chat' : 'graph';
|
||||
const channelTitle =
|
||||
(channel && props.associations.graph?.[channel]?.metadata?.title) ||
|
||||
(channel && props.associations?.[app]?.[channel]?.metadata?.title) ||
|
||||
channel;
|
||||
|
||||
return (
|
||||
|
@ -12,7 +12,7 @@ import { Associations } from "~/types";
|
||||
|
||||
type DatedTimebox = [BigInteger, Timebox];
|
||||
|
||||
function filterNotification(groups: string[]) {
|
||||
function filterNotification(associations: Associations, groups: string[]) {
|
||||
if (groups.length === 0) {
|
||||
return () => true;
|
||||
}
|
||||
@ -23,6 +23,9 @@ function filterNotification(groups: string[]) {
|
||||
} else if ("group" in n.index) {
|
||||
const { group } = n.index.group;
|
||||
return groups.findIndex((g) => group === g) !== -1;
|
||||
} else if ("chat" in n.index) {
|
||||
const group = associations.chat[n.index.chat]?.["group-path"];
|
||||
return groups.findIndex((g) => group === g) !== -1;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@ -56,7 +59,7 @@ export default function Inbox(props: {
|
||||
const notificationsByDay = f.flow(
|
||||
f.map<DatedTimebox>(([date, nots]) => [
|
||||
date,
|
||||
nots.filter(filterNotification(props.filter)),
|
||||
nots.filter(filterNotification(associations, props.filter)),
|
||||
]),
|
||||
f.groupBy<DatedTimebox>(([date]) =>
|
||||
moment(daToUnix(date)).format("DDMMYYYY")
|
||||
@ -75,6 +78,7 @@ export default function Inbox(props: {
|
||||
associations={props.associations}
|
||||
graphConfig={props.notificationsGraphConfig}
|
||||
groupConfig={props.notificationsGroupConfig}
|
||||
chatConfig={props.notificationsChatConfig}
|
||||
api={api}
|
||||
/>
|
||||
)}
|
||||
@ -92,6 +96,7 @@ export default function Inbox(props: {
|
||||
api={api}
|
||||
graphConfig={props.notificationsGraphConfig}
|
||||
groupConfig={props.notificationsGroupConfig}
|
||||
chatConfig={props.notificationsChatConfig}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
@ -119,6 +124,7 @@ function DaySection({
|
||||
api,
|
||||
groupConfig,
|
||||
graphConfig,
|
||||
chatConfig,
|
||||
}) {
|
||||
const calendar = latest
|
||||
? MOMENT_CALENDAR_DATE
|
||||
@ -143,6 +149,7 @@ function DaySection({
|
||||
<Notification
|
||||
graphConfig={graphConfig}
|
||||
groupConfig={groupConfig}
|
||||
chatConfig={chatConfig}
|
||||
api={api}
|
||||
associations={associations}
|
||||
notification={not}
|
||||
|
@ -8,12 +8,14 @@ import {
|
||||
NotificationGraphConfig,
|
||||
GroupNotificationsConfig,
|
||||
NotifIndex,
|
||||
Associations
|
||||
Associations,
|
||||
} from "~/types";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
||||
import { GroupNotification } from "./group";
|
||||
import { GraphNotification } from "./graph";
|
||||
import { ChatNotification } from "./chat";
|
||||
import { BigInteger } from "big-integer";
|
||||
|
||||
interface NotificationProps {
|
||||
notification: IndexedNotification;
|
||||
@ -23,12 +25,14 @@ interface NotificationProps {
|
||||
archived: boolean;
|
||||
graphConfig: NotificationGraphConfig;
|
||||
groupConfig: GroupNotificationsConfig;
|
||||
chatConfig: string[];
|
||||
}
|
||||
|
||||
function getMuted(
|
||||
idx: NotifIndex,
|
||||
groups: GroupNotificationsConfig,
|
||||
graphs: NotificationGraphConfig
|
||||
graphs: NotificationGraphConfig,
|
||||
chat: string[]
|
||||
) {
|
||||
if ("graph" in idx) {
|
||||
const { graph } = idx.graph;
|
||||
@ -37,6 +41,9 @@ function getMuted(
|
||||
if ("group" in idx) {
|
||||
return _.findIndex(groups || [], (g) => g === idx.group.group) === -1;
|
||||
}
|
||||
if ("chat" in idx) {
|
||||
return _.findIndex(chat || [], (c) => c === idx.chat) === -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -48,6 +55,7 @@ function NotificationWrapper(props: {
|
||||
archived: boolean;
|
||||
graphConfig: NotificationGraphConfig;
|
||||
groupConfig: GroupNotificationsConfig;
|
||||
chatConfig: string[];
|
||||
}) {
|
||||
const { api, time, notif, children } = props;
|
||||
|
||||
@ -55,7 +63,12 @@ function NotificationWrapper(props: {
|
||||
return api.hark.archive(time, notif.index);
|
||||
}, [time, notif]);
|
||||
|
||||
const isMuted = getMuted(notif.index, props.groupConfig, props.graphConfig);
|
||||
const isMuted = getMuted(
|
||||
notif.index,
|
||||
props.groupConfig,
|
||||
props.graphConfig,
|
||||
props.chatConfig
|
||||
);
|
||||
|
||||
const onChangeMute = useCallback(async () => {
|
||||
const func = isMuted ? "unmute" : "mute";
|
||||
@ -84,19 +97,26 @@ export function Notification(props: NotificationProps) {
|
||||
const { notification, associations, archived } = props;
|
||||
const { read, contents, time } = notification.notification;
|
||||
|
||||
const Wrapper = ({ children }) => (
|
||||
<NotificationWrapper
|
||||
archived={archived}
|
||||
notif={notification}
|
||||
time={props.time}
|
||||
api={props.api}
|
||||
graphConfig={props.graphConfig}
|
||||
groupConfig={props.groupConfig}
|
||||
chatConfig={props.chatConfig}
|
||||
>
|
||||
{children}
|
||||
</NotificationWrapper>
|
||||
);
|
||||
|
||||
if ("graph" in notification.index) {
|
||||
const index = notification.index.graph;
|
||||
const c: GraphNotificationContents = (contents as any).graph;
|
||||
|
||||
return (
|
||||
<NotificationWrapper
|
||||
archived={archived}
|
||||
notif={notification}
|
||||
time={props.time}
|
||||
api={props.api}
|
||||
graphConfig={props.graphConfig}
|
||||
groupConfig={props.groupConfig}
|
||||
>
|
||||
<Wrapper>
|
||||
<GraphNotification
|
||||
api={props.api}
|
||||
index={index}
|
||||
@ -108,21 +128,14 @@ export function Notification(props: NotificationProps) {
|
||||
time={time}
|
||||
associations={associations}
|
||||
/>
|
||||
</NotificationWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
if ("group" in notification.index) {
|
||||
const index = notification.index.group;
|
||||
const c: GroupNotificationContents = (contents as any).group;
|
||||
return (
|
||||
<NotificationWrapper
|
||||
archived={archived}
|
||||
notif={notification}
|
||||
time={props.time}
|
||||
api={props.api}
|
||||
graphConfig={props.graphConfig}
|
||||
groupConfig={props.groupConfig}
|
||||
>
|
||||
<Wrapper>
|
||||
<GroupNotification
|
||||
api={props.api}
|
||||
index={index}
|
||||
@ -134,7 +147,27 @@ export function Notification(props: NotificationProps) {
|
||||
time={time}
|
||||
associations={associations}
|
||||
/>
|
||||
</NotificationWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
if ("chat" in notification.index) {
|
||||
const index = notification.index.chat;
|
||||
const c: ChatNotificationContents = (contents as any).chat;
|
||||
return (
|
||||
<Wrapper>
|
||||
<ChatNotification
|
||||
api={props.api}
|
||||
index={index}
|
||||
contents={c}
|
||||
contacts={props.contacts}
|
||||
read={read}
|
||||
archived={archived}
|
||||
groups={{}}
|
||||
timebox={props.time}
|
||||
time={time}
|
||||
associations={associations}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { Dropdown } from "~/views/components/Dropdown";
|
||||
import { Association, NotificationGraphConfig } from "~/types";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
||||
import {appIsGraph} from "~/logic/lib/util";
|
||||
import { appIsGraph } from "~/logic/lib/util";
|
||||
|
||||
const ChannelMenuItem = ({
|
||||
icon,
|
||||
@ -29,7 +29,8 @@ const ChannelMenuItem = ({
|
||||
interface ChannelMenuProps {
|
||||
association: Association;
|
||||
api: GlobalApi;
|
||||
notificationConfig: NotificationGraphConfig;
|
||||
graphNotificationConfig: NotificationGraphConfig;
|
||||
chatNotificationConfig: string[];
|
||||
}
|
||||
|
||||
export function ChannelMenu(props: ChannelMenuProps) {
|
||||
@ -49,10 +50,19 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
||||
|
||||
const isOurs = ship.slice(1) === window.ship;
|
||||
|
||||
const isMuted =
|
||||
props.notificationConfig.watching.findIndex((a) => a === appPath) === -1;
|
||||
const isMuted = appIsGraph(app)
|
||||
? props.graphNotificationConfig.watching.findIndex((a) => a === appPath) ===
|
||||
-1
|
||||
: props.chatNotificationConfig.findIndex((a) => a === appPath) === -1;
|
||||
const onChangeMute = async () => {
|
||||
const func = isMuted ? "listenGraph" : "ignoreGraph";
|
||||
const func =
|
||||
association["app-name"] === "chat"
|
||||
? isMuted
|
||||
? "listenChat"
|
||||
: "ignoreChat"
|
||||
: isMuted
|
||||
? "listenGraph"
|
||||
: "ignoreGraph";
|
||||
await api.hark[func](appPath);
|
||||
};
|
||||
const onUnsubscribe = useCallback(async () => {
|
||||
@ -100,18 +110,16 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
||||
borderRadius={1}
|
||||
borderColor="lightGray"
|
||||
>
|
||||
{appIsGraph(metadata.module) && (
|
||||
<ChannelMenuItem color="blue" icon="Inbox">
|
||||
<StatelessAsyncAction
|
||||
m="2"
|
||||
bg="white"
|
||||
name="notif"
|
||||
onClick={onChangeMute}
|
||||
>
|
||||
{isMuted ? "Unmute" : "Mute"} this channel
|
||||
</StatelessAsyncAction>
|
||||
</ChannelMenuItem>
|
||||
)}
|
||||
<ChannelMenuItem color="blue" icon="Inbox">
|
||||
<StatelessAsyncAction
|
||||
m="2"
|
||||
bg="white"
|
||||
name="notif"
|
||||
onClick={onChangeMute}
|
||||
>
|
||||
{isMuted ? "Unmute" : "Mute"} this channel
|
||||
</StatelessAsyncAction>
|
||||
</ChannelMenuItem>
|
||||
{isOurs ? (
|
||||
<>
|
||||
<ChannelMenuItem color="red" icon="TrashCan">
|
||||
|
@ -54,6 +54,7 @@ export function Resource(props: ResourceProps) {
|
||||
render={(routeProps) => (
|
||||
<ResourceSkeleton
|
||||
notificationsGraphConfig={props.notificationsGraphConfig}
|
||||
notificationsChatConfig={props.notificationsChatConfig}
|
||||
baseUrl={props.baseUrl}
|
||||
{...skelProps}
|
||||
atRoot
|
||||
|
@ -99,7 +99,8 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
|
||||
</TruncatedBox>
|
||||
<Box flexGrow={1} />
|
||||
<ChannelMenu
|
||||
notificationConfig={props.notificationsGraphConfig}
|
||||
graphNotificationConfig={props.notificationsGraphConfig}
|
||||
chatNotificationConfig={props.notificationsChatConfig}
|
||||
association={association}
|
||||
api={api}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user