Hold one copy of a message, and the words it ends with
A channel is replayed twice over — once on JOIN, once for the CHATHISTORY we ask for — and in the second form an edited message is two rows, the older of them still holding what it used to say. Landing both put the pre-edit words under the line already showing the current ones, and left them there whenever the revision fell outside the window we asked for. The msgid is the message, across every revision, so it is what a second copy is recognised by. The copy in hand stays, unless the one arriving is the server saying this message has been edited — then its words win, which is the whole point of asking.
61622f7 parent: b52a754 modified
src/frq/state.jolt +30 -3 | @@ -198,8 +198,34 @@ | ||
| 198 | 198 | (swap! channels |
| 199 | 199 | (fn [m] |
| 200 | 200 | (let [m (ensure-channel m channel) |
| 201 | - viewing? (and (chat-visible?) (= channel @current))] | |
| 202 | - (-> m | |
| 201 | + viewing? (and (chat-visible?) (= channel @current)) | |
| 202 | + ;; The server hands the same message over more than once: a | |
| 203 | + ;; JOIN replays the backlog, the CHATHISTORY we ask for | |
| 204 | + ;; replays it again, and a line can have arrived live before | |
| 205 | + ;; either. The msgid is the message's identity and it | |
| 206 | + ;; survives every revision, so holding the copy we have is | |
| 207 | + ;; what keeps a rejoin from doubling the buffer — and what | |
| 208 | + ;; keeps a replayed *pre-edit* row from landing under a line | |
| 209 | + ;; already showing the current text. | |
| 210 | + seen? (and id (some #(= id (:id %)) | |
| 211 | + (get-in m [channel :messages])))] | |
| 212 | + (cond | |
| 213 | + ;; The copy we already hold is the pre-edit one, and this is | |
| 214 | + ;; the server's collapsed row saying so. Same message, later | |
| 215 | + ;; word: take the text rather than the arrival order. | |
| 216 | + (and seen? edited?) | |
| 217 | + (assoc-in m [channel :messages] | |
| 218 | + (mapv (fn [msg] | |
| 219 | + (if (= id (:id msg)) | |
| 220 | + (assoc msg :text text :edited? true | |
| 221 | + :images (media/image-urls text)) | |
| 222 | + msg)) | |
| 223 | + (get-in m [channel :messages]))) | |
| 224 | + | |
| 225 | + seen? m | |
| 226 | + | |
| 227 | + :else | |
| 228 | + (-> m | |
| 203 | 229 | (update-in [channel :messages] conj |
| 204 | 230 | {:from from :text text :system? (= "*" from) |
| 205 | 231 | :actor who |
| @@ -215,7 +241,8 @@ | ||
| 215 | 241 | :edited? (boolean edited?) |
| 216 | 242 | ;; emoji -> the nicks who put it there |
| 217 | 243 | :reactions (or reactions {})}) |
| 218 | - (update-in [channel :unread] (if viewing? (constantly 0) inc))))))))) | |
| 244 | + (update-in [channel :unread] | |
| 245 | + (if viewing? (constantly 0) inc)))))))))) | |
| 219 | 246 | |
| 220 | 247 | (defn open-channel! |
| 221 | 248 | "Show a buffer. A channel we are not in is joined on the way — a row can |
| @@ -198,8 +198,34 @@ | |||
| 198 | (swap! channels | 198 | (swap! channels |
| 199 | (fn [m] | 199 | (fn [m] |
| 200 | (let [m (ensure-channel m channel) | 200 | (let [m (ensure-channel m channel) |
| 201 | - viewing? (and (chat-visible?) (= channel @current))] | 201 | + viewing? (and (chat-visible?) (= channel @current)) |
| 202 | - (-> m | 202 | + ;; The server hands the same message over more than once: a |
| 203 | + ;; JOIN replays the backlog, the CHATHISTORY we ask for | ||
| 204 | + ;; replays it again, and a line can have arrived live before | ||
| 205 | + ;; either. The msgid is the message's identity and it | ||
| 206 | + ;; survives every revision, so holding the copy we have is | ||
| 207 | + ;; what keeps a rejoin from doubling the buffer — and what | ||
| 208 | + ;; keeps a replayed *pre-edit* row from landing under a line | ||
| 209 | + ;; already showing the current text. | ||
| 210 | + seen? (and id (some #(= id (:id %)) | ||
| 211 | + (get-in m [channel :messages])))] | ||
| 212 | + (cond | ||
| 213 | + ;; The copy we already hold is the pre-edit one, and this is | ||
| 214 | + ;; the server's collapsed row saying so. Same message, later | ||
| 215 | + ;; word: take the text rather than the arrival order. | ||
| 216 | + (and seen? edited?) | ||
| 217 | + (assoc-in m [channel :messages] | ||
| 218 | + (mapv (fn [msg] | ||
| 219 | + (if (= id (:id msg)) | ||
| 220 | + (assoc msg :text text :edited? true | ||
| 221 | + :images (media/image-urls text)) | ||
| 222 | + msg)) | ||
| 223 | + (get-in m [channel :messages]))) | ||
| 224 | + | ||
| 225 | + seen? m | ||
| 226 | + | ||
| 227 | + :else | ||
| 228 | + (-> m | ||
| 203 | (update-in [channel :messages] conj | 229 | (update-in [channel :messages] conj |
| 204 | {:from from :text text :system? (= "*" from) | 230 | {:from from :text text :system? (= "*" from) |
| 205 | :actor who | 231 | :actor who |
| @@ -215,7 +241,8 @@ | |||
| 215 | :edited? (boolean edited?) | 241 | :edited? (boolean edited?) |
| 216 | ;; emoji -> the nicks who put it there | 242 | ;; emoji -> the nicks who put it there |
| 217 | :reactions (or reactions {})}) | 243 | :reactions (or reactions {})}) |
| 218 | - (update-in [channel :unread] (if viewing? (constantly 0) inc))))))))) | 244 | + (update-in [channel :unread] |
| 245 | + (if viewing? (constantly 0) inc)))))))))) | ||
| 219 | 246 | ||
| 220 | (defn open-channel! | 247 | (defn open-channel! |
| 221 | "Show a buffer. A channel we are not in is joined on the way — a row can | 248 | "Show a buffer. A channel we are not in is joined on the way — a row can |