diff --git a/lib/app/bloc/app_bloc.dart b/lib/app/bloc/app_bloc.dart index 9a260ca2..5b2a02a2 100644 --- a/lib/app/bloc/app_bloc.dart +++ b/lib/app/bloc/app_bloc.dart @@ -6,6 +6,7 @@ import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/material.dart'; import 'package:ht_auth_repository/ht_auth_repository.dart'; import 'package:ht_data_repository/ht_data_repository.dart'; +import 'package:ht_main/app/config/config.dart' as local_config; import 'package:ht_shared/ht_shared.dart'; // Import shared models and exceptions part 'app_event.dart'; @@ -15,24 +16,23 @@ class AppBloc extends Bloc { AppBloc({ required HtAuthRepository authenticationRepository, required HtDataRepository userAppSettingsRepository, - required HtDataRepository appConfigRepository, // Added - }) : _authenticationRepository = authenticationRepository, - _userAppSettingsRepository = userAppSettingsRepository, - _appConfigRepository = appConfigRepository, // Added - // Initialize with default state, load settings after user is known - // Provide a default UserAppSettings instance - super( - // AppConfig will be null initially, fetched later - const AppState( - settings: UserAppSettings(id: 'default'), - selectedBottomNavigationIndex: 0, - appConfig: null, - ), - ) { + required HtDataRepository appConfigRepository, + required local_config.AppEnvironment environment, // Added + }) : _authenticationRepository = authenticationRepository, + _userAppSettingsRepository = userAppSettingsRepository, + _appConfigRepository = appConfigRepository, + super( + AppState( + settings: const UserAppSettings(id: 'default'), + selectedBottomNavigationIndex: 0, + appConfig: null, + environment: environment, // Pass environment to AppState + ), + ) { on(_onAppUserChanged); on(_onAppSettingsRefreshed); on(_onAppConfigFetchRequested); - on(_onAppUserAccountActionShown); // Added + on(_onAppUserAccountActionShown); on(_onLogoutRequested); on(_onThemeModeChanged); on(_onFlexSchemeChanged); diff --git a/lib/app/bloc/app_state.dart b/lib/app/bloc/app_state.dart index ccfcd344..a95175cb 100644 --- a/lib/app/bloc/app_state.dart +++ b/lib/app/bloc/app_state.dart @@ -34,7 +34,8 @@ class AppState extends Equatable { this.status = AppStatus.initial, this.user, // User is now nullable and defaults to null this.locale, // Added locale - this.appConfig, // Added AppConfig + this.appConfig, + this.environment, }); /// The index of the currently selected item in the bottom navigation bar. @@ -66,7 +67,10 @@ class AppState extends Equatable { final Locale? locale; // Added locale /// The global application configuration (remote config). - final AppConfig? appConfig; // Added AppConfig + final AppConfig? appConfig; + + /// The current application environment (e.g., production, development, demo). + final local_config.AppEnvironment? environment; /// Creates a copy of the current state with updated values. AppState copyWith({ @@ -79,10 +83,12 @@ class AppState extends Equatable { User? user, UserAppSettings? settings, // Add settings to copyWith Locale? locale, // Added locale - AppConfig? appConfig, // Added AppConfig + AppConfig? appConfig, + local_config.AppEnvironment? environment, // Added AppEnvironment bool clearFontFamily = false, bool clearLocale = false, // Added to allow clearing locale bool clearAppConfig = false, // Added to allow clearing appConfig + bool clearEnvironment = false, // Added to allow clearing environment }) { return AppState( selectedBottomNavigationIndex: @@ -95,7 +101,9 @@ class AppState extends Equatable { user: user ?? this.user, settings: settings ?? this.settings, // Copy settings locale: clearLocale ? null : locale ?? this.locale, // Added locale - appConfig: clearAppConfig ? null : appConfig ?? this.appConfig, // Added + appConfig: clearAppConfig ? null : appConfig ?? this.appConfig, + environment: + clearEnvironment ? null : environment ?? this.environment, // Added ); } @@ -110,6 +118,7 @@ class AppState extends Equatable { user, settings, // Include settings in props locale, // Added locale to props - appConfig, // Added AppConfig to props + appConfig, + environment, ]; } diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 7f9ee467..6e0b5584 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -9,6 +9,7 @@ import 'package:ht_auth_repository/ht_auth_repository.dart'; // Auth Repository import 'package:ht_data_repository/ht_data_repository.dart'; // Generic Data Repository import 'package:ht_kv_storage_service/ht_kv_storage_service.dart'; // KV Storage Interface import 'package:ht_main/app/bloc/app_bloc.dart'; +import 'package:ht_main/app/config/app_environment.dart'; import 'package:ht_main/authentication/bloc/authentication_bloc.dart'; import 'package:ht_main/l10n/app_localizations.dart'; import 'package:ht_main/l10n/l10n.dart'; @@ -30,6 +31,7 @@ class App extends StatelessWidget { htUserContentPreferencesRepository, required HtDataRepository htAppConfigRepository, required HtKVStorageService kvStorageService, + required AppEnvironment environment, // Added super.key, }) : _htAuthenticationRepository = htAuthenticationRepository, _htHeadlinesRepository = htHeadlinesRepository, @@ -39,7 +41,8 @@ class App extends StatelessWidget { _htUserAppSettingsRepository = htUserAppSettingsRepository, _htUserContentPreferencesRepository = htUserContentPreferencesRepository, _htAppConfigRepository = htAppConfigRepository, - _kvStorageService = kvStorageService; + _kvStorageService = kvStorageService, + _environment = environment; // Added final HtAuthRepository _htAuthenticationRepository; final HtDataRepository _htHeadlinesRepository; @@ -51,6 +54,7 @@ class App extends StatelessWidget { _htUserContentPreferencesRepository; final HtDataRepository _htAppConfigRepository; final HtKVStorageService _kvStorageService; + final AppEnvironment _environment; // Added @override Widget build(BuildContext context) { @@ -76,8 +80,9 @@ class App extends StatelessWidget { authenticationRepository: context.read(), userAppSettingsRepository: context.read>(), - appConfigRepository: // Added - context.read>(), // Added + appConfigRepository: + context.read>(), + environment: _environment, // Pass environment ), ), BlocProvider( @@ -97,6 +102,7 @@ class App extends StatelessWidget { htUserContentPreferencesRepository: _htUserContentPreferencesRepository, htAppConfigRepository: _htAppConfigRepository, + environment: _environment, // Pass environment ), ), ); @@ -113,6 +119,7 @@ class _AppView extends StatefulWidget { required this.htUserAppSettingsRepository, required this.htUserContentPreferencesRepository, required this.htAppConfigRepository, + required this.environment, // Added }); final HtAuthRepository htAuthenticationRepository; @@ -124,6 +131,7 @@ class _AppView extends StatefulWidget { final HtDataRepository htUserContentPreferencesRepository; final HtDataRepository htAppConfigRepository; + final AppEnvironment environment; // Added @override State<_AppView> createState() => _AppViewState(); diff --git a/lib/authentication/view/email_code_verification_page.dart b/lib/authentication/view/email_code_verification_page.dart index 447c5405..8d08aa40 100644 --- a/lib/authentication/view/email_code_verification_page.dart +++ b/lib/authentication/view/email_code_verification_page.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ht_main/app/bloc/app_bloc.dart'; // Added +import 'package:ht_main/app/config/config.dart'; // Added for AppEnvironment import 'package:ht_main/authentication/bloc/authentication_bloc.dart'; import 'package:ht_main/l10n/l10n.dart'; import 'package:ht_main/shared/constants/app_spacing.dart'; @@ -72,6 +74,30 @@ class EmailCodeVerificationPage extends StatelessWidget { ), // Softer color textAlign: TextAlign.center, ), + // Display demo code if in demo environment + BlocSelector( + selector: (state) => state.environment, + builder: (context, environment) { + if (environment == AppEnvironment.demo) { + return Column( + children: [ + const SizedBox(height: AppSpacing.md), + Text( + l10n.demoVerificationCodeMessage( + '123456', + ), // Demo code + style: textTheme.bodyMedium?.copyWith( + color: colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + ], + ); + } + return const SizedBox.shrink(); + }, + ), const SizedBox( height: AppSpacing.xl, ), // Increased spacing diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 62209b9b..422ff804 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -19,7 +19,10 @@ import 'package:ht_main/shared/localization/en_timeago_messages.dart'; import 'package:ht_shared/ht_shared.dart'; import 'package:timeago/timeago.dart' as timeago; -Future bootstrap(app_config.AppConfig appConfig) async { +Future bootstrap( + app_config.AppConfig appConfig, + app_config.AppEnvironment environment, // Added +) async { WidgetsFlutterBinding.ensureInitialized(); Bloc.observer = const AppBlocObserver(); @@ -215,5 +218,6 @@ Future bootstrap(app_config.AppConfig appConfig) async { htUserContentPreferencesRepository: userContentPreferencesRepository, htAppConfigRepository: appConfigRepository, kvStorageService: kvStorage, + environment: environment, // Pass environment to App ); } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index cfd8253d..33ce8212 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1381,6 +1381,12 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Start following categories to see them here.'** String get followedCategoriesEmptySubheadline; + + /// Message shown in demo mode to provide the verification code + /// + /// In en, this message translates to: + /// **'Demo Mode: Use code {code}'** + String demoVerificationCodeMessage(String code); } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index a5d4cd6f..77393eb0 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -705,4 +705,9 @@ class AppLocalizationsAr extends AppLocalizations { @override String get followedCategoriesEmptySubheadline => 'Start following categories to see them here.'; + + @override + String demoVerificationCodeMessage(String code) { + return 'وضع العرض التوضيحي: استخدم الرمز $code'; + } } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 542d79f3..534f1a60 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -707,4 +707,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get followedCategoriesEmptySubheadline => 'Start following categories to see them here.'; + + @override + String demoVerificationCodeMessage(String code) { + return 'Demo Mode: Use code $code'; + } } diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 38630f09..adeeb46a 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -895,5 +895,15 @@ "savedHeadlinesEmptySubheadline": "لم تقم بحفظ أي مقالات بعد. ابدأ الاستكشاف!", "@savedHeadlinesEmptySubheadline": { "description": "Subheadline for empty state on saved headlines page" + }, + "demoVerificationCodeMessage": "وضع العرض التوضيحي: استخدم الرمز {code}", + "@demoVerificationCodeMessage": { + "description": "رسالة تظهر في وضع العرض التوضيحي لتوفير رمز التحقق", + "placeholders": { + "code": { + "type": "String", + "example": "123456" + } + } } } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 0e3bf466..eddec6d4 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -911,5 +911,15 @@ "followedCategoriesEmptySubheadline": "Start following categories to see them here.", "@followedCategoriesEmptySubheadline": { "description": "Subheadline for empty state on followed categories page" + }, + "demoVerificationCodeMessage": "Demo Mode: Use code {code}", + "@demoVerificationCodeMessage": { + "description": "Message shown in demo mode to provide the verification code", + "placeholders": { + "code": { + "type": "String", + "example": "123456" + } + } } } diff --git a/lib/main.dart b/lib/main.dart index 646499e1..3bbebf89 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ void main() async { AppEnvironment.demo => AppConfig.demo(), }; - final appWidget = await bootstrap(appConfig); + final appWidget = await bootstrap(appConfig, currentEnvironment); // Only remove the splash screen on web after the app is ready. if (kIsWeb) {