Merge branch 'la-unread' (#2064)

* origin/la-unread:
  chat-js: fix unread counts and indentation

Signed-off-by: Jared Tobin <jared@tlon.io>
This commit is contained in:
Jared Tobin 2019-12-10 17:05:43 +08:00
commit d21310ace8
No known key found for this signature in database
GPG Key ID: 0E4647D58F8A69E4
5 changed files with 265 additions and 257 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,6 @@ import _ from 'lodash';
import { Route, Link } from "react-router-dom";
import { store } from "/store";
import { Message } from '/components/lib/message';
import { SidebarSwitcher } from '/components/lib/icons/icon-sidebar-switch.js';
import { ChatTabBar } from '/components/lib/chat-tabbar';
@ -14,264 +13,264 @@ import { deSig } from '/lib/util';
export class ChatScreen extends Component {
constructor(props) {
super(props);
constructor(props) {
super(props);
this.state = {
station: `/${props.match.params.ship}/${props.match.params.station}`,
numPages: 1,
scrollLocked: false
};
this.state = {
station: `/${props.match.params.ship}/${props.match.params.station}`,
numPages: 1,
scrollLocked: false
};
this.hasAskedForMessages = false;
this.onScroll = this.onScroll.bind(this);
this.hasAskedForMessages = false;
this.onScroll = this.onScroll.bind(this);
this.updateReadInterval = setInterval(
this.updateReadNumber.bind(this),
1000
);
}
this.updateReadInterval = setInterval(
this.updateReadNumber.bind(this),
1000
);
}
componentDidMount() {
this.updateReadNumber();
}
componentDidMount() {
this.updateReadNumber();
}
componentWillUnmount() {
if (this.updateReadInterval) {
clearInterval(this.updateReadInterval);
this.updateReadInterval = null;
}
}
componentWillUnmount() {
if (this.updateReadInterval) {
clearInterval(this.updateReadInterval);
this.updateReadInterval = null;
}
}
componentDidUpdate(prevProps, prevState) {
const { props, state } = this;
componentDidUpdate(prevProps, prevState) {
const { props, state } = this;
if (
prevProps.match.params.station !== props.match.params.station ||
prevProps.match.params.ship !== props.match.params.ship
) {
this.hasAskedForMessages = false;
if (
prevProps.match.params.station !== props.match.params.station ||
prevProps.match.params.ship !== props.match.params.ship
) {
this.hasAskedForMessages = false;
clearInterval(this.updateReadInterval);
clearInterval(this.updateReadInterval);
this.setState(
{
station: `/${props.match.params.ship}/${props.match.params.station}`,
scrollLocked: false
},
() => {
this.scrollToBottom();
this.updateReadInterval = setInterval(
this.updateReadNumber.bind(this),
1000
);
this.updateReadNumber();
}
);
} else if (Object.keys(props.inbox).length === 0) {
props.history.push("/~chat");
} else if (
props.envelopes.length - prevProps.envelopes.length >=
200
) {
this.hasAskedForMessages = false;
}
}
updateReadNumber() {
const { props, state } = this;
if (props.read < props.envelopes.length) {
props.api.chat.read(state.station);
}
}
askForMessages() {
const { props, state } = this;
if (
state.numPages * 100 < props.envelopes.length - 400 ||
this.hasAskedForMessages
) {
return;
}
if (props.envelopes.length > 0) {
let end = props.envelopes[0].number;
if (end > 0) {
let start = end - 400 > 0 ? end - 400 : 0;
if (start === 0 && end === 1) {
return;
}
this.hasAskedForMessages = true;
props.subscription.fetchMessages(start, end - 1, state.station);
}
}
}
scrollToBottom() {
if (!this.state.scrollLocked && this.scrollElement) {
this.scrollElement.scrollIntoView({ behavior: "smooth" });
}
}
onScroll(e) {
if (
navigator.userAgent.includes("Safari") &&
navigator.userAgent.includes("Chrome")
) {
// Google Chrome
if (e.target.scrollTop === 0) {
this.setState(
{
numPages: this.state.numPages + 1,
scrollLocked: true
},
() => {
this.askForMessages();
}
);
} else if (
e.target.scrollHeight - Math.round(e.target.scrollTop) ===
e.target.clientHeight
) {
this.setState({
numPages: 1,
scrollLocked: false
});
}
} else if (navigator.userAgent.includes("Safari")) {
// Safari
if (e.target.scrollTop === 0) {
this.setState({
numPages: 1,
scrollLocked: false
});
} else if (
e.target.scrollHeight + Math.round(e.target.scrollTop) <=
e.target.clientHeight + 10
) {
this.setState(
{
numPages: this.state.numPages + 1,
scrollLocked: true
},
() => {
this.askForMessages();
}
);
}
} else {
console.log("Your browser is not supported.");
}
}
render() {
const { props, state } = this;
let messages = props.envelopes.slice(0);
let lastMsgNum = messages.length > 0 ? messages.length : 0;
if (messages.length > 100 * state.numPages) {
messages = messages.slice(
messages.length - 100 * state.numPages,
messages.length
);
}
let pendingMessages = props.pendingMessages.has(state.station)
? props.pendingMessages.get(state.station)
: [];
pendingMessages.map(function(value) {
return (value.pending = true);
});
let reversedMessages = messages.concat(pendingMessages);
reversedMessages = reversedMessages.reverse();
reversedMessages = reversedMessages.map((msg, i) => {
// Render sigil if previous message is not by the same sender
let aut = ["author"];
let renderSigil =
_.get(reversedMessages[i + 1], aut) !==
_.get(msg, aut, msg.author);
let paddingTop = renderSigil;
let paddingBot =
_.get(reversedMessages[i - 1], aut) !==
_.get(msg, aut, msg.author);
return (
<Message
key={msg.uid}
msg={msg}
renderSigil={renderSigil}
paddingTop={paddingTop}
paddingBot={paddingBot}
pending={!!msg.pending}
/>
);
});
let group = Array.from(props.group.values());
let isinPopout = this.props.popout ? "popout/" : "";
return (
<div
key={state.station}
className="h-100 w-100 overflow-hidden flex flex-column">
<div
className="w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8"
style={{ height: "1rem" }}>
<Link to="/~chat/">{"⟵ All Chats"}</Link>
</div>
<div
className="pl3 pt2 bb b--gray4 flex relative overflow-x-scroll overflow-x-auto-l overflow-x-auto-xl flex-shrink-0"
style={{ height: 48 }}>
<SidebarSwitcher
sidebarShown={this.props.sidebarShown}
popout={this.props.popout}
/>
<Link to={`/~chat/` + isinPopout + `room` + state.station}
className="pt2">
<h2
className="mono dib f8 fw4 v-top"
style={{ width: "max-content" }}>
{state.station.substr(1)}
</h2>
</Link>
<ChatTabBar
{...props}
station={state.station}
numPeers={group.length}
isOwner={deSig(props.match.params.ship) === window.ship}
popout={this.props.popout}
/>
</div>
<div
className="overflow-y-scroll pt3 pb2 flex flex-column-reverse"
style={{ height: "100%", resize: "vertical" }}
onScroll={this.onScroll}>
<div
ref={el => {
this.scrollElement = el;
}}></div>
{reversedMessages}
</div>
<ChatInput
api={props.api}
numMsgs={lastMsgNum}
station={state.station}
owner={deSig(props.match.params.ship)}
permissions={props.permissions}
placeholder="Message..."
/>
</div>
);
}
this.setState(
{
station: `/${props.match.params.ship}/${props.match.params.station}`,
scrollLocked: false
},
() => {
this.scrollToBottom();
this.updateReadInterval = setInterval(
this.updateReadNumber.bind(this),
1000
);
this.updateReadNumber();
}
);
} else if (Object.keys(props.inbox).length === 0) {
props.history.push("/~chat");
} else if (
props.envelopes.length - prevProps.envelopes.length >=
200
) {
this.hasAskedForMessages = false;
}
}
updateReadNumber() {
const { props, state } = this;
if (props.read < props.length) {
props.api.chat.read(state.station);
}
}
askForMessages() {
const { props, state } = this;
if (
state.numPages * 100 < props.length - 400 ||
this.hasAskedForMessages
) {
return;
}
if (props.length > 0) {
let end = props.envelopes[0].number;
if (end > 0) {
let start = end - 400 > 0 ? end - 400 : 0;
if (start === 0 && end === 1) {
return;
}
this.hasAskedForMessages = true;
props.subscription.fetchMessages(start, end - 1, state.station);
}
}
}
scrollToBottom() {
if (!this.state.scrollLocked && this.scrollElement) {
this.scrollElement.scrollIntoView({ behavior: "smooth" });
}
}
onScroll(e) {
if (
navigator.userAgent.includes("Safari") &&
navigator.userAgent.includes("Chrome")
) {
// Google Chrome
if (e.target.scrollTop === 0) {
this.setState(
{
numPages: this.state.numPages + 1,
scrollLocked: true
},
() => {
this.askForMessages();
}
);
} else if (
e.target.scrollHeight - Math.round(e.target.scrollTop) ===
e.target.clientHeight
) {
this.setState({
numPages: 1,
scrollLocked: false
});
}
} else if (navigator.userAgent.includes("Safari")) {
// Safari
if (e.target.scrollTop === 0) {
this.setState({
numPages: 1,
scrollLocked: false
});
} else if (
e.target.scrollHeight + Math.round(e.target.scrollTop) <=
e.target.clientHeight + 10
) {
this.setState(
{
numPages: this.state.numPages + 1,
scrollLocked: true
},
() => {
this.askForMessages();
}
);
}
} else {
console.log("Your browser is not supported.");
}
}
render() {
const { props, state } = this;
let messages = props.envelopes.slice(0);
let lastMsgNum = messages.length > 0 ? messages.length : 0;
if (messages.length > 100 * state.numPages) {
messages = messages.slice(
messages.length - 100 * state.numPages,
messages.length
);
}
let pendingMessages = props.pendingMessages.has(state.station)
? props.pendingMessages.get(state.station)
: [];
pendingMessages.map(function(value) {
return (value.pending = true);
});
let reversedMessages = messages.concat(pendingMessages);
reversedMessages = reversedMessages.reverse();
reversedMessages = reversedMessages.map((msg, i) => {
// Render sigil if previous message is not by the same sender
let aut = ["author"];
let renderSigil =
_.get(reversedMessages[i + 1], aut) !==
_.get(msg, aut, msg.author);
let paddingTop = renderSigil;
let paddingBot =
_.get(reversedMessages[i - 1], aut) !==
_.get(msg, aut, msg.author);
return (
<Message
key={msg.uid}
msg={msg}
renderSigil={renderSigil}
paddingTop={paddingTop}
paddingBot={paddingBot}
pending={!!msg.pending}
/>
);
});
let group = Array.from(props.group.values());
let isinPopout = this.props.popout ? "popout/" : "";
return (
<div
key={state.station}
className="h-100 w-100 overflow-hidden flex flex-column">
<div
className="w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8"
style={{ height: "1rem" }}>
<Link to="/~chat/">{"⟵ All Chats"}</Link>
</div>
<div
className="pl3 pt2 bb b--gray4 flex relative overflow-x-scroll overflow-x-auto-l overflow-x-auto-xl flex-shrink-0"
style={{ height: 48 }}>
<SidebarSwitcher
sidebarShown={this.props.sidebarShown}
popout={this.props.popout}
/>
<Link to={`/~chat/` + isinPopout + `room` + state.station}
className="pt2">
<h2
className="mono dib f8 fw4 v-top"
style={{ width: "max-content" }}>
{state.station.substr(1)}
</h2>
</Link>
<ChatTabBar
{...props}
station={state.station}
numPeers={group.length}
isOwner={deSig(props.match.params.ship) === window.ship}
popout={this.props.popout}
/>
</div>
<div
className="overflow-y-scroll pt3 pb2 flex flex-column-reverse"
style={{ height: "100%", resize: "vertical" }}
onScroll={this.onScroll}>
<div
ref={el => {
this.scrollElement = el;
}}></div>
{reversedMessages}
</div>
<ChatInput
api={props.api}
numMsgs={lastMsgNum}
station={state.station}
owner={deSig(props.match.params.ship)}
permissions={props.permissions}
placeholder="Message..."
/>
</div>
);
}
}

View File

@ -22,8 +22,16 @@ export class SidebarInvite extends Component {
{props.invite.text}
</p>
</div>
<a className="dib pointer pa2 f9 bg-green2 white mt4" onClick={this.onAccept.bind(this)}>Accept Invite</a>
<a className="dib pointer ml4 pa2 f9 bg-black white mt4" onClick={this.onDecline.bind(this)}>Decline</a>
<a
className="dib pointer pa2 f9 bg-green2 white mt4"
onClick={this.onAccept.bind(this)}>
Accept Invite
</a>
<a
className="dib pointer ml4 pa2 f9 bg-black white mt4"
onClick={this.onDecline.bind(this)}>
Decline
</a>
</div>
)
}

View File

@ -133,7 +133,7 @@ export class Root extends Component {
let station = `/${props.match.params.ship}/${props.match.params.station}`;
let mailbox = state.inbox[station] || {
config: {
read: -1,
read: 0,
length: 0
},
envelopes: []
@ -154,6 +154,7 @@ export class Root extends Component {
api={api}
subscription={subscription}
read={mailbox.config.read}
length={mailbox.config.length}
envelopes={mailbox.envelopes}
inbox={state.inbox}
group={write}