blob: 357ed3ddad06b9e2861427ef7587fc6a2690ff3c [file] [log] [blame]
Adrià Vilanova Martínez0d92a0c2023-11-06 01:37:20 +01001import {createPlainTooltip} from '../../../../common/tooltip.js';
2import {kItemMetadataState, kItemMetadataStateI18n} from '../consts.js';
3
4export default class ThreadExtraInfoService {
5 /**
6 * Get a |chipContentList| array with the chips related to the thread, and a
7 * |tooltips| array with the corresponding tooltips which should be
8 * initialized after the chips are added to the DOM.
9 */
10 static getThreadChips(thread) {
11 let chips = [];
12 let tooltips = [];
13
14 const [pendingStateInfo, pendingTooltip] = this.getPendingStateChip(thread);
15 if (pendingStateInfo) chips.push(pendingStateInfo);
16 if (pendingTooltip) tooltips.push(pendingTooltip);
17
18 chips.push(...this.getTrendingChips(thread));
19 chips.push(...this.getMetadataChips(thread));
20
21 const [liveReviewInfo, liveReviewTooltip] =
22 this.getLiveReviewStatusChip(thread);
23 if (liveReviewInfo) chips.push(liveReviewInfo);
24 if (liveReviewTooltip) tooltips.push(liveReviewTooltip);
25
26 return [chips, tooltips];
27 }
28
29 static getPendingStateChip(thread) {
30 const endPendingStateTimestampMicros = thread?.['2']?.['39'];
31 const endPendingStateTimestamp =
32 Math.floor(endPendingStateTimestampMicros / 1e3);
33 const now = Date.now();
34 if (!endPendingStateTimestampMicros || endPendingStateTimestamp < now)
35 return [null, null];
36
37 const span = document.createElement('span');
38 span.textContent =
39 chrome.i18n.getMessage('inject_extrainfo_message_pendingstate');
40
41 const date = new Date(endPendingStateTimestamp).toLocaleString();
42 const pendingTooltip = createPlainTooltip(
43 span,
44 chrome.i18n.getMessage(
45 'inject_extrainfo_message_pendingstate_tooltip', [date]),
46 false);
47 return [span, pendingTooltip];
48 }
49
50 static getTrendingChips(thread) {
51 const chips = [];
52
53 const isTrending = thread?.['2']?.['25'];
54 const isTrendingAutoMarked = thread?.['39'];
55 if (isTrendingAutoMarked)
56 chips.push(document.createTextNode(
57 chrome.i18n.getMessage('inject_extrainfo_thread_autotrending')));
58 else if (isTrending)
59 chips.push(document.createTextNode(
60 chrome.i18n.getMessage('inject_extrainfo_thread_trending')));
61
62 return chips;
63 }
64
65 static getMetadataChips(thread) {
66 const itemMetadata = thread?.['2']?.['12'];
67
68 return [
69 this.getStateChip(itemMetadata),
70 this.getShadowBlockChip(itemMetadata),
71 ].filter(chip => chip !== null);
72 }
73
74 static getLiveReviewStatusChip(thread) {
75 const liveReviewStatus = thread?.['2']?.['38'];
76 const verdict = liveReviewStatus?.['1'];
77 if (!verdict) return [null, null];
78
79 const [label, labelClass] = this.getLiveReviewStatusLabel(verdict);
80 if (!label || !labelClass) return [null, null];
81
82 const reviewedBy = liveReviewStatus?.['2'];
83 const timestamp = liveReviewStatus?.['3'];
84 const date = (new Date(Math.floor(timestamp / 1e3))).toLocaleString();
85
86 let a = document.createElement('a');
87 a.href = 'https://support.google.com/s/community/user/' + reviewedBy;
88 a.classList.add(labelClass);
89 a.textContent = chrome.i18n.getMessage(
90 'inject_extrainfo_message_livereviewverdict',
91 [chrome.i18n.getMessage(
92 'inject_extrainfo_message_livereviewverdict_' + label)]);
93 let liveReviewTooltip = createPlainTooltip(a, date, false);
94 return [a, liveReviewTooltip];
95 }
96
97 static getStateChip(itemMetadata) {
98 const state = itemMetadata?.['1'];
99 if (!state || state == 1) return null;
100
101 const stateI18nKey =
102 'inject_extrainfo_message_state_' + kItemMetadataStateI18n[state];
103 const stateLocalized = chrome.i18n.getMessage(stateI18nKey) ?? state;
104
105 const span = document.createElement('span');
106 span.textContent = chrome.i18n.getMessage(
107 'inject_extrainfo_message_state', [stateLocalized]);
108 span.title = kItemMetadataState[state] ?? state;
109 return span;
110 }
111
112 static getLiveReviewStatusLabel(verdict) {
113 let label, labelClass;
114 switch (verdict) {
115 case 1: // LIVE_REVIEW_RELEVANT
116 label = 'relevant';
117 labelClass = 'TWPT-extrainfo-good';
118 break;
119
120 case 2: // LIVE_REVIEW_OFF_TOPIC
121 label = 'offtopic';
122 labelClass = 'TWPT-extrainfo-bad';
123 break;
124
125 case 3: // LIVE_REVIEW_ABUSE
126 label = 'abuse';
127 labelClass = 'TWPT-extrainfo-bad';
128 break;
129
130 default:
131 return [null, null];
132 }
133 return [label, labelClass];
134 }
135
136 static getShadowBlockChip(itemMetadata) {
137 const shadowBlockInfo = itemMetadata?.['10'];
138 const blockedTimestampMicros = shadowBlockInfo?.['2'];
139 if (!blockedTimestampMicros) return null;
140
141 const isBlocked = shadowBlockInfo?.['1'];
142 let span = document.createElement('span');
143 span.textContent = chrome.i18n.getMessage(
144 'inject_extrainfo_message_shadowblock' +
145 (isBlocked ? 'active' : 'notactive'));
146 if (isBlocked) span.classList.add('TWPT-extrainfo-bad');
147 return span;
148 }
149
150 static getThreadFromThreadList(threadList, currentThreadInfo) {
151 return threadList?.find?.(thread => {
152 const threadInfo = thread?.['2']?.['1'];
153 const threadId = threadInfo?.['1'];
154 const forumId = threadInfo?.['3'];
155 return threadId == currentThreadInfo.thread &&
156 forumId == currentThreadInfo.forum;
157 });
158 }
159}