hark: rework unread tracking, again

This commit is contained in:
Liam Fitzgerald 2020-12-01 15:46:28 +10:00
parent b7b4000986
commit 9ee62a2e55
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
17 changed files with 211 additions and 129 deletions

View File

@ -707,7 +707,7 @@
=/ older ?=(%older i.t.t.path)
=/ =ship (slav %p i.t.t.t.path)
=/ =term i.t.t.t.t.path
=/ count=@ud (slav %ud i.t.t.t.t.t.path)
=/ count=(unit @ud) (slaw %ud i.t.t.t.t.t.path)
=/ =index:store
(turn t.t.t.t.t.t.path (cury slav %ud))
=/ parent=index:store
@ -723,7 +723,9 @@
[ship term]
%- ~(gas by *(map index:store node:store))
%+ turn
%+ scag count
%- ?~ count same
|= =(list [atom node:store])
(slag u.count list)
%- ?:(older same flop)
%- tap:orm
%+ subset:orm u.graph
@ -745,7 +747,6 @@
=/ children
(get-node-children ship term index)
?~ children [~ ~]
~& count
:- ~ :- ~ :- %graph-update
!> ^- update:store
:+ %0

View File

@ -1,7 +1,7 @@
:: hark-graph-hook: notifications for graph-store [landscape]
::
/- store=hark-store, post, group-store, metadata-store, hook=hark-graph-hook
/+ resource, metadata, default-agent, dbug, graph-store
/+ resource, metadata, default-agent, dbug, graph-store, graph
::
::
~% %hark-graph-hook-top ..is ~
@ -49,6 +49,7 @@
ha ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
met ~(. metadata bowl)
gra ~(. graph bowl)
::
++ on-init
:_ this
@ -59,9 +60,21 @@
|= old=vase
^- (quip card _this)
:_ this(state !<(state-0 old))
?: (~(has by wex.bowl) [/graph our.bowl %graph-store])
~
~[watch-graph:ha]
%+ welp
?: (~(has by wex.bowl) [/graph our.bowl %graph-store])
~
~[watch-graph:ha]
%+ turn
^- (list mark)
:~ %graph-validator-chat
%graph-validator-link
%graph-validator-publish
==
|= =mark
^- card
=/ =wire /validator/[mark]
=/ =rave:clay [%sing %c [%da now.bowl] /[mark]/notification-kind]
[%pass wire %arvo %c %warp our.bowl [%home `rave]]
::
++ on-watch
|= =path
@ -146,32 +159,39 @@
[cards this]
==
++ add-graph
|= rid=resource
|= [rid=resource =graph:graph-store]
^- (quip card _state)
=^ cards state
(check-nodes (tap-deep:gra graph) rid)
:- cards
?. &(watch-on-self =(our.bowl entity.rid))
[~ state]
`state(watching (~(put in watching) [rid ~]))
state
state(watching (~(put in watching) [rid ~]))
::
++ graph-update
|= =update:graph-store
^- (quip card _state)
?: ?=(%add-graph -.q.update)
(add-graph resource.q.update)
(add-graph resource.q.update graph.q.update)
?. ?=(%add-nodes -.q.update)
[~ state]
=/ group=resource
(need (group-from-app-resource:met %graph resource.q.update))
=/ =metadata:metadata-store
(need (peek-metadata:met %graph group resource.q.update))
=* rid resource.q.update
(check-nodes ~(tap by nodes.q.update) rid)
::
++ check-nodes
|= $: nodes=(list [p=index:graph-store q=node:graph-store])
rid=resource
==
=/ group=resource
(need (group-from-app-resource:met %graph rid))
=/ =metadata:metadata-store
(need (peek-metadata:met %graph group rid))
=+ %+ scry:ha
,mark=(unit mark)
/gx/graph-store/graph-mark/(scot %p entity.rid)/[name.rid]/noun
=+ %+ scry:ha
,=tube:clay
/cc/[q.byk.bowl]/[(fall mark %graph-validator-link)]/notification-kind
=/ nodes=(list [p=index:graph-store q=node:graph-store])
~(tap by nodes.q.update)
=| cards=(list card)
|^
?~ nodes
@ -208,7 +228,7 @@
^- (quip card _state)
=^ child-cards state
(check-node-children node tube)
=+ !< notif-kind=(unit [name=@t parent-lent=@ud mode=?(%each %since)])
=+ !< notif-kind=(unit [name=@t parent-lent=@ud mode=?(%each %count)])
(tube !>([0 post.node]))
?~ notif-kind
[child-cards state]
@ -252,13 +272,13 @@
++ self-post
|= $: =node:graph-store
=index:store
mode=?(%since %each)
mode=?(%count %each)
==
^- (quip card _state)
=| cards=(list card)
=? cards ?=(%since mode)
=? cards ?=(%count mode)
:_ cards
(poke-hark %read-since index index.post.node)
(poke-hark %read-count index)
?. ?=(%.y watch-on-self)
[cards state]
:- cards
@ -271,9 +291,9 @@
hark-action+!>(action)
::
++ update-unread-count
|= [mode=?(%since %each) =index:store time=@da ref=index:graph-store]
?: ?=(%since mode)
(poke-hark %unread-since index time)
|= [mode=?(%count %each) =index:store time=@da ref=index:graph-store]
?: ?=(%count mode)
(poke-hark %unread-count index time)
(poke-hark %unread-each index ref time)
::
++ add-unread
@ -286,7 +306,17 @@
++ on-peek on-peek:def
::
++ on-leave on-leave:def
++ on-arvo on-arvo:def
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card _this)
?+ wire (on-arvo:def wire sign-arvo)
::
[%validator @ ~]
:_ this
=* validator i.t.wire
=/ =rave:clay [%next %c [%da now.bowl] /[validator]/notification-kind]
[%pass wire %arvo %c %warp our.bowl [%home `rave]]~
==
++ on-fail on-fail:def
--

View File

@ -10,8 +10,8 @@
:: Usefull for non-linear, low-volume applications, i.e. blogs,
:: collections
::
/- store=hark-store, post, group-store, metadata-store
/+ resource, metadata, default-agent, dbug, graph-store
/- post, group-store, metadata-store
/+ resource, metadata, default-agent, dbug, graph-store, graphl=graph, verb, store=hark-store
::
::
~% %hark-store-top ..is ~
@ -27,7 +27,7 @@
+$ state-1
$: %1
unreads-each=(jug index:store index:graph-store)
unreads-since=(map index:store index:graph-store)
unreads-count=(map index:store @ud)
last-seen=(map index:store @da)
=notifications:store
archive=notifications:store
@ -52,6 +52,7 @@
=* state -
::
=<
%+ verb |
%- agent:dbug
^- agent:gall
~% %hark-store-agent ..card ~
@ -60,6 +61,7 @@
ha ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
met ~(. metadata bowl)
gra ~(. graphl bowl)
::
++ on-init
:_ this
@ -101,6 +103,7 @@
++ initial-updates
^- update:store
:- %more
=- ~&(- -)
^- (list update:store)
:+ give-unreads
[%set-dnd dnd]
@ -115,11 +118,12 @@
++ give-since-unreads
^- (list [index:store index-stats:store])
%+ turn
~(tap by unreads-since)
|= [=index:store since=index:graph-store]
~(tap by unreads-count)
|= [=index:store count=@ud]
?> ?=(%graph -.index)
:* index
~(wyt in (~(gut by by-index) index ~))
[%since since]
[%count count]
(~(gut by last-seen) index *time)
==
++ give-each-unreads
@ -186,6 +190,7 @@
::
++ hark-action
|= =action:store
~& -.action
^- (quip card _state)
|^
?- -.action
@ -195,8 +200,8 @@
%read-each (read-each +.action)
%unread-each (unread-each +.action)
::
%read-since (read-since +.action)
%unread-since (unread-since +.action)
%read-count (read-count +.action)
%unread-count (unread-count +.action)
::
%read-note (read-note +.action)
%unread-note (unread-note +.action)
@ -218,7 +223,7 @@
?~ existing-notif
notification
(merge-notification:ha u.existing-notif notification)
=. read.new %.y
=. read.new %.n
=/ new-timebox=timebox:store
(~(put by timebox) index new)
:- (give:ha [/updates]~ %added current-timebox index new)
@ -316,15 +321,15 @@
notifications (change-read-status:ha time index %.n)
==
::
++ read-since
|= [=index:store since=index:graph-store]
++ read-count
|= =index:store
^- (quip card _state)
=^ cards state
(read-index index)
:- %+ weld cards
(give:ha [/updates]~ %read-since index since)
(give:ha [/updates]~ %read-count index)
%_ state
unreads-since (~(put by unreads-since) index since)
unreads-count (~(put by unreads-count) index 0)
==
::
++ read-boxes
@ -351,12 +356,15 @@
^- (quip card _state)
`state
::
++ unread-since
++ unread-count
|= [=index:store time=@da]
^- (quip card _state)
:- (give:ha [/updates]~ %unread-since index time)
:- (give:ha [/updates]~ %unread-count index time)
=/ curr=@ud
(~(gut by unreads-count) index 0)
%_ state
last-seen (~(put by last-seen) index time)
unreads-count (~(put by unreads-count) index +(curr))
==
::
++ seen

View File

@ -17,6 +17,17 @@
%+ scry-for update:store
/graph/(scot %p entity.res)/[name.res]
::
++ gut-younger-node-siblings
|= [res=resource =index:store]
^- (map index:store node:store)
=+ %+ scry-for ,=update:store
%+ weld
/node-siblings/younger/(scot %p entity.res)/[name.res]/all
(turn index (cury scot %ud))
?> ?=(%0 -.update)
?> ?=(%add-nodes -.q.update)
nodes.q.update
::
++ got-node
|= [res=resource =index:store]
^- node:store
@ -53,4 +64,27 @@
?> ?=(%0 -.update)
?> ?=(%keys -.q.update)
resources.q.update
::
++ tap-deep
|= =graph:store
^- (list [index:store node:store])
=| =index:store
=/ nodes=(list [atom node:store])
(tap:orm:store graph)
|- =* tap-nodes $
^- (list [index:store node:store])
%- zing
%+ turn
nodes
|= [=atom =node:store]
^- (list [index:store node:store])
%+ welp
^- (list [index:store node:store])
[(snoc index atom) node]~
?. ?=(%graph -.children.node)
~
%_ tap-nodes
index (snoc index atom)
nodes (tap:orm:store p.children.node)
==
--

View File

@ -69,7 +69,7 @@
read-note+notif-ref
add-note+add
set-dnd+bo
read-since+read-graph-index
read-count+index
read-each+read-graph-index
==
--
@ -88,10 +88,10 @@
%set-dnd b+dnd.upd
%count (numb count.upd)
%more (more +.upd)
%read-each (read-graph +.upd)
%read-since (read-graph +.upd)
%read-each (read-each +.upd)
%read-count (index +.upd)
%unread-each (unread-each +.upd)
%unread-since (unread-since +.upd)
%unread-count (unread-count +.upd)
%unreads (unreads +.upd)
::
?(%archive %read-note %unread-note)
@ -106,7 +106,7 @@
%+ turn l
|= [idx=^index stats=^index-stats]
%- pairs
:~ unread+(numb unread)
:~ stats+(index-stats stats)
index+(index idx)
==
::
@ -115,8 +115,10 @@
%+ frond
-.unreads
?- -.unreads
%since (index:enjs:graph-store index.unreads)
%each a+(turn ~(tap by indices.unreads) index:enjs:graph-store)
::
%count
(numb num.unreads)
==
::
++ index-stats
@ -257,7 +259,7 @@
(indexed-notification index notification)
==
::
++ read-graph
++ read-each
|= [=^index target=index:graph-store]
%- pairs
:~ index+(^index index)
@ -272,7 +274,7 @@
last+(time tim)
==
::
++ unread-since
++ unread-count
|= [=^index tim=@da]
%- pairs
:~ index+(^index index)

View File

@ -5,7 +5,7 @@
++ noun i
++ notification-kind
?+ index.p.i ~
[@ ~] `[%message 0]
[@ ~] `[%message 0 %count]
==
--
++ grab

View File

@ -6,7 +6,7 @@
++ notification-kind
?+ index.p.i ~
[@ ~] `[%link 0 %each]
[@ @ @ ~] `[%comment 1 %since]
[@ @ @ ~] `[%comment 1 %count]
==
--
++ grab

View File

@ -9,7 +9,7 @@
++ notification-kind
?+ index.p.i ~
[@ %1 @ ~] `[%note 0 %each]
[@ %2 @ @ ~] `[%comment 1 %since]
[@ %2 @ @ ~] `[%comment 1 %count]
==
--
++ grab

View File

@ -61,8 +61,8 @@
$% [%add-note =index =notification]
[%archive time=@da index]
::
[%unread-since =index time=@da]
[%read-since =index =index:graph-store]
[%unread-count =index =time]
[%read-count =index]
::
[%unread-each =index ref=index:graph-store time=@da]
[%read-each index ref=index:graph-store]
@ -82,7 +82,7 @@
[notifications=@ud =unreads last-seen=@da]
::
+$ unreads
$% [%since =index:graph-store]
$% [%count num=@ud]
[%each indices=(set index:graph-store)]
==
::

View File

@ -58,7 +58,7 @@ export class HarkApi extends BaseApi<StoreState> {
}
read(time: BigInteger, index: NotifIndex) {
return this.actOnNotification('read', time, index);
return this.actOnNotification('read-note', time, index);
}
readIndex(index: NotifIndex) {
@ -68,21 +68,20 @@ export class HarkApi extends BaseApi<StoreState> {
}
unread(time: BigInteger, index: NotifIndex) {
return this.actOnNotification('unread', time, index);
return this.actOnNotification('unread-note', time, index);
}
markSinceAsRead(association: Association, parent: string, description: GraphNotifDescription, since: string) {
markCountAsRead(association: Association, parent: string, description: GraphNotifDescription) {
return this.harkAction(
{ 'read-since': {
index: { graph: {
{ 'read-count': {
graph: {
graph: association['app-path'],
group: association['group-path'],
module: association.metadata.module,
description,
index: parent
} },
target: since
}});
});
}

View File

@ -150,16 +150,18 @@ function readEach(json: any, state: HarkState) {
}
function readSince(json: any, state: HarkState) {
const data = _.get(json, 'read-since');
const data = _.get(json, 'read-count');
if(data) {
updateUnreadSince(state, data.index, data.target)
updateUnreadCount(state, data, () => 0)
}
}
function unreadSince(json: any, state: HarkState) {
const data = _.get(json, 'unread-since');
const data = _.get(json, 'unread-count');
console.log(data);
if(data) {
updateNotificationStats(state, data.index, 'last', () => data.last)
updateUnreadCount(state, data.index, u => u + 1);
}
}
@ -174,12 +176,13 @@ function unreadEach(json: any, state: HarkState) {
function unreads(json: any, state: HarkState) {
const data = _.get(json, 'unreads');
if(data) {
console.log(data);
data.forEach(({ index, stats }) => {
const { unreads, notifications, last } = stats;
updateNotificationStats(state, index, 'notifications', x => x + notifications);
updateNotificationStats(state, index, 'last', () => last);
if('since' in unreads) {
updateUnreadSince(state, index, unreads.since);
if('count' in unreads) {
updateUnreadCount(state, index, () => unreads.count);
} else {
unreads.each.forEach((u: string) => {
updateUnreads(state, index, s => s.add(u));
@ -189,11 +192,13 @@ function unreads(json: any, state: HarkState) {
}
}
function updateUnreadSince(state: HarkState, index: NotifIndex, since: string) {
function updateUnreadCount(state: HarkState, index: NotifIndex, count: (c: number) => number) {
if(!('graph' in index)) {
return;
}
_.set(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], since);
const property = [index.graph.graph, index.graph.index, 'unreads'];
const curr = _.get(state.unreads.graph, property, 0);
_.set(state.unreads.graph, property, count(curr));
}
function updateUnreads(state: HarkState, index: NotifIndex, f: (us: Set<string>) => void) {

View File

@ -7,7 +7,7 @@ import { Envelope } from './chat-update';
export type GraphNotifDescription = "link" | "comment" | "note" | "mention";
export interface UnreadStats {
unreads: Set<string>;
unreads: Set<string> | { index: string; count: number; };
notifications: number;
last: number;
}

View File

@ -56,7 +56,7 @@ export function ChatResource(props: ChatResourceProps) {
envelopes.length > 0) ||
false;
const unreadCount = length - read;
const unreadCount = props.unreads.graph?.[station]?.['/']?.unreads || 0;
const unreadMsg = unreadCount > 0 && envelopes[unreadCount - 1];
const [,, owner, name] = station.split('/');
@ -66,7 +66,7 @@ export function ChatResource(props: ChatResourceProps) {
const chatInput = useRef<ChatInput>();
useEffect(() => {
props.api.graph.getNewest(owner, name, 10);
props.api.graph.getNewest(owner, name, 20);
}, [station]);
const onFileDrag = useCallback(
@ -121,7 +121,7 @@ export function ChatResource(props: ChatResourceProps) {
isChatLoading={isChatLoading}
isChatUnsynced={isChatUnsynced}
graph={graph}
unreadCount={0}
unreadCount={unreadCount}
unreadMsg={false}
envelopes={[]}
contacts={contacts}

View File

@ -51,7 +51,7 @@ interface ChatWindowState {
fetchPending: boolean;
idle: boolean;
initialized: boolean;
lastRead: number;
unreadIndex: BigInteger;
}
export default class ChatWindow extends Component<ChatWindowProps, ChatWindowState> {
@ -60,19 +60,23 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
private prevSize = 0;
private loadedNewest = false;
private loadedOldest = false;
private unreadIndex: BigInteger | null = null;
INITIALIZATION_MAX_TIME = 1500;
constructor(props) {
constructor(props: ChatWindowProps) {
super(props);
this.state = {
fetchPending: false,
idle: true,
initialized: false,
lastRead: props.unreadCount ? props.mailboxSize - props.unreadCount : -1,
unreadIndex: bigInt.zero
};
this.calculateUnreadIndex();
this.dismissUnread = this.dismissUnread.bind(this);
this.scrollToUnread = this.scrollToUnread.bind(this);
this.handleWindowBlur = this.handleWindowBlur.bind(this);
@ -102,6 +106,17 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
window.removeEventListener('focus', this.handleWindowFocus);
}
calculateUnreadIndex() {
const { graph, unreadCount } = this.props;
const unreadIndex = graph.keys()[unreadCount];
if(!unreadIndex) {
return;
}
this.setState({
unreadIndex
})
}
handleWindowBlur() {
this.setState({ idle: true });
}
@ -127,13 +142,16 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
this.stayLockedIfActive();
}*/
/*if (unreadCount > prevProps.unreadCount && this.state.idle) {
this.setState({
lastRead: unreadCount ? mailboxSize - unreadCount : -1,
});
}*/
if (unreadCount > prevProps.unreadCount && this.state.idle) {
this.calculateUnreadIndex();
}
if(this.prevSize !== graph.size) {
if(this.state.unreadIndex.eq(bigInt.zero)) {
this.calculateUnreadIndex();
this.scrollToUnread();
}
this.prevSize = graph.size;
this.virtualList?.calculateVisibleItems();
}
@ -145,15 +163,12 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
if (!this.state.fetchPending && prevState.fetchPending) {
this.virtualList?.calculateVisibleItems();
}
*/
if (station !== prevProps.station) {
this.virtualList?.resetScroll();
this.scrollToUnread();
this.setState({
lastRead: unreadCount ? mailboxSize - unreadCount : -1,
});
this.calculateUnreadIndex();
}
*/
}
stayLockedIfActive() {
@ -164,16 +179,19 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
}
scrollToUnread() {
/*
const { mailboxSize, unreadCount, scrollTo } = this.props;
const target = scrollTo || (mailboxSize - unreadCount);
this.virtualList?.scrollToData(target);
*/
const { unreadIndex } = this.state;
if(unreadIndex.eq(bigInt.zero)) {
return;
}
this.virtualList?.scrollToData(unreadIndex);
}
dismissUnread() {
const { association } = this.props;
if (this.state.fetchPending) return;
if (this.props.unreadCount === 0) return;
//this.props.api.hark.markCountAsRead(association, '/', 'message');
//this.props.api.chat.read(this.props.station);
//this.props.api.hark.readIndex({ chat: { chat: this.props.station, mention: false }});
}
@ -234,7 +252,6 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
const {
stationPendingMessages,
unreadCount,
unreadMsg,
isChatLoading,
isChatUnsynced,
api,
@ -258,12 +275,14 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
const messageProps = { association, group, contacts, hideAvatars, hideNicknames, remoteContentPolicy, unreadMarkerRef, history, api };
const keys = graph.keys().reverse();
const unreadIndex = keys[this.props.unreadCount];
const unreadMsg = unreadIndex && graph.get(unreadIndex);
return (
<>
<UnreadNotice
unreadCount={unreadCount}
unreadMsg={unreadCount === 1 && unreadMsg && unreadMsg.author === window.ship ? false : unreadMsg}
unreadMsg={unreadCount === 1 && unreadMsg && unreadMsg?.post.author === window.ship ? false : unreadMsg}
dismissUnread={this.dismissUnread}
onClick={this.scrollToUnread}
/>
@ -281,19 +300,21 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
data={graph}
size={graph.size}
renderer={({ index, measure, scrollWindow }) => {
const msg = graph.get(index)!.post;
const msg = graph.get(index)?.post;
if (!msg) return null;
if (!this.state.initialized) {
return <MessagePlaceholder key={index.toString()} height="64px" index={index} />;
}
const isPending: boolean = 'pending' in msg && Boolean(msg.pending);
const isLastMessage: boolean = Boolean(index.eq(bigInt(lastMessage)));
const isLastRead: boolean = Boolean(!isLastMessage && index.eq(bigInt(this.state.lastRead)));
const highlighted = bigInt(this.props.scrollTo || -1).eq(index);
const props = { measure, highlighted, scrollWindow, isPending, isLastRead, isLastMessage, msg, ...messageProps };
const graphIdx = keys.findIndex(idx => idx.eq(index));
const prevIdx = keys[graphIdx+1];
const nextIdx = keys[graphIdx-1];
const isLastRead: boolean = this.state.unreadIndex.eq(index);
const props = { measure, highlighted, scrollWindow, isPending, isLastRead, isLastMessage, msg, ...messageProps };
return (
<ChatMessage
key={index.toString()}

View File

@ -9,8 +9,8 @@ export const UnreadNotice = (props) => {
return null;
}
let datestamp = moment.unix(unreadMsg.when / 1000).format('YYYY.M.D');
const timestamp = moment.unix(unreadMsg.when / 1000).format('HH:mm');
let datestamp = moment.unix(unreadMsg.post['time-sent'] / 1000).format('YYYY.M.D');
const timestamp = moment.unix(unreadMsg.post['time-sent'] / 1000).format('HH:mm');
if (datestamp === moment().format('YYYY.M.D')) {
datestamp = null;
@ -55,4 +55,4 @@ export const UnreadNotice = (props) => {
</Box>
</Box>
);
}
}

View File

@ -20,22 +20,13 @@ interface UnjoinedResourceProps {
inbox: Inbox;
}
function isJoined(app: string, path: string) {
function isJoined(path: string) {
return function (
props: Pick<UnjoinedResourceProps, "inbox" | "graphKeys">
props: Pick<UnjoinedResourceProps, "graphKeys">
) {
const graphKey = path.substr(7);
switch (app) {
case "link":
case "publish":
return props.graphKeys.has(graphKey);
case "chat":
return !!props.inbox[path];
default:
console.log("Bad app name");
return false;
}
};
return props.graphKeys.has(graphKey);
}
}
export function UnjoinedResource(props: UnjoinedResourceProps) {
@ -48,26 +39,14 @@ export function UnjoinedResource(props: UnjoinedResourceProps) {
const app = useMemo(() => module || appName, [props.association]);
const onJoin = async () => {
let ship, name;
switch (app) {
case "publish":
case "link":
[, , ship, name] = appPath.split("/");
await api.graph.joinGraph(ship, name);
break;
case "chat":
[, ship, name] = appPath.split("/");
await api.chat.join(ship, appPath, true);
break;
default:
throw new Error("Unknown resource type");
}
await waiter(isJoined(app, appPath));
const [, , ship, name] = appPath.split("/");
await api.graph.joinGraph(ship, name);
await waiter(isJoined(appPath));
history.push(`${props.baseUrl}/resource/${app}${appPath}`);
};
useEffect(() => {
if (isJoined(app, appPath)({ inbox, graphKeys })) {
if (isJoined(appPath)({ graphKeys })) {
history.push(`${props.baseUrl}/resource/${app}${appPath}`);
}
}, [props.association, inbox, graphKeys, notebooks]);

View File

@ -170,14 +170,17 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
const firstVisibleKey = visibleItems.peekSmallest()?.[0] ?? this.estimateIndexFromScrollTop(scrollTop)!;
if (data.peekSmallest()![0].eq(firstVisibleKey)) {
const smallest = data.peekSmallest();
if (smallest && smallest[0].eq(firstVisibleKey)) {
this.loadRows(false);
}
const lastVisibleKey =
visibleItems.peekLargest()?.[0]
?? bigInt(this.estimateIndexFromScrollTop(scrollTop + windowHeight)!);
if (data.peekLargest()![0].eq(lastVisibleKey)) {
const largest = data.peekLargest();
if (largest && largest[0].eq(lastVisibleKey)) {
this.loadRows(true);
}
onCalculateVisibleItems ? onCalculateVisibleItems(visibleItems) : null;