generated from nhcarrigan/template
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:
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user