blob: 2cd1771ba276a40d9b190c2fe99f57926a9c5454 [file] [log] [blame]
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;