generated from nhcarrigan/template
feat: implement comprehensive activity feed feature
Implements issue #56 with a timeline-style activity feed showing recent user activity across the library. Activity Types Displayed: - Suggestions: Shows suggested items with status badges - Likes: Shows liked content with links to items - Comments: Shows comments with preview text - Achievements: Shows earned achievements with icons and points Database & Backend: - Created ActivityService to aggregate from existing data sources - No new database table needed (uses existing suggestions, likes, comments, achievements) - Efficient querying with parallel data fetching - Privacy-aware: Only shows activity from users with profilePublic: true - Filters out banned users automatically API Endpoints: - GET /api/activity - Get general activity feed with pagination - GET /api/activity/:userId - Get specific user's activity - Query parameters: limit (max 100, default 50), offset (default 0) - Returns: activities array, total count, hasMore flag Frontend Activity Feed: - Beautiful timeline layout with user avatars and badges - Relative timestamps ("5m ago", "2h ago", "3d ago") - Activity type icons (💡 💬 ❤️ 🏆) - Colour-coded status badges for suggestions - Comment previews with styled quote blocks - Achievement displays with icons and points - Infinite scroll with "Load More" button - Links to user profiles and content items UI Components: - Responsive card-based design - User avatars with placeholder fallback - Badge display (STAFF, MOD, VIP, primary badges) - Entity links that route to appropriate media pages - Clean typography and spacing - Loading and empty states Privacy & Performance: - Respects profilePublic setting (existing privacy control) - Only displays activity from non-banned users - Pagination to prevent loading too much data - Efficient aggregation with limit multipliers - Clean separation of concerns (service/routes/component) Navigation: - Added /activity route - Added "📰 Activity Feed" link to user dropdown menu - Positioned between Leaderboard and About Implementation Notes: - Built entirely from existing data (no activity table needed) - Retroactive: Shows all historical activity automatically - Type-safe with full TypeScript interfaces - Activity union type for type discrimination - Standalone Angular component - Clean, maintainable code structure
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
export * from "./lib/achievement.constants";
|
||||
export * from "./lib/achievement.types";
|
||||
export type * from "./lib/activity.types";
|
||||
export type * from "./lib/art.types";
|
||||
export * from "./lib/audit.types";
|
||||
export * from "./lib/auth.types";
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @copyright 2026 NHCarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
enum ActivityType {
|
||||
suggestion = "SUGGESTION",
|
||||
like = "LIKE",
|
||||
comment = "COMMENT",
|
||||
achievement = "ACHIEVEMENT",
|
||||
}
|
||||
|
||||
interface ActivityUser {
|
||||
id: string;
|
||||
username: string;
|
||||
slug: string | null;
|
||||
avatar: string | null;
|
||||
primaryBadge: string | null;
|
||||
isVip: boolean;
|
||||
isMod: boolean;
|
||||
isStaff: boolean;
|
||||
}
|
||||
|
||||
interface BaseActivity {
|
||||
id: string;
|
||||
type: ActivityType;
|
||||
user: ActivityUser;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
interface SuggestionActivity extends BaseActivity {
|
||||
type: ActivityType.suggestion;
|
||||
entityType: string;
|
||||
suggestionTitle: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface LikeActivity extends BaseActivity {
|
||||
type: ActivityType.like;
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
entityTitle: string;
|
||||
}
|
||||
|
||||
interface CommentActivity extends BaseActivity {
|
||||
type: ActivityType.comment;
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
entityTitle: string;
|
||||
commentPreview: string;
|
||||
}
|
||||
|
||||
interface AchievementActivity extends BaseActivity {
|
||||
type: ActivityType.achievement;
|
||||
achievementKey: string;
|
||||
achievementName: string;
|
||||
achievementIcon: string;
|
||||
achievementPoints: number;
|
||||
}
|
||||
|
||||
type Activity = SuggestionActivity | LikeActivity | CommentActivity | AchievementActivity;
|
||||
|
||||
interface ActivityFeedResponse {
|
||||
activities: Activity[];
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
export { ActivityType };
|
||||
export type {
|
||||
Activity,
|
||||
ActivityFeedResponse,
|
||||
ActivityUser,
|
||||
AchievementActivity,
|
||||
CommentActivity,
|
||||
LikeActivity,
|
||||
SuggestionActivity,
|
||||
};
|
||||
Reference in New Issue
Block a user