Merge pull request #2447 from urbit/la-unsubscribed-state

chat-hook: added ability to subscribe to chat-hook state from the front-end
This commit is contained in:
ixv 2020-03-11 23:36:42 -07:00 committed by GitHub
commit 33c422fca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 187 additions and 92 deletions

View File

@ -20,7 +20,7 @@
==
+$ state-0 [%0 state-base]
+$ state-base
$: synced=(map path ship)
$: =synced
invite-created=_|
allow-history=(map path ?)
==
@ -95,12 +95,6 @@
(hookup-group new-chat kind.newp)
[(record-group new-chat new-chat)]~
(recreate-chat host old-chat new-chat)
::
?. =(our.bol host) ~
?: ?=(%white kind.newp)
(send-invites new-chat ~(tap in who.newp))
%+ send-invites new-chat
(parse-subscribers wex.bol old-chat)
==
::
++ recreate-chat
@ -109,7 +103,8 @@
=/ old-mailbox=mailbox
(need (scry:cc (unit mailbox) %chat-store [%mailbox chat]))
=* enves envelopes.old-mailbox
:~ (chat-poke:cc [%delete chat])
:~ (chat-poke:cc [%delete new-chat])
(chat-poke:cc [%delete chat])
(chat-poke:cc [%create new-chat])
(chat-poke:cc [%messages new-chat enves])
(chat-poke:cc [%read new-chat])
@ -218,31 +213,6 @@
%metadata-action
!> ^- metadata-action
[%add group [%chat chat] metadata]
::
++ send-invites
|= [chat=path who=(list ship)]
^- (list card)
%+ murn who
|= =ship
^- (unit card)
?: =(our.bol ship) ~
%- some
%^ make-poke %invite-hook
%invite-action
!> ^- invite-action
=/ =invite
=+ (crip "upgrade {(spud chat)} (please accept in OS1)")
[our.bol %chat-hook chat ship -]
[%invite /chat (sham chat ship eny.bol) invite]
::
++ parse-subscribers
|= [=boat:agent:gall old-chat=path]
^- (list ship)
%+ murn ~(tap in boat)
|= [[=wire sub=ship app=term] [acked=? =path]]
^- (unit ship)
?. =(old-chat path) ~
`sub
--
::
++ on-poke
@ -268,6 +238,7 @@
?+ path (on-watch:def path)
[%backlog *] [(watch-backlog:cc t.path) this]
[%mailbox *] [(watch-mailbox:cc t.path) this]
[%synced *] [(watch-synced:cc t.path) this]
==
::
++ on-agent
@ -351,12 +322,15 @@
%add-owned
?> (team:title our.bol src.bol)
=/ chat-path [%mailbox path.act]
=/ chat-wire [%store path.act]
?: (~(has by synced) path.act) [~ state]
=: synced (~(put by synced) path.act our.bol)
allow-history (~(put by allow-history) path.act allow-history.act)
==
:_ state
[%pass chat-path %agent [our.bol %chat-store] %watch chat-path]~
:~ [%pass chat-wire %agent [our.bol %chat-store] %watch chat-path]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
::
%add-synced
?> (team:title our.bol src.bol)
@ -372,21 +346,31 @@
%+ weld path.act
?~(mailbox /0 /(scot %ud (lent envelopes.u.mailbox)))
:_ state
[%pass chat-history %agent [ship.act %chat-hook] %watch chat-history]~
:~ [%pass chat-history %agent [ship.act %chat-hook] %watch chat-history]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
::
%remove
=/ ship (~(get by synced) path.act)
?~ ship [~ state]
?: &(!=(u.ship src.bol) ?!((team:title our.bol src.bol)))
[~ state]
:_ state(synced (~(del by synced) path.act))
=. synced (~(del by synced) path.act)
:_ state
%- zing
:~ (pull-wire [%backlog (weld path.act /0)])
(pull-wire [%mailbox path.act])
[%give %kick ~[[%mailbox path.act]] ~]~
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]~
==
==
::
++ watch-synced
|= pax=path
^- (list card)
?> (team:title our.bol src.bol)
[%give %fact ~ %chat-hook-update !>([%initial synced])]~
::
++ watch-mailbox
|= pax=path
^- (list card)
@ -519,8 +503,11 @@
?+ -.fact [~ state]
%delete
?. (~(has by synced) path.fact) [~ state]
:_ state(synced (~(del by synced) path.fact))
[%pass [%mailbox path.fact] %agent [our.bol %chat-store] %leave ~]~
=. synced (~(del by synced) path.fact)
:_ state
:~ [%pass [%mailbox path.fact] %agent [our.bol %chat-store] %leave ~]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
::
%message
:_ state
@ -548,9 +535,12 @@
=/ shp (~(get by synced) path.fact)
?~ shp [~ state]
?. =(u.shp src.bol) [~ state]
:_ state(synced (~(del by synced) path.fact))
=. synced (~(del by synced) path.fact)
:_ state
:- (chat-poke [%delete path.fact])
[%pass [%mailbox path.fact] %agent [src.bol %chat-hook] %leave ~]~
:~ [%pass [%mailbox path.fact] %agent [src.bol %chat-hook] %leave ~]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
::
%message
:_ state
@ -577,6 +567,14 @@
[%pass /permissions %agent [our.bol %permission-store] %watch /updates]~
::
?+ wir !!
[%store @ *]
~& store-kick+wir
?. (~(has by synced) t.wir) [~ state]
~& %chat-store-resubscribe
=/ mailbox=(unit mailbox) (chat-scry t.wir)
:_ state
[%pass wir %agent [our.bol %chat-store] %watch [%mailbox t.wir]]~
::
[%mailbox @ *]
~& mailbox-kick+wir
?. (~(has by synced) t.wir) [~ state]
@ -605,13 +603,21 @@
|= [wir=wire saw=(unit tang)]
^- (quip card _state)
?~ saw [~ state]
?> ?=(^ wir)
:_ state(synced (~(del by synced) t.wir))
%. ~
%- slog
:* leaf+"chat-hook failed subscribe on {(spud t.wir)}"
leaf+"stack trace:"
u.saw
?+ wir [~ state]
[%store @ *]
[~ state(synced (~(del by synced) t.wir))]
::
[%backlog @ @ @ *]
=/ pax `path`(oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir)
=. synced (~(del by synced) pax)
:_ state
:- [%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
%. ~
%- slog
:* leaf+"chat-hook failed subscribe on {(spud pax)}"
leaf+"stack trace:"
u.saw
==
==
::
++ chat-poke

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
/- *chat-store, *chat-view
/- *chat-store, *chat-hook, *chat-view
/+ chat-eval
|%
::
@ -118,6 +118,17 @@
[%config (conf config.mailbox)]
==
::
++ hook-update-to-json
|= upd=chat-hook-update
=, enjs:format
^- json
%+ frond %chat-hook-update
%- pairs
%+ turn ~(tap by synced.upd)
|= [pax=^path shp=^ship]
^- [cord json]
[(spat pax) s+(scot %p shp)]
::
++ update-to-json
|= upd=chat-update
=, enjs:format
@ -209,6 +220,33 @@
::
--
::
++ json-to-hook-action
|= jon=json
^- chat-hook-action
=, dejs:format
=< (parse-json jon)
|%
++ parse-json
%- of
:~ [%add-owned add-owned]
[%add-synced add-synced]
[%remove pa]
==
::
++ add-owned
%- ot
:~ [%path pa]
[%allow-history bo]
==
::
++ add-synced
%- ot
:~ [%ship (su ;~(pfix sig fed:ag))]
[%path pa]
[%ask-history bo]
==
--
::
++ json-to-view-action
|= jon=json
^- chat-view-action

View File

@ -1,40 +1,11 @@
/- *chat-hook
=, dejs:format
/+ *chat-json
|_ act=chat-hook-action
++ grab
|%
++ noun chat-hook-action
++ json
|= jon=^json
=< (parse-chat-hook-action jon)
|%
++ parse-chat-hook-action
%- of
:~
[%add-owned add-owned]
[%add-synced add-synced]
[%remove pa]
==
::
++ add-owned
%- ot
:~ [%path pa]
[%security sec]
[%allow-history bo]
==
::
++ add-synced
%- ot
:~ [%ship (su ;~(pfix sig fed:ag))]
[%path pa]
[%ask-history bo]
==
::
++ sec
^- $-(^json rw-security)
(su (perk %channel %village %journal %mailbox ~))
::
--
(json-to-hook-action jon)
--
--

View File

@ -0,0 +1,13 @@
/+ *chat-json
|_ upd=chat-hook-update
++ grow
|%
++ json (hook-update-to-json upd)
--
::
++ grab
|%
++ noun chat-hook-update
--
::
--

View File

@ -1,5 +1,6 @@
/- *rw-security
|%
+$ synced (map path ship)
+$ chat-hook-action
$% :: %add-owned: make a chatroom accessible to foreign ships
::
@ -12,4 +13,6 @@
::
[%remove =path]
==
::
+$ chat-hook-update [%initial =synced]
--

View File

@ -26,6 +26,10 @@ class UrbitApi {
join: this.chatViewJoin.bind(this),
};
this.chatHook = {
addSynced: this.chatHookAddSynced.bind(this)
};
this.invite = {
accept: this.inviteAccept.bind(this),
decline: this.inviteDecline.bind(this)
@ -124,6 +128,17 @@ class UrbitApi {
this.chatAction({ read: { path } });
}
chatHookAddSynced(ship, path, askHistory) {
return this.action("chat-hook", "chat-hook-action", {
'add-synced': {
ship,
path,
'ask-history': askHistory
}
});
}
chatViewAction(data) {
return this.action("chat-view", "json", data);
}

View File

@ -5,6 +5,7 @@ import _ from 'lodash';
import { Route, Link } from "react-router-dom";
import { store } from "/store";
import { ResubscribeElement } from '/components/lib/resubscribe-element';
import { Message } from '/components/lib/message';
import { SidebarSwitcher } from '/components/lib/icons/icon-sidebar-switch.js';
import { ChatTabBar } from '/components/lib/chat-tabbar';
@ -44,11 +45,6 @@ export class ChatScreen extends Component {
componentDidUpdate(prevProps, prevState) {
const { props, state } = this;
const station =
props.match.params[1] === undefined ?
`/${props.match.params.ship}/${props.match.params.station}` :
`/${props.match.params[1]}/${props.match.params.ship}/${props.match.params.station}`;
if (
prevProps.match.params.station !== props.match.params.station ||
prevProps.match.params.ship !== props.match.params.ship
@ -58,10 +54,7 @@ export class ChatScreen extends Component {
clearInterval(this.updateReadInterval);
this.setState(
{
station: station,
scrollLocked: false
},
{ scrollLocked: false },
() => {
this.scrollToBottom();
this.updateReadInterval = setInterval(
@ -71,7 +64,7 @@ export class ChatScreen extends Component {
this.updateReadNumber();
}
);
} else if (props.chatInitialized && !(station in props.inbox)) {
} else if (props.chatInitialized && !(props.station in props.inbox)) {
props.history.push("/~chat");
} else if (
props.envelopes.length - prevProps.envelopes.length >=
@ -268,6 +261,7 @@ export class ChatScreen extends Component {
numPeers={group.length}
isOwner={deSig(props.match.params.ship) === window.ship}
popout={this.props.popout}
api={props.api}
/>
</div>
<div
@ -278,7 +272,17 @@ export class ChatScreen extends Component {
ref={el => {
this.scrollElement = el;
}}></div>
{reversedMessages}
{ (
!(props.station in props.chatSynced) &&
(reversedMessages.length > 0)
) ? (
<ResubscribeElement
api={props.api}
host={props.match.params.ship}
station={props.station} />
) : (<div/>)
}
{reversedMessages}
</div>
<ChatInput
api={props.api}

View File

@ -4,7 +4,7 @@ import classnames from 'classnames';
export class ChatTabBar extends Component {
render() {
let props = this.props;

View File

@ -0,0 +1,31 @@
import React, { Component } from 'react';
import classnames from 'classnames';
export class ResubscribeElement extends Component {
onClickResubscribe() {
this.props.api.chatHook.addSynced(
this.props.host,
this.props.station,
true);
}
render() {
let props = this.props;
return (
<div className="db pa3 ma3 ba b--yellow2 bg-yellow0">
<p className="lh-copy db">
Your ship has been disconnected from the chat's host.
This may be due to a bad connection, going offline, lack of permission,
or an over-the-air update.
</p>
<a onClick={this.onClickResubscribe.bind(this)}
className="db underline black pointer mt3">
Reconnect to this chat
</a>
</div>
);
}
}

View File

@ -89,6 +89,7 @@ export class MemberScreen extends Component {
numPeers={perm.length}
isOwner={deSig(props.match.params.ship) === window.ship}
popout={this.props.popout}
api={props.api}
/>
</div>
<div className="w-100 pl3 mt0 mt4-m mt4-l mt4-xl cf pr6">

View File

@ -181,6 +181,7 @@ export class Root extends Component {
sidebar={renderChannelSidebar(props, station)}
>
<ChatScreen
chatSynced={state.chatSynced}
station={station}
association={association}
api={api}

View File

@ -249,6 +249,8 @@ export class SettingsScreen extends Component {
{...props}
station={props.station}
numPeers={writeGroup.length}
host={props.match.params.ship}
api={props.api}
/>
</div>
<div className="w-100 pl3 mt4 cf">

View File

@ -35,6 +35,11 @@ export class InitialReducer {
if (data) {
state.contacts = data;
}
data = _.get(json, 'chat-hook-update', false);
if (data) {
state.chatSynced = data;
}
}
}

View File

@ -12,6 +12,7 @@ class Store {
constructor() {
this.state = {
inbox: {},
chatSynced: {},
groups: {},
contacts: {},
permissions: {},

View File

@ -18,6 +18,10 @@ export class Subscription {
this.handleEvent.bind(this),
this.handleError.bind(this),
this.handleQuitAndResubscribe.bind(this));
api.bind('/synced', 'PUT', api.authTokens.ship, 'chat-hook',
this.handleEvent.bind(this),
this.handleError.bind(this),
this.handleQuitAndResubscribe.bind(this));
api.bind('/primary', 'PUT', api.authTokens.ship, 'invite-view',
this.handleEvent.bind(this),
this.handleError.bind(this),