@salesforcedevs/docs-components 1.17.0-hack-alpha8 → 1.17.2-accessfix-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lwc.config.json +0 -1
- package/package.json +1 -1
- package/src/modules/doc/componentPlayground/componentPlayground.css +1 -1
- package/src/modules/doc/componentPlayground/componentPlayground.html +2 -2
- package/src/modules/doc/componentPlayground/componentPlayground.ts +4 -0
- package/src/modules/doc/content/content.css +6 -17
- package/src/modules/doc/heading/heading.css +0 -56
- package/src/modules/doc/heading/heading.html +0 -40
- package/src/modules/doc/heading/heading.ts +0 -24
- package/src/modules/doc/xmlContent/xmlContent.html +9 -1
- package/src/modules/doc/xmlContent/xmlContent.ts +18 -8
- package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +0 -1
- package/src/modules/doc/commentPopup/README.md +0 -322
- package/src/modules/doc/commentPopup/commentDevHelper.ts +0 -380
- package/src/modules/doc/commentPopup/commentPopup.css +0 -440
- package/src/modules/doc/commentPopup/commentPopup.html +0 -138
- package/src/modules/doc/commentPopup/commentPopup.ts +0 -565
- package/src/modules/doc/commentPopup/commentUtils.ts +0 -382
|
@@ -1,565 +0,0 @@
|
|
|
1
|
-
import { LightningElement, api, track } from "lwc";
|
|
2
|
-
import { normalizeBoolean } from "dxUtils/normalizers";
|
|
3
|
-
|
|
4
|
-
interface Comment {
|
|
5
|
-
email: string;
|
|
6
|
-
comment_text: string;
|
|
7
|
-
timestamp: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface ApiCommentPayload {
|
|
11
|
-
branch: string;
|
|
12
|
-
file_path: string;
|
|
13
|
-
heading_title: string;
|
|
14
|
-
start_line: string;
|
|
15
|
-
end_line: string;
|
|
16
|
-
comment: {
|
|
17
|
-
comment_text: string;
|
|
18
|
-
email: string;
|
|
19
|
-
timestamp: string;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface ApiCommentResponse {
|
|
24
|
-
request_branch: string;
|
|
25
|
-
paths: Array<{
|
|
26
|
-
path: string;
|
|
27
|
-
titles: Array<{
|
|
28
|
-
title: string;
|
|
29
|
-
comments: Comment[];
|
|
30
|
-
}>;
|
|
31
|
-
}>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Local storage key for comments
|
|
35
|
-
const COMMENTS_STORAGE_KEY = "dsc_comments";
|
|
36
|
-
const API_URL = "https://cx-helper-engine-2-356c8dd6c7cc.herokuapp.com";
|
|
37
|
-
|
|
38
|
-
export default class CommentPopup extends LightningElement {
|
|
39
|
-
iconSymbol = "chat";
|
|
40
|
-
iconSize = "medium";
|
|
41
|
-
popupTitle = "Leave a Comment";
|
|
42
|
-
submitButtonLabel = "Post Comment";
|
|
43
|
-
emailPlaceholder = "Enter your email";
|
|
44
|
-
commentPlaceholder = "Enter your comment";
|
|
45
|
-
|
|
46
|
-
private _headingTitle?: string;
|
|
47
|
-
private _filePath?: string;
|
|
48
|
-
private _startLine?: string;
|
|
49
|
-
private _endLine?: string;
|
|
50
|
-
private _currentBranch?: string;
|
|
51
|
-
|
|
52
|
-
@api get headingTitle() {
|
|
53
|
-
return this._headingTitle;
|
|
54
|
-
}
|
|
55
|
-
set headingTitle(value) {
|
|
56
|
-
this._headingTitle = value;
|
|
57
|
-
this.handlePropertyChange();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@api get filePath() {
|
|
61
|
-
return this._filePath;
|
|
62
|
-
}
|
|
63
|
-
set filePath(value) {
|
|
64
|
-
this._filePath = value;
|
|
65
|
-
this.handlePropertyChange();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@api get startLine() {
|
|
69
|
-
return this._startLine;
|
|
70
|
-
}
|
|
71
|
-
set startLine(value) {
|
|
72
|
-
this._startLine = value;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@api get endLine() {
|
|
76
|
-
return this._endLine;
|
|
77
|
-
}
|
|
78
|
-
set endLine(value) {
|
|
79
|
-
this._endLine = value;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@api get currentBranch() {
|
|
83
|
-
return this._currentBranch;
|
|
84
|
-
}
|
|
85
|
-
set currentBranch(value) {
|
|
86
|
-
this._currentBranch = value;
|
|
87
|
-
this.handlePropertyChange();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private _open = false;
|
|
91
|
-
@api get open() {
|
|
92
|
-
return this._open;
|
|
93
|
-
}
|
|
94
|
-
set open(value) {
|
|
95
|
-
this._open = normalizeBoolean(value);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@track private comments: Comment[] = [];
|
|
99
|
-
@track private email = "";
|
|
100
|
-
@track private comment = "";
|
|
101
|
-
@track private emailError = "";
|
|
102
|
-
@track private commentError = "";
|
|
103
|
-
@track private isSubmitting = false;
|
|
104
|
-
@track private isLoading = false;
|
|
105
|
-
@track private apiError = "";
|
|
106
|
-
@track private submitSuccess = false;
|
|
107
|
-
|
|
108
|
-
get showComments() {
|
|
109
|
-
return this.comments.length > 0;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
get commentCount() {
|
|
113
|
-
return this.comments.length;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
get showCommentCount() {
|
|
117
|
-
return this.commentCount > 0;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
get sortedComments() {
|
|
121
|
-
return [...this.comments].sort(
|
|
122
|
-
(a, b) =>
|
|
123
|
-
new Date(b.timestamp).getTime() -
|
|
124
|
-
new Date(a.timestamp).getTime()
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
get isFormValid() {
|
|
129
|
-
return this.email.trim() !== "" && this.comment.trim() !== "";
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
get submitButtonDisabled() {
|
|
133
|
-
return !this.isFormValid || this.isSubmitting;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
handleIconClick() {
|
|
137
|
-
this._open = !this._open;
|
|
138
|
-
// Comments are already loaded when component is connected, no need to fetch again
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private handlePropertyChange() {
|
|
142
|
-
// Only fetch comments if component has required properties
|
|
143
|
-
if (this._currentBranch && this._filePath && this._headingTitle) {
|
|
144
|
-
this.fetchComments();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
handleEmailChange(event: Event) {
|
|
149
|
-
this.email = (event.target as HTMLInputElement).value;
|
|
150
|
-
this.emailError = "";
|
|
151
|
-
this.apiError = "";
|
|
152
|
-
this.submitSuccess = false;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
handleCommentChange(event: Event) {
|
|
156
|
-
this.comment = (event.target as HTMLTextAreaElement).value;
|
|
157
|
-
this.commentError = "";
|
|
158
|
-
this.apiError = "";
|
|
159
|
-
this.submitSuccess = false;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async handleSubmit() {
|
|
163
|
-
if (!this.validateForm()) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
this.isSubmitting = true;
|
|
168
|
-
this.apiError = "";
|
|
169
|
-
this.submitSuccess = false;
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
await this.addComment();
|
|
173
|
-
this.resetForm();
|
|
174
|
-
this.submitSuccess = true;
|
|
175
|
-
|
|
176
|
-
// Auto-hide success message after 3 seconds
|
|
177
|
-
setTimeout(() => {
|
|
178
|
-
this.submitSuccess = false;
|
|
179
|
-
}, 3000);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.error("Error submitting comment:", error);
|
|
182
|
-
this.apiError = "Failed to post comment. Please try again.";
|
|
183
|
-
} finally {
|
|
184
|
-
this.isSubmitting = false;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private validateForm(): boolean {
|
|
189
|
-
let isValid = true;
|
|
190
|
-
|
|
191
|
-
if (!this.email.trim()) {
|
|
192
|
-
this.emailError = "Email is required";
|
|
193
|
-
isValid = false;
|
|
194
|
-
} else if (!this.isValidEmail(this.email)) {
|
|
195
|
-
this.emailError = "Please enter a valid email address";
|
|
196
|
-
isValid = false;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (!this.comment.trim()) {
|
|
200
|
-
this.commentError = "Comment is required";
|
|
201
|
-
isValid = false;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return isValid;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
private isValidEmail(email: string): boolean {
|
|
208
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
209
|
-
return emailRegex.test(email);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private async addComment() {
|
|
213
|
-
// Validate required fields before creating payload
|
|
214
|
-
if (!this._currentBranch || !this._filePath || !this._headingTitle) {
|
|
215
|
-
throw new Error(
|
|
216
|
-
"Missing required fields: branch, file_path, or heading_title"
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const payload: ApiCommentPayload = {
|
|
221
|
-
branch: this._currentBranch,
|
|
222
|
-
file_path: this._filePath,
|
|
223
|
-
heading_title: this._headingTitle,
|
|
224
|
-
start_line: this._startLine || "",
|
|
225
|
-
end_line: this._endLine || "",
|
|
226
|
-
comment: {
|
|
227
|
-
comment_text: this.comment.trim(),
|
|
228
|
-
email: this.email.trim(),
|
|
229
|
-
timestamp: new Date().toISOString()
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
console.log(
|
|
234
|
-
"Posting comment with payload:",
|
|
235
|
-
JSON.stringify(payload, null, 2)
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// Try API first, fallback to localStorage
|
|
239
|
-
try {
|
|
240
|
-
const response = await fetch(`${API_URL}/post-comment`, {
|
|
241
|
-
method: "POST",
|
|
242
|
-
headers: {
|
|
243
|
-
"Content-Type": "application/json"
|
|
244
|
-
},
|
|
245
|
-
body: JSON.stringify(payload)
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
if (response.ok) {
|
|
249
|
-
const responseData = await response.json();
|
|
250
|
-
console.log(
|
|
251
|
-
"Comment posted successfully via API:",
|
|
252
|
-
responseData
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
// Refresh comments after successful post
|
|
256
|
-
await this.fetchComments();
|
|
257
|
-
|
|
258
|
-
// Dispatch custom event
|
|
259
|
-
this.dispatchEvent(
|
|
260
|
-
new CustomEvent("commentadded", {
|
|
261
|
-
detail: {
|
|
262
|
-
...payload.comment,
|
|
263
|
-
id: responseData.id // Include any ID returned by the API
|
|
264
|
-
}
|
|
265
|
-
})
|
|
266
|
-
);
|
|
267
|
-
} else {
|
|
268
|
-
console.warn(
|
|
269
|
-
`API call failed (${response.status}): ${response.statusText}, falling back to localStorage`
|
|
270
|
-
);
|
|
271
|
-
// Fallback to localStorage if API fails
|
|
272
|
-
await this.saveCommentToLocalStorage(payload);
|
|
273
|
-
console.log(
|
|
274
|
-
"Comment saved to local storage successfully (fallback)"
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
// Refresh comments after successful save
|
|
278
|
-
await this.fetchComments();
|
|
279
|
-
|
|
280
|
-
// Dispatch custom event
|
|
281
|
-
this.dispatchEvent(
|
|
282
|
-
new CustomEvent("commentadded", {
|
|
283
|
-
detail: {
|
|
284
|
-
...payload.comment,
|
|
285
|
-
id: Date.now().toString() // Generate temporary ID
|
|
286
|
-
}
|
|
287
|
-
})
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
} catch (error) {
|
|
291
|
-
console.error(
|
|
292
|
-
"Error posting comment via API, falling back to localStorage:",
|
|
293
|
-
error
|
|
294
|
-
);
|
|
295
|
-
// Fallback to localStorage if API call throws an error
|
|
296
|
-
try {
|
|
297
|
-
await this.saveCommentToLocalStorage(payload);
|
|
298
|
-
console.log(
|
|
299
|
-
"Comment saved to local storage successfully (fallback)"
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
// Refresh comments after successful save
|
|
303
|
-
await this.fetchComments();
|
|
304
|
-
|
|
305
|
-
// Dispatch custom event
|
|
306
|
-
this.dispatchEvent(
|
|
307
|
-
new CustomEvent("commentadded", {
|
|
308
|
-
detail: {
|
|
309
|
-
...payload.comment,
|
|
310
|
-
id: Date.now().toString() // Generate temporary ID
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
);
|
|
314
|
-
} catch (localStorageError) {
|
|
315
|
-
console.error(
|
|
316
|
-
"Error saving comment to local storage:",
|
|
317
|
-
localStorageError
|
|
318
|
-
);
|
|
319
|
-
throw localStorageError;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
private async saveCommentToLocalStorage(payload: ApiCommentPayload) {
|
|
325
|
-
try {
|
|
326
|
-
// Get existing comments from localStorage
|
|
327
|
-
const existingData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
328
|
-
const allComments = existingData ? JSON.parse(existingData) : {};
|
|
329
|
-
|
|
330
|
-
// Create a unique key for this specific comment location
|
|
331
|
-
const commentKey = `${payload.branch}_${payload.file_path}_${payload.heading_title}`;
|
|
332
|
-
|
|
333
|
-
// Initialize array for this location if it doesn't exist
|
|
334
|
-
if (!allComments[commentKey]) {
|
|
335
|
-
allComments[commentKey] = [];
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Add the new comment
|
|
339
|
-
allComments[commentKey].push(payload.comment);
|
|
340
|
-
|
|
341
|
-
// Save back to localStorage
|
|
342
|
-
localStorage.setItem(
|
|
343
|
-
COMMENTS_STORAGE_KEY,
|
|
344
|
-
JSON.stringify(allComments)
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
console.log("Comments saved to localStorage:", allComments);
|
|
348
|
-
} catch (error) {
|
|
349
|
-
console.error("Error saving to localStorage:", error);
|
|
350
|
-
throw new Error("Failed to save comment to local storage");
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
private resetForm() {
|
|
355
|
-
this.email = "";
|
|
356
|
-
this.comment = "";
|
|
357
|
-
this.emailError = "";
|
|
358
|
-
this.commentError = "";
|
|
359
|
-
|
|
360
|
-
// Force DOM update by accessing the form elements
|
|
361
|
-
const emailInput = this.template.querySelector(
|
|
362
|
-
"#email-input"
|
|
363
|
-
) as HTMLInputElement;
|
|
364
|
-
const commentInput = this.template.querySelector(
|
|
365
|
-
"#comment-input"
|
|
366
|
-
) as HTMLTextAreaElement;
|
|
367
|
-
|
|
368
|
-
if (emailInput) {
|
|
369
|
-
emailInput.value = "";
|
|
370
|
-
}
|
|
371
|
-
if (commentInput) {
|
|
372
|
-
commentInput.value = "";
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
handleClose() {
|
|
377
|
-
this._open = false;
|
|
378
|
-
this.resetForm();
|
|
379
|
-
this.apiError = "";
|
|
380
|
-
this.submitSuccess = false;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
handleOverlayClick(event: Event) {
|
|
384
|
-
if (event.target === event.currentTarget) {
|
|
385
|
-
this.handleClose();
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
handleKeyDown(event: KeyboardEvent) {
|
|
390
|
-
if (event.key === "Escape") {
|
|
391
|
-
this.handleClose();
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
connectedCallback() {
|
|
396
|
-
document.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
397
|
-
// Fetch comments when component is connected
|
|
398
|
-
this.fetchComments();
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
private async fetchComments() {
|
|
402
|
-
if (!this._currentBranch || !this._filePath || !this._headingTitle) {
|
|
403
|
-
console.warn("Cannot fetch comments: missing required parameters");
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
this.isLoading = true;
|
|
408
|
-
this.apiError = "";
|
|
409
|
-
|
|
410
|
-
try {
|
|
411
|
-
// API IMPLEMENTATION - Try to fetch from /get-comments endpoint
|
|
412
|
-
const params = new URLSearchParams({
|
|
413
|
-
branch: this._currentBranch
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
console.log("Fetching comments with params:", params.toString());
|
|
417
|
-
|
|
418
|
-
const response = await fetch(
|
|
419
|
-
`${API_URL}/get-comments?${params.toString()}`
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
if (response.ok) {
|
|
423
|
-
const data: ApiCommentResponse = await response.json();
|
|
424
|
-
console.log("Fetched comments from API:", data);
|
|
425
|
-
|
|
426
|
-
// Find comments for this specific file path and heading title
|
|
427
|
-
const comments = this.extractCommentsFromApiResponse(data);
|
|
428
|
-
this.comments = comments;
|
|
429
|
-
} else {
|
|
430
|
-
console.warn(
|
|
431
|
-
`API call failed (${response.status}): ${response.statusText}, falling back to localStorage`
|
|
432
|
-
);
|
|
433
|
-
// Fallback to localStorage if API fails
|
|
434
|
-
const comments = await this.getCommentsFromLocalStorage();
|
|
435
|
-
console.log(
|
|
436
|
-
"Fetched comments from localStorage (fallback):",
|
|
437
|
-
comments
|
|
438
|
-
);
|
|
439
|
-
this.comments = comments;
|
|
440
|
-
}
|
|
441
|
-
} catch (error) {
|
|
442
|
-
console.error(
|
|
443
|
-
"Error fetching comments from API, falling back to localStorage:",
|
|
444
|
-
error
|
|
445
|
-
);
|
|
446
|
-
// Fallback to localStorage if API call throws an error
|
|
447
|
-
try {
|
|
448
|
-
const comments = await this.getCommentsFromLocalStorage();
|
|
449
|
-
console.log(
|
|
450
|
-
"Fetched comments from localStorage (fallback):",
|
|
451
|
-
comments
|
|
452
|
-
);
|
|
453
|
-
this.comments = comments;
|
|
454
|
-
} catch (localStorageError) {
|
|
455
|
-
console.error(
|
|
456
|
-
"Error fetching comments from localStorage:",
|
|
457
|
-
localStorageError
|
|
458
|
-
);
|
|
459
|
-
this.apiError =
|
|
460
|
-
"Failed to load comments. Please try again later.";
|
|
461
|
-
this.comments = [];
|
|
462
|
-
}
|
|
463
|
-
} finally {
|
|
464
|
-
this.isLoading = false;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
private extractCommentsFromApiResponse(
|
|
469
|
-
data: ApiCommentResponse
|
|
470
|
-
): Comment[] {
|
|
471
|
-
try {
|
|
472
|
-
// Find the path that matches our file path
|
|
473
|
-
const matchingPath = data.paths.find(
|
|
474
|
-
(path) => path.path === this._filePath
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
if (!matchingPath) {
|
|
478
|
-
console.log(
|
|
479
|
-
`No comments found for file path: ${this._filePath}`
|
|
480
|
-
);
|
|
481
|
-
return [];
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Find the title that matches our heading title
|
|
485
|
-
const matchingTitle = matchingPath.titles.find(
|
|
486
|
-
(title) => title.title === this._headingTitle
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
if (!matchingTitle) {
|
|
490
|
-
console.log(
|
|
491
|
-
`No comments found for heading title: ${this._headingTitle}`
|
|
492
|
-
);
|
|
493
|
-
return [];
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
console.log(
|
|
497
|
-
`Found ${matchingTitle.comments.length} comments for ${this._filePath} - ${this._headingTitle}`
|
|
498
|
-
);
|
|
499
|
-
return matchingTitle.comments;
|
|
500
|
-
} catch (error) {
|
|
501
|
-
console.error(
|
|
502
|
-
"Error extracting comments from API response:",
|
|
503
|
-
error
|
|
504
|
-
);
|
|
505
|
-
return [];
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
private async getCommentsFromLocalStorage(): Promise<Comment[]> {
|
|
510
|
-
try {
|
|
511
|
-
const existingData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
512
|
-
if (!existingData) {
|
|
513
|
-
return [];
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
const allComments = JSON.parse(existingData);
|
|
517
|
-
const commentKey = `${this._currentBranch}_${this._filePath}_${this._headingTitle}`;
|
|
518
|
-
|
|
519
|
-
return allComments[commentKey] || [];
|
|
520
|
-
} catch (error) {
|
|
521
|
-
console.error("Error reading from localStorage:", error);
|
|
522
|
-
return [];
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
disconnectedCallback() {
|
|
527
|
-
document.removeEventListener("keydown", this.handleKeyDown.bind(this));
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
private formatTimestamp(timestamp: string): string {
|
|
531
|
-
const now = new Date();
|
|
532
|
-
const commentDate = new Date(timestamp);
|
|
533
|
-
const diff = now.getTime() - commentDate.getTime();
|
|
534
|
-
const minutes = Math.floor(diff / 60000);
|
|
535
|
-
const hours = Math.floor(diff / 3600000);
|
|
536
|
-
const days = Math.floor(diff / 86400000);
|
|
537
|
-
|
|
538
|
-
if (minutes < 1) {
|
|
539
|
-
return "Just now";
|
|
540
|
-
} else if (minutes < 60) {
|
|
541
|
-
return `${minutes} minute${minutes > 1 ? "s" : ""} ago`;
|
|
542
|
-
} else if (hours < 24) {
|
|
543
|
-
return `${hours} hour${hours > 1 ? "s" : ""} ago`;
|
|
544
|
-
}
|
|
545
|
-
return `${days} day${days > 1 ? "s" : ""} ago`;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
get formattedComments() {
|
|
549
|
-
return this.sortedComments.map((comment) => ({
|
|
550
|
-
...comment,
|
|
551
|
-
comment: comment.comment_text, // Map API field to template expectation
|
|
552
|
-
formattedTimestamp: this.formatTimestamp(comment.timestamp),
|
|
553
|
-
maskedEmail: this.maskEmail(comment.email)
|
|
554
|
-
}));
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
private maskEmail(email: string): string {
|
|
558
|
-
const [localPart, domain] = email.split("@");
|
|
559
|
-
const maskedLocal =
|
|
560
|
-
localPart.length > 2
|
|
561
|
-
? localPart.substring(0, 2) + "*".repeat(localPart.length - 2)
|
|
562
|
-
: localPart;
|
|
563
|
-
return `${maskedLocal}@${domain}`;
|
|
564
|
-
}
|
|
565
|
-
}
|