interface: show unread counts on group tiles

This commit is contained in:
Liam Fitzgerald 2020-11-16 12:23:42 +10:00
parent ec580cf8c1
commit fb09c36fbe
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
9 changed files with 111 additions and 52 deletions

View File

@ -61,6 +61,12 @@ export class HarkApi extends BaseApi<StoreState> {
return this.actOnNotification('read', time, index);
}
readIndex(index: NotifIndex) {
return this.harkAction({
'read-index': index
});
}
unread(time: BigInteger, index: NotifIndex) {
return this.actOnNotification('unread', time, index);
}

View File

@ -6,19 +6,21 @@ import {
} from "~/types";
import { makePatDa } from "~/logic/lib/util";
import _ from "lodash";
import {StoreState} from "../store/type";
type HarkState = {
notifications: Notifications;
archivedNotifications: Notifications;
notificationsCount: number;
notificationsGraphConfig: NotificationGraphConfig;
notificationsGroupConfig: GroupNotificationsConfig;
notificationsChatConfig: string[];
};
type HarkState = Pick<StoreState,
"notificationsChatConfig"
| "notificationsGroupConfig"
| "notificationsGraphConfig"
| "notifications"
| "notificationsCount"
| "archivedNotifications"
| "unreads">;
export const HarkReducer = (json: any, state: HarkState) => {
const data = _.get(json, "harkUpdate", false);
if (data) {
console.log(data);
reduce(data, state);
}
const graphHookData = _.get(json, "hark-graph-hook-update", false);
@ -139,48 +141,56 @@ function reduce(data: any, state: HarkState) {
timebox(data, state);
more(data, state);
dnd(data, state);
count(data, state);
added(data, state);
graphUnreads(data, state);
unreads(data, state);
}
function graphUnreads(json: any, state: HarkState) {
const data = _.get(json, 'graph-unreads');
function unreads(json: any, state: HarkState) {
const data = _.get(json, 'unreads');
if(data) {
state.graphUnreads = data;
data.forEach(({ index, unread }) => {
updateUnreads(state, index, x => x + unread);
});
}
}
function updateUnreads(state: HarkState, index: NotifIndex, f: (u: number) => number) {
state.notificationsCount = f(state.notificationsCount);
if('graph' in index) {
const curr = state.unreads.graph[index.graph.graph] || 0;
state.unreads.graph[index.graph.graph] = f(curr);
} else if('group' in index) {
const curr = state.unreads.group[index.group.group] || 0;
state.unreads.group[index.group.group] = f(curr);
} else if('chat' in index) {
const curr = state.unreads.chat[index.chat.chat] || 0
state.unreads.chat[index.chat.chat] = f(curr);
}
}
function added(json: any, state: HarkState) {
const data = _.get(json, "added", false);
if (data) {
const { index, notification } = data;
const time = makePatDa(data.time);
const timebox = state.notifications.get(time) || [];
const arrIdx = timebox.findIndex((idxNotif) =>
notifIdxEqual(index, idxNotif.index)
);
if (arrIdx !== -1) {
if(timebox[arrIdx]?.notification?.read) {
updateUnreads(state, index, x => x+1);
}
timebox[arrIdx] = { index, notification };
state.notifications.set(time, timebox);
} else {
updateUnreads(state, index, x => x+1);
state.notifications.set(time, [...timebox, { index, notification }]);
state.notificationsCount++;
if('graph' in index) {
const curr = state.graphUnreads[index.graph.graph] || 0;
state.graphUnreads[index.graph.graph] = curr+1;
}
}
}
}
function count(json: any, state: HarkState) {
const data = _.get(json, "count", false);
if (data !== false) {
state.notificationsCount = data;
}
}
const dnd = (json: any, state: HarkState) => {
const data = _.get(json, "set-dnd", undefined);
if (!_.isUndefined(data)) {
@ -254,12 +264,7 @@ function read(json: any, state: HarkState) {
const data = _.get(json, "read", false);
if (data) {
const { time, index } = data;
state.notificationsCount--;
if('graph' in index) {
const curr = state.graphUnreads[index.graph.graph] || 0;
state.graphUnreads[index.graph.graph] = curr-1;
}
updateUnreads(state, index, x => x-1);
setRead(time, index, true, state);
}
}
@ -268,11 +273,7 @@ function unread(json: any, state: HarkState) {
const data = _.get(json, "unread", false);
if (data) {
const { time, index } = data;
state.notificationsCount++;
if('graph' in index) {
const curr = state.graphUnreads[index.graph.graph] || 0;
state.graphUnreads[index.graph.graph] = curr+1;
}
updateUnreads(state, index, x => x+1);
setRead(time, index, false, state);
}
}
@ -292,9 +293,10 @@ function archive(json: any, state: HarkState) {
);
state.notifications.set(time, unarchived);
const archiveBox = state.archivedNotifications.get(time) || [];
state.notificationsCount -= archived.filter(
const readCount = archived.filter(
({ notification }) => !notification.read
).length;
updateUnreads(state, index, x => x - readCount);
state.archivedNotifications.set(time, [
...archiveBox,
...archived.map(({ notification, index }) => ({

View File

@ -107,7 +107,11 @@ export default class GlobalStore extends BaseStore<StoreState> {
watching: [],
},
notificationsCount: 0,
graphUnreads: {}
unreads: {
graph: {},
group: {},
chat: {},
}
};
}

View File

@ -14,7 +14,8 @@ import {
NotificationGraphConfig,
GroupNotificationsConfig,
LocalUpdateRemoteContentPolicy,
BackgroundConfig
BackgroundConfig,
Unreads
} from "~/types";
export interface StoreState {
@ -59,11 +60,12 @@ export interface StoreState {
inbox: Inbox;
pendingMessages: Map<Path, Envelope[]>;
archivedNotifications: Notifications;
notifications: Notifications;
notificationsGraphConfig: NotificationGraphConfig;
notificationsGroupConfig: GroupNotificationsConfig;
notificationsChatConfig: string[];
notificationsCount: number,
doNotDisturb: boolean;
graphUnreads: Record<string, number>;
unreads: Unreads;
}

View File

@ -60,6 +60,11 @@ export interface NotificationGraphConfig {
watching: WatchedIndex[]
}
export interface Unreads {
chat: Record<string, number>;
group: Record<string, number>;
graph: Record<string, number>;
}
interface WatchedIndex {
graph: string;

View File

@ -181,6 +181,7 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
if (this.state.fetchPending) return;
if (this.props.unreadCount === 0) return;
this.props.api.chat.read(this.props.station);
this.props.api.hark.readIndex({ chat: { chat: this.props.station, mention: false }});
}
fetchMessages(start, end, force = false): Promise<void> {

View File

@ -63,7 +63,7 @@ export default class LaunchApp extends React.Component {
weather={props.weather}
/>
<Box display={["none", "block"]} width="100%" gridColumn="1 / -1"></Box>
<Groups groups={props.groups} associations={props.associations} />
<Groups unreads={props.unreads} groups={props.groups} associations={props.associations} />
</Box>
</ScrollbarLessBox>
<Box

View File

@ -1,9 +1,11 @@
import React from "react";
import { Box, Text } from "@tlon/indigo-react";
import { Box, Text, Col } from "@tlon/indigo-react";
import f from "lodash/fp";
import _ from "lodash";
import { Associations, Association } from "~/types";
import { Associations, Association, Unreads } from "~/types";
import { alphabeticalOrder } from "~/logic/lib/util";
import Tile from '../components/tiles/tile';
import Tile from "../components/tiles/tile";
interface GroupsProps {
associations: Associations;
@ -12,20 +14,57 @@ interface GroupsProps {
const sortGroupsAlph = (a: Association, b: Association) =>
alphabeticalOrder(a.metadata.title, b.metadata.title);
const getKindUnreads = (associations: Associations) => (path: string) => (
kind: "chat" | "graph"
): ((unreads: Unreads) => number) =>
f.flow(
(x) => x[kind],
f.pickBy((_v, key) => associations[kind]?.[key]["group-path"] === path),
f.values,
f.reduce(f.add, 0)
);
export default function Groups(props: GroupsProps & Parameters<typeof Box>[0]) {
const { associations, ...boxProps } = props;
const { associations, unreads, ...boxProps } = props;
const groups = Object.values(associations?.contacts || {})
.filter(e => e['group-path'] in props.groups)
.filter((e) => e["group-path"] in props.groups)
.sort(sortGroupsAlph);
const getUnreads = getKindUnreads(associations || {});
return (
<>
{groups.map((group) => (
<Tile to={`/~landscape${group["group-path"]}`}>
<Text>{group.metadata.title}</Text>
</Tile>
))}
{groups.map((group) => {
const path = group["group-path"];
const unreadCount = (["chat", "graph"] as const)
.map(getUnreads(path))
.map((f) => f(unreads))
.reduce(f.add, 0);
return (
<Group
unreads={unreadCount}
path={group["group-path"]}
title={group.metadata.title}
/>
);
})}
</>
);
}
interface GroupProps {
path: string;
title: string;
unreads: number;
}
function Group(props: GroupProps) {
const { path, title, unreads } = props;
return (
<Tile to={`/~landscape${path}`}>
<Col height="100%" justifyContent="space-between">
<Text>{title}</Text>
{unreads > 0 && <Text gray>{unreads} unread </Text>}
</Col>
</Tile>
);
}

View File

@ -43,7 +43,7 @@ interface SkeletonProps {
export function Skeleton(props: SkeletonProps) {
const chatConfig = useChat(props.inbox, props.chatSynced);
const graphConfig = useGraphModule(props.graphKeys, props.graphs, props.graphUnreads);
const graphConfig = useGraphModule(props.graphKeys, props.graphs, props.unreads.graph);
const config = useMemo(
() => ({
graph: graphConfig,