diff --git a/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart b/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart index 9f08d173b..8294fbea0 100644 --- a/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart +++ b/app/lib/activation_code/src/bloc/enter_activation_code_bloc.dart @@ -15,6 +15,7 @@ import 'package:flutter/material.dart'; import 'package:key_value_store/key_value_store.dart'; import 'package:rxdart/rxdart.dart'; import 'package:helper_functions/helper_functions.dart'; +import 'package:sharezone/l10n/feature_flag_l10n.dart'; import '../models/enter_activation_code_result.dart'; import 'enter_activation_code_activator.dart'; @@ -26,6 +27,7 @@ class EnterActivationCodeBloc extends BlocBase { final _enterActivationCodeSubject = BehaviorSubject(); final KeyValueStore keyValueStore; + final FeatureFlagl10n featureFlagl10n; String? _lastEnteredValue; @@ -34,6 +36,7 @@ class EnterActivationCodeBloc extends BlocBase { this.crashAnalytics, this.appFunctions, this.keyValueStore, + this.featureFlagl10n, ) { _changeEnterActivationCodeResult(NoDataEnterActivationCodeResult()); } @@ -89,6 +92,11 @@ class EnterActivationCodeBloc extends BlocBase { return; } + if (_lastEnteredValue?.trim().toLowerCase() == 'l10n') { + _togglel10nFeatureFlag(); + return; + } + _changeEnterActivationCodeResult(LoadingEnterActivationCodeResult()); final enterActivationCodeResult = await _runAppFunction(enteredValue); @@ -107,6 +115,18 @@ class EnterActivationCodeBloc extends BlocBase { ); } + void _togglel10nFeatureFlag() { + final currentValue = featureFlagl10n.isl10nEnabled; + featureFlagl10n.toggle(); + + _changeEnterActivationCodeResult( + SuccessfulEnterActivationCodeResult( + 'l10n', + 'l10n wurde ${!currentValue ? 'aktiviert' : 'deaktiviert'}. Starte die App neu, um die Ă„nderungen zu sehen.', + ), + ); + } + Future _clearCache(BuildContext context) async { await Future.wait([ keyValueStore.clear(), diff --git a/app/lib/activation_code/src/bloc/enter_activation_code_bloc_factory.dart b/app/lib/activation_code/src/bloc/enter_activation_code_bloc_factory.dart index adf961012..5c752eab4 100644 --- a/app/lib/activation_code/src/bloc/enter_activation_code_bloc_factory.dart +++ b/app/lib/activation_code/src/bloc/enter_activation_code_bloc_factory.dart @@ -12,18 +12,21 @@ import 'package:bloc_base/bloc_base.dart'; import 'package:crash_analytics/crash_analytics.dart'; import 'package:key_value_store/key_value_store.dart'; import 'package:sharezone/activation_code/src/bloc/enter_activation_code_bloc.dart'; +import 'package:sharezone/l10n/feature_flag_l10n.dart'; class EnterActivationCodeBlocFactory extends BlocBase { final CrashAnalytics crashAnalytics; final Analytics analytics; final SharezoneAppFunctions appFunctions; final KeyValueStore keyValueStore; + final FeatureFlagl10n featureFlagl10n; EnterActivationCodeBlocFactory({ required this.analytics, required this.crashAnalytics, required this.appFunctions, required this.keyValueStore, + required this.featureFlagl10n, }); EnterActivationCodeBloc createBloc() { @@ -32,6 +35,7 @@ class EnterActivationCodeBlocFactory extends BlocBase { crashAnalytics, appFunctions, keyValueStore, + featureFlagl10n, ); } diff --git a/app/lib/l10n/feature_flag_l10n.dart b/app/lib/l10n/feature_flag_l10n.dart new file mode 100644 index 000000000..3fafa20ff --- /dev/null +++ b/app/lib/l10n/feature_flag_l10n.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:sharezone/util/cache/streaming_key_value_store.dart'; + +class FeatureFlagl10n extends ChangeNotifier { + FeatureFlagl10n(this.keyValueStore) { + _subscription = keyValueStore + .getBool('l10n_enabled', defaultValue: false) + .listen((event) { + final newValue = event == true; + if (isl10nEnabled != newValue) { + isl10nEnabled = newValue; + notifyListeners(); + } + }); + } + + final StreamingKeyValueStore keyValueStore; + late StreamSubscription _subscription; + bool isl10nEnabled = false; + + void toggle() { + isl10nEnabled = !isl10nEnabled; + keyValueStore.setBool('l10n_enabled', isl10nEnabled); + notifyListeners(); + } + + @override + void dispose() { + _subscription.cancel(); + super.dispose(); + } +} diff --git a/app/lib/l10n/flutter_app_local_gateway.dart b/app/lib/l10n/flutter_app_local_gateway.dart index badc45ea9..9a53b2f43 100644 --- a/app/lib/l10n/flutter_app_local_gateway.dart +++ b/app/lib/l10n/flutter_app_local_gateway.dart @@ -1,16 +1,23 @@ import 'dart:convert'; +import 'package:sharezone/l10n/feature_flag_l10n.dart'; import 'package:sharezone/util/cache/streaming_key_value_store.dart'; import 'package:sharezone_localizations/sharezone_localizations.dart'; class FlutterAppLocaleProviderGateway extends AppLocaleProviderGateway { - FlutterAppLocaleProviderGateway({required this.keyValueStore}); + FlutterAppLocaleProviderGateway({ + required this.keyValueStore, + required this.featureFlagl10n, + }); + final FeatureFlagl10n featureFlagl10n; final StreamingKeyValueStore keyValueStore; @override Stream getLocale() { - final defaultValue = jsonEncode(AppLocales.system.toMap()); + final defaultValue = jsonEncode(featureFlagl10n.isl10nEnabled + ? AppLocales.system.toMap() + : AppLocales.en.toMap()); return keyValueStore .getString('locale', defaultValue: defaultValue) .map((event) => AppLocales.fromMap(jsonDecode(event))); diff --git a/app/lib/main/sharezone.dart b/app/lib/main/sharezone.dart index ade560bd0..e98ac182b 100644 --- a/app/lib/main/sharezone.dart +++ b/app/lib/main/sharezone.dart @@ -21,6 +21,7 @@ import 'package:provider/provider.dart'; import 'package:sharezone/dynamic_links/beitrittsversuch.dart'; import 'package:sharezone/dynamic_links/dynamic_link_bloc.dart'; import 'package:sharezone/dynamic_links/dynamic_links.dart'; +import 'package:sharezone/l10n/feature_flag_l10n.dart'; import 'package:sharezone/l10n/flutter_app_local_gateway.dart'; import 'package:sharezone/main/auth_app.dart'; import 'package:sharezone/main/bloc_dependencies.dart'; @@ -75,6 +76,8 @@ class Sharezone extends StatefulWidget { class _SharezoneState extends State with WidgetsBindingObserver { late SignUpBloc signUpBloc; late StreamSubscription authSubscription; + late StreamingKeyValueStore streamingKeyValueStore; + late FeatureFlagl10n featureFlagl10n; @override void initState() { @@ -96,6 +99,11 @@ class _SharezoneState extends State with WidgetsBindingObserver { authSubscription = listenToAuthStateChanged().listen((user) { authUserSubject.sink.add(user); }); + + streamingKeyValueStore = FlutterStreamingKeyValueStore( + widget.blocDependencies.streamingSharedPreferences, + ); + featureFlagl10n = FeatureFlagl10n(streamingKeyValueStore); } void logAppOpen() { @@ -132,13 +140,12 @@ class _SharezoneState extends State with WidgetsBindingObserver { MobileDeviceInformationRetriever(), ), ), + ChangeNotifierProvider.value(value: featureFlagl10n), ChangeNotifierProvider( create: (context) => AppLocaleProvider( gateway: FlutterAppLocaleProviderGateway( - keyValueStore: FlutterStreamingKeyValueStore( - widget.blocDependencies - .streamingSharedPreferences, - ), + keyValueStore: streamingKeyValueStore, + featureFlagl10n: featureFlagl10n, ), ), ), diff --git a/app/lib/main/sharezone_bloc_providers.dart b/app/lib/main/sharezone_bloc_providers.dart index 50e902348..122fc9224 100644 --- a/app/lib/main/sharezone_bloc_providers.dart +++ b/app/lib/main/sharezone_bloc_providers.dart @@ -84,6 +84,7 @@ import 'package:sharezone/ical_links/dialog/ical_links_dialog_controller_factory import 'package:sharezone/ical_links/list/ical_links_page_controller.dart'; import 'package:sharezone/ical_links/shared/ical_link_analytics.dart'; import 'package:sharezone/ical_links/shared/ical_links_gateway.dart'; +import 'package:sharezone/l10n/feature_flag_l10n.dart'; import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/main/bloc_dependencies.dart'; import 'package:sharezone/main/onboarding/onboarding_navigator.dart'; @@ -593,6 +594,7 @@ class _SharezoneBlocProvidersState extends State { analytics: analytics, appFunctions: api.references.functions, keyValueStore: widget.blocDependencies.keyValueStore, + featureFlagl10n: context.read(), ), ), BlocProvider( diff --git a/app/lib/settings/settings_page.dart b/app/lib/settings/settings_page.dart index b485dec49..d7a28a121 100644 --- a/app/lib/settings/settings_page.dart +++ b/app/lib/settings/settings_page.dart @@ -10,6 +10,8 @@ import 'package:analytics/analytics.dart'; import 'package:bloc_provider/bloc_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:provider/provider.dart'; +import 'package:sharezone/l10n/feature_flag_l10n.dart'; import 'package:sharezone/legal/terms_of_service/terms_of_service_page.dart'; import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/navigation/logic/navigation_bloc.dart'; @@ -146,34 +148,36 @@ class _LegalSection extends StatelessWidget { class _AppSettingsSection extends StatelessWidget { @override Widget build(BuildContext context) { - return const _SettingsSection( + final featureFlagl10n = context.watch(); + return _SettingsSection( title: 'App-Einstellungen', children: [ - _SettingsOption( + const _SettingsOption( title: "Mein Konto", icon: Icon(Icons.account_circle), tag: MyProfilePage.tag, ), - _SettingsOption( + const _SettingsOption( title: "Benachrichtigungen", icon: Icon(Icons.notifications_active), tag: NotificationPage.tag, ), - _SettingsOption( + const _SettingsOption( title: "Erscheinungsbild", icon: Icon(Icons.color_lens), tag: ThemePage.tag, ), - _SettingsOption( + const _SettingsOption( title: "Stundenplan", icon: Icon(Icons.access_time), tag: TimetableSettingsPage.tag, ), - _SettingsOption( - title: "Sprache", - icon: Icon(Icons.language), - tag: LanguagePage.tag, - ), + if (featureFlagl10n.isl10nEnabled) + const _SettingsOption( + title: "Sprache", + icon: Icon(Icons.language), + tag: LanguagePage.tag, + ), ], ); }