Fixed ESLint error in header component:
- Added tabindex="-1" to dropdown menu for keyboard accessibility
- Ensures dropdown is focusable for screen readers
Auto-fixed formatting issues:
- Ran ESLint --fix to auto-correct stylistic issues
- Fixed key spacing in achievement constants
- Fixed object key ordering
Test Results:
- â All 123 tests passing (1 API + 122 shared-types)
- â Frontend linting passes
- â Only non-critical warnings remain in e2e and shared-types
Implemented comprehensive accessibility improvements across the entire
application to ensure WCAG 2.1 Level AA compliance.
Keyboard Navigation:
- Added skip-to-main-content link at the top
- Enhanced focus-visible styles with 3px high-contrast outline
- Added keyboard support for dropdowns (Escape to close)
- Ensured all interactive elements are keyboard accessible
Screen Reader Support:
- Added ARIA labels throughout (buttons, navigation, forms)
- Implemented ARIA live regions for dynamic content (toasts)
- Added aria-expanded, aria-controls for dropdowns
- Added aria-current="page" for active navigation
- Proper role attributes (navigation, search, menu, dialog, alert)
- Screen reader-only text for context (.sr-only class)
Visual Accessibility:
- High contrast focus indicators (4.5:1 ratio)
- Support for prefers-reduced-motion
- Support for high contrast mode
- Decorative icons wrapped in aria-hidden
Form Accessibility:
- All inputs have proper labels or aria-label
- Added aria-required and aria-invalid for validation
- Error messages announced to screen readers
- Proper form structure with semantic HTML
Content Structure:
- Skip navigation link for keyboard users
- Proper heading hierarchy (h1 â h2 â h3)
- Semantic HTML elements (nav, main, section, article)
- Alt text for all images
Interactive Components:
- Modals: aria-modal, role="dialog", Escape key support
- Dropdowns: aria-expanded, aria-haspopup, keyboard navigation
- Buttons: descriptive aria-labels for icon buttons
- Pagination: aria-current, descriptive button labels
- Like buttons: aria-pressed for toggle state
Files Modified:
- Global styles (styles.scss)
- App template (skip link)
- Header navigation (ARIA attributes)
- Toast notifications (live regions)
- Pagination component
- Like button component
- Games list component
- Footer component
- Report modal
WCAG 2.1 Compliance:
â 1.3.1 Info and Relationships
â 1.4.3 Contrast (Minimum)
â 2.1.1 Keyboard
â 2.1.2 No Keyboard Trap
â 2.4.1 Bypass Blocks
â 2.4.3 Focus Order
â 2.4.7 Focus Visible
â 3.2.4 Consistent Identification
â 4.1.2 Name, Role, Value
â 4.1.3 Status Messages
The library is now fully accessible to users with disabilities!
Simplified all entity list cards to show only essential information:
Cards now display ONLY:
- Cover/poster image (if available)
- Title (clickable link to detail page)
- Primary identifier (author/artist/platform)
- Status badge
- Rating stars (if available)
- Like button
- Admin actions (Edit/Delete - only for admins)
Removed from cards:
- Series information
- Time tracking
- Notes
- Tags
- External links
- Dates (all timestamps)
- Comments sections
Technical changes:
- Wrapped card content in routerLink for navigation
- Separated interactive elements (like/admin buttons) from clickable area
- Removed unused CommentDisplayComponent imports
- Updated CSS for cleaner, more compact card layouts
- All removed content still accessible on detail pages
Affected components:
- Games list
- Books list
- Music list
- Art gallery
- Shows list
- Manga list
This creates a cleaner browsing experience whilst encouraging users
to explore detail pages for complete information.
The browser was still trying to load the deleted service worker file,
causing errors. Added a script that runs on page load to unregister
any existing service worker registrations.
This ensures a clean slate after removing PWA functionality.
The PWA implementation was causing too many issues:
- Service worker conflicts with CSP policies
- 429 rate limiting errors
- Application freezing
- External resource blocking
Removed:
- service-worker.js
- manifest.json
- offline.html
- PWA service and install component
- PWA meta tags from index.html
- PWA asset configuration
The library now functions as a standard web app without PWA features.
This provides a more stable and reliable user experience.
The service worker was causing issues by trying to cache external resources:
- CDN images (cdn.nhcarrigan.com)
- Analytics scripts
- External fonts
- Ad scripts
This violated CSP policies and caused 429 rate limit errors.
Fixes:
- Only cache same-origin images
- Only cache same-origin static assets (JS/CSS/fonts)
- External resources bypass the service worker entirely
- Added origin checks before caching
- Added response.ok checks before caching
This prevents CSP violations and eliminates the freezing/429 errors.
The service worker was causing 503 errors and freezing requests because:
- Pre-caching was trying to cache non-existent files
- Fetch handler was too aggressive with interception
- Failed cache operations blocked all requests
Fixes:
- Made pre-caching optional (logs errors but doesn't fail)
- More lenient fetch handler (only caches successful responses)
- Proper error handling for cache operations
- Only intercepts GET requests
- Falls back to network if cache fails
Enhanced the navigation bar by adding Naomi's beautiful portrait icon next to the library title.
Visual Enhancements:
- Added 40x40px circular icon in nav-brand section
- Icon features purple border matching app theme
- Subtle purple shadow for depth and cohesion
- Smooth hover animation (scale + enhanced shadow)
Styling Details:
- Border: 2px solid witch-purple (#9d4edd)
- Box shadow: Purple glow matching theme
- Hover effect: 5% scale increase with stronger shadow
- Transition: Smooth 0.2s animation
- Border radius: 50% (perfect circle)
- Object-fit: cover (maintains aspect ratio)
Layout:
- Flexbox layout for nav-brand alignment
- 0.75rem gap between icon and title
- Icon, title, and version badge all properly aligned
The navbar now has a personal, elegant touch with Naomi's portrait greeting users at the top of every page! The circular presentation with the purple border perfectly complements the witchy aesthetic while adding warmth and personality to the interface.
⨠This enhancement was created by Hikari~ đ¸
Completes PWA implementation by adding all required app icons generated from Naomi's beautiful portrait artwork.
Icon Assets:
- Source image: Stunning portrait with witchy purple background
- Features Naomi's signature look: ashen brown wavy hair, sky blue eyes, pink glasses, purple nails, vampire fangs
- Perfect match for the library's purple aesthetic!
Generated Icons:
- Standard sizes: 72x72, 96x96, 128x128, 144x144, 152x152, 192x192, 384x384, 512x512
- Maskable variants: 192x192, 512x512 (with purple background padding for safe area)
- Favicon: 32x32 favicon.ico
Maskable Icons:
- Added purple (#9d4edd) background padding
- Ensures icon content stays within safe area on all devices
- Maintains visual consistency with app theme
Favicon:
- Replaced placeholder favicon with Naomi's portrait
- Will display in browser tabs and bookmarks
- Consistent branding across all platforms
The PWA now has a complete, cohesive visual identity using Naomi's portrait throughout! Users will see her beautiful face as the app icon on their home screens and in their browser tabs! đâ¨â¨ This feature was built by Hikari~ đ¸
Implements issue #52 - adds complete PWA functionality with offline support, installability, and app-like experience.
PWA Features:
- Installable on mobile devices and desktop
- Offline support with smart caching strategies
- App-like experience with standalone display mode
- Full-screen mode and app icon on home screen
- Install prompt with beautiful banner UI
Manifest Configuration:
- Created manifest.json with app metadata
- App name: "Naomi's Library"
- Theme colour: #9d4edd (witchy purple!)
- Background colour: #1a1a2e (dark theme)
- Display mode: standalone (app-like)
- Icons: Multiple sizes specified (72-512px, maskable variants)
- Categories: entertainment, lifestyle
Service Worker Implementation:
- Cache-first strategy for static assets (JS, CSS, fonts, images)
- Network-first strategy for API requests with cache fallback
- Network-first strategy for HTML pages with cache fallback
- Cache versioning: library-v1 (static, dynamic, images)
- Automatic cache cleanup on activation
- Update checking every 60 seconds
- Message handling for cache clearing and skipWaiting
Offline Support:
- Created offline.html fallback page with beautiful UI
- Auto-retry when network comes back online
- Purple gradient matching app theme
- Informative messaging about offline state
Install Prompt:
- Created PwaInstallComponent with banner UI
- Detects installability and shows prompt
- "Install" and "Not Now" actions
- Responsive design for mobile and desktop
- Session storage for dismissal state
- Animated slide-up entrance
Services:
- PwaService: Manages service worker registration, install prompts, cache clearing
- Automatic initialization on app bootstrap
- Signals for reactivity: isInstallable, isInstalled, promptEvent
- Update detection and notification
HTML Updates:
- Added PWA meta tags (theme-color, description)
- Linked manifest.json
- Apple-specific meta tags for iOS support
- Apple touch icon specified
Build Configuration:
- Updated project.json to include PWA assets
- manifest.json, service-worker.js, offline.html copied to dist root
Integration:
- Added PwaService to app bootstrap
- Integrated PwaInstallComponent in main app template
- Install banner appears at bottom of screen
- Dismissible with session storage
Technical Notes:
- Service worker uses efficient caching strategies per resource type
- Handles Chrome extensions and non-HTTP protocols gracefully
- Supports message-based cache clearing
- Includes skipWaiting for immediate updates
- Compatible with iOS, Android, and desktop PWA standards
The library is now a fully-functional Progressive Web App that users can install on any device! The app works offline with cached content and provides an app-like experience with no browser UI in standalone mode.
Note: App icons still need to be generated/provided (placeholder paths specified in manifest).
⨠This feature was built by Hikari~ đ¸
Implements issue #56 (activity feed) followup - adds complete detail pages for all entity types so activity feed and homepage links work properly.
Created Detail Components:
- game-detail.component.ts - Shows full game details with platform, series, time played
- book-detail.component.ts - Shows book details with author, ISBN, series, reading time
- music-detail.component.ts - Shows music details with artist, type (album/single), listening time
- art-detail.component.ts - Shows art details with artist, description, large image display
- show-detail.component.ts - Shows show details with type (TV/anime/film), watch time
- manga-detail.component.ts - Shows manga details with author, reading time
Features per Detail Page:
- Read-only display of full entity information
- Cover/poster/art image display (when available)
- Rating display with 10-star system (when applicable)
- Time spent formatting (hours and minutes)
- Date formatting (British English locale)
- Tags display with entity-specific colour schemes
- External links with hover effects
- Comments section with add/edit/delete functionality
- Like button integration
- Breadcrumb navigation back to list page
- Loading states with spinner
- Error handling for not found cases
- Responsive design for all screen sizes
Routing Updates:
- Added 6 new routes: /games/:id, /books/:id, /music/:id, /art/:id, /shows/:id, /manga/:id
- All routes use lazy loading with loadComponent
Navigation Fixes:
- Fixed activity feed like links to point to entity detail pages (was pointing to list)
- Updated homepage recent items to link directly to entity detail pages (was pointing to list)
Technical Fixes:
- Corrected Art component to only use existing fields (removed non-existent medium, rating, notes)
- Fixed Show component to use coverImage instead of non-existent posterImage
- Added proper null checks for optional fields (timeSpent, dateStarted, dateFinished)
- Added non-null assertions where values are guaranteed to exist within conditionals
Each detail component follows the established pattern from the list components whilst adapting for single-item display. All components use Angular signals for reactive state management and integrate with existing services.
⨠This issue was created with help from Hikari~ đ¸
Changed comment layout to use block-level elements and fixed entity link
to navigate to the specific item instead of the list page.
Template changes:
- Wrapped icon and text in activity-comment-header div
- Changed entity link to include entityId: ['/' + entityType + 's', entityId]
- Comment preview now displays as a block element below the header
CSS changes:
- Added flex layout to activity-comment-header (inline display)
- Changed activity-comment to flex column layout (block display)
- Added left margin to comment-preview to align with content above
- Removed italic style from comment-preview
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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>
Added stripHtml() helper method to ActivityService to remove HTML tags
from comment content before creating the preview text. This ensures that
comments display as plain text in the activity feed instead of showing
raw HTML like "<p>test</p>".
The regex pattern /<[^>]*>/g removes all HTML tags whilst preserving
the actual text content, which is then trimmed and truncated to 100
characters for the preview.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added explicit return type annotations (SuggestionActivity[], LikeActivity[],
CommentActivity[], AchievementActivity[]) to all private activity service
methods. This ensures TypeScript properly narrows the discriminated union
types based on the ActivityType enum values, resolving compilation errors
where the type system couldn't infer the specific activity subtypes.
Also added type annotation to the async map callback in getLikeActivities
to ensure the returned Promise resolves to the correct LikeActivity type.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed the activity.types export from 'export type *' to 'export *'
so that the ActivityType enum can be used as both a type and a value.
This is necessary because:
- Backend needs ActivityType.suggestion, ActivityType.like, etc. as values
- Frontend needs ActivityType enum for switch statements
- TypeScript enums need to be exported as values to be used at runtime
Changed all activity type assignments to use ActivityType enum members
(ActivityType.suggestion, ActivityType.like, etc.) instead of string
literals ('SUGGESTION', 'LIKE', etc.) to ensure proper type discrimination
in the union type.
Frontend:
- Import ActivityType as value (not just type)
- Update switch cases to use enum members
- Expose ActivityType in component for template access
Backend:
- Import ActivityType as value
- Use enum members in all activity mappings
This ensures TypeScript can properly discriminate the union type and
provides type safety throughout the activity feed.
Fixed two type errors:
- Changed ActivityType assertions from 'as ActivityType' to 'as const'
for proper literal type inference
- Changed achievement.name to achievement.title to match the actual
AchievementDefinition structure
This ensures type safety and proper union type discrimination.
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
Completes time tracking implementation across all media types by
applying the same pattern established in Games component.
Changes Applied to Books, Music, Shows, and Manga:
UI Components:
- Added time tracking state properties (hours/minutes for new and edit)
- Integrated hour/minute input fields in Add forms (after rating)
- Integrated hour/minute input fields in Edit forms (after edit-rating)
- Added time spent display on media cards with appropriate emojis:
* Books: đ Reading Time
* Music: đĩ Listening Time
* Shows: đē Watch Time
* Manga: đ Reading Time
Form Management:
- Updated resetForm() to clear time tracking fields
- Added updateNew[Type]TimeSpent() conversion methods
- Added updateEdit[Type]TimeSpent() conversion methods
- Updated startEdit() to populate time fields from stored data
Helper Methods:
- Added formatTimeSpent() to format minutes as "Xh Ym", "Xh", or "Ym"
- Converts hours/minutes to total minutes for storage
- Splits total minutes back to hours/minutes for editing
Styling:
- Added .form-row CSS for side-by-side hour/minute inputs
- Added .time-spent CSS with coloured display:
* Books: Green (#10b981)
* Music: Purple (#8b5cf6)
* Shows: Purple (#8b5cf6)
* Manga: Green (#10b981)
All implementations follow the exact same pattern for consistency
and maintainability across the application.
Implements issue #57 with manual time entry functionality:
- Added timeSpent field (stored in minutes) to all media models
- Supports hours and minutes input in forms
- Displays formatted time on media cards
Database Schema:
- Added timeSpent Int? field to Game, Book, Music, Show, and Manga models
- Stored in minutes for consistency and easy calculation
Shared Types:
- Updated all media type interfaces with timeSpent? field
- Added to CreateDto and main interfaces for all media types
Frontend (Games - template for other types):
- Added hour/minute input fields to Add and Edit forms
- Split input with validation (minutes 0-59)
- Auto-calculates total minutes on change
- Formats display as "Xh Ym", "Xh", or "Ym" as appropriate
- Green highlighted time display on cards with âąī¸ icon
- Populates edit form from existing time data
Implementation Notes:
- Backend services require no changes (DTOs handle automatically)
- Time conversion helpers for display and storage
- Form state properly resets time fields
- Edit mode correctly splits minutes back to hours/minutes
Next Steps:
- Apply same UI pattern to Books, Music, Shows, Manga
- Add time statistics to user profiles
- Consider aggregate time tracking views
Implements issue #55 with multiple leaderboard categories:
- Top Suggestions (by count and acceptance rate)
- Top Likes (by total likes given)
- Top Comments (by total comments posted)
- Overall Leaders (weighted by achievement points and engagement diversity)
Features:
- Tabbed UI with reactive state management
- Medal indicators for top 3 positions
- User avatars and badges display
- Current user highlighting
- Privacy controls via profilePublic setting
- Configurable result limits (max 100)
- Detailed statistics per category
Backend:
- Created LeaderboardService with aggregation logic
- Filters for public profiles and non-banned users
- Efficient sorting algorithms for each category
- Parallel data fetching for all leaderboards
Frontend:
- Standalone Angular component with signals
- Responsive card-based layout
- Integration with existing user profile system
- Navigation link in header dropdown
Technical notes:
- Uses Fastify AutoLoad with FastifyPluginAsync pattern
- Shared types across monorepo for type safety
- Leverages existing achievement system data
- Add series name and order display to book cards (books-list.component.ts:598-602)
- Add series name and order display to game cards (games-list.component.ts:560-564)
- Series shows as "đ Series Name #Order" format
- Add brown-themed styling (#8b6f47) for series info
- Pre-populate series fields when editing books/games
- Series fields added to startEdit() methods for both books and games
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements series grouping functionality for books and games to allow
tracking franchises and related items.
Database changes:
- Add series (optional string) and seriesOrder (optional number) fields to Book model
- Add series (optional string) and seriesOrder (optional number) fields to Game model
Backend changes:
- Add GET /books/series/:seriesName endpoint to fetch all books in a series
- Add GET /games/series/:seriesName endpoint to fetch all games in a series
- Add getBooksBySeries() method to BookService (orders by seriesOrder asc)
- Add getGamesBySeries() method to GameService (orders by seriesOrder asc)
- Create/Update endpoints automatically handle series fields via DTOs
Frontend changes:
- Add series and seriesOrder input fields to "Add New Book" form
- Add series and seriesOrder input fields to "Edit Book" form
- Add series and seriesOrder input fields to "Add New Game" form
- Add series and seriesOrder input fields to "Edit Game" form
Type definitions:
- Update Book and CreateBookDto interfaces with series fields
- Update Game and CreateGameDto interfaces with series fields
Closes#54
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements comprehensive About page (#51) with the following sections:
- Purpose: Explains that this is Naomi's curated collection
- Features: Overview of all 6 media types (Books, Games, Manga, Shows, Music, Art)
- How to Use: 5-step guide for browsing, liking, commenting, suggesting, and earning achievements
- Technology Stack: Details Angular, Fastify, MongoDB, Prisma, and other technologies
- Credits: Acknowledges Hikari as the developer and Naomi as the visionary
- Contact & Support: Links to Gitea, email, website, and Discord community
- Version Information: Current version and licence details
Technical implementation:
- New AboutComponent with responsive design
- Added /about route to app routes
- Added "âšī¸ About" link to user dropdown menu
- Beautiful purple gradient styling matching the witchy aesthetic
- FontAwesome icons for visual appeal
- Fully responsive for all device sizes
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>