mirror of
https://codeberg.org/emersion/gamja.git
synced 2024-11-14 19:05:01 -05:00
Add support for CHATHISTORY TARGETS
The main motivation is to avoid missing direct messages coming from other users. A nice side-effect is that we no longer need to issue CHATHISTORY queries for each channel we JOIN: instead, we can only fetch history for targets known to have new messages available (as indicated by CHATHISTORY TARGETS). We use read receipts instead of delivery receipts, so that reloading the webapp restores the exact same state (ie, unread messages are re-fetched). References: https://github.com/ircv3/ircv3-specifications/pull/450
This commit is contained in:
parent
74d9dea5bb
commit
91208a6d47
2 changed files with 56 additions and 11 deletions
|
@ -450,6 +450,20 @@ export default class App extends Component {
|
|||
this.saveReceipts();
|
||||
}
|
||||
|
||||
latestReceipt(type) {
|
||||
var last = null;
|
||||
this.receipts.forEach((receipts, target) => {
|
||||
var delivery = receipts[type];
|
||||
if (target == "*" || !delivery || !delivery.time) {
|
||||
return;
|
||||
}
|
||||
if (!last || delivery.time > last.time) {
|
||||
last = delivery;
|
||||
}
|
||||
});
|
||||
return last;
|
||||
}
|
||||
|
||||
addMessage(netID, bufName, msg) {
|
||||
var client = this.clients.get(netID);
|
||||
|
||||
|
@ -608,6 +622,21 @@ export default class App extends Component {
|
|||
params: [this.state.connectParams.autojoin.join(",")],
|
||||
});
|
||||
}
|
||||
|
||||
var lastReceipt = this.latestReceipt(ReceiptType.READ);
|
||||
if (lastReceipt && lastReceipt.time && client.enabledCaps["draft/chathistory"] && (!client.enabledCaps["soju.im/bouncer-networks"] || client.params.bouncerNetwork)) {
|
||||
var now = irc.formatDate(new Date());
|
||||
client.fetchHistoryTargets(now, lastReceipt.time).then((targets) => {
|
||||
targets.forEach((target) => {
|
||||
var from = this.getReceipt(target, ReceiptType.READ);
|
||||
if (!from) {
|
||||
from = lastReceipt;
|
||||
}
|
||||
var to = { time: msg.tags.time || irc.formatDate(new Date()) };
|
||||
this.fetchBacklog(client, target.name, from, to);
|
||||
});
|
||||
});
|
||||
}
|
||||
break;
|
||||
case irc.RPL_MYINFO:
|
||||
// TODO: parse available modes
|
||||
|
@ -729,17 +758,6 @@ export default class App extends Component {
|
|||
this.switchBuffer({ network: netID, name: channel });
|
||||
this.switchToChannel = null;
|
||||
}
|
||||
|
||||
var receipt = this.getReceipt(channel, ReceiptType.READ);
|
||||
if (client.isMyNick(msg.prefix.name) && receipt && client.enabledCaps["draft/chathistory"] && client.enabledCaps["server-time"]) {
|
||||
var after = receipt;
|
||||
var before = { time: msg.tags.time || irc.formatDate(new Date()) };
|
||||
client.fetchHistoryBetween(channel, after, before, CHATHISTORY_MAX_SIZE).catch((err) => {
|
||||
this.setState({ error: "Failed to fetch history: " + err });
|
||||
this.receipts.delete(channel);
|
||||
this.saveReceipts();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "PART":
|
||||
var channel = msg.params[0];
|
||||
|
@ -886,6 +904,7 @@ export default class App extends Component {
|
|||
case "PONG":
|
||||
case "BATCH":
|
||||
case "TAGMSG":
|
||||
case "CHATHISTORY":
|
||||
// Ignore these
|
||||
break;
|
||||
default:
|
||||
|
@ -928,6 +947,14 @@ export default class App extends Component {
|
|||
return irc.STD_CHANNEL_TYPES.indexOf(name[0]) >= 0;
|
||||
}
|
||||
|
||||
fetchBacklog(client, target, after, before) {
|
||||
client.fetchHistoryBetween(target, after, before, CHATHISTORY_MAX_SIZE).catch((err) => {
|
||||
this.setState({ error: "Failed to fetch history for '" + taregt + "': " + err });
|
||||
this.receipts.delete(channel);
|
||||
this.saveReceipts();
|
||||
});
|
||||
}
|
||||
|
||||
open(target) {
|
||||
var netID = getActiveNetworkID(this.state);
|
||||
var client = this.clients.get(netID);
|
||||
|
|
|
@ -575,6 +575,24 @@ export default class Client extends EventTarget {
|
|||
});
|
||||
}
|
||||
|
||||
fetchHistoryTargets(t1, t2) {
|
||||
var msg = {
|
||||
command: "CHATHISTORY",
|
||||
params: ["TARGETS", "timestamp=" + t1, "timestamp=" + t2, 1000],
|
||||
};
|
||||
return this.fetchBatch(msg, "draft/chathistory-targets").then((batch) => {
|
||||
return batch.messages.map((msg) => {
|
||||
if (msg.command != "CHATHISTORY" || msg.params[0] != "TARGETS") {
|
||||
throw new Error("Cannot fetch chat history targets: unexpected message " + msg);
|
||||
}
|
||||
return {
|
||||
name: msg.params[1],
|
||||
latestMessage: msg.params[2],
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
listBouncerNetworks() {
|
||||
if (!this.enabledCaps["soju.im/bouncer-networks"]) {
|
||||
return Promise.reject(new Error("Server doesn't support the BOUNCER extension"));
|
||||
|
|
Loading…
Reference in a new issue