mirror of
https://github.com/urbit/shrub.git
synced 2024-12-01 14:42:02 +03:00
Merge branch 'master' into mp/chat/firefox-take-2
This commit is contained in:
commit
41be649764
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9c47244803d87e8ab25bc257d34336ae72e9155a5400e5635a6a5f3ae608b239
|
||||
size 9952079
|
||||
oid sha256:418f7512f9ff24de9c222cb4f666ef892c35376c7ee50f9f9e389c7ffa4711fc
|
||||
size 10010102
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c8dfc019cdf53e0d627e1afad91e4c9cd092f767c6ba0c8c9864dd422a14d814
|
||||
size 1234152
|
||||
oid sha256:9d131da321b891c126f62cc587c5e27c257695ff9ae15e502356159fba7f9bf3
|
||||
size 1234415
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8ebc16b420b2ce8e8bf7e6b0c611a102355f9ec013475da3cf66cc078f35e4b3
|
||||
size 12458781
|
||||
oid sha256:816bb2cb6b59153c611ef8bc5342c2e02eb5ef3514f8f510c5aa2387df92e9a9
|
||||
size 12470565
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,17 @@
|
||||
class Channel {
|
||||
constructor() {
|
||||
this.init();
|
||||
this.deleteOnUnload();
|
||||
|
||||
// a way to handle channel errors
|
||||
//
|
||||
//
|
||||
this.onChannelError = (err) => {
|
||||
console.error('event source error: ', err);
|
||||
};
|
||||
}
|
||||
|
||||
init() {
|
||||
// unique identifier: current time and random number
|
||||
//
|
||||
this.uid =
|
||||
@ -40,8 +52,10 @@ class Channel {
|
||||
// disconnect function may be called exactly once.
|
||||
//
|
||||
this.outstandingSubscriptions = new Map();
|
||||
}
|
||||
|
||||
this.deleteOnUnload();
|
||||
setOnChannelError(onError = (err) => {}) {
|
||||
this.onChannelError = onError;
|
||||
}
|
||||
|
||||
deleteOnUnload() {
|
||||
@ -196,8 +210,9 @@ class Channel {
|
||||
}
|
||||
|
||||
this.eventSource.onerror = e => {
|
||||
console.error("eventSource error:", e);
|
||||
this.delete();
|
||||
this.init();
|
||||
this.onChannelError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,9 +344,11 @@
|
||||
=* loop-whos $
|
||||
?~ whos loop-socs(socs t.socs)
|
||||
=^ caz state
|
||||
?: ?=(%remove -.upd)
|
||||
(leave-from-peer i.socs pax.upd i.whos)
|
||||
(listen-to-peer i.socs pax.upd i.whos)
|
||||
?. ?=(%remove -.upd)
|
||||
(listen-to-peer i.socs pax.upd i.whos)
|
||||
?: =(our.bowl i.whos)
|
||||
(handle-listen-action %leave i.socs)
|
||||
(leave-from-peer i.socs pax.upd i.whos)
|
||||
loop-whos(whos t.whos, cards (weld cards caz))
|
||||
::
|
||||
:: link subscriptions
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -35,7 +35,7 @@
|
||||
=/ dbug
|
||||
!<(poke vase)
|
||||
=; =tang
|
||||
((slog tang) [~ this])
|
||||
((%*(. slog pri 1) tang) [~ this])
|
||||
?- -.dbug
|
||||
%bowl [(sell !>(bowl))]~
|
||||
::
|
||||
@ -47,6 +47,9 @@
|
||||
(ream grab.dbug)
|
||||
::
|
||||
%incoming
|
||||
=; =tang
|
||||
?^ tang tang
|
||||
[%leaf "no matching subscriptions"]~
|
||||
%+ murn
|
||||
%+ sort ~(tap by sup.bowl)
|
||||
|= [[* a=[=ship =path]] [* b=[=ship =path]]]
|
||||
@ -66,6 +69,9 @@
|
||||
==
|
||||
::
|
||||
%outgoing
|
||||
=; =tang
|
||||
?^ tang tang
|
||||
[%leaf "no matching subscriptions"]~
|
||||
%+ murn
|
||||
%+ sort ~(tap by wex.bowl)
|
||||
|= [[[a=wire *] *] [[b=wire *] *]]
|
||||
|
@ -40,6 +40,10 @@
|
||||
==
|
||||
++ both-parser
|
||||
;~ pose
|
||||
%+ cook
|
||||
|= [author=@ date-created=@da content=@t]
|
||||
^- comment
|
||||
[author date-created content %.n]
|
||||
new-parser
|
||||
%+ cook
|
||||
|= [author=@ @ @ date-created=@da @ content=@t]
|
||||
|
@ -3165,10 +3165,12 @@
|
||||
?~ q.parsed
|
||||
=/ =path (rail-to-path source-rail)
|
||||
%- return-error
|
||||
:- :- %leaf
|
||||
%+ weld "ford: %hood: syntax error at "
|
||||
"[{<p.p.parsed>} {<q.p.parsed>}] in {<path>}"
|
||||
~
|
||||
=/ lyn p.p.parsed
|
||||
=/ col q.p.parsed
|
||||
:~ leaf+(runt [(dec col) '-'] "^")
|
||||
leaf+(trip (snag (dec lyn) (to-wain:format q.q.as-cage)))
|
||||
leaf+"ford: %hood: syntax error at [{<lyn>} {<col>}] in {<path>}"
|
||||
==
|
||||
::
|
||||
(return-result %success %hood p.u.q.parsed)
|
||||
::
|
||||
|
@ -77,7 +77,7 @@ export class ChatScreen extends Component {
|
||||
);
|
||||
} else if (props.chatInitialized &&
|
||||
!(props.station in props.inbox) &&
|
||||
!(props.station in props.chatSynced) ) {
|
||||
(!!props.chatSynced && !(props.station in props.chatSynced))) {
|
||||
|
||||
props.history.push("/~chat");
|
||||
} else if (
|
||||
@ -241,7 +241,7 @@ export class ChatScreen extends Component {
|
||||
}
|
||||
|
||||
let pendingMessages = props.pendingMessages.has(props.station)
|
||||
? props.pendingMessages.get(props.station).reverse()
|
||||
? props.pendingMessages.get(props.station)
|
||||
: [];
|
||||
|
||||
pendingMessages.map(function (value) {
|
||||
@ -302,12 +302,14 @@ export class ChatScreen extends Component {
|
||||
<div
|
||||
className="overflow-y-scroll bg-white bg-gray0-d pt3 pb2 flex flex-column-reverse"
|
||||
style={{ height: "100%", resize: "vertical" }}
|
||||
onScroll={this.onScroll}
|
||||
>
|
||||
<div
|
||||
ref={el => {
|
||||
this.scrollElement = el;
|
||||
}}></div>
|
||||
{(
|
||||
props.chatSynced &&
|
||||
!(props.station in props.chatSynced) &&
|
||||
(messages.length > 0)
|
||||
) ? (
|
||||
@ -320,7 +322,7 @@ export class ChatScreen extends Component {
|
||||
{messageElements}
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props, state } = this;
|
||||
|
@ -22,12 +22,11 @@ export class JoinScreen extends Component {
|
||||
componentDidMount() {
|
||||
const { props } = this;
|
||||
if (props.autoJoin !== "/undefined/undefined" &&
|
||||
props.autoJoin !== "/~/undefined/undefined") {
|
||||
props.autoJoin !== "/~/undefined/undefined") {
|
||||
let station = props.autoJoin.split('/');
|
||||
let sig = props.autoJoin.includes("/~/");
|
||||
|
||||
let ship = !!sig ? station[2] : station[1];
|
||||
station = station.join('/');
|
||||
if (
|
||||
station.length < 2 ||
|
||||
(!!sig && station.length < 3) ||
|
||||
@ -38,7 +37,7 @@ export class JoinScreen extends Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
station = props.autoJoin;
|
||||
|
||||
this.setState({
|
||||
station,
|
||||
@ -50,7 +49,7 @@ export class JoinScreen extends Component {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { props, state } = this;
|
||||
if (state.station in props.inbox ||
|
||||
props.chatSynced !== prevProps.chatSynced) {
|
||||
(props.chatSynced !== prevProps.chatSynced && state.station !== '/')) {
|
||||
this.setState({ awaiting: false });
|
||||
props.history.push(`/~chat/room${state.station}`);
|
||||
}
|
||||
@ -59,24 +58,13 @@ export class JoinScreen extends Component {
|
||||
onClickJoin() {
|
||||
const { props, state } = this;
|
||||
|
||||
let text = state.station;
|
||||
if (text in props.inbox ||
|
||||
text.length === 0) {
|
||||
this.setState({
|
||||
error: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let station = text.split('/');
|
||||
let sig = state.station.includes("~/");
|
||||
let ship = !!sig ? station[1] : station[0];
|
||||
|
||||
station = station.join('/');
|
||||
let station = state.station.split('/');
|
||||
let sig = state.station.includes("/~/");
|
||||
|
||||
let ship = !!sig ? station[2] : station[1];
|
||||
if (
|
||||
(!sig && station.split('/').length < 2) ||
|
||||
(!!sig && station.split('/').length < 3) ||
|
||||
station.length < 2 ||
|
||||
(!!sig && station.length < 3) ||
|
||||
!urbitOb.isValidPatp(ship)
|
||||
) {
|
||||
this.setState({
|
||||
@ -84,16 +72,19 @@ export class JoinScreen extends Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
station = state.station.trim();
|
||||
|
||||
this.setState({
|
||||
station,
|
||||
awaiting: true,
|
||||
station: `/${station}`
|
||||
}, () => props.api.chatView.join(ship, `/${station}`, true))
|
||||
}, () => {
|
||||
props.api.chatView.join(ship, station, true)
|
||||
});
|
||||
}
|
||||
|
||||
stationChange(event) {
|
||||
this.setState({
|
||||
station: event.target.value
|
||||
station: `/${event.target.value}`
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,11 @@ import { Sigil } from '/components/lib/icons/sigil';
|
||||
|
||||
import { uuid, uxToHex, hexToRgba } from '/lib/util';
|
||||
|
||||
const DEFAULT_INPUT_HEIGHT = 28;
|
||||
|
||||
// line height
|
||||
const INPUT_LINE_HEIGHT = 28;
|
||||
|
||||
const INPUT_TOP_PADDING = 3;
|
||||
|
||||
|
||||
function getAdvance(a, b) {
|
||||
@ -103,7 +107,7 @@ export class ChatInput extends Component {
|
||||
|
||||
this.state = {
|
||||
message: '',
|
||||
textareaHeight: DEFAULT_INPUT_HEIGHT,
|
||||
textareaHeight: INPUT_LINE_HEIGHT + INPUT_TOP_PADDING + 1,
|
||||
patpSuggestions: [],
|
||||
selectedSuggestion: null
|
||||
};
|
||||
@ -309,9 +313,8 @@ export class ChatInput extends Component {
|
||||
}
|
||||
|
||||
textareaInput() {
|
||||
const newHeight = this.textareaRef.current.scrollHeight < DEFAULT_INPUT_HEIGHT * 8
|
||||
? `${this.textareaRef.current.scrollHeight}px`
|
||||
: `${DEFAULT_INPUT_HEIGHT * 8}px`
|
||||
const maxHeight = INPUT_LINE_HEIGHT * 8 + INPUT_TOP_PADDING;
|
||||
const newHeight = `${Math.min(maxHeight, this.textareaRef.current.scrollHeight)}px`;
|
||||
|
||||
this.setState({
|
||||
textareaHeight: newHeight
|
||||
@ -418,7 +421,7 @@ export class ChatInput extends Component {
|
||||
|
||||
this.setState({
|
||||
message: '',
|
||||
textareaHeight: DEFAULT_INPUT_HEIGHT
|
||||
textareaHeight: INPUT_LINE_HEIGHT + INPUT_TOP_PADDING + 1
|
||||
});
|
||||
}
|
||||
|
||||
@ -459,8 +462,8 @@ export class ChatInput extends Component {
|
||||
</div>
|
||||
<div className="fr h-100 flex bg-gray0-d" style={{ flexGrow: 1 }}>
|
||||
<textarea
|
||||
className={"pl3 bn bg-gray0-d white-d"}
|
||||
style={{ flexGrow: 1, height: state.textareaHeight, paddingTop: 6, resize: "none" }}
|
||||
className={"pl3 bn bg-gray0-d white-d lh-copy"}
|
||||
style={{ flexGrow: 1, height: state.textareaHeight, paddingTop: INPUT_TOP_PADDING, resize: "none" }}
|
||||
autoCapitalize="none"
|
||||
autoFocus={(
|
||||
/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(
|
||||
|
@ -39,7 +39,7 @@ export class Root extends Component {
|
||||
if (envelopes.length === 0) {
|
||||
messagePreviews[stat] = false;
|
||||
} else {
|
||||
messagePreviews[stat] = envelopes[envelopes.length - 1];
|
||||
messagePreviews[stat] = envelopes[0];
|
||||
}
|
||||
|
||||
unreads[stat] =
|
||||
|
@ -9,9 +9,22 @@ import { LocalReducer } from '/reducers/local.js';
|
||||
|
||||
class Store {
|
||||
constructor() {
|
||||
this.state = {
|
||||
this.state = this.initialState();
|
||||
|
||||
this.initialReducer = new InitialReducer();
|
||||
this.permissionUpdateReducer = new PermissionUpdateReducer();
|
||||
this.contactUpdateReducer = new ContactUpdateReducer();
|
||||
this.chatUpdateReducer = new ChatUpdateReducer();
|
||||
this.inviteUpdateReducer = new InviteUpdateReducer();
|
||||
this.metadataReducer = new MetadataReducer();
|
||||
this.localReducer = new LocalReducer();
|
||||
this.setState = () => {};
|
||||
}
|
||||
|
||||
initialState() {
|
||||
return {
|
||||
inbox: {},
|
||||
chatSynced: {},
|
||||
chatSynced: null,
|
||||
contacts: {},
|
||||
permissions: {},
|
||||
invites: {},
|
||||
@ -24,15 +37,6 @@ class Store {
|
||||
pendingMessages: new Map([]),
|
||||
chatInitialized: false
|
||||
};
|
||||
|
||||
this.initialReducer = new InitialReducer();
|
||||
this.permissionUpdateReducer = new PermissionUpdateReducer();
|
||||
this.contactUpdateReducer = new ContactUpdateReducer();
|
||||
this.chatUpdateReducer = new ChatUpdateReducer();
|
||||
this.inviteUpdateReducer = new InviteUpdateReducer();
|
||||
this.metadataReducer = new MetadataReducer();
|
||||
this.localReducer = new LocalReducer();
|
||||
this.setState = () => {};
|
||||
}
|
||||
|
||||
setStateHandler(setState) {
|
||||
@ -42,6 +46,11 @@ class Store {
|
||||
handleEvent(data) {
|
||||
let json = data.data;
|
||||
|
||||
if ('clear' in json && json.clear) {
|
||||
this.setState(this.initialState());
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(json);
|
||||
this.initialReducer.reduce(json, this.state);
|
||||
this.permissionUpdateReducer.reduce(json, this.state);
|
||||
|
@ -13,11 +13,24 @@ export class Subscription {
|
||||
start() {
|
||||
if (api.authTokens) {
|
||||
this.firstRoundSubscription();
|
||||
window.urb.setOnChannelError(this.onChannelError.bind(this));
|
||||
} else {
|
||||
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
|
||||
}
|
||||
}
|
||||
|
||||
onChannelError(err) {
|
||||
console.error('event source error: ', err);
|
||||
console.log('initiating new channel');
|
||||
this.firstRoundSubscriptionComplete = false;
|
||||
setTimeout(2000, () => {
|
||||
store.handleEvent({
|
||||
data: { clear : true}
|
||||
});
|
||||
this.start();
|
||||
});
|
||||
}
|
||||
|
||||
subscribe(path, app) {
|
||||
api.bind(path, 'PUT', api.authTokens.ship, app,
|
||||
this.handleEvent.bind(this),
|
||||
|
@ -80,7 +80,6 @@ export class Root extends Component {
|
||||
associations={associations}
|
||||
invites={invites}
|
||||
groups={groups}
|
||||
rightPanelHide={true}
|
||||
sidebarShown={state.sidebarShown}
|
||||
selectedGroups={selectedGroups}
|
||||
links={links}
|
||||
@ -118,7 +117,6 @@ export class Root extends Component {
|
||||
invites={invites}
|
||||
groups={groups}
|
||||
selected={resourcePath}
|
||||
rightPanelHide={true}
|
||||
sidebarShown={state.sidebarShown}
|
||||
selectedGroups={selectedGroups}
|
||||
links={links}
|
||||
@ -155,7 +153,6 @@ export class Root extends Component {
|
||||
invites={invites}
|
||||
groups={groups}
|
||||
selected={resourcePath}
|
||||
rightPanelHide={true}
|
||||
sidebarShown={state.sidebarShown}
|
||||
selectedGroups={selectedGroups}
|
||||
popout={popout}
|
||||
|
@ -167,7 +167,7 @@ a {
|
||||
.cm-s-tlon span.cm-variable-3, .cm-s-tlon span.cm-type { color: black; }
|
||||
.cm-s-tlon span.cm-property { color: black; }
|
||||
.cm-s-tlon span.cm-operator { color: black; }
|
||||
.cm-s-tlon span.cm-comment { color: black; background-color: var(--light-gray); padding:2px; border-radius: 2px;}
|
||||
.cm-s-tlon span.cm-comment { color: black; background-color: var(--light-gray); display: inline-block; border-radius: 2px;}
|
||||
.cm-s-tlon span.cm-string { color: var(--dark-gray); }
|
||||
.cm-s-tlon span.cm-string-2 { color: var(--gray); }
|
||||
.cm-s-tlon span.cm-qualifier { color: #555; }
|
||||
@ -362,10 +362,6 @@ md img {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
.CodeMirror-selected {
|
||||
background: #4d4d4d !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cm-s-tlon span.cm-def {
|
||||
color: white;
|
||||
@ -392,12 +388,7 @@ md img {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cm-s-tlon span.cm-comment {
|
||||
color: black;
|
||||
background-color: var(--gray);
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.cm-s-tlon span.cm-string {
|
||||
color: var(--gray);
|
||||
}
|
||||
@ -417,4 +408,18 @@ md img {
|
||||
.cm-s-tlon span.cm-link {
|
||||
color: var(--gray);
|
||||
}
|
||||
}
|
||||
|
||||
/* set rules w/ both color & bg-color last to preserve legibility */
|
||||
.CodeMirror-selected {
|
||||
background: var(--medium-gray) !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cm-s-tlon span.cm-comment {
|
||||
color: black;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
background-color: rgba(255,255,255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user