diff --git a/components/app.js b/components/app.js index 5fc2034..53ae60c 100644 --- a/components/app.js +++ b/components/app.js @@ -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); diff --git a/lib/client.js b/lib/client.js index 39ca4d3..fb95cc5 100644 --- a/lib/client.js +++ b/lib/client.js @@ -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"));