Introduce buffer IDs

This commit is contained in:
Simon Ser 2021-01-21 20:41:44 +01:00
parent abc2fbcfb1
commit 50ea6e121e
2 changed files with 77 additions and 34 deletions

View file

@ -99,6 +99,18 @@ function updateState(state, updater) {
return { ...state, ...updated }; return { ...state, ...updated };
} }
function getBuffer(state, network, name) {
if (!network) {
network = state.activeNetwork;
}
for (var buf of state.buffers.values()) {
if (buf.network === network && buf.name === name) {
return buf;
}
}
return null;
}
export default class App extends Component { export default class App extends Component {
client = null; client = null;
state = { state = {
@ -125,6 +137,7 @@ export default class App extends Component {
buffer = createRef(); buffer = createRef();
composer = createRef(); composer = createRef();
reconnectTimeoutID = null; reconnectTimeoutID = null;
lastBufferID = 0;
constructor(props) { constructor(props) {
super(props); super(props);
@ -192,6 +205,9 @@ export default class App extends Component {
} }
var updated = updateState(net, updater); var updated = updateState(net, updater);
if (!updated) {
return;
}
var networks = new Map(state.networks); var networks = new Map(state.networks);
networks.set(id, updated); networks.set(id, updated);
@ -199,27 +215,44 @@ export default class App extends Component {
}, callback); }, callback);
} }
setBufferState(name, updater, callback) { setBufferState(id, updater, callback) {
this.setState((state) => { this.setState((state) => {
var buf = state.buffers.get(name); var buf;
switch (typeof id) {
case "object":
buf = getBuffer(state, id.network, id.name);
break;
case "number":
buf = state.buffers.get(id);
break;
default:
throw new Error("Invalid buffer ID type: " + (typeof id));
}
if (!buf) { if (!buf) {
return; return;
} }
var updated = updateState(buf, updater); var updated = updateState(buf, updater);
if (!updated) {
return;
}
var buffers = new Map(state.buffers); var buffers = new Map(state.buffers);
buffers.set(name, updated); buffers.set(buf.id, updated);
return { buffers }; return { buffers };
}, callback); }, callback);
} }
createBuffer(netID, name) { createBuffer(netID, name, callback) {
var id = null;
this.setState((state) => { this.setState((state) => {
if (state.buffers.get(name)) { if (getBuffer(state, netID, name)) {
return; return;
} }
this.lastBufferID++;
id = this.lastBufferID;
var type; var type;
if (name == SERVER_BUFFER) { if (name == SERVER_BUFFER) {
type = BufferType.SERVER; type = BufferType.SERVER;
@ -231,6 +264,7 @@ export default class App extends Component {
var bufferList = Array.from(state.buffers.values()); var bufferList = Array.from(state.buffers.values());
bufferList.push({ bufferList.push({
id,
name, name,
type, type,
network: netID, network: netID,
@ -243,24 +277,34 @@ export default class App extends Component {
unread: Unread.NONE, unread: Unread.NONE,
}); });
bufferList = bufferList.sort(compareBuffers); bufferList = bufferList.sort(compareBuffers);
var buffers = new Map(bufferList.map((buf) => [buf.name, buf])); var buffers = new Map(bufferList.map((buf) => [buf.id, buf]));
return { buffers }; return { buffers };
}, () => {
if (callback) {
callback(id);
}
}); });
} }
switchBuffer(name) { switchBuffer(name) {
var lastReadReceipt = this.getReceipt(name, ReceiptType.READ); var lastReadReceipt = this.getReceipt(name, ReceiptType.READ);
// TODO: only mark as read if user scrolled at the bottom // TODO: only mark as read if user scrolled at the bottom
this.setBufferState(name, { this.setBufferState({ name }, {
unread: Unread.NONE, unread: Unread.NONE,
lastReadReceipt, lastReadReceipt,
}); });
this.setState({ activeBuffer: name }, () => { var buf;
this.setState((state) => {
buf = getBuffer(state, null, name);
if (!buf) {
return;
}
return { activeBuffer: buf.id };
}, () => {
if (this.composer.current) { if (this.composer.current) {
this.composer.current.focus(); this.composer.current.focus();
} }
var buf = this.state.buffers.get(name);
if (!buf || buf.messages.length == 0) { if (!buf || buf.messages.length == 0) {
return; return;
} }
@ -356,11 +400,11 @@ export default class App extends Component {
this.setReceipt(bufName, ReceiptType.DELIVERED, msg); this.setReceipt(bufName, ReceiptType.DELIVERED, msg);
this.setBufferState(bufName, (buf, state) => { this.setBufferState({ network: netID, name: bufName}, (buf, state) => {
// TODO: set unread if scrolled up // TODO: set unread if scrolled up
var unread = buf.unread; var unread = buf.unread;
var lastReadReceipt = buf.lastReadReceipt; var lastReadReceipt = buf.lastReadReceipt;
if (state.activeBuffer != buf.name) { if (state.activeBuffer != buf.id) {
unread = Unread.union(unread, msgUnread); unread = Unread.union(unread, msgUnread);
} else { } else {
this.setReceipt(bufName, ReceiptType.READ, msg); this.setReceipt(bufName, ReceiptType.READ, msg);
@ -461,18 +505,18 @@ export default class App extends Component {
name: msg.params[1], name: msg.params[1],
version: msg.params[2], version: msg.params[2],
}; };
this.setBufferState(SERVER_BUFFER, { serverInfo }); this.setBufferState({ network: netID, name: SERVER_BUFFER}, { serverInfo });
break; break;
case irc.RPL_NOTOPIC: case irc.RPL_NOTOPIC:
var channel = msg.params[1]; var channel = msg.params[1];
this.setBufferState(channel, { topic: null }); this.setBufferState({ network: netID, name: channel}, { topic: null });
break; break;
case irc.RPL_TOPIC: case irc.RPL_TOPIC:
var channel = msg.params[1]; var channel = msg.params[1];
var topic = msg.params[2]; var topic = msg.params[2];
this.setBufferState(channel, { topic }); this.setBufferState({ network: netID, name: channel}, { topic });
break; break;
case irc.RPL_TOPICWHOTIME: case irc.RPL_TOPICWHOTIME:
// Ignore // Ignore
@ -481,7 +525,7 @@ export default class App extends Component {
var channel = msg.params[2]; var channel = msg.params[2];
var membersList = msg.params[3].split(" "); var membersList = msg.params[3].split(" ");
this.setBufferState(channel, (buf) => { this.setBufferState({ network: netID, name: channel}, (buf) => {
var members = new Map(buf.members); var members = new Map(buf.members);
membersList.forEach((s) => { membersList.forEach((s) => {
var member = irc.parseMembership(s); var member = irc.parseMembership(s);
@ -504,13 +548,13 @@ export default class App extends Component {
realname: last.slice(last.indexOf(" ") + 1), realname: last.slice(last.indexOf(" ") + 1),
}; };
this.setBufferState(who.nick, { who, offline: false }); this.setBufferState({ network: netID, name: who.nick}, { who, offline: false });
break; break;
case irc.RPL_ENDOFWHO: case irc.RPL_ENDOFWHO:
var target = msg.params[1]; var target = msg.params[1];
if (!this.isChannel(target) && target.indexOf("*") < 0) { if (!this.isChannel(target) && target.indexOf("*") < 0) {
// Not a channel nor a mask, likely a nick // Not a channel nor a mask, likely a nick
this.setBufferState(target, (buf) => { this.setBufferState({ network: netID, name: target}, (buf) => {
// TODO: mark user offline if we have old WHO info but this // TODO: mark user offline if we have old WHO info but this
// WHO reply is empty // WHO reply is empty
if (buf.who) { if (buf.who) {
@ -532,7 +576,7 @@ export default class App extends Component {
var channel = msg.params[0]; var channel = msg.params[0];
this.createBuffer(netID, channel); this.createBuffer(netID, channel);
this.setBufferState(channel, (buf) => { this.setBufferState({ network: netID, name: channel}, (buf) => {
var members = new Map(buf.members); var members = new Map(buf.members);
members.set(msg.prefix.name, null); members.set(msg.prefix.name, null);
return { members }; return { members };
@ -559,7 +603,7 @@ export default class App extends Component {
case "PART": case "PART":
var channel = msg.params[0]; var channel = msg.params[0];
this.setBufferState(channel, (buf) => { this.setBufferState({ network: netID, name: channel}, (buf) => {
var members = new Map(buf.members); var members = new Map(buf.members);
members.delete(msg.prefix.name); members.delete(msg.prefix.name);
return { members }; return { members };
@ -613,13 +657,13 @@ export default class App extends Component {
var channel = msg.params[0]; var channel = msg.params[0];
var topic = msg.params[1]; var topic = msg.params[1];
this.setBufferState(channel, { topic }); this.setBufferState({ network: netID, name: channel}, { topic });
this.addMessage(netID, channel, msg); this.addMessage(netID, channel, msg);
break; break;
case "AWAY": case "AWAY":
var awayMessage = msg.params[0]; var awayMessage = msg.params[0];
this.setBufferState(msg.prefix.name, (buf) => { this.setBufferState({ network: netID, name: msg.prefix.name}, (buf) => {
var who = { ...buf.who, away: !!awayMessage }; var who = { ...buf.who, away: !!awayMessage };
return { who }; return { who };
}); });
@ -738,12 +782,12 @@ export default class App extends Component {
return; return;
} }
var target = this.state.activeBuffer; var buf = this.state.buffers.get(this.state.activeBuffer);
if (!target) { if (!buf) {
return; return;
} }
this.privmsg(target, text); this.privmsg(buf.name, text);
} }
handleBufferListClick(name) { handleBufferListClick(name) {
@ -783,10 +827,10 @@ export default class App extends Component {
return repl; return repl;
} }
if (!this.state.activeBuffer) { var buf = this.state.buffers.get(this.state.activeBuffer);
if (!buf || !buf.members) {
return null; return null;
} }
var buf = this.state.buffers.get(this.state.activeBuffer);
return fromList(buf.members.keys(), prefix); return fromList(buf.members.keys(), prefix);
} }
@ -842,17 +886,16 @@ export default class App extends Component {
} }
handleBufferScrollTop() { handleBufferScrollTop() {
var target = this.state.activeBuffer; var buf = this.state.buffers.get(this.state.activeBuffer);
if (!target || target == SERVER_BUFFER) { if (!buf || buf.type == BufferType.SERVER) {
return; return;
} }
if (!this.client.enabledCaps["draft/chathistory"] || !this.client.enabledCaps["server-time"]) { if (!this.client.enabledCaps["draft/chathistory"] || !this.client.enabledCaps["server-time"]) {
return; return;
} }
if (this.endOfHistory.get(target)) { if (this.endOfHistory.get(buf.name)) {
return; return;
} }
var buf = this.state.buffers.get(target);
var before; var before;
if (buf.messages.length > 0) { if (buf.messages.length > 0) {
@ -862,11 +905,11 @@ export default class App extends Component {
} }
// Avoids sending multiple CHATHISTORY commands in parallel // Avoids sending multiple CHATHISTORY commands in parallel
this.endOfHistory.set(target, true); this.endOfHistory.set(buf.name, true);
var params = ["BEFORE", target, "timestamp=" + before, CHATHISTORY_PAGE_SIZE]; var params = ["BEFORE", buf.name, "timestamp=" + before, CHATHISTORY_PAGE_SIZE];
this.roundtripChatHistory(params).then((batch) => { this.roundtripChatHistory(params).then((batch) => {
this.endOfHistory.set(target, batch.messages.length < CHATHISTORY_PAGE_SIZE); this.endOfHistory.set(buf.name, batch.messages.length < CHATHISTORY_PAGE_SIZE);
}); });
} }

View file

@ -31,7 +31,7 @@ export default function BufferList(props) {
return html` return html`
<ul> <ul>
${Array.from(props.buffers.values()).map((buf) => html` ${Array.from(props.buffers.values()).map((buf) => html`
<${BufferItem} key=${buf.name} buffer=${buf} onClick=${() => props.onBufferClick(buf.name)} active=${props.activeBuffer == buf.name}/> <${BufferItem} key=${buf.id} buffer=${buf} onClick=${() => props.onBufferClick(buf.name)} active=${props.activeBuffer == buf.id}/>
`)} `)}
</ul> </ul>
`; `;