mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-14 17:41:33 +03:00
Merge pull request #3892 from urbit/lf/hark-chat-hook
hark-chat-hook: Notifications
This commit is contained in:
commit
f31dd0c047
@ -5,7 +5,7 @@
|
|||||||
/- glob
|
/- glob
|
||||||
/+ default-agent, verb, dbug
|
/+ default-agent, verb, dbug
|
||||||
|%
|
|%
|
||||||
++ hash 0v4.cf9m1.t0ofg.dtig4.av3jh.2f2db
|
++ hash 0v6.9vk2h.hr87m.nn63p.8kmo5.k4ljt
|
||||||
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
|
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
|
||||||
+$ all-states
|
+$ all-states
|
||||||
$% state-0
|
$% state-0
|
||||||
|
184
pkg/arvo/app/hark-chat-hook.hoon
Normal file
184
pkg/arvo/app/hark-chat-hook.hoon
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
:: hark-chat-hook: notifications for chat-store [landscape]
|
||||||
|
::
|
||||||
|
/- store=hark-store, post, group-store, metadata-store, hook=hark-chat-hook
|
||||||
|
/+ resource, metadata, default-agent, dbug, chat-store
|
||||||
|
::
|
||||||
|
~% %hark-chat-hook-top ..is ~
|
||||||
|
|%
|
||||||
|
+$ card card:agent:gall
|
||||||
|
+$ versioned-state
|
||||||
|
$% state-0
|
||||||
|
==
|
||||||
|
::
|
||||||
|
+$ state-0
|
||||||
|
$: %0
|
||||||
|
watching=(set path)
|
||||||
|
mentions=_&
|
||||||
|
==
|
||||||
|
::
|
||||||
|
--
|
||||||
|
::
|
||||||
|
=| state-0
|
||||||
|
=* state -
|
||||||
|
::
|
||||||
|
=<
|
||||||
|
%- agent:dbug
|
||||||
|
^- agent:gall
|
||||||
|
~% %hark-chat-hook-agent ..card ~
|
||||||
|
|_ =bowl:gall
|
||||||
|
+* this .
|
||||||
|
ha ~(. +> bowl)
|
||||||
|
def ~(. (default-agent this %|) bowl)
|
||||||
|
met ~(. metadata bowl)
|
||||||
|
::
|
||||||
|
++ on-init
|
||||||
|
:_ this
|
||||||
|
~[watch-chat:ha]
|
||||||
|
::
|
||||||
|
++ on-save !>(state)
|
||||||
|
++ on-load
|
||||||
|
|= old=vase
|
||||||
|
^- (quip card _this)
|
||||||
|
`this(state !<(state-0 old))
|
||||||
|
::
|
||||||
|
++ on-watch
|
||||||
|
|= =path
|
||||||
|
^- (quip card _this)
|
||||||
|
=^ cards state
|
||||||
|
?+ path (on-watch:def path)
|
||||||
|
::
|
||||||
|
[%updates ~]
|
||||||
|
:_ state
|
||||||
|
%+ give:ha ~
|
||||||
|
:* %initial
|
||||||
|
watching
|
||||||
|
==
|
||||||
|
==
|
||||||
|
[cards this]
|
||||||
|
::
|
||||||
|
++ on-poke
|
||||||
|
~/ %hark-chat-hook-poke
|
||||||
|
|= [=mark =vase]
|
||||||
|
^- (quip card _this)
|
||||||
|
|^
|
||||||
|
?> (team:title our.bowl src.bowl)
|
||||||
|
=^ cards state
|
||||||
|
?+ mark (on-poke:def mark vase)
|
||||||
|
%hark-chat-hook-action
|
||||||
|
(hark-chat-hook-action !<(action:hook vase))
|
||||||
|
==
|
||||||
|
[cards this]
|
||||||
|
::
|
||||||
|
++ hark-chat-hook-action
|
||||||
|
|= =action:hook
|
||||||
|
^- (quip card _state)
|
||||||
|
|^
|
||||||
|
?- -.action
|
||||||
|
%listen (listen +.action)
|
||||||
|
%ignore (ignore +.action)
|
||||||
|
%set-mentions (set-mentions +.action)
|
||||||
|
==
|
||||||
|
++ listen
|
||||||
|
|= chat=path
|
||||||
|
^- (quip card _state)
|
||||||
|
:- (give:ha ~[/updates] [%listen chat])
|
||||||
|
state(watching (~(put in watching) chat))
|
||||||
|
::
|
||||||
|
++ ignore
|
||||||
|
|= chat=path
|
||||||
|
^- (quip card _state)
|
||||||
|
:- (give:ha ~[/updates] [%ignore chat])
|
||||||
|
state(watching (~(del in watching) chat))
|
||||||
|
::
|
||||||
|
++ set-mentions
|
||||||
|
|= ment=?
|
||||||
|
^- (quip card _state)
|
||||||
|
:- (give:ha ~[/updates] [%set-mentions ment])
|
||||||
|
state(mentions ment)
|
||||||
|
--
|
||||||
|
--
|
||||||
|
::
|
||||||
|
++ on-agent
|
||||||
|
~/ %hark-chat-hook-agent
|
||||||
|
|= [=wire =sign:agent:gall]
|
||||||
|
^- (quip card _this)
|
||||||
|
|^
|
||||||
|
?+ -.sign (on-agent:def wire sign)
|
||||||
|
%kick
|
||||||
|
:_ this
|
||||||
|
?. ?=([%chat ~] wire)
|
||||||
|
~
|
||||||
|
~[watch-chat:ha]
|
||||||
|
::
|
||||||
|
%fact
|
||||||
|
?. ?=(%chat-update p.cage.sign)
|
||||||
|
(on-agent:def wire sign)
|
||||||
|
=^ cards state
|
||||||
|
(chat-update !<(update:chat-store q.cage.sign))
|
||||||
|
[cards this]
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ chat-update
|
||||||
|
|= =update:chat-store
|
||||||
|
^- (quip card _state)
|
||||||
|
:_ state
|
||||||
|
?+ -.update ~
|
||||||
|
%message (process-envelope path.update envelope.update)
|
||||||
|
::
|
||||||
|
%messages
|
||||||
|
%- zing
|
||||||
|
(turn envelopes.update (cury process-envelope path.update))
|
||||||
|
==
|
||||||
|
++ is-mention
|
||||||
|
|= [=path =envelope:chat-store]
|
||||||
|
?. ?=(%text -.letter.envelope) %.n
|
||||||
|
?& mentions
|
||||||
|
?= ^
|
||||||
|
(find (scow %p our.bowl) (trip text.letter.envelope))
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ is-notification
|
||||||
|
|= [=path =envelope:chat-store]
|
||||||
|
?& (~(has in watching) path)
|
||||||
|
!=(author.envelope our.bowl)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ process-envelope
|
||||||
|
|= [=path =envelope:chat-store]
|
||||||
|
^- (list card)
|
||||||
|
=/ mention=?
|
||||||
|
(is-mention path envelope)
|
||||||
|
?. ?|(mention (is-notification path envelope))
|
||||||
|
~
|
||||||
|
=/ =index:store
|
||||||
|
[%chat path mention]
|
||||||
|
=/ =contents:store
|
||||||
|
[%chat ~[envelope]]
|
||||||
|
~[(poke-store %add index when.envelope %.n contents)]
|
||||||
|
::
|
||||||
|
++ poke-store
|
||||||
|
|= =action:store
|
||||||
|
^- card
|
||||||
|
=/ =cage
|
||||||
|
hark-action+!>(action)
|
||||||
|
[%pass /store %agent [our.bowl %hark-store] %poke cage]
|
||||||
|
--
|
||||||
|
::
|
||||||
|
++ on-peek on-peek:def
|
||||||
|
::
|
||||||
|
++ on-leave on-leave:def
|
||||||
|
++ on-arvo on-arvo:def
|
||||||
|
++ on-fail on-fail:def
|
||||||
|
--
|
||||||
|
|_ =bowl:gall
|
||||||
|
::
|
||||||
|
::
|
||||||
|
++ give
|
||||||
|
|= [paths=(list path) =update:hook]
|
||||||
|
^- (list card)
|
||||||
|
[%give %fact paths hark-chat-hook-update+!>(update)]~
|
||||||
|
::
|
||||||
|
++ watch-chat
|
||||||
|
^- card
|
||||||
|
[%pass /chat %agent [our.bowl %chat-store] %watch /updates]
|
||||||
|
--
|
@ -236,6 +236,10 @@
|
|||||||
|= [existing=notification:store new=notification:store]
|
|= [existing=notification:store new=notification:store]
|
||||||
^- notification:store
|
^- notification:store
|
||||||
?- -.contents.existing
|
?- -.contents.existing
|
||||||
|
::
|
||||||
|
%chat
|
||||||
|
?> ?=(%chat -.contents.new)
|
||||||
|
existing(list.contents (weld list.contents.existing list.contents.new))
|
||||||
::
|
::
|
||||||
%graph
|
%graph
|
||||||
?> ?=(%graph -.contents.new)
|
?> ?=(%graph -.contents.new)
|
||||||
|
@ -24,6 +24,6 @@
|
|||||||
<div id="portal-root"></div>
|
<div id="portal-root"></div>
|
||||||
<script src="/~landscape/js/channel.js"></script>
|
<script src="/~landscape/js/channel.js"></script>
|
||||||
<script src="/~landscape/js/session.js"></script>
|
<script src="/~landscape/js/session.js"></script>
|
||||||
<script src="/~landscape/js/bundle/index.20c57e72701ff6afa573.js"></script>
|
<script src="/~landscape/js/bundle/index.5f9890df6be59a4ff9c5.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
33
pkg/arvo/lib/hark/chat-hook.hoon
Normal file
33
pkg/arvo/lib/hark/chat-hook.hoon
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/- sur=hark-chat-hook
|
||||||
|
^?
|
||||||
|
=< [. sur]
|
||||||
|
=, sur
|
||||||
|
|%
|
||||||
|
::
|
||||||
|
++ dejs
|
||||||
|
=, dejs:format
|
||||||
|
|%
|
||||||
|
::
|
||||||
|
++ action
|
||||||
|
%- of
|
||||||
|
:~ listen+pa
|
||||||
|
ignore+pa
|
||||||
|
set-mentions+bo
|
||||||
|
==
|
||||||
|
--
|
||||||
|
::
|
||||||
|
++ enjs
|
||||||
|
=, enjs:format
|
||||||
|
|%
|
||||||
|
::
|
||||||
|
++ update
|
||||||
|
|= upd=^update
|
||||||
|
%+ frond -.upd
|
||||||
|
?- -.upd
|
||||||
|
?(%listen %ignore) (path chat.upd)
|
||||||
|
%set-mentions b+mentions.upd
|
||||||
|
%initial a+(turn ~(tap in watching.upd) path)
|
||||||
|
==
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
/- sur=hark-store, post
|
/- sur=hark-store, post
|
||||||
/+ resource, graph-store, group-store
|
/+ resource, graph-store, group-store, chat-store
|
||||||
^?
|
^?
|
||||||
=< [. sur]
|
=< [. sur]
|
||||||
=, sur
|
=, sur
|
||||||
@ -11,6 +11,13 @@
|
|||||||
%- of
|
%- of
|
||||||
:~ graph+graph-index
|
:~ graph+graph-index
|
||||||
group+group-index
|
group+group-index
|
||||||
|
chat+chat-index
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ chat-index
|
||||||
|
%- ot
|
||||||
|
:~ chat+pa
|
||||||
|
mention+bo
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ group-index
|
++ group-index
|
||||||
@ -106,7 +113,16 @@
|
|||||||
?- -.index
|
?- -.index
|
||||||
%graph (graph-index +.index)
|
%graph (graph-index +.index)
|
||||||
%group (group-index +.index)
|
%group (group-index +.index)
|
||||||
|
%chat (chat-index +.index)
|
||||||
==
|
==
|
||||||
|
::
|
||||||
|
++ chat-index
|
||||||
|
|= [chat=^path mention=?]
|
||||||
|
^- json
|
||||||
|
%- pairs
|
||||||
|
:~ chat+(path chat)
|
||||||
|
mention+b+mention
|
||||||
|
==
|
||||||
::
|
::
|
||||||
++ graph-index
|
++ graph-index
|
||||||
|= [group=resource graph=resource module=@t description=@t]
|
|= [group=resource graph=resource module=@t description=@t]
|
||||||
@ -144,8 +160,15 @@
|
|||||||
?- -.contents
|
?- -.contents
|
||||||
%graph (graph-contents +.contents)
|
%graph (graph-contents +.contents)
|
||||||
%group (group-contents +.contents)
|
%group (group-contents +.contents)
|
||||||
|
%chat (chat-contents +.contents)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
|
++ chat-contents
|
||||||
|
|= =(list envelope:chat-store)
|
||||||
|
^- json
|
||||||
|
:- %a
|
||||||
|
(turn list envelope:enjs:chat-store)
|
||||||
|
::
|
||||||
++ graph-contents
|
++ graph-contents
|
||||||
|= =(list post:post)
|
|= =(list post:post)
|
||||||
^- json
|
^- json
|
||||||
|
13
pkg/arvo/mar/hark/chat-hook-action.hoon
Normal file
13
pkg/arvo/mar/hark/chat-hook-action.hoon
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/+ *hark-chat-hook
|
||||||
|
|_ act=action
|
||||||
|
++ grad %noun
|
||||||
|
++ grow
|
||||||
|
|%
|
||||||
|
++ noun act
|
||||||
|
--
|
||||||
|
++ grab
|
||||||
|
|%
|
||||||
|
++ noun action
|
||||||
|
++ json action:dejs
|
||||||
|
--
|
||||||
|
--
|
16
pkg/arvo/mar/hark/chat-hook-update.hoon
Normal file
16
pkg/arvo/mar/hark/chat-hook-update.hoon
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/+ *hark-chat-hook
|
||||||
|
|_ upd=update
|
||||||
|
++ grad %noun
|
||||||
|
++ grow
|
||||||
|
|%
|
||||||
|
++ noun upd
|
||||||
|
++ json
|
||||||
|
%+ frond:enjs:format
|
||||||
|
%hark-chat-hook-update
|
||||||
|
(update:enjs upd)
|
||||||
|
--
|
||||||
|
++ grab
|
||||||
|
|%
|
||||||
|
++ noun update
|
||||||
|
--
|
||||||
|
--
|
16
pkg/arvo/sur/hark-chat-hook.hoon
Normal file
16
pkg/arvo/sur/hark-chat-hook.hoon
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
^?
|
||||||
|
|%
|
||||||
|
::
|
||||||
|
+$ action
|
||||||
|
$% [?(%listen %ignore) chat=path]
|
||||||
|
[%set-mentions mentions=?]
|
||||||
|
==
|
||||||
|
::
|
||||||
|
+$ update
|
||||||
|
$%
|
||||||
|
action
|
||||||
|
$: %initial
|
||||||
|
watching=(set path)
|
||||||
|
==
|
||||||
|
==
|
||||||
|
--
|
@ -1,10 +1,11 @@
|
|||||||
/- *resource, graph-store, post, group-store, metadata-store
|
/- *resource, graph-store, post, group-store, metadata-store, chat-store
|
||||||
^?
|
^?
|
||||||
|%
|
|%
|
||||||
::
|
::
|
||||||
+$ index
|
+$ index
|
||||||
$% [%graph group=resource graph=resource module=@t description=@t]
|
$% [%graph group=resource graph=resource module=@t description=@t]
|
||||||
[%group group=resource description=@t]
|
[%group group=resource description=@t]
|
||||||
|
[%chat chat=path mention=?]
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
+$ group-contents
|
+$ group-contents
|
||||||
@ -19,6 +20,7 @@
|
|||||||
+$ contents
|
+$ contents
|
||||||
$% [%graph =(list post:post)]
|
$% [%graph =(list post:post)]
|
||||||
[%group =(list group-contents)]
|
[%group =(list group-contents)]
|
||||||
|
[%chat =(list envelope:chat-store)]
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
+$ timebox
|
+$ timebox
|
||||||
|
@ -2,6 +2,7 @@ import BaseApi from "./base";
|
|||||||
import { StoreState } from "../store/type";
|
import { StoreState } from "../store/type";
|
||||||
import { dateToDa, decToUd } from "../lib/util";
|
import { dateToDa, decToUd } from "../lib/util";
|
||||||
import {NotifIndex} from "~/types";
|
import {NotifIndex} from "~/types";
|
||||||
|
import { BigInteger } from 'big-integer';
|
||||||
|
|
||||||
export class HarkApi extends BaseApi<StoreState> {
|
export class HarkApi extends BaseApi<StoreState> {
|
||||||
private harkAction(action: any): Promise<any> {
|
private harkAction(action: any): Promise<any> {
|
||||||
@ -15,6 +16,10 @@ export class HarkApi extends BaseApi<StoreState> {
|
|||||||
private groupHookAction(action: any) {
|
private groupHookAction(action: any) {
|
||||||
return this.action("hark-group-hook", "hark-group-hook-action", action);
|
return this.action("hark-group-hook", "hark-group-hook-action", action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private chatHookAction(action: any) {
|
||||||
|
return this.action("hark-chat-hook", "hark-chat-hook-action", action);
|
||||||
|
}
|
||||||
|
|
||||||
private actOnNotification(frond: string, intTime: BigInteger, index: NotifIndex) {
|
private actOnNotification(frond: string, intTime: BigInteger, index: NotifIndex) {
|
||||||
const time = decToUd(intTime.toString());
|
const time = decToUd(intTime.toString());
|
||||||
@ -26,12 +31,11 @@ export class HarkApi extends BaseApi<StoreState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private graphHookAction(action: any) {
|
async setMentions(mentions: boolean) {
|
||||||
return this.action("hark-graph-hook", "hark-graph-hook-action", action);
|
await this.graphHookAction({
|
||||||
}
|
'set-mentions': mentions
|
||||||
|
});
|
||||||
setMentions(mentions: boolean) {
|
return this.chatHookAction({
|
||||||
return this.graphHookAction({
|
|
||||||
'set-mentions': mentions
|
'set-mentions': mentions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -73,6 +77,9 @@ export class HarkApi extends BaseApi<StoreState> {
|
|||||||
const { group } = index.group;
|
const { group } = index.group;
|
||||||
return this.ignoreGroup(group);
|
return this.ignoreGroup(group);
|
||||||
}
|
}
|
||||||
|
if('chat' in index) {
|
||||||
|
return this.ignoreChat(index.chat);
|
||||||
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +90,9 @@ export class HarkApi extends BaseApi<StoreState> {
|
|||||||
if('group' in index) {
|
if('group' in index) {
|
||||||
return this.listenGroup(index.group.group);
|
return this.listenGroup(index.group.group);
|
||||||
}
|
}
|
||||||
|
if('chat' in index) {
|
||||||
|
return this.listenChat(index.chat);
|
||||||
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +108,12 @@ export class HarkApi extends BaseApi<StoreState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignoreChat(chat: string) {
|
||||||
|
return this.chatHookAction({
|
||||||
|
ignore: chat
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
listenGroup(group: string) {
|
listenGroup(group: string) {
|
||||||
return this.groupHookAction({
|
return this.groupHookAction({
|
||||||
@ -111,6 +127,12 @@ export class HarkApi extends BaseApi<StoreState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listenChat(chat: string) {
|
||||||
|
return this.chatHookAction({
|
||||||
|
listen: chat
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getTimeSubset(start?: Date, end?: Date) {
|
async getTimeSubset(start?: Date, end?: Date) {
|
||||||
const s = start ? dateToDa(start) : "-";
|
const s = start ? dateToDa(start) : "-";
|
||||||
const e = end ? dateToDa(end) : "-";
|
const e = end ? dateToDa(end) : "-";
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
Notifications,
|
Notifications,
|
||||||
Notification,
|
|
||||||
NotifIndex,
|
NotifIndex,
|
||||||
NotificationGraphConfig,
|
NotificationGraphConfig,
|
||||||
GroupNotificationsConfig,
|
GroupNotificationsConfig,
|
||||||
@ -14,6 +13,7 @@ type HarkState = {
|
|||||||
notificationsCount: number;
|
notificationsCount: number;
|
||||||
notificationsGraphConfig: NotificationGraphConfig;
|
notificationsGraphConfig: NotificationGraphConfig;
|
||||||
notificationsGroupConfig: GroupNotificationsConfig;
|
notificationsGroupConfig: GroupNotificationsConfig;
|
||||||
|
notificationsChatConfig: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HarkReducer = (json: any, state: HarkState) => {
|
export const HarkReducer = (json: any, state: HarkState) => {
|
||||||
@ -36,8 +36,39 @@ export const HarkReducer = (json: any, state: HarkState) => {
|
|||||||
groupListen(groupHookData, state);
|
groupListen(groupHookData, state);
|
||||||
groupIgnore(groupHookData, state);
|
groupIgnore(groupHookData, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chatHookData = _.get(json, "hark-chat-hook-update", false);
|
||||||
|
if(chatHookData) {
|
||||||
|
|
||||||
|
chatInitial(chatHookData, state);
|
||||||
|
chatListen(chatHookData, state);
|
||||||
|
chatIgnore(chatHookData, state);
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function chatInitial(json: any, state: HarkState) {
|
||||||
|
const data = _.get(json, "initial", false);
|
||||||
|
if (data) {
|
||||||
|
state.notificationsChatConfig = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function chatListen(json: any, state: HarkState) {
|
||||||
|
const data = _.get(json, "listen", false);
|
||||||
|
if (data) {
|
||||||
|
state.notificationsChatConfig = [...state.notificationsChatConfig, data];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chatIgnore(json: any, state: HarkState) {
|
||||||
|
const data = _.get(json, "ignore", false);
|
||||||
|
if (data) {
|
||||||
|
state.notificationsChatConfig = state.notificationsChatConfig.filter(x => x !== data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function groupInitial(json: any, state: HarkState) {
|
function groupInitial(json: any, state: HarkState) {
|
||||||
const data = _.get(json, "initial", false);
|
const data = _.get(json, "initial", false);
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -177,6 +208,9 @@ function notifIdxEqual(a: NotifIndex, b: NotifIndex) {
|
|||||||
a.group.group === b.group.group &&
|
a.group.group === b.group.group &&
|
||||||
a.group.description === b.group.description
|
a.group.description === b.group.description
|
||||||
);
|
);
|
||||||
|
} else if ("chat" in a && "chat" in b) {
|
||||||
|
return a.chat.chat === b.chat.chat &&
|
||||||
|
a.chat.mention === b.chat.mention;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,7 @@ export default class GlobalStore extends BaseStore<StoreState> {
|
|||||||
notifications: new BigIntOrderedMap<Timebox>(),
|
notifications: new BigIntOrderedMap<Timebox>(),
|
||||||
archivedNotifications: new BigIntOrderedMap<Timebox>(),
|
archivedNotifications: new BigIntOrderedMap<Timebox>(),
|
||||||
notificationsGroupConfig: [],
|
notificationsGroupConfig: [],
|
||||||
|
notificationsChatConfig: [],
|
||||||
notificationsGraphConfig: {
|
notificationsGraphConfig: {
|
||||||
watchOnSelf: false,
|
watchOnSelf: false,
|
||||||
mentions: false,
|
mentions: false,
|
||||||
|
@ -62,6 +62,7 @@ export interface StoreState {
|
|||||||
notifications: Notifications;
|
notifications: Notifications;
|
||||||
notificationsGraphConfig: NotificationGraphConfig;
|
notificationsGraphConfig: NotificationGraphConfig;
|
||||||
notificationsGroupConfig: GroupNotificationsConfig;
|
notificationsGroupConfig: GroupNotificationsConfig;
|
||||||
|
notificationsChatConfig: string[];
|
||||||
notificationsCount: number,
|
notificationsCount: number,
|
||||||
doNotDisturb: boolean;
|
doNotDisturb: boolean;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ export default class GlobalSubscription extends BaseSubscription<StoreState> {
|
|||||||
this.subscribe('/updates', 'hark-store');
|
this.subscribe('/updates', 'hark-store');
|
||||||
this.subscribe('/updates', 'hark-graph-hook');
|
this.subscribe('/updates', 'hark-graph-hook');
|
||||||
this.subscribe('/updates', 'hark-group-hook');
|
this.subscribe('/updates', 'hark-group-hook');
|
||||||
|
this.subscribe('/updates', 'hark-chat-hook');
|
||||||
}
|
}
|
||||||
|
|
||||||
restart() {
|
restart() {
|
||||||
|
@ -2,6 +2,7 @@ import _ from "lodash";
|
|||||||
import { Post } from "./graph-update";
|
import { Post } from "./graph-update";
|
||||||
import { GroupUpdate } from "./group-update";
|
import { GroupUpdate } from "./group-update";
|
||||||
import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap";
|
import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap";
|
||||||
|
import { Envelope } from './chat-update';
|
||||||
|
|
||||||
type GraphNotifDescription = "link" | "comment";
|
type GraphNotifDescription = "link" | "comment";
|
||||||
|
|
||||||
@ -17,17 +18,26 @@ export interface GroupNotifIndex {
|
|||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChatNotifIndex {
|
||||||
|
chat: string;
|
||||||
|
mention: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type NotifIndex =
|
export type NotifIndex =
|
||||||
| { graph: GraphNotifIndex }
|
| { graph: GraphNotifIndex }
|
||||||
| { group: GroupNotifIndex };
|
| { group: GroupNotifIndex }
|
||||||
|
| { chat: ChatNotifIndex };
|
||||||
|
|
||||||
export type GraphNotificationContents = Post[];
|
export type GraphNotificationContents = Post[];
|
||||||
|
|
||||||
export type GroupNotificationContents = GroupUpdate[];
|
export type GroupNotificationContents = GroupUpdate[];
|
||||||
|
|
||||||
|
export type ChatNotificationContents = Envelope[];
|
||||||
|
|
||||||
export type NotificationContents =
|
export type NotificationContents =
|
||||||
| { graph: GraphNotificationContents }
|
| { graph: GraphNotificationContents }
|
||||||
| { group: GroupNotificationContents };
|
| { group: GroupNotificationContents }
|
||||||
|
| { chat: ChatNotificationContents };
|
||||||
|
|
||||||
interface Notification {
|
interface Notification {
|
||||||
read: boolean;
|
read: boolean;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useCallback } from "react";
|
import React, { useRef, useCallback, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { RouteComponentProps } from "react-router-dom";
|
||||||
import { Col } from "@tlon/indigo-react";
|
import { Col } from "@tlon/indigo-react";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -94,6 +94,15 @@ export function ChatResource(props: ChatResourceProps) {
|
|||||||
station,
|
station,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const scrollTo = new URLSearchParams(location.search).get('msg');
|
||||||
|
useEffect(() => {
|
||||||
|
const clear = () => {
|
||||||
|
props.history.replace(location.pathname);
|
||||||
|
}
|
||||||
|
setTimeout(clear, 10000);
|
||||||
|
return clear;
|
||||||
|
}, [station]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col {...bind} height="100%" overflow="hidden" position="relative">
|
<Col {...bind} height="100%" overflow="hidden" position="relative">
|
||||||
{dragging && <SubmitDragger />}
|
{dragging && <SubmitDragger />}
|
||||||
@ -118,6 +127,7 @@ export function ChatResource(props: ChatResourceProps) {
|
|||||||
hideNicknames={props.hideNicknames}
|
hideNicknames={props.hideNicknames}
|
||||||
hideAvatars={props.hideAvatars}
|
hideAvatars={props.hideAvatars}
|
||||||
location={props.location}
|
location={props.location}
|
||||||
|
scrollTo={scrollTo ? parseInt(scrollTo, 10) : undefined}
|
||||||
/>
|
/>
|
||||||
<ChatInput
|
<ChatInput
|
||||||
ref={chatInput}
|
ref={chatInput}
|
||||||
|
@ -31,8 +31,8 @@ export const DayBreak = ({ when }) => (
|
|||||||
interface ChatMessageProps {
|
interface ChatMessageProps {
|
||||||
measure(element): void;
|
measure(element): void;
|
||||||
msg: Envelope | IMessage;
|
msg: Envelope | IMessage;
|
||||||
previousMsg: Envelope | IMessage | undefined;
|
previousMsg?: Envelope | IMessage;
|
||||||
nextMsg: Envelope | IMessage | undefined;
|
nextMsg?: Envelope | IMessage;
|
||||||
isLastRead: boolean;
|
isLastRead: boolean;
|
||||||
group: Group;
|
group: Group;
|
||||||
association: Association;
|
association: Association;
|
||||||
@ -48,6 +48,7 @@ interface ChatMessageProps {
|
|||||||
unreadMarkerRef: React.RefObject<HTMLDivElement>;
|
unreadMarkerRef: React.RefObject<HTMLDivElement>;
|
||||||
history: any;
|
history: any;
|
||||||
api: any;
|
api: any;
|
||||||
|
highlighted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ChatMessage extends Component<ChatMessageProps> {
|
export default class ChatMessage extends Component<ChatMessageProps> {
|
||||||
@ -84,7 +85,8 @@ export default class ChatMessage extends Component<ChatMessageProps> {
|
|||||||
isLastMessage,
|
isLastMessage,
|
||||||
unreadMarkerRef,
|
unreadMarkerRef,
|
||||||
history,
|
history,
|
||||||
api
|
api,
|
||||||
|
highlighted
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const renderSigil = Boolean((nextMsg && msg.author !== nextMsg.author) || !nextMsg || msg.number === 1);
|
const renderSigil = Boolean((nextMsg && msg.author !== nextMsg.author) || !nextMsg || msg.number === 1);
|
||||||
@ -115,7 +117,8 @@ export default class ChatMessage extends Component<ChatMessageProps> {
|
|||||||
isPending,
|
isPending,
|
||||||
history,
|
history,
|
||||||
api,
|
api,
|
||||||
scrollWindow
|
scrollWindow,
|
||||||
|
highlighted
|
||||||
};
|
};
|
||||||
|
|
||||||
const unreadContainerStyle = {
|
const unreadContainerStyle = {
|
||||||
@ -124,6 +127,7 @@ export default class ChatMessage extends Component<ChatMessageProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
bg={highlighted ? 'washedBlue' : 'white'}
|
||||||
width='100%'
|
width='100%'
|
||||||
display='flex'
|
display='flex'
|
||||||
flexWrap='wrap'
|
flexWrap='wrap'
|
||||||
@ -165,6 +169,8 @@ interface MessageProps {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class MessageWithSigil extends PureComponent<MessageProps> {
|
export class MessageWithSigil extends PureComponent<MessageProps> {
|
||||||
|
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
msg,
|
msg,
|
||||||
@ -176,8 +182,8 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
|
|||||||
hideAvatars,
|
hideAvatars,
|
||||||
remoteContentPolicy,
|
remoteContentPolicy,
|
||||||
measure,
|
measure,
|
||||||
history,
|
|
||||||
api,
|
api,
|
||||||
|
history,
|
||||||
scrollWindow
|
scrollWindow
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -185,8 +191,8 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
|
|||||||
const contact = msg.author in contacts ? contacts[msg.author] : false;
|
const contact = msg.author in contacts ? contacts[msg.author] : false;
|
||||||
const showNickname = !hideNicknames && contact && contact.nickname;
|
const showNickname = !hideNicknames && contact && contact.nickname;
|
||||||
const name = showNickname ? contact.nickname : cite(msg.author);
|
const name = showNickname ? contact.nickname : cite(msg.author);
|
||||||
const color = contact ? `#${uxToHex(contact.color)}` : '#000000';
|
const color = contact ? `#${uxToHex(contact.color)}` : this.isDark ? '#000000' :'#FFFFFF'
|
||||||
const sigilClass = contact ? '' : 'mix-blend-diff';
|
const sigilClass = contact ? '' : this.isDark ? 'mix-blend-diff' : 'mix-blend-darken';
|
||||||
|
|
||||||
let nameSpan = null;
|
let nameSpan = null;
|
||||||
|
|
||||||
@ -213,7 +219,7 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
|
|||||||
scrollWindow={scrollWindow}
|
scrollWindow={scrollWindow}
|
||||||
history={history}
|
history={history}
|
||||||
api={api}
|
api={api}
|
||||||
className="fl pr3 v-top bg-white bg-gray0-d pt1"
|
className="fl pr3 v-top pt1"
|
||||||
/>
|
/>
|
||||||
<Box flexGrow='1' display='block' className="clamp-message">
|
<Box flexGrow='1' display='block' className="clamp-message">
|
||||||
<Box
|
<Box
|
||||||
@ -239,7 +245,7 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
|
|||||||
<Text flexShrink='0' gray mono className="v-mid">{timestamp}</Text>
|
<Text flexShrink='0' gray mono className="v-mid">{timestamp}</Text>
|
||||||
<Text gray mono ml={2} className="v-mid child dn-s">{datestamp}</Text>
|
<Text gray mono ml={2} className="v-mid child dn-s">{datestamp}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box fontSize='14px'><MessageContent content={msg.letter} remoteContentPolicy={remoteContentPolicy} measure={measure} /></Box>
|
<Box fontSize='14px'><MessageContent content={msg.letter} remoteContentPolicy={remoteContentPolicy} measure={measure} /></Box>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -43,6 +43,7 @@ type ChatWindowProps = RouteComponentProps<{
|
|||||||
hideNicknames: boolean;
|
hideNicknames: boolean;
|
||||||
hideAvatars: boolean;
|
hideAvatars: boolean;
|
||||||
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
|
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
|
||||||
|
scrollTo?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatWindowState {
|
interface ChatWindowState {
|
||||||
@ -84,6 +85,10 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
|||||||
window.addEventListener('focus', this.handleWindowFocus);
|
window.addEventListener('focus', this.handleWindowFocus);
|
||||||
this.initialFetch();
|
this.initialFetch();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if(this.props.scrollTo) {
|
||||||
|
this.scrollToUnread();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ initialized: true });
|
this.setState({ initialized: true });
|
||||||
}, this.INITIALIZATION_MAX_TIME);
|
}, this.INITIALIZATION_MAX_TIME);
|
||||||
}
|
}
|
||||||
@ -167,8 +172,9 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrollToUnread() {
|
scrollToUnread() {
|
||||||
const { mailboxSize, unreadCount } = this.props;
|
const { mailboxSize, unreadCount, scrollTo } = this.props;
|
||||||
this.virtualList?.scrollToData(mailboxSize - unreadCount);
|
const target = scrollTo || (mailboxSize - unreadCount);
|
||||||
|
this.virtualList?.scrollToData(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissUnread() {
|
dismissUnread() {
|
||||||
@ -297,7 +303,8 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
|||||||
const isPending: boolean = 'pending' in msg && Boolean(msg.pending);
|
const isPending: boolean = 'pending' in msg && Boolean(msg.pending);
|
||||||
const isLastMessage: boolean = Boolean(index === lastMessage)
|
const isLastMessage: boolean = Boolean(index === lastMessage)
|
||||||
const isLastRead: boolean = Boolean(!isLastMessage && index === this.state.lastRead);
|
const isLastRead: boolean = Boolean(!isLastMessage && index === this.state.lastRead);
|
||||||
const props = { measure, scrollWindow, isPending, isLastRead, isLastMessage, msg, ...messageProps };
|
const highlighted = index === this.props.scrollTo;
|
||||||
|
const props = { measure, highlighted, scrollWindow, isPending, isLastRead, isLastMessage, msg, ...messageProps };
|
||||||
return (
|
return (
|
||||||
<ChatMessage
|
<ChatMessage
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -87,6 +87,10 @@ h2 {
|
|||||||
mix-blend-mode: difference;
|
mix-blend-mode: difference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mix-blend-darken {
|
||||||
|
mix-blend-mode: darken;
|
||||||
|
}
|
||||||
|
|
||||||
.placeholder-inter::placeholder {
|
.placeholder-inter::placeholder {
|
||||||
font-family: "Inter", sans-serif;
|
font-family: "Inter", sans-serif;
|
||||||
}
|
}
|
||||||
|
99
pkg/interface/src/views/apps/notifications/chat.tsx
Normal file
99
pkg/interface/src/views/apps/notifications/chat.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import React, { useCallback } from "react";
|
||||||
|
import _ from "lodash";
|
||||||
|
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(mention: boolean, lent: number) {
|
||||||
|
const msg = pluralize("message", lent !== 1);
|
||||||
|
if (mention) {
|
||||||
|
return `mentioned you in ${msg} in`;
|
||||||
|
}
|
||||||
|
return `sent ${msg} 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 { chat, mention } = index;
|
||||||
|
const association = props.associations.chat[chat];
|
||||||
|
const groupPath = association["group-path"];
|
||||||
|
const appPath = association["app-path"];
|
||||||
|
|
||||||
|
const group = props.groups[groupPath];
|
||||||
|
|
||||||
|
const desc = describeNotification(mention, 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={chat}
|
||||||
|
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;
|
time: number;
|
||||||
read: boolean;
|
read: boolean;
|
||||||
associations: Associations;
|
associations: Associations;
|
||||||
|
chat?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { description, channel, group, moduleIcon, read } = props;
|
const { description, channel, group, moduleIcon, read } = props;
|
||||||
const contacts = props.contacts[group] || {};
|
const contacts = props.contacts[group] || {};
|
||||||
@ -70,8 +71,9 @@ export function Header(props: {
|
|||||||
const groupTitle =
|
const groupTitle =
|
||||||
props.associations.contacts?.[props.group]?.metadata?.title || props.group;
|
props.associations.contacts?.[props.group]?.metadata?.title || props.group;
|
||||||
|
|
||||||
|
const app = props.chat ? 'chat' : 'graph';
|
||||||
const channelTitle =
|
const channelTitle =
|
||||||
(channel && props.associations.graph?.[channel]?.metadata?.title) ||
|
(channel && props.associations?.[app]?.[channel]?.metadata?.title) ||
|
||||||
channel;
|
channel;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -12,7 +12,7 @@ import { Associations } from "~/types";
|
|||||||
|
|
||||||
type DatedTimebox = [BigInteger, Timebox];
|
type DatedTimebox = [BigInteger, Timebox];
|
||||||
|
|
||||||
function filterNotification(groups: string[]) {
|
function filterNotification(associations: Associations, groups: string[]) {
|
||||||
if (groups.length === 0) {
|
if (groups.length === 0) {
|
||||||
return () => true;
|
return () => true;
|
||||||
}
|
}
|
||||||
@ -23,6 +23,9 @@ function filterNotification(groups: string[]) {
|
|||||||
} else if ("group" in n.index) {
|
} else if ("group" in n.index) {
|
||||||
const { group } = n.index.group;
|
const { group } = n.index.group;
|
||||||
return groups.findIndex((g) => group === g) !== -1;
|
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;
|
return true;
|
||||||
};
|
};
|
||||||
@ -56,7 +59,7 @@ export default function Inbox(props: {
|
|||||||
const notificationsByDay = f.flow(
|
const notificationsByDay = f.flow(
|
||||||
f.map<DatedTimebox>(([date, nots]) => [
|
f.map<DatedTimebox>(([date, nots]) => [
|
||||||
date,
|
date,
|
||||||
nots.filter(filterNotification(props.filter)),
|
nots.filter(filterNotification(associations, props.filter)),
|
||||||
]),
|
]),
|
||||||
f.groupBy<DatedTimebox>(([date]) =>
|
f.groupBy<DatedTimebox>(([date]) =>
|
||||||
moment(daToUnix(date)).format("DDMMYYYY")
|
moment(daToUnix(date)).format("DDMMYYYY")
|
||||||
@ -75,6 +78,7 @@ export default function Inbox(props: {
|
|||||||
associations={props.associations}
|
associations={props.associations}
|
||||||
graphConfig={props.notificationsGraphConfig}
|
graphConfig={props.notificationsGraphConfig}
|
||||||
groupConfig={props.notificationsGroupConfig}
|
groupConfig={props.notificationsGroupConfig}
|
||||||
|
chatConfig={props.notificationsChatConfig}
|
||||||
api={api}
|
api={api}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -92,6 +96,7 @@ export default function Inbox(props: {
|
|||||||
api={api}
|
api={api}
|
||||||
graphConfig={props.notificationsGraphConfig}
|
graphConfig={props.notificationsGraphConfig}
|
||||||
groupConfig={props.notificationsGroupConfig}
|
groupConfig={props.notificationsGroupConfig}
|
||||||
|
chatConfig={props.notificationsChatConfig}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@ -119,6 +124,7 @@ function DaySection({
|
|||||||
api,
|
api,
|
||||||
groupConfig,
|
groupConfig,
|
||||||
graphConfig,
|
graphConfig,
|
||||||
|
chatConfig,
|
||||||
}) {
|
}) {
|
||||||
const calendar = latest
|
const calendar = latest
|
||||||
? MOMENT_CALENDAR_DATE
|
? MOMENT_CALENDAR_DATE
|
||||||
@ -143,6 +149,7 @@ function DaySection({
|
|||||||
<Notification
|
<Notification
|
||||||
graphConfig={graphConfig}
|
graphConfig={graphConfig}
|
||||||
groupConfig={groupConfig}
|
groupConfig={groupConfig}
|
||||||
|
chatConfig={chatConfig}
|
||||||
api={api}
|
api={api}
|
||||||
associations={associations}
|
associations={associations}
|
||||||
notification={not}
|
notification={not}
|
||||||
|
@ -8,12 +8,14 @@ import {
|
|||||||
NotificationGraphConfig,
|
NotificationGraphConfig,
|
||||||
GroupNotificationsConfig,
|
GroupNotificationsConfig,
|
||||||
NotifIndex,
|
NotifIndex,
|
||||||
Associations
|
Associations,
|
||||||
} from "~/types";
|
} from "~/types";
|
||||||
import GlobalApi from "~/logic/api/global";
|
import GlobalApi from "~/logic/api/global";
|
||||||
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
||||||
import { GroupNotification } from "./group";
|
import { GroupNotification } from "./group";
|
||||||
import { GraphNotification } from "./graph";
|
import { GraphNotification } from "./graph";
|
||||||
|
import { ChatNotification } from "./chat";
|
||||||
|
import { BigInteger } from "big-integer";
|
||||||
|
|
||||||
interface NotificationProps {
|
interface NotificationProps {
|
||||||
notification: IndexedNotification;
|
notification: IndexedNotification;
|
||||||
@ -23,12 +25,14 @@ interface NotificationProps {
|
|||||||
archived: boolean;
|
archived: boolean;
|
||||||
graphConfig: NotificationGraphConfig;
|
graphConfig: NotificationGraphConfig;
|
||||||
groupConfig: GroupNotificationsConfig;
|
groupConfig: GroupNotificationsConfig;
|
||||||
|
chatConfig: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMuted(
|
function getMuted(
|
||||||
idx: NotifIndex,
|
idx: NotifIndex,
|
||||||
groups: GroupNotificationsConfig,
|
groups: GroupNotificationsConfig,
|
||||||
graphs: NotificationGraphConfig
|
graphs: NotificationGraphConfig,
|
||||||
|
chat: string[]
|
||||||
) {
|
) {
|
||||||
if ("graph" in idx) {
|
if ("graph" in idx) {
|
||||||
const { graph } = idx.graph;
|
const { graph } = idx.graph;
|
||||||
@ -37,6 +41,9 @@ function getMuted(
|
|||||||
if ("group" in idx) {
|
if ("group" in idx) {
|
||||||
return _.findIndex(groups || [], (g) => g === idx.group.group) === -1;
|
return _.findIndex(groups || [], (g) => g === idx.group.group) === -1;
|
||||||
}
|
}
|
||||||
|
if ("chat" in idx) {
|
||||||
|
return _.findIndex(chat || [], (c) => c === idx.chat) === -1;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +55,7 @@ function NotificationWrapper(props: {
|
|||||||
archived: boolean;
|
archived: boolean;
|
||||||
graphConfig: NotificationGraphConfig;
|
graphConfig: NotificationGraphConfig;
|
||||||
groupConfig: GroupNotificationsConfig;
|
groupConfig: GroupNotificationsConfig;
|
||||||
|
chatConfig: string[];
|
||||||
}) {
|
}) {
|
||||||
const { api, time, notif, children } = props;
|
const { api, time, notif, children } = props;
|
||||||
|
|
||||||
@ -55,7 +63,12 @@ function NotificationWrapper(props: {
|
|||||||
return api.hark.archive(time, notif.index);
|
return api.hark.archive(time, notif.index);
|
||||||
}, [time, notif]);
|
}, [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 onChangeMute = useCallback(async () => {
|
||||||
const func = isMuted ? "unmute" : "mute";
|
const func = isMuted ? "unmute" : "mute";
|
||||||
@ -84,19 +97,26 @@ export function Notification(props: NotificationProps) {
|
|||||||
const { notification, associations, archived } = props;
|
const { notification, associations, archived } = props;
|
||||||
const { read, contents, time } = notification.notification;
|
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) {
|
if ("graph" in notification.index) {
|
||||||
const index = notification.index.graph;
|
const index = notification.index.graph;
|
||||||
const c: GraphNotificationContents = (contents as any).graph;
|
const c: GraphNotificationContents = (contents as any).graph;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NotificationWrapper
|
<Wrapper>
|
||||||
archived={archived}
|
|
||||||
notif={notification}
|
|
||||||
time={props.time}
|
|
||||||
api={props.api}
|
|
||||||
graphConfig={props.graphConfig}
|
|
||||||
groupConfig={props.groupConfig}
|
|
||||||
>
|
|
||||||
<GraphNotification
|
<GraphNotification
|
||||||
api={props.api}
|
api={props.api}
|
||||||
index={index}
|
index={index}
|
||||||
@ -108,21 +128,14 @@ export function Notification(props: NotificationProps) {
|
|||||||
time={time}
|
time={time}
|
||||||
associations={associations}
|
associations={associations}
|
||||||
/>
|
/>
|
||||||
</NotificationWrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ("group" in notification.index) {
|
if ("group" in notification.index) {
|
||||||
const index = notification.index.group;
|
const index = notification.index.group;
|
||||||
const c: GroupNotificationContents = (contents as any).group;
|
const c: GroupNotificationContents = (contents as any).group;
|
||||||
return (
|
return (
|
||||||
<NotificationWrapper
|
<Wrapper>
|
||||||
archived={archived}
|
|
||||||
notif={notification}
|
|
||||||
time={props.time}
|
|
||||||
api={props.api}
|
|
||||||
graphConfig={props.graphConfig}
|
|
||||||
groupConfig={props.groupConfig}
|
|
||||||
>
|
|
||||||
<GroupNotification
|
<GroupNotification
|
||||||
api={props.api}
|
api={props.api}
|
||||||
index={index}
|
index={index}
|
||||||
@ -134,7 +147,27 @@ export function Notification(props: NotificationProps) {
|
|||||||
time={time}
|
time={time}
|
||||||
associations={associations}
|
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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
|
import _ from 'lodash';
|
||||||
import { Box, Col, Text, Row } from "@tlon/indigo-react";
|
import { Box, Col, Text, Row } from "@tlon/indigo-react";
|
||||||
import { Link, Switch, Route } from "react-router-dom";
|
import { Link, Switch, Route } from "react-router-dom";
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ export function FormikOnBlur<
|
|||||||
) {
|
) {
|
||||||
const { values } = formikBag;
|
const { values } = formikBag;
|
||||||
formikBag.submitForm().then(() => {
|
formikBag.submitForm().then(() => {
|
||||||
formikBag.resetForm({ values });
|
formikBag.resetForm({ values, touched: {} });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
@ -6,7 +6,7 @@ import { Dropdown } from "~/views/components/Dropdown";
|
|||||||
import { Association, NotificationGraphConfig } from "~/types";
|
import { Association, NotificationGraphConfig } from "~/types";
|
||||||
import GlobalApi from "~/logic/api/global";
|
import GlobalApi from "~/logic/api/global";
|
||||||
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
||||||
import {appIsGraph} from "~/logic/lib/util";
|
import { appIsGraph } from "~/logic/lib/util";
|
||||||
|
|
||||||
const ChannelMenuItem = ({
|
const ChannelMenuItem = ({
|
||||||
icon,
|
icon,
|
||||||
@ -29,7 +29,8 @@ const ChannelMenuItem = ({
|
|||||||
interface ChannelMenuProps {
|
interface ChannelMenuProps {
|
||||||
association: Association;
|
association: Association;
|
||||||
api: GlobalApi;
|
api: GlobalApi;
|
||||||
notificationConfig: NotificationGraphConfig;
|
graphNotificationConfig: NotificationGraphConfig;
|
||||||
|
chatNotificationConfig: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChannelMenu(props: ChannelMenuProps) {
|
export function ChannelMenu(props: ChannelMenuProps) {
|
||||||
@ -49,10 +50,19 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
|||||||
|
|
||||||
const isOurs = ship.slice(1) === window.ship;
|
const isOurs = ship.slice(1) === window.ship;
|
||||||
|
|
||||||
const isMuted =
|
const isMuted = appIsGraph(app)
|
||||||
props.notificationConfig.watching.findIndex((a) => a === appPath) === -1;
|
? props.graphNotificationConfig.watching.findIndex((a) => a === appPath) ===
|
||||||
|
-1
|
||||||
|
: props.chatNotificationConfig.findIndex((a) => a === appPath) === -1;
|
||||||
const onChangeMute = async () => {
|
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);
|
await api.hark[func](appPath);
|
||||||
};
|
};
|
||||||
const onUnsubscribe = useCallback(async () => {
|
const onUnsubscribe = useCallback(async () => {
|
||||||
@ -100,18 +110,16 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
|||||||
borderRadius={1}
|
borderRadius={1}
|
||||||
borderColor="lightGray"
|
borderColor="lightGray"
|
||||||
>
|
>
|
||||||
{appIsGraph(metadata.module) && (
|
<ChannelMenuItem color="blue" icon="Inbox">
|
||||||
<ChannelMenuItem color="blue" icon="Inbox">
|
<StatelessAsyncAction
|
||||||
<StatelessAsyncAction
|
m="2"
|
||||||
m="2"
|
bg="white"
|
||||||
bg="white"
|
name="notif"
|
||||||
name="notif"
|
onClick={onChangeMute}
|
||||||
onClick={onChangeMute}
|
>
|
||||||
>
|
{isMuted ? "Unmute" : "Mute"} this channel
|
||||||
{isMuted ? "Unmute" : "Mute"} this channel
|
</StatelessAsyncAction>
|
||||||
</StatelessAsyncAction>
|
</ChannelMenuItem>
|
||||||
</ChannelMenuItem>
|
|
||||||
)}
|
|
||||||
{isOurs ? (
|
{isOurs ? (
|
||||||
<>
|
<>
|
||||||
<ChannelMenuItem color="red" icon="TrashCan">
|
<ChannelMenuItem color="red" icon="TrashCan">
|
||||||
|
@ -54,6 +54,7 @@ export function Resource(props: ResourceProps) {
|
|||||||
render={(routeProps) => (
|
render={(routeProps) => (
|
||||||
<ResourceSkeleton
|
<ResourceSkeleton
|
||||||
notificationsGraphConfig={props.notificationsGraphConfig}
|
notificationsGraphConfig={props.notificationsGraphConfig}
|
||||||
|
notificationsChatConfig={props.notificationsChatConfig}
|
||||||
baseUrl={props.baseUrl}
|
baseUrl={props.baseUrl}
|
||||||
{...skelProps}
|
{...skelProps}
|
||||||
atRoot
|
atRoot
|
||||||
|
@ -99,7 +99,8 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
|
|||||||
</TruncatedBox>
|
</TruncatedBox>
|
||||||
<Box flexGrow={1} />
|
<Box flexGrow={1} />
|
||||||
<ChannelMenu
|
<ChannelMenu
|
||||||
notificationConfig={props.notificationsGraphConfig}
|
graphNotificationConfig={props.notificationsGraphConfig}
|
||||||
|
chatNotificationConfig={props.notificationsChatConfig}
|
||||||
association={association}
|
association={association}
|
||||||
api={api}
|
api={api}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user