There were three separate places that rendered a mail row:
EmailTile — used in SearchScreen and EmailListScreen._buildEmailList(), accepted an Email object
Inline ListTile in EmailListScreen._buildThreadList() — accepted an EmailThread
Inline ListTile in CombinedInboxScreen._buildThreadTile() — accepted an EmailThread
What changed
New lib/ui/widgets/thread_tile.dart — a single ThreadTile widget that renders an EmailThread with all the thread-level features (message count badge, preview snippet, flagged star, location label). The _fmtDate date-formatting cache previously duplicated in both screens is now owned by this widget.
EmailThread.fromEmail(Email e) factory added to lib/core/models/email.dart — maps a single Email to a one-message EmailThread so search results can be fed to ThreadTile without changing the repository layer.
EmailListScreen and CombinedInboxScreen inline ListTile blocks replaced with ThreadTile. Dismissible wrappers and all selection/swipe logic stay in the screens.
SearchScreen and EmailListScreen._buildEmailList() now convert Email → EmailThread.fromEmail() and use ThreadTile.
EmailTile deleted — no longer has any usages.
Verification
dart analyze on all changed files reports no issues.
## Summary
Closes #429
There were three separate places that rendered a mail row:
- `EmailTile` — used in `SearchScreen` and `EmailListScreen._buildEmailList()`, accepted an `Email` object
- Inline `ListTile` in `EmailListScreen._buildThreadList()` — accepted an `EmailThread`
- Inline `ListTile` in `CombinedInboxScreen._buildThreadTile()` — accepted an `EmailThread`
## What changed
- **New `lib/ui/widgets/thread_tile.dart`** — a single `ThreadTile` widget that renders an `EmailThread` with all the thread-level features (message count badge, preview snippet, flagged star, location label). The `_fmtDate` date-formatting cache previously duplicated in both screens is now owned by this widget.
- **`EmailThread.fromEmail(Email e)` factory** added to `lib/core/models/email.dart` — maps a single `Email` to a one-message `EmailThread` so search results can be fed to `ThreadTile` without changing the repository layer.
- **`EmailListScreen`** and **`CombinedInboxScreen`** inline `ListTile` blocks replaced with `ThreadTile`. `Dismissible` wrappers and all selection/swipe logic stay in the screens.
- **`SearchScreen`** and **`EmailListScreen._buildEmailList()`** now convert `Email` → `EmailThread.fromEmail()` and use `ThreadTile`.
- **`EmailTile` deleted** — no longer has any usages.
## Verification
`dart analyze` on all changed files reports no issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Closes #429
There were three separate places that rendered a mail row:
EmailTile— used inSearchScreenandEmailListScreen._buildEmailList(), accepted anEmailobjectListTileinEmailListScreen._buildThreadList()— accepted anEmailThreadListTileinCombinedInboxScreen._buildThreadTile()— accepted anEmailThreadWhat changed
lib/ui/widgets/thread_tile.dart— a singleThreadTilewidget that renders anEmailThreadwith all the thread-level features (message count badge, preview snippet, flagged star, location label). The_fmtDatedate-formatting cache previously duplicated in both screens is now owned by this widget.EmailThread.fromEmail(Email e)factory added tolib/core/models/email.dart— maps a singleEmailto a one-messageEmailThreadso search results can be fed toThreadTilewithout changing the repository layer.EmailListScreenandCombinedInboxScreeninlineListTileblocks replaced withThreadTile.Dismissiblewrappers and all selection/swipe logic stay in the screens.SearchScreenandEmailListScreen._buildEmailList()now convertEmail→EmailThread.fromEmail()and useThreadTile.EmailTiledeleted — no longer has any usages.Verification
dart analyzeon all changed files reports no issues.