| import { ProtobufNumber, ProtobufObject } from '../../common/protojs.types'; |
| import { kAdditionalInfoClass } from '../../features/flattenThreads/core/flattenThreads.js'; |
| import GapModel from '../../models/Gap'; |
| import MessageModel from '../../models/Message'; |
| import StartupDataModel from '../../models/StartupData'; |
| import ThreadModel from '../../models/Thread'; |
| import { Modifier } from './types'; |
| |
| const currentUser = StartupDataModel.buildFromCCDOM().getCurrentUserModel(); |
| |
| const flattenThread: Modifier = { |
| urlRegex: /api\/ViewThread/i, |
| featureGated: true, |
| features: ['flattenthreads', 'flattenthreads_switch_enabled'], |
| isEnabled(options) { |
| return ( |
| options['flattenthreads'] && options['flattenthreads_switch_enabled'] |
| ); |
| }, |
| async interceptor(response) { |
| if (!response[1]?.[40]) return response; |
| |
| const thread = new ThreadModel(response[1]); |
| |
| // Do the actual flattening |
| const originalMogs = thread.getMessageOrGapModels(); |
| let extraMogs: Array<MessageModel | GapModel> = []; |
| originalMogs.forEach((mog) => { |
| if (mog instanceof GapModel) return; |
| const cogs = mog.getCommentsAndGaps(); |
| extraMogs = extraMogs.concat(cogs); |
| mog.clearCommentsAndGaps(); |
| }); |
| const mogs = originalMogs.concat(extraMogs); |
| |
| // Add some message data to the payload so the extension can show the parent |
| // comment/reply in the case of comments. |
| const newPayloads: Record<string, string> = {}; |
| let prevReplyId: ProtobufNumber | undefined = undefined; |
| let prevReplyParentId: ProtobufNumber | undefined = undefined; |
| mogs.forEach((m) => { |
| if (m instanceof GapModel) return; |
| |
| const info = getAdditionalInformation( |
| m, |
| mogs, |
| prevReplyId, |
| prevReplyParentId, |
| ); |
| prevReplyId = m.getId(); |
| prevReplyParentId = info.isComment ? info.parentId : undefined; |
| |
| const extraInfoEl = document.createElement('code'); |
| extraInfoEl.textContent = JSON.stringify(info); |
| extraInfoEl.setAttribute('style', 'display: none'); |
| extraInfoEl.classList.add(kAdditionalInfoClass); |
| newPayloads[m.getId()] = m.getPayload() + extraInfoEl.outerHTML; |
| }); |
| mogs.forEach( |
| (m) => m instanceof MessageModel && m.setPayload(newPayloads[m.getId()]), |
| ); |
| |
| // Clear parent_message_id fields |
| mogs.forEach((m) => m instanceof MessageModel && m.clearParentMessageId()); |
| |
| // Sort the messages by date |
| mogs.sort((a, b) => { |
| const c = |
| a instanceof MessageModel |
| ? a.getCreatedMicroseconds() |
| : a.getStartTimestamp(); |
| const d = |
| b instanceof MessageModel |
| ? b.getCreatedMicroseconds() |
| : b.getStartTimestamp(); |
| const diff = c - d; |
| return diff > 0 ? 1 : diff < 0 ? -1 : 0; |
| }); |
| |
| thread.setRawCommentsAndGaps(mogs.map((mog) => mog.toRawMessageOrGap())); |
| |
| // Set last_message to the last message after sorting |
| thread.setLastMessage(thread.getRawCommentsAndGaps().slice(-1)?.[1]); |
| |
| // Set num_messages to the updated value, since we've flattened the replies. |
| thread.setNumMessages(thread.getRawCommentsAndGaps().length); |
| |
| response[1] = thread.toRawThread(); |
| return response; |
| }, |
| }; |
| |
| interface BaseAdditionalInformation { |
| id: ProtobufNumber; |
| authorName: string; |
| canComment: boolean; |
| } |
| |
| type CommentAdditionalInformation = |
| | { |
| isComment: false; |
| } |
| | { |
| isComment: true; |
| parentId: ProtobufNumber; |
| prevMessage: { |
| id: ProtobufNumber; |
| payload: string; |
| author: ProtobufObject | null; |
| }; |
| }; |
| |
| export type AdditionalInformation = BaseAdditionalInformation & |
| CommentAdditionalInformation; |
| |
| function getAdditionalInformation( |
| message: MessageModel, |
| mogs: Array<MessageModel | GapModel>, |
| prevReplyId: ProtobufNumber | undefined, |
| prevReplyParentId: ProtobufNumber | undefined, |
| ): AdditionalInformation { |
| const id = message.getId(); |
| const parentId = message.getParentMessageId(); |
| const authorName = message.getAuthor()?.[1]?.[1]; |
| const canComment = message.canComment(currentUser); |
| if (!parentId) { |
| return { |
| isComment: false, |
| id, |
| authorName, |
| canComment, |
| }; |
| } |
| |
| let prevId; |
| if (parentId == prevReplyParentId && prevReplyParentId) { |
| prevId = prevReplyId; |
| } else { |
| prevId = parentId; |
| } |
| |
| const prevMessage = prevId |
| ? mogs.find( |
| (m): m is MessageModel => |
| m instanceof MessageModel && m.getId() == prevId, |
| ) |
| : null; |
| |
| return { |
| isComment: true, |
| id, |
| authorName, |
| parentId, |
| prevMessage: { |
| id: prevId, |
| payload: prevMessage?.getPayload(), |
| author: prevMessage?.getAuthor(), |
| }, |
| canComment, |
| }; |
| } |
| |
| export default flattenThread; |