diff --git a/assets/translations/en.json b/assets/translations/en.json index 08967745..893d5884 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -197,9 +197,6 @@ "migration_done": "Finish" }, "not_ready_card": { - "begin": "Please finish application setup using ", - "insertion": "Setup Wizard", - "end": " for further work", "in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work." }, "service_page": { diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 23a388e9..a26808ed 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -197,9 +197,6 @@ "migration_done": "Завершить" }, "not_ready_card": { - "begin": "Завершите настройку приложения используя ", - "insertion": "Мастер Настройки", - "end": " для продолжения работы", "in_menu": "Сервер ещё не настроен, воспользуйтесь мастером подключения." }, "service_page": { diff --git a/assets/translations/uk.json b/assets/translations/uk.json index 904932ba..00d76b50 100644 --- a/assets/translations/uk.json +++ b/assets/translations/uk.json @@ -299,9 +299,6 @@ "data_migration_notice": "Під час переносу всі послуги будуть вимкнені." }, "not_ready_card": { - "begin": "Будь ласка, завершіть налаштування програми ", - "insertion": "Майстер налаштувань", - "end": " Для подальшої роботи", "in_menu": "Сервер ще не налаштовано. Будь ласка, завершіть налаштування за допомогою майстра налаштування для подальшої роботи." }, "service_page": { diff --git a/fastlane/metadata/android/en-US/changelogs/0.8.0.txt b/fastlane/metadata/android/en-US/changelogs/0.8.0.txt index e7d8dbad..b03a63d2 100644 --- a/fastlane/metadata/android/en-US/changelogs/0.8.0.txt +++ b/fastlane/metadata/android/en-US/changelogs/0.8.0.txt @@ -31,3 +31,7 @@ For developers: - App now only uses GraphQL API to communicate with the server. All REST API calls have been removed. - Server can now be deployed with staging ACME certificates - Language assets have been reorganized + +Translations: +- Added translation for Ukrainian +- Also activated unfinished translations for German, French, Spanish, Czech, Polish, Thai diff --git a/lib/config/localization.dart b/lib/config/localization.dart index 297928ed..fb50b58e 100644 --- a/lib/config/localization.dart +++ b/lib/config/localization.dart @@ -10,7 +10,17 @@ class Localization extends StatelessWidget { final Widget? child; @override Widget build(final BuildContext context) => EasyLocalization( - supportedLocales: const [Locale('ru'), Locale('en')], + supportedLocales: const [ + Locale('ru'), + Locale('en'), + Locale('uk'), + Locale('de'), + Locale('fr'), + Locale('es'), + Locale('cs'), + Locale('pl'), + Locale('th'), + ], path: 'assets/translations', fallbackLocale: const Locale('en'), useFallbackTranslations: true, diff --git a/lib/ui/components/not_ready_card/not_ready_card.dart b/lib/ui/components/not_ready_card/not_ready_card.dart index e161e8d3..379abf27 100644 --- a/lib/ui/components/not_ready_card/not_ready_card.dart +++ b/lib/ui/components/not_ready_card/not_ready_card.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart'; import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -9,45 +8,26 @@ class NotReadyCard extends StatelessWidget { const NotReadyCard({super.key}); @override - Widget build(final BuildContext context) => Container( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - color: BrandColors.gray6, - ), - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'not_ready_card.begin'.tr(), - style: const TextStyle(color: BrandColors.white), - ), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.5), - child: GestureDetector( - onTap: () => Navigator.of(context).push( - materialRoute( - const InitializingPage(), - ), - ), - child: Text( - 'not_ready_card.insertion'.tr(), - style: body1Style.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - decoration: TextDecoration.underline, - // height: 1.1, - ), - ), - ), + Widget build(final BuildContext context) => FilledCard( + tertiary: true, + child: ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + onTap: () => Navigator.of(context).push( + materialRoute( + const InitializingPage(), + ), + ), + title: Text( + 'not_ready_card.in_menu'.tr(), + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.onTertiaryContainer, ), - ), - TextSpan( - text: 'not_ready_card.end'.tr(), - style: const TextStyle(color: BrandColors.white), - ), - ], + ), + trailing: Icon( + Icons.arrow_forward_ios_outlined, + size: 16, + color: Theme.of(context).colorScheme.onTertiaryContainer, ), ), ); diff --git a/lib/ui/pages/onboarding/onboarding.dart b/lib/ui/pages/onboarding/onboarding.dart index c00b4f12..ac865b11 100644 --- a/lib/ui/pages/onboarding/onboarding.dart +++ b/lib/ui/pages/onboarding/onboarding.dart @@ -21,17 +21,15 @@ class _OnboardingPageState extends State { } @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - body: PageView( - controller: pageController, - children: [ - _withPadding(firstPage()), - _withPadding(secondPage()), - ], - ), - ), - ); + Widget build(final BuildContext context) => Scaffold( + body: PageView( + controller: pageController, + children: [ + _withPadding(firstPage()), + _withPadding(secondPage()), + ], + ), + ); Widget _withPadding(final Widget child) => Padding( padding: const EdgeInsets.symmetric( @@ -45,29 +43,32 @@ class _OnboardingPageState extends State { maxHeight: MediaQuery.of(context).size.height, ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 30), - Text( - 'onboarding.page1_title'.tr(), - style: Theme.of(context).textTheme.headlineSmall, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page1_text'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 16), - Flexible( - child: Center( - child: Image.asset( - _fileName( - context: context, - path: 'assets/images/onboarding', - fileExtention: 'png', - fileName: 'onboarding1', + Expanded( + child: ListView( + children: [ + const SizedBox(height: 30), + Text( + 'onboarding.page1_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, ), - ), + const SizedBox(height: 16), + Text( + 'onboarding.page1_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 32), + Center( + child: Image.asset( + _fileName( + context: context, + path: 'assets/images/onboarding', + fileExtention: 'png', + fileName: 'onboarding1', + ), + ), + ), + ], ), ), BrandButton.rised( @@ -90,49 +91,54 @@ class _OnboardingPageState extends State { maxHeight: MediaQuery.of(context).size.height, ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 30), - Text( - 'onboarding.page2_title'.tr(), - style: Theme.of(context).textTheme.headlineSmall, + Expanded( + child: ListView( + children: [ + const SizedBox(height: 30), + Text( + 'onboarding.page2_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_server_provider_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_server_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_dns_provider_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_dns_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_backup_provider_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_backup_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + ], + ), ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_text'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_server_provider_title'.tr(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_server_provider_text'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_dns_provider_title'.tr(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_dns_provider_text'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_backup_provider_title'.tr(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 16), - Text( - 'onboarding.page2_backup_provider_text'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 16), BrandButton.rised( onPressed: () { context.read().turnOffOnboarding(); diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index ee01085e..d234c984 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -72,7 +72,7 @@ class _ProvidersPageState extends State { children: [ if (!isReady) ...[ const NotReadyCard(), - const SizedBox(height: 24), + const SizedBox(height: 16), ], _Card( state: getServerStatus(), diff --git a/lib/ui/pages/root_route.dart b/lib/ui/pages/root_route.dart index 9b62dae0..ce1f344c 100644 --- a/lib/ui/pages/root_route.dart +++ b/lib/ui/pages/root_route.dart @@ -52,40 +52,38 @@ class _RootPageState extends State with TickerProviderStateMixin { final bool isReady = context.watch().state is ServerInstallationFinished; - return SafeArea( - child: Provider( - create: (final _) => ChangeTab(tabController.animateTo), - child: Scaffold( - body: TabBarView( - controller: tabController, - children: const [ - ProvidersPage(), - ServicesPage(), - UsersPage(), - MorePage(), - ], - ), - bottomNavigationBar: BrandTabBar( - controller: tabController, - ), - floatingActionButton: isReady - ? SizedBox( - height: 104 + 16, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ScaleTransition( - scale: _animation, - child: const AddUserFab(), - ), - const SizedBox(height: 16), - const BrandFab(), - ], - ), - ) - : null, + return Provider( + create: (final _) => ChangeTab(tabController.animateTo), + child: Scaffold( + body: TabBarView( + controller: tabController, + children: const [ + ProvidersPage(), + ServicesPage(), + UsersPage(), + MorePage(), + ], ), + bottomNavigationBar: BrandTabBar( + controller: tabController, + ), + floatingActionButton: isReady + ? SizedBox( + height: 104 + 16, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ScaleTransition( + scale: _animation, + child: const AddUserFab(), + ), + const SizedBox(height: 16), + const BrandFab(), + ], + ), + ) + : null, ), ); } diff --git a/lib/ui/pages/services/service_page.dart b/lib/ui/pages/services/service_page.dart index d923a5b0..22db2bf6 100644 --- a/lib/ui/pages/services/service_page.dart +++ b/lib/ui/pages/services/service_page.dart @@ -9,8 +9,8 @@ import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; -import 'package:url_launcher/url_launcher.dart'; class ServicePage extends StatefulWidget { const ServicePage({required this.serviceId, super.key}); @@ -59,7 +59,7 @@ class _ServicePageState extends State { if (service.url != null) ListTile( iconColor: Theme.of(context).colorScheme.onBackground, - onTap: () => _launchURL(service.url), + onTap: () => launchURL(service.url), leading: const Icon(Icons.open_in_browser), title: Text( 'service_page.open_in_browser'.tr(), @@ -232,15 +232,3 @@ class ServiceStatusCard extends StatelessWidget { } } } - -void _launchURL(final url) async { - try { - final Uri uri = Uri.parse(url); - await launchUrl( - uri, - mode: LaunchMode.externalApplication, - ); - } catch (e) { - print(e); - } -} diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 606fc9d7..54192367 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -12,9 +12,9 @@ import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/ui/pages/services/service_page.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; -import 'package:url_launcher/url_launcher.dart'; class ServicesPage extends StatefulWidget { const ServicesPage({super.key}); @@ -23,18 +23,6 @@ class ServicesPage extends StatefulWidget { State createState() => _ServicesPageState(); } -void _launchURL(final url) async { - try { - final Uri uri = Uri.parse(url); - await launchUrl( - uri, - mode: LaunchMode.externalApplication, - ); - } catch (e) { - print(e); - } -} - class _ServicesPageState extends State { @override Widget build(final BuildContext context) { @@ -145,7 +133,7 @@ class _Card extends StatelessWidget { Column( children: [ GestureDetector( - onTap: () => _launchURL( + onTap: () => launchURL( service.url, ), child: Text( diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart index 72d82520..ca502b65 100644 --- a/lib/ui/pages/setup/initializing/initializing.dart +++ b/lib/ui/pages/setup/initializing/initializing.dart @@ -67,6 +67,9 @@ class InitializingPage extends StatelessWidget { }, ) ], + title: Text( + 'more_page.configuration_wizard'.tr(), + ), bottom: PreferredSize( preferredSize: const Size.fromHeight(28), child: Padding( diff --git a/lib/ui/pages/setup/initializing/server_provider_picker.dart b/lib/ui/pages/setup/initializing/server_provider_picker.dart index 9775a945..48f2590e 100644 --- a/lib/ui/pages/setup/initializing/server_provider_picker.dart +++ b/lib/ui/pages/setup/initializing/server_provider_picker.dart @@ -12,7 +12,7 @@ import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart'; import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart'; import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; import 'package:selfprivacy/ui/components/info_box/info_box.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; class ServerProviderPicker extends StatefulWidget { const ServerProviderPicker({ @@ -249,7 +249,7 @@ class ProviderSelectionPage extends StatelessWidget { // Outlined button that will open website BrandOutlinedButton( onPressed: () => - _launchURL('https://www.hetzner.com/cloud'), + launchURL('https://www.hetzner.com/cloud'), title: 'initializing.select_provider_site_button'.tr(), ), ], @@ -323,7 +323,7 @@ class ProviderSelectionPage extends StatelessWidget { // Outlined button that will open website BrandOutlinedButton( onPressed: () => - _launchURL('https://www.digitalocean.com'), + launchURL('https://www.digitalocean.com'), title: 'initializing.select_provider_site_button'.tr(), ), ], @@ -336,15 +336,3 @@ class ProviderSelectionPage extends StatelessWidget { ), ); } - -void _launchURL(final url) async { - try { - final Uri uri = Uri.parse(url); - await launchUrl( - uri, - mode: LaunchMode.externalApplication, - ); - } catch (e) { - print(e); - } -} diff --git a/lib/utils/launch_url.dart b/lib/utils/launch_url.dart new file mode 100644 index 00000000..d7ccd0fa --- /dev/null +++ b/lib/utils/launch_url.dart @@ -0,0 +1,13 @@ +import 'package:url_launcher/url_launcher.dart'; + +void launchURL(final url) async { + try { + final Uri uri = Uri.parse(url); + await launchUrl( + uri, + mode: LaunchMode.externalApplication, + ); + } catch (e) { + print(e); + } +}