Add flattenthreads experiment
This experiment allows users to flatten the replies in threads, so they
are shown linearly in a chronological way instead of nested.
When the option is enabled, a switch is added to the thread page which
lets the user switch between flattening replies and not flattening them.
Some UI is still missing (see the design document[1]).
[1]: https://docs.google.com/document/d/1P-HanTHxaOFF_FHh0uSv0GIhG1dxWTJTGoT6VPjjvY0/edit
Bug: twpowertools:153
Change-Id: I43f94442cadc12b752700f0e8d974522be621d3e
diff --git a/src/models/Thread.js b/src/models/Thread.js
new file mode 100644
index 0000000..e02c0a5
--- /dev/null
+++ b/src/models/Thread.js
@@ -0,0 +1,99 @@
+import GapModel from './Gap.js';
+import MessageModel from './Message.js';
+
+export default class ThreadModel {
+ /**
+ * The following code is based on logic written by Googlers in the TW frontend
+ * and thus is not included as part of the MIT license.
+ */
+ static mergeMessageOrGaps(a, b) {
+ if (a.length == 0 || b.length == 0)
+ return a.length > 0 ? a : b.length > 0 ? b : [];
+
+ let e = [];
+ for (let g = 0, k = 0, m = 0, q = a[g], u = b[k];
+ g < a.length && k < b.length;) {
+ if (q instanceof MessageModel && u instanceof MessageModel) {
+ if (q.getCreatedMicroseconds() === u.getCreatedMicroseconds()) {
+ u.mergeCommentOrGapViews(q);
+ }
+
+ e.push(u);
+
+ if (g === a.length - 1 || k === b.length - 1) {
+ for (; ++g < a.length;) e.push(a[g]);
+ for (; ++k < b.length;) e.push(b[k]);
+ break;
+ }
+
+ q = a[++g];
+ u = b[++k];
+ } else {
+ if (u instanceof GapModel) {
+ let z;
+ for (z = q instanceof MessageModel ? q.getCreatedMicroseconds() :
+ q.getEndTimestamp();
+ z < u.getEndTimestamp();) {
+ e.push(q);
+ m += q instanceof GapModel ? q.getCount() : 1;
+ if (g === a.length - 1) break;
+ q = a[++g];
+ z = q instanceof MessageModel ? q.getCreatedMicroseconds() :
+ q.getEndTimestamp();
+ }
+ if (q instanceof GapModel && u.getCount() - m > 0 &&
+ z >= u.getEndTimestamp()) {
+ const gm = new GapModel();
+ gm.setCount(u.getCount() - m);
+ gm.setStartMicroseconds('' + q.getStartTimestamp());
+ gm.setEndMicroseconds('' + u.getEndTimestamp());
+ gm.setParentId(u.getParentId());
+ e.push(gm);
+ m = u.getCount() - m;
+ } else {
+ m = 0;
+ }
+ if (k === b.length - 1) break;
+ u = b[++k];
+ }
+ if (q instanceof GapModel) {
+ let z;
+ for (z = u instanceof MessageModel ? u.getCreatedMicroseconds() :
+ u.getEndTimestamp();
+ z < q.getEndTimestamp();) {
+ e.push(u);
+ m += u instanceof GapModel ? u.getCount() : 1;
+ if (k === b.length - 1) break;
+ u = b[++k];
+ z = u instanceof MessageModel ? u.getCreatedMicroseconds() :
+ u.getEndTimestamp();
+ }
+ if (u instanceof GapModel && q.getCount() - m > 0 &&
+ z >= q.getEndTimestamp()) {
+ const gm = new GapModel();
+ gm.setCount(q.getCount() - m);
+ gm.setStartMicroseconds('' + u.getStartTimestamp());
+ gm.setEndMicroseconds('' + q.getEndTimestamp());
+ gm.setParentId(q.getParentId());
+ e.push(gm);
+ m = q.getCount() - m;
+ } else {
+ m = 0;
+ }
+ if (g === a.length - 1) break;
+ q = a[++g];
+ }
+ }
+ }
+ return e;
+ }
+
+ static mergeMessageOrGapsMultiarray(mogsModels) {
+ if (mogsModels.length < 1) return [];
+ let mergeResult = mogsModels[0];
+ for (let i = 1; i < mogsModels.length; ++i) {
+ mergeResult = ThreadModel.mergeMessageOrGaps(mergeResult, mogsModels[i]);
+ }
+ return mergeResult;
+ }
+}