mirror of
https://codeberg.org/emersion/gamja.git
synced 2024-11-15 03:15:01 -05:00
Introduce buffer IDs
This commit is contained in:
parent
abc2fbcfb1
commit
50ea6e121e
2 changed files with 77 additions and 34 deletions
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
`;
|
`;
|
||||||
|
|
Loading…
Reference in a new issue