Infinite scrolling works, localStorage works, progressive loading of information works, new subscription pattern works

This commit is contained in:
Logan Allen 2019-05-31 11:32:13 -07:00
parent 2a2221f03a
commit bc4f1968f2
14 changed files with 611 additions and 132 deletions

View File

@ -51,6 +51,15 @@ class UrbitApi {
});
}
notify(aud, bool) {
this.hall({
notify: {
aud,
pes: !!bool ? 'hear' : 'gone'
}
});
}
permit(cir, aud, message) {
this.hall({
permit: {

View File

@ -16,14 +16,47 @@ export class ChatScreen extends Component {
station: props.match.params.ship + "/" + props.match.params.station,
circle: props.match.params.station,
host: props.match.params.ship,
numPeople: 0
numPeople: 0,
numPages: 1,
scrollLocked: false
};
this.topMessage = {};
this.buildMessage = this.buildMessage.bind(this);
this.onScroll = this.onScroll.bind(this);
}
componentDidMount() {
this.updateNumPeople();
this.scrollElement.scrollIntoView(false);
}
scrollToBottom() {
if (!this.state.scrollLocked) {
console.log('scroll to bottom');
this.scrollElement.scrollIntoView({ behavior: 'smooth' });
}
}
onScroll(e) {
if (e.target.scrollTop === 0) {
let topMessage = this.topMessage;
this.setState({
numPages: this.state.numPages + 1,
scrollLocked: true
}, () => {
this.topMessage[1].scrollIntoView(true);
});
} else if (
(e.target.scrollHeight - Math.round(e.target.scrollTop)) ===
e.target.clientHeight
) {
this.setState({
numPages: 1,
scrollLocked: false
});
}
}
componentDidUpdate(prevProps, prevState) {
@ -41,6 +74,7 @@ export class ChatScreen extends Component {
this.updateReadNumber();
this.updateNumPeople();
this.updateNumMessagesLoaded(prevProps, prevState);
this.scrollToBottom();
}
updateReadNumber() {
@ -77,7 +111,7 @@ export class ChatScreen extends Component {
}
}
buildMessage(msg) {
buildMessage(msg, index) {
let details = msg.printship ? null : getMessageContent(msg.gam);
if (msg.printship) {
@ -89,13 +123,29 @@ export class ChatScreen extends Component {
</a>
);
}
return (
<Message key={msg.gam.uid} msg={msg.gam} details={details} />
);
if (index % 50 === 0) {
let pageNum = index / 50;
return (
<div ref={ el => { this.topMessage[pageNum] = el; }}>
<Message
key={msg.gam.uid} msg={msg.gam} details={details} />
</div>
);
} else {
return (
<Message key={msg.gam.uid} msg={msg.gam} details={details} />
);
}
}
render() {
const { props, state } = this;
let messages = this.props.messages[this.state.station] || [];
if (messages.length > 50 * state.numPages) {
messages =
messages.slice(messages.length - (50 * state.numPages), messages.length);
}
let chatMessages = messages.map(this.buildMessage);
return (
@ -104,8 +154,12 @@ export class ChatScreen extends Component {
<h2>{this.state.circle}</h2>
<ChatTabBar {...this.props} station={this.state.station} />
</div>
<div className="overflow-y-scroll" style={{ flexGrow: 1 }}>
<div
className="overflow-y-scroll"
style={{ flexGrow: 1 }}
onScroll={this.onScroll}>
{chatMessages}
<div ref={ el => { this.scrollElement = el; }}></div>
</div>
<ChatInput
api={this.props.api}

View File

@ -105,6 +105,42 @@ export class ChatInput extends Component {
}
render() {
const { props, state } = this;
/*let closure = () => {
let aud, sep;
let wen = Date.now();
let uid = uuid();
let aut = window.ship;
let config = props.configs[state.station];
aud = [props.station];
sep = {
lin: {
msg: Date.now().toString(),
pat: false
}
}
let message = {
uid,
aut,
wen,
aud,
sep,
};
props.api.hall({
convey: [message]
});
setTimeout(closure, 1000);
};
closure();*/
return (
<div className="mt2 pa3 cf flex black bt">
<div className="fl" style={{ flexBasis: 35, height: 40 }}>
@ -114,8 +150,8 @@ export class ChatInput extends Component {
<input className="ml2 bn"
style={{ flexGrow: 1 }}
ref={this.textareaRef}
placeholder={this.props.placeholder}
value={this.state.message}
placeholder={props.placeholder}
value={state.message}
onChange={this.messageChange} />
<div className="pointer" onClick={this.messageSubmit}>
<IconSend />

View File

@ -1,11 +1,10 @@
import _ from 'lodash';
export class ChatReducer {
export class ConfigReducer {
reduce(json, state) {
let data = _.get(json, 'chat', false);
if (data) {
state.messages = data.messages;
state.inbox = data.inbox;
state.configs = data.configs;
state.circles = data.circles;

View File

@ -0,0 +1,15 @@
import _ from 'lodash';
export class InitialReducer {
reduce(json, state) {
let data = _.get(json, 'initial', false);
if (data) {
state.messages = data.messages;
state.inbox = data.inbox;
state.configs = data.configs;
state.circles = data.circles;
}
}
}

View File

@ -7,6 +7,7 @@ export class UpdateReducer {
if (data) {
this.reduceInbox(_.get(data, 'inbox', false), state);
this.reduceMessage(_.get(data, 'message', false), state);
this.reduceMessages(_.get(data, 'messages', false), state);
this.reduceConfig(_.get(data, 'config', false), state);
this.reduceCircles(_.get(data, 'circles', false), state);
}
@ -26,6 +27,22 @@ export class UpdateReducer {
}
}
reduceMessages(messages, state) {
if (messages.circle in state.messages) {
let station = state.messages[messages.circle];
if (
station.length > 0 &&
station[station.length - 1].num === station.length - 1 &&
messages.start === station.length
) {
state.messages[messages.circle] =
state.messages[messages.circle].concat(messages.envelopes);
} else {
console.error('%messages has indices inconsistent with localStorage');
}
}
}
reduceConfig(config, state) {
if (config) {
state.configs[config.circle] = config.config;

View File

@ -1,20 +1,34 @@
import _ from 'lodash';
import { ChatReducer } from '/reducers/chat';
import { InitialReducer } from '/reducers/initial';
import { ConfigReducer } from '/reducers/config';
import { UpdateReducer } from '/reducers/update';
class Store {
constructor() {
this.state = {
inbox: {},
messages: [],
configs: {},
circles: []
};
let state = localStorage.getItem('store');
this.chatReducer = new ChatReducer();
if (!state) {
this.state = {
inbox: {},
messages: [],
configs: {},
circles: [],
local: false
};
} else {
this.state = JSON.parse(state);
// TODO: wtf???
delete this.state.messages[undefined];
console.log(this.state);
this.state.local = true;
}
this.initialReducer = new InitialReducer();
this.configReducer = new ConfigReducer();
this.updateReducer = new UpdateReducer();
this.setState = () => {};
}
setStateHandler(setState) {
@ -22,14 +36,17 @@ class Store {
}
handleEvent(data) {
console.log(data);
let json = data.data;
this.chatReducer.reduce(json, this.state);
this.initialReducer.reduce(json, this.state);
this.configReducer.reduce(json, this.state);
this.updateReducer.reduce(json, this.state);
this.setState(this.state);
localStorage.setItem('store', JSON.stringify(this.state));
}
}
export let store = new Store();
window.store = store;

View File

@ -7,33 +7,32 @@ export class Subscription {
start() {
if (api.authTokens) {
this.initializeChat();
//this.setCleanupTasks();
} else {
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
}
}
/*setCleanupTasks() {
window.addEventListener("beforeunload", e => {
api.bindPaths.forEach(p => {
this.wipeSubscription(p);
});
});
}
wipeSubscription(path) {
api.hall({
wipe: {
sub: [{
hos: api.authTokens.ship,
pax: path
}]
}
});
}*/
initializeChat() {
api.bind('/primary', 'PUT', api.authTokens.ship, 'chat',
if (store.state.local) {
let path = [];
let msg = Object.keys(store.state.messages);
for (let i = 0; i < msg.length; i++) {
let cir = msg[i];
let len = store.state.messages[cir].length;
path.push(`${cir}/${len}`);
}
path = path.join('/');
api.bind(`/primary/${path}`, 'PUT', api.authTokens.ship, 'chat',
this.handleEvent.bind(this),
this.handleError.bind(this));
} else {
api.bind('/primary', 'PUT', api.authTokens.ship, 'chat',
this.handleEvent.bind(this),
this.handleError.bind(this));
}
api.bind('/updates', 'PUT', api.authTokens.ship, 'chat',
this.handleEvent.bind(this),
this.handleError.bind(this));
}

View File

@ -44,9 +44,9 @@
|= old=(unit state)
^- (quip move _this)
?~ old
=/ inboxpat /circle/inbox/config/group
=/ inboxpat /circle/inbox/config-l/config-r/group-r/group-l
=/ circlespat /circles/[(scot %p our.bol)]
=/ inboxwir /circle/[(scot %p our.bol)]/inbox/config/group
=/ inboxwir /circle/[(scot %p our.bol)]/inbox/config/group-r/group-l
=/ inboxi/poke
:- %hall-action
[%source %inbox %.y (silt [[our.bol %i] ~]~)]
@ -58,13 +58,97 @@
==
[~ this(sta u.old)]
::
:: +peer-primary: subscribe to our data and updates
:: +peer-messages: subscribe to subset of messages and updates
::
+$ internal-state
$:
lis=(list [circle:hall @])
item=[cir=circle:hall count=@ud]
index=@
==
++ generate-circle-indices
|= wir=wire
^- (list [circle:hall @])
=/ data
%^ spin (swag [0 (lent wir)] wir) *internal-state
|= [a=@ta b=internal-state]
^- [* out=internal-state]
=/ switch (dvr index.b 3)
?: =(q.switch 0) :: remainder 0, should be a ship
?: =(index.b 0) :: if item is null, don't add to list
:- 0
%= b
hos.cir.item (slav %p a)
index +(index.b)
==
:: if item is not null, add to list
:- 0
%= b
hos.cir.item (slav %p a)
nom.cir.item *name:hall
count.item 0
lis (snoc lis.b item.b)
index +(index.b)
==
?: =(q.switch 1) :: remainder 1, should be a circle name
:- 0
%= b
nom.cir.item a
index +(index.b)
==
?: =(q.switch 2) :: remainder 2, should be a number
:- 0
%= b
count.item (need (rush a dem))
index +(index.b)
==
!! :: impossible
?: =(index.q.data 0)
~
(snoc lis.q.data item.q.data)
::
++ peer-primary
|= wir=wire
^- (quip move _this)
=/ indices (generate-circle-indices wir)
?~ indices
:_ this
[ost.bol %diff %chat-initial str.sta]~
=* messages messages.str.sta
=/ lisunitmov/(list (unit move))
%+ turn indices
|= [cir=circle:hall start=@ud]
^- (unit move)
=/ wholelist/(unit (list envelope:hall)) (~(get by messages) cir)
?~ wholelist
~
=/ end/@ (lent u.wholelist)
?: (gte start end)
~
:- ~
:* ost.bol
%diff
%chat-update
[%messages cir start end (swag [start end] u.wholelist)]
==
=/ lismov/(list move)
%+ turn
%+ skim lisunitmov
|= umov=(unit move)
^- ?
?~ umov
%.n
%.y
need
:_ this
[ost.bol %diff %chat-streams str.sta]~
%+ weld
[ost.bol %diff %chat-config str.sta]~
lismov
::
++ peer-updates
|= wir=wire
^- (quip move _this)
[~ this]
::
++ poke-noun
|= a=*
@ -93,7 +177,7 @@
++ send-chat-update
|= upd=update
^- (list move)
%+ turn (prey:pubsub:userlib /primary bol)
%+ turn (prey:pubsub:userlib /updates bol)
|= [=bone *]
[bone %diff %chat-update upd]
::
@ -123,19 +207,12 @@
:: %circle wire
::
%circle
:: ?+ -.piz
:: ::
:: :: %peers prize
:: ::
:::: %peers
:::: ?> ?=(%peers -.piz)
:::: [~ this]
:: ::
:: :: %circle prize
:: ::
:: %circle
?> ?=(%circle -.piz)
~& piz
~& pes.piz
=/ circle/circle:hall [our.bol &2:wir]
?: =(circle [our.bol %inbox])
::
@ -145,27 +222,46 @@
%- ~(uni in configs.str.sta)
^- (map circle:hall (unit config:hall))
(~(run by rem.cos.piz) |=(a=config:hall `a))
~& pes.piz
::
=/ circles/(list circle:hall)
%+ turn ~(tap in src.loc.cos.piz)
|= src=source:hall
^- circle:hall
cir.src
::
=/ meslis/(list [circle:hall (list envelope:hall)])
%+ turn circles
|= cir=circle:hall
^- [circle:hall (list envelope:hall)]
[cir ~]
::
=/ localpeers/(set @p)
%- silt %+ turn ~(tap by loc.pes.piz)
|= [shp=@p stat=status:hall]
shp
::
=/ peers/(map circle:hall (set @p))
%- ~(rep by rem.pes.piz)
|= [[cir=circle:hall grp=group:hall] acc=(map circle:hall (set @p))]
^- (map circle:hall (set @p))
=/ newset
%- silt %+ turn ~(tap by grp)
|= [shp=@p stat=status:hall]
shp
(~(put by acc) cir newset)
::
:-
%+ turn ~(tap in (~(del in (silt circles)) [our.bol %inbox]))
|= cir=circle:hall
^- move
=/ pat/path /circle/[nom.cir]/config/grams
=/ pat/path /circle/[nom.cir]/config-l/config-r/group-r/group-l
[ost.bol %peer pat [our.bol %hall] pat]
::
%= this
inbox.str.sta loc.cos.piz
configs.str.sta configs
messages.str.sta (molt meslis)
peers.str.sta (~(put by peers) [our.bol %inbox] localpeers)
==
::
:: fill remote configs with message data
@ -224,22 +320,27 @@
messages.str.sta (~(put by messages) circle (snoc nes nev.sto))
==
::
:: %peer:
:: %status:
::
%peer
?> ?=(%peer -.sto)
~& add.sto
~& who.sto
~& qer.sto
[~ this]
%status
?> ?=(%status -.sto)
=/ peers/(set @p)
?: =(%remove -.dif.sto)
(~(del in (~(got by peers.str.sta) cir.sto)) who.sto)
(~(put in (~(got by peers.str.sta) cir.sto)) who.sto)
:- (send-chat-update [%peers cir.sto peers])
%= this
peers.str.sta (~(put by peers.str.sta) cir.sto peers)
==
::
:: %config: config has changed
::
%config
=* circ cir.sto
::
?+ -.dif.sto
[~ this]
=* circ cir.sto
::
?+ -.dif.sto
[~ this]
::
:: %full: set all of config without side effects
::
@ -277,8 +378,8 @@
[~ this]
=* affectedcir cir.src.dif.sto
=/ newwir/wire
/circle/[(scot %p hos.affectedcir)]/[nom.affectedcir]/grams/config
=/ pat/path /circle/[nom.affectedcir]/grams/config
/circle/[(scot %p hos.affectedcir)]/[nom.affectedcir]/grams/config/group-r/group-l
=/ pat/path /circle/[nom.affectedcir]/grams/config/group-r/group-l
:: we've added a source to our inbox
::
?: add.dif.sto
@ -411,7 +512,7 @@
::
%circle
=/ shp/@p (slav %p &2:wir)
=/ pat /circle/[&3:wir]/grams/config
=/ pat /circle/[&3:wir]/grams/config/group-r/group-l
:_ this
[ost.bol %peer wir [shp %hall] wir]~
::

View File

@ -17,6 +17,10 @@
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
function getCjsExportFromNamespace (n) {
return n && n.default || n;
}
/*
object-assign
(c) Sindre Sorhus
@ -47458,6 +47462,8 @@
isBuffer: isBuffer
});
var require$$0 = getCjsExportFromNamespace(bufferEs6);
var bn = createCommonjsModule(function (module) {
(function (module, exports) {
@ -47510,7 +47516,7 @@
var Buffer;
try {
Buffer = bufferEs6.Buffer;
Buffer = require$$0.Buffer;
} catch (e) {
}
@ -51734,6 +51740,15 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
});
}
notify(aud, bool) {
this.hall({
notify: {
aud,
pes: !!bool ? 'hear' : 'gone'
}
});
}
permit(cir, aud, message) {
this.hall({
permit: {
@ -51814,11 +51829,22 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
let api = new UrbitApi();
window.api = api;
class ChatReducer {
class InitialReducer {
reduce(json, state) {
let data = lodash.get(json, 'initial', false);
if (data) {
state.messages = data.messages;
state.inbox = data.inbox;
state.configs = data.configs;
state.circles = data.circles;
}
}
}
class ConfigReducer {
reduce(json, state) {
let data = lodash.get(json, 'chat', false);
if (data) {
state.messages = data.messages;
state.inbox = data.inbox;
state.configs = data.configs;
state.circles = data.circles;
@ -51832,6 +51858,7 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
if (data) {
this.reduceInbox(lodash.get(data, 'inbox', false), state);
this.reduceMessage(lodash.get(data, 'message', false), state);
this.reduceMessages(lodash.get(data, 'messages', false), state);
this.reduceConfig(lodash.get(data, 'config', false), state);
this.reduceCircles(lodash.get(data, 'circles', false), state);
}
@ -51851,6 +51878,22 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
}
}
reduceMessages(messages, state) {
if (messages.circle in state.messages) {
let station = state.messages[messages.circle];
if (
station.length > 0 &&
station[station.length - 1].num === station.length - 1 &&
messages.start === station.length
) {
state.messages[messages.circle] =
state.messages[messages.circle].concat(messages.envelopes);
} else {
console.error('%messages has indices inconsistent with localStorage');
}
}
}
reduceConfig(config, state) {
if (config) {
state.configs[config.circle] = config.config;
@ -51867,16 +51910,29 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
class Store {
constructor() {
this.state = {
inbox: {},
messages: [],
configs: {},
circles: []
};
let state = localStorage.getItem('store');
this.chatReducer = new ChatReducer();
if (!state) {
this.state = {
inbox: {},
messages: [],
configs: {},
circles: [],
local: false
};
} else {
this.state = JSON.parse(state);
// TODO: wtf???
delete this.state.messages[undefined];
console.log(this.state);
this.state.local = true;
}
this.initialReducer = new InitialReducer();
this.configReducer = new ConfigReducer();
this.updateReducer = new UpdateReducer();
this.setState = () => {};
}
setStateHandler(setState) {
@ -51884,16 +51940,20 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
}
handleEvent(data) {
console.log(data);
let json = data.data;
this.chatReducer.reduce(json, this.state);
this.initialReducer.reduce(json, this.state);
this.configReducer.reduce(json, this.state);
this.updateReducer.reduce(json, this.state);
this.setState(this.state);
localStorage.setItem('store', JSON.stringify(this.state));
}
}
let store = new Store();
window.store = store;
const _jsxFileName = "/Users/logan/Dev/interface/apps/chat/src/js/components/skeleton.js";
@ -56957,20 +57017,56 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
}
render() {
const { props, state } = this;
/*let closure = () => {
let aud, sep;
let wen = Date.now();
let uid = uuid();
let aut = window.ship;
let config = props.configs[state.station];
aud = [props.station];
sep = {
lin: {
msg: Date.now().toString(),
pat: false
}
}
let message = {
uid,
aut,
wen,
aud,
sep,
};
props.api.hall({
convey: [message]
});
setTimeout(closure, 1000);
};
closure();*/
return (
react.createElement('div', { className: "mt2 pa3 cf flex black bt" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 109}}
, react.createElement('div', { className: "fl", style: { flexBasis: 35, height: 40 }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 110}}
, react.createElement(Sigil, { ship: window.ship, size: 32, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 111}} )
react.createElement('div', { className: "mt2 pa3 cf flex black bt" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 145}}
, react.createElement('div', { className: "fl", style: { flexBasis: 35, height: 40 }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 146}}
, react.createElement(Sigil, { ship: window.ship, size: 32, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 147}} )
)
, react.createElement('div', { className: "fr h-100 flex" , style: { flexGrow: 1, height: 40 }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 113}}
, react.createElement('div', { className: "fr h-100 flex" , style: { flexGrow: 1, height: 40 }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 149}}
, react.createElement('input', { className: "ml2 bn" ,
style: { flexGrow: 1 },
ref: this.textareaRef,
placeholder: this.props.placeholder,
value: this.state.message,
onChange: this.messageChange, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 114}} )
, react.createElement('div', { className: "pointer", onClick: this.messageSubmit, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 120}}
, react.createElement(IconSend, {__self: this, __source: {fileName: _jsxFileName$9, lineNumber: 121}} )
placeholder: props.placeholder,
value: state.message,
onChange: this.messageChange, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 150}} )
, react.createElement('div', { className: "pointer", onClick: this.messageSubmit, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 156}}
, react.createElement(IconSend, {__self: this, __source: {fileName: _jsxFileName$9, lineNumber: 157}} )
)
)
)
@ -56987,14 +57083,47 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
station: props.match.params.ship + "/" + props.match.params.station,
circle: props.match.params.station,
host: props.match.params.ship,
numPeople: 0
numPeople: 0,
numPages: 1,
scrollLocked: false
};
this.topMessage = {};
this.buildMessage = this.buildMessage.bind(this);
this.onScroll = this.onScroll.bind(this);
}
componentDidMount() {
this.updateNumPeople();
this.scrollElement.scrollIntoView(false);
}
scrollToBottom() {
if (!this.state.scrollLocked) {
console.log('scroll to bottom');
this.scrollElement.scrollIntoView({ behavior: 'smooth' });
}
}
onScroll(e) {
if (e.target.scrollTop === 0) {
let topMessage = this.topMessage;
this.setState({
numPages: this.state.numPages + 1,
scrollLocked: true
}, () => {
this.topMessage[1].scrollIntoView(true);
});
} else if (
(e.target.scrollHeight - Math.round(e.target.scrollTop)) ===
e.target.clientHeight
) {
this.setState({
numPages: 1,
scrollLocked: false
});
}
}
componentDidUpdate(prevProps, prevState) {
@ -57012,6 +57141,7 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
this.updateReadNumber();
this.updateNumPeople();
this.updateNumMessagesLoaded(prevProps, prevState);
this.scrollToBottom();
}
updateReadNumber() {
@ -57048,42 +57178,62 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
}
}
buildMessage(msg) {
buildMessage(msg, index) {
let details = msg.printship ? null : getMessageContent(msg.gam);
if (msg.printship) {
return (
react.createElement('a', {
className: "vanilla hoverline text-600 text-mono" ,
href: prettyShip(msg.gam.aut)[1], __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 85}}
href: prettyShip(msg.gam.aut)[1], __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 119}}
, prettyShip(`~${msg.gam.aut}`)[0]
)
);
}
return (
react.createElement(Message, { key: msg.gam.uid, msg: msg.gam, details: details, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 93}} )
);
if (index % 50 === 0) {
let pageNum = index / 50;
return (
react.createElement('div', { ref: el => { this.topMessage[pageNum] = el; }, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 130}}
, react.createElement(Message, {
key: msg.gam.uid, msg: msg.gam, details: details, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 131}} )
)
);
} else {
return (
react.createElement(Message, { key: msg.gam.uid, msg: msg.gam, details: details, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 137}} )
);
}
}
render() {
const { props, state } = this;
let messages = this.props.messages[this.state.station] || [];
if (messages.length > 50 * state.numPages) {
messages =
messages.slice(messages.length - (50 * state.numPages), messages.length);
}
let chatMessages = messages.map(this.buildMessage);
return (
react.createElement('div', { className: "h-100 w-100 overflow-hidden flex flex-column" , __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 102}}
, react.createElement('div', { className: "pl2 pt2 bb mb3" , __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 103}}
, react.createElement('h2', {__self: this, __source: {fileName: _jsxFileName$a, lineNumber: 104}}, this.state.circle)
, react.createElement(ChatTabBar, { ...this.props, station: this.state.station, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 105}} )
react.createElement('div', { className: "h-100 w-100 overflow-hidden flex flex-column" , __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 152}}
, react.createElement('div', { className: "pl2 pt2 bb mb3" , __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 153}}
, react.createElement('h2', {__self: this, __source: {fileName: _jsxFileName$a, lineNumber: 154}}, this.state.circle)
, react.createElement(ChatTabBar, { ...this.props, station: this.state.station, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 155}} )
)
, react.createElement('div', { className: "overflow-y-scroll", style: { flexGrow: 1 }, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 107}}
, react.createElement('div', {
className: "overflow-y-scroll",
style: { flexGrow: 1 },
onScroll: this.onScroll, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 157}}
, chatMessages
, react.createElement('div', { ref: el => { this.scrollElement = el; }, __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 162}})
)
, react.createElement(ChatInput, {
api: this.props.api,
configs: this.props.configs,
station: this.state.station,
circle: this.state.circle,
placeholder: "Message...", __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 110}} )
placeholder: "Message...", __self: this, __source: {fileName: _jsxFileName$a, lineNumber: 164}} )
)
)
}
@ -57545,33 +57695,32 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
start() {
if (api.authTokens) {
this.initializeChat();
//this.setCleanupTasks();
} else {
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
}
}
/*setCleanupTasks() {
window.addEventListener("beforeunload", e => {
api.bindPaths.forEach(p => {
this.wipeSubscription(p);
});
});
}
wipeSubscription(path) {
api.hall({
wipe: {
sub: [{
hos: api.authTokens.ship,
pax: path
}]
}
});
}*/
initializeChat() {
api.bind('/primary', 'PUT', api.authTokens.ship, 'chat',
if (store.state.local) {
let path = [];
let msg = Object.keys(store.state.messages);
for (let i = 0; i < msg.length; i++) {
let cir = msg[i];
let len = store.state.messages[cir].length;
path.push(`${cir}/${len}`);
}
path = path.join('/');
api.bind(`/primary/${path}`, 'PUT', api.authTokens.ship, 'chat',
this.handleEvent.bind(this),
this.handleError.bind(this));
} else {
api.bind('/primary', 'PUT', api.authTokens.ship, 'chat',
this.handleEvent.bind(this),
this.handleError.bind(this));
}
api.bind('/updates', 'PUT', api.authTokens.ship, 'chat',
this.handleEvent.bind(this),
this.handleError.bind(this));
}

View File

@ -16,8 +16,9 @@
::
+$ diff
$% [%hall-rumor rumor:hall]
[%chat-streams streams]
[%chat-initial streams]
[%chat-update update]
[%chat-config streams]
==
::
+$ poke
@ -49,6 +50,7 @@
+$ update
$% [%inbox con=config:hall]
[%message cir=circle:hall env=envelope:hall]
[%messages cir=circle:hall start=@ud end=@ud env=(list envelope:hall)]
[%config cir=circle:hall con=config:hall]
[%circles cir=(set name:hall)]
[%peers cir=circle:hall per=(set @p)]

View File

@ -24,19 +24,20 @@
^- [@t ^json]
:- (crip (circ:en-tape:hall-json cir))
?~(con ~ (conf:enjs:hall-json u.con))
::
:- %messages
%- pairs
%+ turn ~(tap by messages.str)
|= [cir=circle:hall lis=(list envelope:hall)]
^- [@t ^json]
:- (crip (circ:en-tape:hall-json cir))
[%a (turn lis enve:enjs:hall-json)]
::
:- %circles :- %a
%+ turn ~(tap in circles.str)
|= nom=name:hall
[%s nom]
::
:- %peers
%- pairs
%+ turn ~(tap by peers.str)
|= [cir=circle:hall per=(set @p)]
^- [@t ^json]
:- (crip (circ:en-tape:hall-json cir))
[%a (turn ~(tap in per) ship)]
::
==
--
::

View File

@ -0,0 +1,56 @@
::
::
/? 309
::
/- hall
/+ chat, hall-json
::
|_ str=streams:chat
++ grow
|%
++ json
=, enjs:format
^- ^json
%+ frond %initial
%- pairs
:~
::
[%inbox (conf:enjs:hall-json inbox.str)]
::
:- %configs
%- pairs
%+ turn ~(tap by configs.str)
|= [cir=circle:hall con=(unit config:hall)]
^- [@t ^json]
:- (crip (circ:en-tape:hall-json cir))
?~(con ~ (conf:enjs:hall-json u.con))
::
:- %messages
%- pairs
%+ turn ~(tap by messages.str)
|= [cir=circle:hall lis=(list envelope:hall)]
^- [@t ^json]
:- (crip (circ:en-tape:hall-json cir))
[%a (turn lis enve:enjs:hall-json)]
::
:- %circles :- %a
%+ turn ~(tap in circles.str)
|= nom=name:hall
[%s nom]
::
:- %peers
%- pairs
%+ turn ~(tap by peers.str)
|= [cir=circle:hall per=(set @p)]
^- [@t ^json]
:- (crip (circ:en-tape:hall-json cir))
[%a (turn ~(tap in per) ship)]
::
==
--
::
++ grab
|%
++ noun streams:chat
--
--

View File

@ -30,6 +30,18 @@
[%envelope (enve:enjs:hall-json env.upd)]
==
::
:: %messages
?: =(%messages -.upd)
?> ?=(%messages -.upd)
:- %messages
%- pairs
:~
[%circle (circ:enjs:hall-json cir.upd)]
[%start (numb start.upd)]
[%end (numb end.upd)]
[%envelopes [%a (turn env.upd enve:enjs:hall-json)]]
==
::
:: %config
?: =(%config -.upd)
?> ?=(%config -.upd)
@ -39,6 +51,8 @@
[%circle (circ:enjs:hall-json cir.upd)]
[%config (conf:enjs:hall-json con.upd)]
==
::
:: %circles
?: =(%circles -.upd)
?> ?=(%circles -.upd)
:- %circles
@ -51,6 +65,16 @@
[%s nom]
==
::
:: %peers
?: =(%peers -.upd)
?> ?=(%peers -.upd)
:- %peers
%- pairs
:~
[%circle (circ:enjs:hall-json cir.upd)]
[%peers [%a (turn ~(tap in per.upd) ship:enjs:format)]]
==
::
:: %noop
[*@t *^json]
==