- EmailRow shared component with hover action buttons: mark read/unread, star/unstar, trash
- Optimistic UI — local state patches immediately on click; row fades and becomes non-interactive during the API call
- Trashed rows disappear from the current folder view via onRemove callback
- Backend: EmailController single-email endpoints (POST /email/{id}/read|unread|star|unstar|trash|untrash)
- Star/Unstar added to CleanupActionType enum and CleanupService (maps to STARRED Gmail label via BatchModifyAsync)
- UserId scope enforced in ResolveTargetsAsync — a user can only act on their own emails
- action buttons use stopPropagation so clicking them does not open Gmail
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New FolderView page at /app/folder/:slug renders a paginated email table
- Sidebar links now route to their respective folder views
- Extended SearchRequestDto with IsInInbox, IsStarred, IsTrashed, GmailLabel, Category, MinSizeBytes
- SearchService applies the new filters; GmailLabel does a label-id lookup (SENT/DRAFT/SPAM)
- Frontend folderToRequest() maps every slug to the correct search payload
- Email rows show sender, subject+snippet, attachment icon, size, date; bold for unread
- Pagination controls for large folders
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>