fix: render HTML comments properly in activity feed

Changed approach from stripping HTML on backend to rendering HTML with
sanitization on frontend, matching the pattern used in comment-display
component. This preserves HTML formatting (bold, italics, etc.) in
comment previews whilst still protecting against XSS attacks.

Backend changes:
- Reverted stripHtml() method (no longer needed)
- Keep full HTML content in commentPreview field

Frontend changes:
- Import and inject SanitizeService
- Changed from text interpolation to [innerHTML] with sanitization
- Changed <p> to <div> for comment preview container

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 00:13:00 -08:00
parent 512e7eec13
commit 6ef787a3b8
2 changed files with 7 additions and 13 deletions
+4 -12
View File
@@ -224,12 +224,11 @@ export class ActivityService {
entityTitle = comment.manga.title; entityTitle = comment.manga.title;
} }
// Strip HTML tags and get first 100 characters of comment // Get first 100 characters of comment
const plainText = this.stripHtml(comment.content);
const commentPreview = const commentPreview =
plainText.length > 100 comment.content.length > 100
? `${plainText.slice(0, 100)}...` ? `${comment.content.slice(0, 100)}...`
: plainText; : comment.content;
return { return {
id: `comment-${comment.id}`, id: `comment-${comment.id}`,
@@ -351,11 +350,4 @@ export class ActivityService {
return "Unknown Item"; return "Unknown Item";
} }
} }
/**
* Strip HTML tags from content for plain text preview.
*/
private stripHtml(html: string): string {
return html.replace(/<[^>]*>/g, "").trim();
}
} }
@@ -10,6 +10,7 @@ import { RouterLink } from '@angular/router';
import type { Activity } from '@library/shared-types'; import type { Activity } from '@library/shared-types';
import { ActivityType } from '@library/shared-types'; import { ActivityType } from '@library/shared-types';
import { ActivityService } from '../../services/activity.service'; import { ActivityService } from '../../services/activity.service';
import { SanitizeService } from '../../services/sanitize.service';
@Component({ @Component({
selector: 'app-activity-feed', selector: 'app-activity-feed',
@@ -97,7 +98,7 @@ import { ActivityService } from '../../services/activity.service';
{{ activity.entityTitle }} {{ activity.entityTitle }}
</a> </a>
</span> </span>
<p class="comment-preview">"{{ activity.commentPreview }}"</p> <div class="comment-preview" [innerHTML]="sanitizeService.sanitizeHtml(activity.commentPreview)"></div>
</div> </div>
} }
@case (ActivityType.achievement) { @case (ActivityType.achievement) {
@@ -360,6 +361,7 @@ import { ActivityService } from '../../services/activity.service';
}) })
export class ActivityFeedComponent implements OnInit { export class ActivityFeedComponent implements OnInit {
private activityService = inject(ActivityService); private activityService = inject(ActivityService);
public sanitizeService = inject(SanitizeService);
// Make ActivityType accessible in template // Make ActivityType accessible in template
ActivityType = ActivityType; ActivityType = ActivityType;