[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

(fix) remove followmode (#82)

* remove followmode

* fix lint errors

* fix settings

* remove spell

authored by

Roscoe Rubin-Rottenberg and committed by
GitHub
2b171d53 3c455118

+79 -248
+48 -1
ios/Podfile.lock
··· 1 1 PODS: 2 + - better_player_plus (1.0.0): 3 + - Cache (~> 6.0.0) 4 + - Flutter 5 + - GCDWebServer 6 + - HLSCachingReverseProxyServer 7 + - PINCache 8 + - Cache (6.0.0) 2 9 - camera_avfoundation (0.0.1): 3 10 - Flutter 4 11 - Flutter (1.0.0) ··· 8 15 - Flutter 9 16 - FlutterMacOS 10 17 - mdk (~> 0.33.0) 18 + - GCDWebServer (3.5.4): 19 + - GCDWebServer/Core (= 3.5.4) 20 + - GCDWebServer/Core (3.5.4) 21 + - HLSCachingReverseProxyServer (0.1.0): 22 + - GCDWebServer (~> 3.5) 23 + - PINCache (>= 3.0.1-beta.3) 11 24 - image_picker_ios (0.0.1): 12 25 - Flutter 13 26 - imgly_camera (1.53.0): ··· 69 82 - IMGLYEditor (= 1.53.0) 70 83 - Kingfisher (7.10.2) 71 84 - mdk (0.33.1) 85 + - package_info_plus (0.4.5): 86 + - Flutter 72 87 - path_provider_foundation (0.0.1): 73 88 - Flutter 74 89 - FlutterMacOS 90 + - PINCache (3.0.4): 91 + - PINCache/Arc-exception-safe (= 3.0.4) 92 + - PINCache/Core (= 3.0.4) 93 + - PINCache/Arc-exception-safe (3.0.4): 94 + - PINCache/Core 95 + - PINCache/Core (3.0.4): 96 + - PINOperation (~> 1.2.3) 97 + - PINOperation (1.2.3) 75 98 - shared_preferences_foundation (0.0.1): 76 99 - Flutter 77 100 - FlutterMacOS ··· 84 107 - video_player_avfoundation (0.0.1): 85 108 - Flutter 86 109 - FlutterMacOS 110 + - wakelock_plus (0.0.1): 111 + - Flutter 87 112 88 113 DEPENDENCIES: 114 + - better_player_plus (from `.symlinks/plugins/better_player_plus/ios`) 89 115 - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) 90 116 - Flutter (from `Flutter`) 91 117 - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) ··· 93 119 - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) 94 120 - imgly_camera (from `.symlinks/plugins/imgly_camera/ios`) 95 121 - imgly_editor (from `.symlinks/plugins/imgly_editor/ios`) 122 + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) 96 123 - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 97 124 - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) 98 125 - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) 99 126 - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) 100 127 - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) 128 + - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) 101 129 102 130 SPEC REPOS: 103 131 trunk: 132 + - Cache 133 + - GCDWebServer 134 + - HLSCachingReverseProxyServer 104 135 - IMGLYApparelEditor 105 136 - IMGLYCamera 106 137 - IMGLYCore ··· 114 145 - IMGLYVideoEditor 115 146 - Kingfisher 116 147 - mdk 148 + - PINCache 149 + - PINOperation 117 150 - SwiftUIIntrospect 118 151 119 152 EXTERNAL SOURCES: 153 + better_player_plus: 154 + :path: ".symlinks/plugins/better_player_plus/ios" 120 155 camera_avfoundation: 121 156 :path: ".symlinks/plugins/camera_avfoundation/ios" 122 157 Flutter: ··· 131 166 :path: ".symlinks/plugins/imgly_camera/ios" 132 167 imgly_editor: 133 168 :path: ".symlinks/plugins/imgly_editor/ios" 169 + package_info_plus: 170 + :path: ".symlinks/plugins/package_info_plus/ios" 134 171 path_provider_foundation: 135 172 :path: ".symlinks/plugins/path_provider_foundation/darwin" 136 173 shared_preferences_foundation: ··· 141 178 :path: ".symlinks/plugins/url_launcher_ios/ios" 142 179 video_player_avfoundation: 143 180 :path: ".symlinks/plugins/video_player_avfoundation/darwin" 181 + wakelock_plus: 182 + :path: ".symlinks/plugins/wakelock_plus/ios" 144 183 145 184 SPEC CHECKSUMS: 185 + better_player_plus: 10794c0ed1b3b4ae058939e22a6172f850a2039b 186 + Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d 146 187 camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf 147 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 188 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 148 189 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 149 190 fvp: 3f9a3f729e969cb76d1692c615c9e33508563121 191 + GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4 192 + HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181 150 193 image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a 151 194 imgly_camera: a54c0c9f3d33d8c21cce402637a7079ef07f402f 152 195 imgly_editor: 449432a405a670b291c81b67f40d10ecf4eb1f09 ··· 163 206 IMGLYVideoEditor: 74d46a79a162ac7706fb8457e3f967ec269fe84a 164 207 Kingfisher: 99edc495d3b7607e6425f0d6f6847b2abd6d716d 165 208 mdk: 622e3452cea55a982c0712ec9ce931d8ec718b22 209 + package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 166 210 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 211 + PINCache: d9a87a0ff397acffe9e2f0db972ac14680441158 212 + PINOperation: fb563bcc9c32c26d6c78aaff967d405aa2ee74a7 167 213 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 168 214 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 169 215 SwiftUIIntrospect: 434b406dc6f5e30744bb6e180f90343f6a8eba8c 170 216 url_launcher_ios: 694010445543906933d732453a59da0a173ae33d 171 217 video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b 218 + wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 172 219 173 220 PODFILE CHECKSUM: e716c32704b29904c5fe233535ab45faecf8c549 174 221
+2
lib/src/core/network/atproto/data/repositories/actor_repository.dart
··· 30 30 /// [did] The DID of the user to check 31 31 Future<bool> isEarlySupporter(String did); 32 32 33 + /// @TODO: Fix preferences for feeds, remove followMode 34 + 33 35 /// Get user preferences from the backend 34 36 Future<UserPreferences> getPreferences(); 35 37
+12 -11
lib/src/core/network/atproto/data/repositories/actor_repository_impl.dart
··· 165 165 166 166 @override 167 167 Future<void> putPreferences(UserPreferences preferences) async { 168 - _logger.d('Updating user preferences: ${preferences.followMode}'); 169 168 return _client.executeWithRetry(() async { 170 169 if (!_client.authRepository.isAuthenticated) { 171 170 _logger.w('Not authenticated'); ··· 178 177 throw Exception('AtProto not initialized'); 179 178 } 180 179 181 - final result = await atproto.post( 182 - NSID.parse('so.sprk.actor.putPreferences'), 183 - body: preferences.toJson(), 184 - headers: {'atproto-proxy': _client.sprkDid}, 185 - // to: (jsonMap) => jsonMap, 186 - ); 180 + // @TODO: Implement putPreferences for feeds not followMode 181 + 182 + // final result = await atproto.post( 183 + // NSID.parse('so.sprk.actor.putPreferences'), 184 + // body: preferences.toJson(), 185 + // headers: {'atproto-proxy': _client.sprkDid}, 186 + // // to: (jsonMap) => jsonMap, 187 + // ); 187 188 188 - if (result.status != HttpStatus.ok) { 189 - _logger.e('Failed to update preferences'); 190 - throw Exception('Failed to update preferences'); 191 - } 189 + // if (result.status != HttpStatus.ok) { 190 + // _logger.e('Failed to update preferences'); 191 + // throw Exception('Failed to update preferences'); 192 + // } 192 193 193 194 _logger.d('Preferences updated successfully'); 194 195 });
+2 -9
lib/src/core/network/atproto/data/repositories/graph_repository_impl.dart
··· 5 5 import 'package:sparksocial/src/core/network/atproto/data/models/graph_models.dart'; 6 6 import 'package:sparksocial/src/core/network/atproto/data/repositories/graph_repository.dart'; 7 7 import 'package:sparksocial/src/core/network/atproto/data/repositories/sprk_repository.dart'; 8 - import 'package:sparksocial/src/core/storage/preferences/settings_repository.dart'; 9 8 import 'package:sparksocial/src/core/utils/logging/log_service.dart'; 10 9 import 'package:sparksocial/src/core/utils/logging/logger.dart'; 11 - import 'package:sparksocial/src/features/settings/ui/pages/profile_settings_page.dart'; 12 10 13 11 /// Implementation of Graph-related API endpoints 14 12 class GraphRepositoryImpl implements GraphRepository { ··· 109 107 throw Exception('Session DID not available'); 110 108 } 111 109 112 - final settingsRepository = GetIt.instance<SettingsRepository>(); 113 - 114 - final followMode = await settingsRepository.getFollowMode(); 115 - _logger.d('Using follow mode: $followMode'); 116 - 117 - final collection = followMode == FollowMode.sprk ? NSID.parse('so.sprk.graph.follow') : NSID.parse('app.bsky.graph.follow'); 118 - final recordType = followMode == FollowMode.sprk ? 'so.sprk.graph.follow' : 'app.bsky.graph.follow'; 110 + final collection = NSID.parse('so.sprk.graph.follow'); 111 + const recordType = 'so.sprk.graph.follow'; 119 112 120 113 try { 121 114 _logger.d('Checking if already following user: $did');
-10
lib/src/core/storage/preferences/settings_repository.dart
··· 1 1 import 'package:sparksocial/src/core/network/atproto/data/models/feed_models.dart'; 2 2 import 'package:sparksocial/src/core/network/atproto/data/models/labeler_models.dart'; 3 - import 'package:sparksocial/src/features/settings/ui/pages/profile_settings_page.dart'; 4 3 5 4 abstract class SettingsRepository { 6 5 Future<bool> getFeedBlurEnabled(); ··· 8 7 9 8 Future<bool> getHideAdultContent(); 10 9 Future<void> setHideAdultContent(bool value); 11 - 12 - Future<FollowMode> getFollowMode(); 13 - Future<void> setFollowMode(FollowMode followMode); 14 - 15 - /// Sync follow mode with backend and update local storage 16 - Future<void> syncFollowModeFromServer(); 17 - 18 - /// Set follow mode locally and sync with backend 19 - Future<void> setFollowModeWithSync(FollowMode followMode); 20 10 21 11 Future<List<Feed>> getFeeds(); 22 12 Future<void> setFeeds(List<Feed> feeds);
-69
lib/src/core/storage/preferences/settings_repository_impl.dart
··· 1 1 import 'package:get_it/get_it.dart'; 2 - import 'package:sparksocial/src/core/network/atproto/data/models/actor_models.dart'; 3 2 import 'package:sparksocial/src/core/network/atproto/data/models/feed_models.dart'; 4 3 import 'package:sparksocial/src/core/network/atproto/data/models/labeler_models.dart'; 5 - import 'package:sparksocial/src/core/network/atproto/data/repositories/sprk_repository.dart'; 6 4 import 'package:sparksocial/src/core/storage/cache/sql_cache_interface.dart'; 7 5 import 'package:sparksocial/src/core/storage/preferences/settings_repository.dart'; 8 6 import 'package:sparksocial/src/core/storage/storage.dart'; 9 7 import 'package:sparksocial/src/core/utils/logging/log_service.dart'; 10 8 import 'package:sparksocial/src/core/utils/logging/logger.dart'; 11 - import 'package:sparksocial/src/features/settings/ui/pages/profile_settings_page.dart'; 12 9 13 10 class SettingsRepositoryImpl implements SettingsRepository { 14 11 SettingsRepositoryImpl() { 15 12 _sqlCache = GetIt.instance<SQLCacheInterface>(); 16 13 _storageManager = GetIt.instance<StorageManager>(); 17 14 _logger = GetIt.instance<LogService>().getLogger('SettingsRepository'); 18 - _sprkRepository = GetIt.instance<SprkRepository>(); 19 15 _setupDefaultLabelPreferences(); 20 16 } 21 17 late final SQLCacheInterface _sqlCache; 22 18 late final StorageManager _storageManager; 23 19 late final SparkLogger _logger; 24 - late final SprkRepository _sprkRepository; 25 20 26 21 Future<void> _setupDefaultLabelPreferences() async { 27 22 if (await _storageManager.preferences.getObject<bool>(StorageKeys.defaultLabelsAreSetupKey) ?? false) { ··· 184 179 } 185 180 186 181 @override 187 - Future<FollowMode> getFollowMode() async { 188 - final followModeString = await _storageManager.preferences.getString(StorageKeys.followModeKey); 189 - return FollowMode.values.firstWhere((mode) => mode.name == followModeString, orElse: () => FollowMode.sprk); 190 - } 191 - 192 - @override 193 - Future<void> setFollowMode(FollowMode followMode) async { 194 - await _storageManager.preferences.setString(StorageKeys.followModeKey, followMode.name); 195 - } 196 - 197 - @override 198 182 Future<void> setFeeds(List<Feed> feeds) async { 199 183 _logger.d('Saving feeds: ${feeds.map((f) => f.name).join(', ')}'); 200 184 // Manually serialize feeds to JSON ··· 374 358 newLabelPreference.toJson(), 375 359 ); 376 360 _logger.d('Label preference created: $value'); 377 - } 378 - } 379 - 380 - @override 381 - Future<void> syncFollowModeFromServer() async { 382 - try { 383 - _logger.d('Syncing follow mode from server...'); 384 - 385 - if (!_sprkRepository.authRepository.isAuthenticated) { 386 - _logger.w('Not authenticated, skipping server sync'); 387 - return; 388 - } 389 - 390 - final preferences = await _sprkRepository.actor.getPreferences(); 391 - final serverFollowMode = FollowMode.values.firstWhere( 392 - (mode) => mode.name == preferences.followMode, 393 - orElse: () => FollowMode.sprk, 394 - ); 395 - 396 - // Get current local value to check if it changed 397 - final currentFollowMode = await getFollowMode(); 398 - 399 - if (currentFollowMode != serverFollowMode) { 400 - _logger.d('Follow mode changed from server: $currentFollowMode -> $serverFollowMode'); 401 - await setFollowMode(serverFollowMode); 402 - } else { 403 - _logger.d('Follow mode is in sync with server: $serverFollowMode'); 404 - } 405 - } catch (e) { 406 - _logger.w('Failed to sync follow mode from server', error: e); 407 - } 408 - } 409 - 410 - @override 411 - Future<void> setFollowModeWithSync(FollowMode followMode) async { 412 - try { 413 - _logger.d('Setting follow mode with sync: $followMode'); 414 - 415 - // Set locally first 416 - await setFollowMode(followMode); 417 - 418 - // Sync with backend if authenticated 419 - if (_sprkRepository.authRepository.isAuthenticated) { 420 - final preferences = UserPreferences(followMode: followMode.name); 421 - await _sprkRepository.actor.putPreferences(preferences); 422 - _logger.d('Follow mode synced with server successfully'); 423 - } else { 424 - _logger.w('Not authenticated, follow mode saved locally only'); 425 - } 426 - } catch (e) { 427 - _logger.e('Failed to sync follow mode with server', error: e); 428 - // Keep the local change even if sync fails 429 - rethrow; 430 361 } 431 362 } 432 363
-5
lib/src/features/auth/ui/pages/onboarding_page.dart
··· 6 6 import 'package:sparksocial/src/core/widgets/custom_text_field.dart'; // Corrected path 7 7 import 'package:sparksocial/src/features/auth/providers/onboarding_notifier.dart'; 8 8 import 'package:sparksocial/src/features/auth/providers/onboarding_providers.dart'; 9 - import 'package:sparksocial/src/features/settings/providers/settings_provider.dart'; 10 - import 'package:sparksocial/src/features/settings/ui/pages/profile_settings_page.dart'; 11 9 12 10 @RoutePage() 13 11 class OnboardingPage extends ConsumerStatefulWidget { ··· 65 63 }); 66 64 67 65 try { 68 - // Set follow mode to bsky 69 - await ref.read(settingsProvider.notifier).setFollowMode(FollowMode.bsky); 70 - 71 66 // Create the profile 72 67 final onboardingState = ref.read(onboardingStateProvider.notifier); 73 68
+1 -14
lib/src/features/settings/providers/settings_provider.dart
··· 6 6 import 'package:sparksocial/src/core/utils/logging/log_service.dart'; 7 7 import 'package:sparksocial/src/core/utils/logging/logger.dart'; 8 8 import 'package:sparksocial/src/features/settings/providers/settings_state.dart'; 9 - import 'package:sparksocial/src/features/settings/ui/pages/profile_settings_page.dart'; 10 9 11 10 part 'settings_provider.g.dart'; 12 11 ··· 44 43 45 44 final feedBlurEnabled = await _repository.getFeedBlurEnabled(); 46 45 final hideAdultContent = await _repository.getHideAdultContent(); 47 - final followMode = await _repository.getFollowMode(); 48 46 final feeds = await _repository.getFeeds(); 49 47 final activeFeed = await _repository.getActiveFeed(); 50 48 final postToBskyEnabled = await _repository.getPostToBskyEnabled(); 51 49 52 50 _logger.d( 53 - 'Settings loaded - activeFeed: ${activeFeed.name}, feeds: ${feeds.map((f) => f.name).join(', ')}, followMode: $followMode', 51 + 'Settings loaded - activeFeed: ${activeFeed.name}, feeds: ${feeds.map((f) => f.name).join(', ')}', 54 52 ); 55 53 56 54 state = SettingsState( 57 55 activeFeed: activeFeed, 58 56 feedBlurEnabled: feedBlurEnabled, 59 57 hideAdultContent: hideAdultContent, 60 - followMode: followMode, 61 58 feeds: feeds, 62 59 postToBskyEnabled: postToBskyEnabled, 63 60 ); ··· 81 78 state = state.copyWith(hideAdultContent: value); 82 79 } 83 80 84 - /// Sets follow mode setting 85 - Future<void> setFollowMode(FollowMode followMode) async { 86 - await _repository.setFollowModeWithSync(followMode); 87 - state = state.copyWith(followMode: followMode); 88 - } 89 - 90 81 /// Sets Post to Bluesky setting 91 82 Future<void> setPostToBsky(bool value) async { 92 83 await _repository.setPostToBskyEnabled(value); ··· 101 92 /// - Manually from the settings UI if user wants to refresh preferences 102 93 Future<void> syncPreferencesFromServer() async { 103 94 try { 104 - _logger.d('Syncing preferences from server...'); 105 - await _repository.syncFollowModeFromServer(); 106 - 107 - // Reload settings to get updated values 108 95 await loadSettings(); 109 96 _logger.d('Preferences synced successfully'); 110 97 } catch (e) {
-2
lib/src/features/settings/providers/settings_state.dart
··· 1 1 import 'package:freezed_annotation/freezed_annotation.dart'; 2 2 import 'package:sparksocial/src/core/network/atproto/data/models/feed_models.dart'; 3 - import 'package:sparksocial/src/features/settings/ui/pages/profile_settings_page.dart'; 4 3 5 4 part 'settings_state.freezed.dart'; 6 5 ··· 11 10 required Feed activeFeed, 12 11 @Default(false) bool feedBlurEnabled, 13 12 @Default(true) bool hideAdultContent, 14 - @Default(FollowMode.sprk) FollowMode followMode, 15 13 @Default([ 16 14 Feed.hardCoded(hardCodedFeed: HardCodedFeedEnum.following), 17 15 Feed.hardCoded(hardCodedFeed: HardCodedFeedEnum.forYou),
-113
lib/src/features/settings/ui/pages/profile_settings_page.dart
··· 4 4 import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 5 import 'package:sparksocial/src/core/routing/app_router.dart'; 6 6 import 'package:sparksocial/src/features/profile/providers/profile_provider.dart'; 7 - import 'package:sparksocial/src/features/settings/providers/settings_provider.dart'; 8 - 9 - enum FollowMode { sprk, bsky } 10 7 11 8 @RoutePage() 12 9 class ProfileSettingsPage extends ConsumerStatefulWidget { ··· 17 14 } 18 15 19 16 class _ProfileSettingsPageState extends ConsumerState<ProfileSettingsPage> { 20 - final Map<String, FollowMode> _followModeMap = {'Spark exclusive': FollowMode.sprk, 'Bluesky synced': FollowMode.bsky}; 21 - 22 17 Future<void> _handleLogout() async { 23 18 try { 24 19 // Show loading indicator ··· 55 50 } 56 51 } 57 52 58 - void _handleFollowModeChange(FollowMode newMode) { 59 - final settingsNotifier = ref.read(settingsProvider.notifier); 60 - settingsNotifier.setFollowMode(newMode); 61 - } 62 - 63 53 @override 64 54 Widget build(BuildContext context) { 65 - final brightness = Theme.of(context).brightness; 66 - final isDark = brightness == Brightness.dark; 67 - final itemColor = isDark ? Colors.grey.shade800 : Colors.grey.shade200; 68 - const pinkColor = Color(0xFFE91E63); 69 - 70 - final displayValues = _followModeMap.keys.toList(); 71 - final modeValues = _followModeMap.values.toList(); 72 - 73 - final settingsState = ref.watch(settingsProvider); 74 - final currentFollowMode = settingsState.followMode; 75 - 76 55 return Scaffold( 77 56 backgroundColor: Theme.of(context).colorScheme.surface, 78 57 appBar: AppBar( ··· 88 67 body: ListView( 89 68 padding: const EdgeInsets.symmetric(horizontal: 16), 90 69 children: [ 91 - Padding( 92 - padding: const EdgeInsets.symmetric(vertical: 8), 93 - child: Container( 94 - decoration: BoxDecoration(color: itemColor, borderRadius: BorderRadius.circular(16)), 95 - padding: const EdgeInsets.all(16), 96 - child: Column( 97 - crossAxisAlignment: CrossAxisAlignment.start, 98 - children: [ 99 - Text( 100 - 'Follow Mode', 101 - style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.bold), 102 - ), 103 - const SizedBox(height: 6), 104 - Text( 105 - 'Choose how your follows are managed across Spark.', 106 - style: TextStyle(color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7), fontSize: 13), 107 - ), 108 - const SizedBox(height: 16), 109 - Row( 110 - mainAxisAlignment: MainAxisAlignment.center, 111 - children: [ 112 - // Spark exclusive button 113 - Expanded( 114 - child: Padding( 115 - padding: const EdgeInsets.symmetric(horizontal: 4), 116 - child: ElevatedButton( 117 - onPressed: () => _handleFollowModeChange(modeValues[0]), 118 - style: ElevatedButton.styleFrom( 119 - backgroundColor: currentFollowMode == modeValues[0] 120 - ? pinkColor 121 - : (isDark ? Colors.grey.shade700 : Colors.grey.shade300), 122 - foregroundColor: currentFollowMode == modeValues[0] 123 - ? Colors.white 124 - : Theme.of(context).colorScheme.onSurface, 125 - padding: const EdgeInsets.symmetric(vertical: 12), 126 - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 127 - elevation: currentFollowMode == modeValues[0] ? 2 : 0, 128 - side: currentFollowMode == modeValues[0] 129 - ? BorderSide.none 130 - : BorderSide(color: isDark ? Colors.grey.shade600 : Colors.grey.shade400, width: 0.5), 131 - ), 132 - child: Text( 133 - displayValues[0], 134 - textAlign: TextAlign.center, 135 - style: const TextStyle(fontWeight: FontWeight.w600), 136 - ), 137 - ), 138 - ), 139 - ), 140 - Expanded( 141 - child: Padding( 142 - padding: const EdgeInsets.symmetric(horizontal: 4), 143 - child: ElevatedButton( 144 - onPressed: () => _handleFollowModeChange(modeValues[1]), 145 - style: ElevatedButton.styleFrom( 146 - backgroundColor: currentFollowMode == modeValues[1] 147 - ? pinkColor 148 - : (isDark ? Colors.grey.shade700 : Colors.grey.shade300), 149 - foregroundColor: currentFollowMode == modeValues[1] 150 - ? Colors.white 151 - : Theme.of(context).colorScheme.onSurface, 152 - padding: const EdgeInsets.symmetric(vertical: 12), 153 - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 154 - elevation: currentFollowMode == modeValues[1] ? 2 : 0, 155 - side: currentFollowMode == modeValues[1] 156 - ? BorderSide.none 157 - : BorderSide(color: isDark ? Colors.grey.shade600 : Colors.grey.shade400, width: 0.5), 158 - ), 159 - child: Text( 160 - displayValues[1], 161 - textAlign: TextAlign.center, 162 - style: const TextStyle(fontWeight: FontWeight.w600), 163 - ), 164 - ), 165 - ), 166 - ), 167 - ], 168 - ), 169 - const SizedBox(height: 8), 170 - Center( 171 - child: Text( 172 - currentFollowMode == FollowMode.sprk 173 - ? 'You are managing follows within Spark only.' 174 - : 'Your follows are synced with Bluesky.', 175 - style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6)), 176 - textAlign: TextAlign.center, 177 - ), 178 - ), 179 - ], 180 - ), 181 - ), 182 - ), 183 70 Padding( 184 71 padding: const EdgeInsets.symmetric(vertical: 8), 185 72 child: Container(
+14 -14
pubspec.lock
··· 897 897 dependency: transitive 898 898 description: 899 899 name: leak_tracker 900 - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" 900 + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" 901 901 url: "https://pub.dev" 902 902 source: hosted 903 - version: "10.0.9" 903 + version: "11.0.2" 904 904 leak_tracker_flutter_testing: 905 905 dependency: transitive 906 906 description: 907 907 name: leak_tracker_flutter_testing 908 - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 908 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" 909 909 url: "https://pub.dev" 910 910 source: hosted 911 - version: "3.0.9" 911 + version: "3.0.10" 912 912 leak_tracker_testing: 913 913 dependency: transitive 914 914 description: 915 915 name: leak_tracker_testing 916 - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 916 + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" 917 917 url: "https://pub.dev" 918 918 source: hosted 919 - version: "3.0.1" 919 + version: "3.0.2" 920 920 lean_builder: 921 921 dependency: transitive 922 922 description: ··· 1454 1454 dependency: transitive 1455 1455 description: 1456 1456 name: test 1457 - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" 1457 + sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" 1458 1458 url: "https://pub.dev" 1459 1459 source: hosted 1460 - version: "1.25.15" 1460 + version: "1.26.2" 1461 1461 test_api: 1462 1462 dependency: transitive 1463 1463 description: 1464 1464 name: test_api 1465 - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 1465 + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" 1466 1466 url: "https://pub.dev" 1467 1467 source: hosted 1468 - version: "0.7.4" 1468 + version: "0.7.6" 1469 1469 test_core: 1470 1470 dependency: transitive 1471 1471 description: 1472 1472 name: test_core 1473 - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" 1473 + sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" 1474 1474 url: "https://pub.dev" 1475 1475 source: hosted 1476 - version: "0.6.8" 1476 + version: "0.6.11" 1477 1477 timing: 1478 1478 dependency: transitive 1479 1479 description: ··· 1598 1598 dependency: transitive 1599 1599 description: 1600 1600 name: vector_math 1601 - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 1601 + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b 1602 1602 url: "https://pub.dev" 1603 1603 source: hosted 1604 - version: "2.1.4" 1604 + version: "2.2.0" 1605 1605 very_good_analysis: 1606 1606 dependency: "direct dev" 1607 1607 description: