Codebase audit: DRY/consistency cleanup of the view tier#176
Conversation
Lay the groundwork for de-duplicating the view tier and correct several inaccuracies that had crept into the architecture docs. Shared infrastructure (additive): - lib/shared/components/async_state_views.dart: LoadingView, EmptyView, InlineError — replacements for the Center(CircularProgressIndicator()), empty-list, and red error-Text snippets inlined across ~25 screens. - member_display.dart: accordInitial() — the `name.isEmpty ? '?' : name[0].toUpperCase()` idiom (~24 copies). - AccordMemberAvatar: status is now optional (null omits the presence dot), so it covers the no-presence avatars too instead of bespoke CircleAvatars. - rest_result_ext.dart: RestResult.fold() + showInfoSnack() companion. - message_time.dart: messageTimestampString() for the full tooltip date; drops the duplicated weekday/month tables from accord_home_message_row. Cleanups: - events: move accord_event_handler.dart from utils/ -> controllers/ (it is the gateway-wiring hub, not a stateless util) and update its importer. - space_cache: log the two previously-silent empty catch blocks. - pubspec: drop freezed/freezed_annotation/json_serializable/ dart_mappable_builder — zero usages in lib/, no build.yaml references them. - CLAUDE.md: client models are hand-rolled (not json_serializable); the per-server AccordClient is owned by AccordAuth (not connections.dart); describe the real gateway-dispatcher + controller data-access pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
Replace duplicated view-tier snippets with the shared helpers added in the previous commit. Behavior-preserving throughout. - LoadingView replaces inline `Center(child: CircularProgressIndicator())` at ~28 sites (admin, moderation, settings, messaging, DM, spaces). - accordInitial() replaces the `name.isEmpty ? '?' : name[0].toUpperCase()` idiom at ~24 sites. - showInfoSnack()/showErrorSnack() replace hand-inlined info/error SnackBars. - AccordMemberAvatar (status omitted) replaces clean inline CircleAvatars. - InlineError standardized on bodySmall (the convention used by every error arm) so future swaps are exact; EmptyView dropped (no call sites). Notes: - in-button/footer fixed-size spinners and avatars with bespoke initial styling were intentionally left as-is (not the shared shape). - accord_home_space_actions: leave-failure toasts go through the pre-captured ScaffoldMessenger (+ errorOr) rather than reaching for context post-await. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
The box was opened at startup but never read or written anywhere in lib/ — leftover Bonfire guild-channel memory. Removing it eliminates the last live Discord-vocabulary identifier in the codebase. No migration needed: nothing consumed the box, so there is no persisted data in use. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
Replace the `Text(error, bodySmall + color: colors.red)` error arm of the async-list state machine with InlineError across the moderation, admin, settings, member and DM screens (~22 sites). Behavior-preserving: InlineError renders bodySmall in the theme red, matching the prior inline style. - Dead `colors` locals left behind in admin_users_tab/admin_spaces_tab build() methods removed. - Skipped non-matching reds (button foregroundColors, icon tints, bold danger labels, colorScheme.error, static descriptions) — only true error arms with the exact bodySmall+red shape were converted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
The codebase split feature widgets between views/ and components/ with no consistent rule (channels/messaging used components/ even for full screens; everyone else used views/). Standardize on views/ (the documented majority). - channels/components/ -> channels/views/ - messaging/components/ -> messaging/views/ (incl. box/ subtree) - user/components/self_status_button.dart -> user/views/ - remove empty notifications/components/ and voice/components/ - update all import paths in lib/ and test/ accordingly Pure move + import-path rewrite; no code logic changed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
…tests - connections_settings_page: restore bodyMedium for the full-region error (was silently downgraded to bodySmall when InlineError was applied; InlineError is documented for the exact bodySmall arm shape only) - updates_page: convert two remaining bodySmall/red error arms to InlineError, completing the sweep started in commit b208895 - test: accordInitial — edge cases (null, empty, whitespace, trim, case) - test: messageTimestampString — weekday/month names, minute padding - test: showInfoSnack — verifies snackbar text is shown
Code review findingsReviewed the full diff (70 files, 5 commits). The PR is structurally solid — the helper extraction is correct, imports are consistent, and the 🔴 High — Style regression in
|
CI analyze failed for two reasons, both fixed here: - Removing freezed/json_serializable/dart_mappable_builder from pubspec.yaml (commit 618bae9) left the tracked pubspec.lock stale, so CI's `flutter pub get` re-resolved and bumped transitive packages — which surfaced new analyzer warnings in the vendored livekit_client fork. With no local Dart toolchain to regenerate the lock, restore the four deps so pubspec.yaml matches the committed lock again (master is green with it). The deps remain genuinely unused; dropping them is left as a follow-up that needs a local `flutter pub get`. CLAUDE.md updated to say so. - Remove the unused `colors` local left in accord_direct_messages_groups.dart build() after its error arm became InlineError (fatal unused_local_variable). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
The beta channel's newer analyzer (beta-3.46.0) flags unawaited_return_in_try_block in the vendored livekit_client fork — return resultFuture inside the try/catch. This is pre-existing beta-SDK drift, not from this PR's changes, but the CI matrix gates on beta with no continue-on-error. Await the (already-completed) completer future at both return sites. The completer is completed with a value immediately before each return, so this is behavior-preserving; it just lets the analyzer see the future is awaited. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpNEK8oWh7ryG1yX43QerF
What this is
An organization/consistency audit of
lib/followed by the fixes. The codebase was already strong on fundamentals (one Riverpod codegen style, single gateway-wiring hub, 100% package-absolute imports, snake_case throughout). The work here targets the two areas that had drifted: duplicated view-tier boilerplate and a few inconsistent/incorrect conventions.Net result: 70 files, +271/−292 (a net reduction) with no behavior changes intended.
Changes (by commit)
1. Shared infrastructure + doc/dep drift (
618bae9)lib/shared/components/async_state_views.dart:LoadingView,InlineError.accordInitial()helper (thename.isEmpty ? '?' : name[0].toUpperCase()idiom, ~24 copies).AccordMemberAvatargeneralized — presence dot is now optional, so it covers the no-presence avatars too.showInfoSnack()companion to the existingshowErrorSnack().messageTimestampString()— kills the duplicated weekday/month tables in the message row.accord_event_handler.dartfromevents/utils/→events/controllers/(it's the gateway-wiring hub, not a stateless util).catchblocks inspace_cache.dart.freezed/freezed_annotation/json_serializable/dart_mappable_builder— zero usages inlib/, nobuild.yamlreferences them.CLAUDE.md: client models are hand-rolled (notjson_serializable); the per-serverAccordClientis owned byAccordAuth(notconnections.dart); documented the real gateway-dispatcher + controller data-access pattern.2. Apply the helpers across views (
efe7500)LoadingViewreplaces inlineCenter(child: CircularProgressIndicator())at ~28 sites.accordInitial,showInfoSnack, andAccordMemberAvatarapplied where they cleanly fit.3. Drop dead
last-guild-channelsHive box (3c46225)4. Consolidate error arms into
InlineError(b208895)Text(error, bodySmall + colors.red)error arms →InlineError. Deadcolorslocals cleaned up. Only true error arms with the exact shape were converted (button foregroundColors, icon tints, bold danger labels,colorScheme.errorleft alone).5. Unify per-feature widget dirs under
views/(b73de3a)channels/components/,messaging/components/(incl.box/),user/components/self_status_button.dart→views/; removed emptynotifications/components/andvoice/components/. Pure move + import-path rewrite (lib + test).Deliberately deferred (rationale)
repositories/layer — the audit flagged that data access lives in controllers/views rather than a repository layer. Building one out across 5 features is a rewrite, whichCLAUDE.mdexplicitly steers away from ("a port, not a rewrite"). Instead, the docs were corrected to describe the actual architecture._page/_view/_screen) + class-name alignment + the half-appliedaccord_prefix — high import/identifier churn for modest gain; better as a focused follow-up than bundled here.Verification note
This environment has no Dart/Flutter toolchain, so the changes were verified by static analysis (import resolution including
part oflibraries, no duplicate/orphaned imports, no dangling symbols) rather thanflutter analyze/flutter test. CI will be the first full compile — I'll drive it to green.🤖 Generated with Claude Code
Generated by Claude Code