+167
-22
analysis_options.yaml
+167
-22
analysis_options.yaml
···
+3
-1
android/app/src/main/AndroidManifest.xml
+3
-1
android/app/src/main/AndroidManifest.xml
···
+23
android/app/src/main/res/xml/network_security_config.xml
+23
android/app/src/main/res/xml/network_security_config.xml
···
+511
docs/CODE_QUALITY_GUIDE.md
+511
docs/CODE_QUALITY_GUIDE.md
···+This guide covers linting, formatting, and automated code quality checks for the Coves mobile app.+curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.deb.sh' | sudo -E bash
+802
docs/IMPLEMENTATION_FEED.md
+802
docs/IMPLEMENTATION_FEED.md
···+This document details the implementation of the feed functionality for the Coves mobile app, including integration with the Coves backend API for authenticated timeline and public discovery feeds.+⚠️ **Security Warning:** `AUTH_SKIP_VERIFY=true` is for Phase 1 local development only. Must be `false` in production.+**Issue:** Initial implementation with `Image.network` had "Connection reset by peer" errors from Kagi proxy+After initial implementation, a comprehensive code review identified several critical issues that have been addressed:+**Problem:** Access tokens were cached in `CovesApiService`, causing 401 errors after ~1 hour when atProto OAuth rotates tokens.+**Fix:** [lib/services/coves_api_service.dart:19-75](../lib/services/coves_api_service.dart#L19-L75)+**Problem:** `FeedScreen` contained authentication decision logic, violating clean architecture.+**Fix:** [lib/providers/feed_provider.dart:45-55](../lib/providers/feed_provider.dart#L45-L55)+**Problem:** Network security config allowed cleartext HTTP without warnings, risking production leak.+**Fix:** [android/app/src/main/res/xml/network_security_config.xml:3-15](../android/app/src/main/res/xml/network_security_config.xml#L3-L15)+**Fix:** [lib/providers/feed_provider.dart:57-117](../lib/providers/feed_provider.dart#L57-L117)+**Problem:** If token refresh failed (e.g., revoked server-side), app stayed in "authenticated" state with broken tokens.+**Fix:** [lib/providers/auth_provider.dart:47-65](../lib/providers/auth_provider.dart#L47-L65)+**Fix:** [lib/screens/home/feed_screen.dart:71-73](../lib/screens/home/feed_screen.dart#L71-L73)+**Fix:** [lib/screens/home/feed_screen.dart:25-28](../lib/screens/home/feed_screen.dart#L25-L28)+**Fix:** [lib/services/coves_api_service.dart:23-24](../lib/services/coves_api_service.dart#L23-L24)+**Fix:** [lib/screens/home/feed_screen.dart:191-195](../lib/screens/home/feed_screen.dart#L191-L195)+**Fix:** [lib/screens/home/feed_screen.dart:367-370](../lib/screens/home/feed_screen.dart#L367-L370)
+117
docs/cloudflare-worker-files/worker.js
+117
docs/cloudflare-worker-files/worker.js
···+deepLink = `intent:/${pathAndQuery}#Intent;scheme=dev.workers.brettmay0212.lingering-darkness-50a6;package=social.coves;end`;
+46
lefthook.yml
+46
lefthook.yml
···+# curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.deb.sh' | sudo -E bash
+6
-4
lib/config/oauth_config.dart
+6
-4
lib/config/oauth_config.dart
·········
+15
-14
lib/main.dart
+15
-14
lib/main.dart
············
+238
lib/models/post.dart
+238
lib/models/post.dart
···
+27
-1
lib/providers/auth_provider.dart
+27
-1
lib/providers/auth_provider.dart
······
+175
lib/providers/feed_provider.dart
+175
lib/providers/feed_provider.dart
···
+129
-126
lib/screens/auth/login_screen.dart
+129
-126
lib/screens/auth/login_screen.dart
······
+1
-4
lib/screens/home/create_post_screen.dart
+1
-4
lib/screens/home/create_post_screen.dart
+318
-30
lib/screens/home/feed_screen.dart
+318
-30
lib/screens/home/feed_screen.dart
······+'Feed post in ${post.post.community.name} by ${post.post.author.displayName ?? post.post.author.handle}. ${post.post.title ?? ""}',
+9
-16
lib/screens/home/main_shell_screen.dart
+9
-16
lib/screens/home/main_shell_screen.dart
·········
+1
-4
lib/screens/home/notifications_screen.dart
+1
-4
lib/screens/home/notifications_screen.dart
+4
-11
lib/screens/home/profile_screen.dart
+4
-11
lib/screens/home/profile_screen.dart
·········
+2
-9
lib/screens/home/search_screen.dart
+2
-9
lib/screens/home/search_screen.dart
······
-1
lib/screens/landing_screen.dart
-1
lib/screens/landing_screen.dart
+184
lib/services/coves_api_service.dart
+184
lib/services/coves_api_service.dart
···
+16
-7
lib/services/oauth_service.dart
+16
-7
lib/services/oauth_service.dart
············// Check if user cancelled (flutter_web_auth_2 throws PlatformException with "CANCELED" code)······
+3
-1
lib/services/pds_discovery_service.dart
+3
-1
lib/services/pds_discovery_service.dart
+2
-7
lib/widgets/logo.dart
+2
-7
lib/widgets/logo.dart
······
+2
macos/Flutter/GeneratedPluginRegistrant.swift
+2
macos/Flutter/GeneratedPluginRegistrant.swift
······SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+1
-6
packages/atproto_oauth_flutter/example/flutter_oauth_example.dart
+1
-6
packages/atproto_oauth_flutter/example/flutter_oauth_example.dart
············
+3
-1
packages/atproto_oauth_flutter/example/identity_resolver_example.dart
+3
-1
packages/atproto_oauth_flutter/example/identity_resolver_example.dart
···
+76
-76
packages/atproto_oauth_flutter/lib/src/client/oauth_client.dart
+76
-76
packages/atproto_oauth_flutter/lib/src/client/oauth_client.dart
···················································
+14
-19
packages/atproto_oauth_flutter/lib/src/dpop/fetch_dpop.dart
+14
-19
packages/atproto_oauth_flutter/lib/src/dpop/fetch_dpop.dart
························
+3
-8
packages/atproto_oauth_flutter/lib/src/errors/oauth_callback_error.dart
+3
-8
packages/atproto_oauth_flutter/lib/src/errors/oauth_callback_error.dart
···
+3
-5
packages/atproto_oauth_flutter/lib/src/errors/oauth_resolver_error.dart
+3
-5
packages/atproto_oauth_flutter/lib/src/errors/oauth_resolver_error.dart
······
+2
-2
packages/atproto_oauth_flutter/lib/src/errors/oauth_response_error.dart
+2
-2
packages/atproto_oauth_flutter/lib/src/errors/oauth_response_error.dart
···
+9
-11
packages/atproto_oauth_flutter/lib/src/identity/did_document.dart
+9
-11
packages/atproto_oauth_flutter/lib/src/identity/did_document.dart
······
+8
-9
packages/atproto_oauth_flutter/lib/src/identity/did_helpers.dart
+8
-9
packages/atproto_oauth_flutter/lib/src/identity/did_helpers.dart
······
+13
-33
packages/atproto_oauth_flutter/lib/src/identity/did_resolver.dart
+13
-33
packages/atproto_oauth_flutter/lib/src/identity/did_resolver.dart
·····················
+10
-27
packages/atproto_oauth_flutter/lib/src/identity/handle_resolver.dart
+10
-27
packages/atproto_oauth_flutter/lib/src/identity/handle_resolver.dart
·····················
+10
-17
packages/atproto_oauth_flutter/lib/src/identity/identity_resolver.dart
+10
-17
packages/atproto_oauth_flutter/lib/src/identity/identity_resolver.dart
···············
+2
-2
packages/atproto_oauth_flutter/lib/src/identity/identity_resolver_error.dart
+2
-2
packages/atproto_oauth_flutter/lib/src/identity/identity_resolver_error.dart
······
+4
-14
packages/atproto_oauth_flutter/lib/src/oauth/client_auth.dart
+4
-14
packages/atproto_oauth_flutter/lib/src/oauth/client_auth.dart
············
+20
-12
packages/atproto_oauth_flutter/lib/src/oauth/oauth_resolver.dart
+20
-12
packages/atproto_oauth_flutter/lib/src/oauth/oauth_resolver.dart
···············
+60
-46
packages/atproto_oauth_flutter/lib/src/oauth/oauth_server_agent.dart
+60
-46
packages/atproto_oauth_flutter/lib/src/oauth/oauth_server_agent.dart
·········-final originKey = '${origin.scheme}://${origin.host}${origin.hasPort ? ':${origin.port}' : ''}';···-print(' Cached nonce: ${cachedNonce != null ? "✅ ${cachedNonce.substring(0, 20)}..." : "❌ not found"}');+' Cached nonce: ${cachedNonce != null ? "✅ ${cachedNonce.substring(0, 20)}..." : "❌ not found"}',···············
+4
-2
packages/atproto_oauth_flutter/lib/src/oauth/oauth_server_factory.dart
+4
-2
packages/atproto_oauth_flutter/lib/src/oauth/oauth_server_factory.dart
···
+13
-12
packages/atproto_oauth_flutter/lib/src/oauth/protected_resource_metadata_resolver.dart
+13
-12
packages/atproto_oauth_flutter/lib/src/oauth/protected_resource_metadata_resolver.dart
··················
+2
-6
packages/atproto_oauth_flutter/lib/src/oauth/validate_client_metadata.dart
+2
-6
packages/atproto_oauth_flutter/lib/src/oauth/validate_client_metadata.dart
······
+4
-5
packages/atproto_oauth_flutter/lib/src/platform/flutter_key.dart
+4
-5
packages/atproto_oauth_flutter/lib/src/platform/flutter_key.dart
······-final signature = signer.generateSignature(Uint8List.fromList(data)) as pointycastle.ECSignature;
+60
-52
packages/atproto_oauth_flutter/lib/src/platform/flutter_oauth_client.dart
+60
-52
packages/atproto_oauth_flutter/lib/src/platform/flutter_oauth_client.dart
·····················
+3
-5
packages/atproto_oauth_flutter/lib/src/platform/flutter_oauth_router_helper.dart
+3
-5
packages/atproto_oauth_flutter/lib/src/platform/flutter_oauth_router_helper.dart
······
+17
-31
packages/atproto_oauth_flutter/lib/src/platform/flutter_stores.dart
+17
-31
packages/atproto_oauth_flutter/lib/src/platform/flutter_stores.dart
··················
+6
-21
packages/atproto_oauth_flutter/lib/src/runtime/runtime.dart
+6
-21
packages/atproto_oauth_flutter/lib/src/runtime/runtime.dart
·········
+4
-8
packages/atproto_oauth_flutter/lib/src/runtime/runtime_implementation.dart
+4
-8
packages/atproto_oauth_flutter/lib/src/runtime/runtime_implementation.dart
······
+9
-9
packages/atproto_oauth_flutter/lib/src/session/oauth_session.dart
+9
-9
packages/atproto_oauth_flutter/lib/src/session/oauth_session.dart
······
+94
-109
packages/atproto_oauth_flutter/lib/src/session/session_getter.dart
+94
-109
packages/atproto_oauth_flutter/lib/src/session/session_getter.dart
······························
+1
-4
packages/atproto_oauth_flutter/lib/src/session/state_store.dart
+1
-4
packages/atproto_oauth_flutter/lib/src/session/state_store.dart
···
+6
-5
packages/atproto_oauth_flutter/lib/src/types.dart
+6
-5
packages/atproto_oauth_flutter/lib/src/types.dart
···
+2
-1
packages/atproto_oauth_flutter/lib/src/util.dart
+2
-1
packages/atproto_oauth_flutter/lib/src/util.dart
···
+1
-4
packages/atproto_oauth_flutter/lib/src/utils/lock.dart
+1
-4
packages/atproto_oauth_flutter/lib/src/utils/lock.dart
···
+9
-3
packages/atproto_oauth_flutter/test/identity_resolver_test.dart
+9
-3
packages/atproto_oauth_flutter/test/identity_resolver_test.dart
·········
+328
pubspec.lock
+328
pubspec.lock
·······························································
+5
pubspec.yaml
+5
pubspec.yaml
······
+252
test/providers/auth_provider_test.dart
+252
test/providers/auth_provider_test.dart
···+test('should return null when not authenticated (skipped - needs integration test)', () async {+test('should sign out user if token refresh fails (skipped - needs integration test)', () async {
+218
test/providers/auth_provider_test.mocks.dart
+218
test/providers/auth_provider_test.mocks.dart
···
+461
test/providers/feed_provider_test.dart
+461
test/providers/feed_provider_test.dart
···
+196
test/providers/feed_provider_test.mocks.dart
+196
test/providers/feed_provider_test.mocks.dart
···
+1
-2
test/widget_test.dart
+1
-2
test/widget_test.dart
···
+284
test/widgets/feed_screen_test.dart
+284
test/widgets/feed_screen_test.dart
···
+252
test/widgets/feed_screen_test.mocks.dart
+252
test/widgets/feed_screen_test.mocks.dart
···