From 1f152a0a24e4ae653ebc1cef1ade903a722b2d78 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 2 Jul 2022 09:58:02 +0300 Subject: [PATCH 01/34] Bump --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index e2303165..942d919e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: avzag description: Extensible parallel language compendium publish_to: "none" -version: 0.8.2+62 +version: 1.0.0+62 environment: sdk: ">=2.13.0 <3.0.0" From cb5f3d11d6366fcea31f54db014dd87b89907496 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 17:22:45 +0300 Subject: [PATCH 02/34] Clean up navigation --- lib/modules/navigation/navigation.dart | 84 ------------------- lib/modules/navigation/services/modules.dart | 33 -------- .../navigation/services/root_guard.dart | 24 ------ .../navigation/widgets/github_tile.dart | 36 -------- .../navigation/widgets/module_tile.dart | 37 -------- .../navigation/widgets/stores_buttons.dart | 33 -------- lib/{modules => }/navigation/loader.dart | 0 lib/navigation/root_guard.dart | 15 ++++ .../route_builders.dart | 0 .../services => navigation}/router.dart | 45 +++++----- 10 files changed, 35 insertions(+), 272 deletions(-) delete mode 100644 lib/modules/navigation/navigation.dart delete mode 100644 lib/modules/navigation/services/modules.dart delete mode 100644 lib/modules/navigation/services/root_guard.dart delete mode 100644 lib/modules/navigation/widgets/github_tile.dart delete mode 100644 lib/modules/navigation/widgets/module_tile.dart delete mode 100644 lib/modules/navigation/widgets/stores_buttons.dart rename lib/{modules => }/navigation/loader.dart (100%) create mode 100644 lib/navigation/root_guard.dart rename lib/{modules/navigation/services => navigation}/route_builders.dart (100%) rename lib/{modules/navigation/services => navigation}/router.dart (84%) diff --git a/lib/modules/navigation/navigation.dart b/lib/modules/navigation/navigation.dart deleted file mode 100644 index 87c7c553..00000000 --- a/lib/modules/navigation/navigation.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:avzag/modules/navigation/widgets/github_tile.dart'; -import 'package:avzag/modules/navigation/widgets/module_tile.dart'; -import 'package:avzag/modules/navigation/widgets/stores_buttons.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; -import 'package:avzag/shared/widgets/expandable_tile.dart'; -import 'package:avzag/shared/widgets/raxys.dart'; -import 'package:avzag/shared/widgets/span_icon.dart'; -import 'package:avzag/store.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'services/modules.dart'; -import 'services/router.gr.dart'; - -class NavigationScreen extends StatelessWidget { - const NavigationScreen({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Drawer( - child: Material( - color: Theme.of(context).scaffoldBackgroundColor, - child: ListView( - children: [ - ExpandableTile( - header: ListTile( - leading: const Raxys( - opacity: .1, - scale: 7, - ), - title: Text( - 'Ævzag', - style: Theme.of(context).textTheme.headline6, - ), - ), - body: ColumnCard( - divider: null, - margin: const EdgeInsets.only(bottom: 12), - children: [ - ListTile( - leading: const Icon(Icons.send_outlined), - title: const Text('Developer Contact'), - subtitle: const Text('Raxys Studios'), - onTap: () => openLink('https://t.me/raxysstudios'), - ), - const GitHubTile(), - SwitchListTile( - title: const Text('Editor Mode'), - subtitle: EditorStore.editor - ? Row( - children: [ - if (EditorStore.admin) - const SpanIcon(Icons.verified_user_outlined), - Text(EditorStore.language!.titled), - ], - ) - : null, - value: EditorStore.editor, - secondary: const Icon(Icons.edit_outlined), - onChanged: (e) => context.pushRoute(const AccountRoute()), - ), - if (kIsWeb) ...[ - const Divider(), - const StoresButtons(), - ], - ], - ), - ), - ColumnCard( - divider: null, - margin: EdgeInsets.zero, - children: [ - for (final m in modules) ModuleTile(m), - ], - ) - ], - ), - ), - ); - } -} diff --git a/lib/modules/navigation/services/modules.dart b/lib/modules/navigation/services/modules.dart deleted file mode 100644 index fd2eecf4..00000000 --- a/lib/modules/navigation/services/modules.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; - -import 'router.gr.dart'; - -class NavModule { - const NavModule( - this.icon, - this.text, [ - this.route, - ]); - - final IconData icon; - final String text; - final PageRouteInfo? route; -} - -const modules = [ - NavModule( - Icons.home_outlined, - 'home', - HomeRoute(), - ), - NavModule( - Icons.book_outlined, - 'dictionary', - DictionaryRootRoute(), - ), - NavModule(Icons.music_note_outlined, 'phonology'), - NavModule(Icons.switch_left_outlined, 'converter'), - NavModule(Icons.forum_outlined, 'phrasebook'), - NavModule(Icons.local_library_outlined, 'bootcamp'), -]; diff --git a/lib/modules/navigation/services/root_guard.dart b/lib/modules/navigation/services/root_guard.dart deleted file mode 100644 index 9d8cf355..00000000 --- a/lib/modules/navigation/services/root_guard.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:avzag/store.dart'; - -import 'modules.dart'; -import 'router.gr.dart'; - -class RootGuard extends AutoRouteGuard { - @override - void onNavigation(NavigationResolver resolver, StackRouter router) { - var route = - router.hasEntries ? const DictionaryRootRoute() : const HomeRoute(); - final saved = prefs.getString('module'); - for (final m in modules) { - if (saved == m.text && m.route != null) { - route = m.route!; - break; - } - } - if (saved == null && router.hasEntries) { - prefs.setString('module', 'dictionary'); - } - router.pushAndPopUntil(route, predicate: (_) => true); - } -} diff --git a/lib/modules/navigation/widgets/github_tile.dart b/lib/modules/navigation/widgets/github_tile.dart deleted file mode 100644 index b52fe60a..00000000 --- a/lib/modules/navigation/widgets/github_tile.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:avzag/shared/utils.dart'; -import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; - -class GitHubTile extends StatefulWidget { - const GitHubTile({Key? key}) : super(key: key); - - @override - State createState() => _GitHubTileState(); -} - -class _GitHubTileState extends State { - var info = 'Loading...'; - - @override - void initState() { - super.initState(); - PackageInfo.fromPlatform().then( - (package) => setState(() { - info = 'v${package.version} • b${package.buildNumber}'; - }), - ); - } - - @override - Widget build(BuildContext context) { - return ListTile( - leading: const Icon(Icons.code_outlined), - title: const Text('GitHub Repository'), - subtitle: Text(info), - onTap: () => openLink( - 'https://github.com/raxysstudios/avzag', - ), - ); - } -} diff --git a/lib/modules/navigation/widgets/module_tile.dart b/lib/modules/navigation/widgets/module_tile.dart deleted file mode 100644 index cbcfceaa..00000000 --- a/lib/modules/navigation/widgets/module_tile.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:avzag/modules/navigation/services/modules.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/store.dart'; -import 'package:flutter/material.dart'; - -import '../services/router.gr.dart'; - -class ModuleTile extends StatelessWidget { - const ModuleTile( - this.module, { - Key? key, - }) : super(key: key); - - final NavModule module; - - @override - Widget build(BuildContext context) { - return ListTile( - leading: Icon(module.icon), - title: Text( - module.text.titled, - style: const TextStyle(fontSize: 18), - ), - trailing: - module.route == null ? const Icon(Icons.construction_outlined) : null, - selected: context.router.currentPath.startsWith(module.route?.path ?? ''), - onTap: () async { - if (module.route != const HomeRoute()) { - await prefs.setString('module', module.text); - } - context.pushRoute(module.route!); - }, - enabled: module.route != null, - ); - } -} diff --git a/lib/modules/navigation/widgets/stores_buttons.dart b/lib/modules/navigation/widgets/stores_buttons.dart deleted file mode 100644 index c23ab4d5..00000000 --- a/lib/modules/navigation/widgets/stores_buttons.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:avzag/shared/utils.dart'; -import 'package:flutter/material.dart'; - -class StoresButtons extends StatelessWidget { - const StoresButtons({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ListTile( - leading: const Icon(Icons.get_app_outlined), - title: Row( - children: [ - Expanded( - child: TextButton( - onPressed: () => openLink( - 'https://play.google.com/store/apps/details?id=com.alkaitagi.avzag', - ), - child: const Text('Google Play'), - ), - ), - Expanded( - child: TextButton( - onPressed: () => openLink( - 'https://apps.apple.com/app/avzag-languages-of-caucasus/id1603226004', - ), - child: const Text('App Store'), - ), - ), - ], - ), - ); - } -} diff --git a/lib/modules/navigation/loader.dart b/lib/navigation/loader.dart similarity index 100% rename from lib/modules/navigation/loader.dart rename to lib/navigation/loader.dart diff --git a/lib/navigation/root_guard.dart b/lib/navigation/root_guard.dart new file mode 100644 index 00000000..2d7cfa63 --- /dev/null +++ b/lib/navigation/root_guard.dart @@ -0,0 +1,15 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:avzag/store.dart'; + +import 'router.gr.dart'; + +class RootGuard extends AutoRouteGuard { + @override + void onNavigation(NavigationResolver resolver, StackRouter router) { + if (prefs.getStringList('languages')?.isNotEmpty ?? false) { + resolver.next(); + } else { + router.replaceAll([const HomeRoute()]); + } + } +} diff --git a/lib/modules/navigation/services/route_builders.dart b/lib/navigation/route_builders.dart similarity index 100% rename from lib/modules/navigation/services/route_builders.dart rename to lib/navigation/route_builders.dart diff --git a/lib/modules/navigation/services/router.dart b/lib/navigation/router.dart similarity index 84% rename from lib/modules/navigation/services/router.dart rename to lib/navigation/router.dart index 23fbd741..bc9e8ce9 100644 --- a/lib/modules/navigation/services/router.dart +++ b/lib/navigation/router.dart @@ -1,11 +1,11 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/modules/account/account.dart'; import 'package:avzag/modules/dictionary/dictionary.dart'; import 'package:avzag/modules/dictionary/word.dart'; import 'package:avzag/modules/dictionary/word_editor.dart'; import 'package:avzag/modules/dictionary/word_loader.dart'; import 'package:avzag/modules/dictionary/words_diff.dart'; import 'package:avzag/modules/home/home.dart'; +import 'package:avzag/modules/settings/settings.dart'; import 'root_guard.dart'; import 'route_builders.dart'; @@ -18,34 +18,11 @@ import 'route_builders.dart'; page: EmptyRouterScreen, name: 'RootRoute', guards: [RootGuard], - ), - AutoRoute( - path: '/account', - page: AccountScreen, - ), - AutoRoute( - path: '/home', - page: HomeScreen, - ), - AutoRoute( - path: '/dictionary', - page: EmptyRouterScreen, - name: 'DictionaryRootRoute', children: [ AutoRoute( path: '', page: DictionaryScreen, ), - CustomRoute( - path: ':id', - page: WordLoaderScreen, - customRouteBuilder: dialogRouteBuilder, - ), - CustomRoute( - path: ':id', - page: WordScreen, - customRouteBuilder: sheetRouteBuilder, - ), CustomRoute( path: 'editor', page: WordEditorScreen, @@ -56,9 +33,27 @@ import 'route_builders.dart'; page: WordsDiffScreen, customRouteBuilder: sheetRouteBuilder, ), + CustomRoute( + path: ':id', + page: WordLoaderScreen, + customRouteBuilder: dialogRouteBuilder, + ), + CustomRoute( + path: ':id', + page: WordScreen, + customRouteBuilder: sheetRouteBuilder, + ), ], ), - RedirectRoute(path: '*', redirectTo: '/') + AutoRoute( + path: '/home', + page: HomeScreen, + ), + AutoRoute( + path: '/settings', + page: SettingsScreen, + ), + RedirectRoute(path: '*', redirectTo: '/'), ], ) class $AppRouter {} From 20c8c56127a8d36fdb3d4d4c7ffb22b64fc31636 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 17:22:57 +0300 Subject: [PATCH 03/34] Join models --- .../dictionary => }/models/entry.dart | 0 lib/models/language.dart | 30 ++++--------------- .../dictionary => }/models/sample.dart | 0 lib/{modules/dictionary => }/models/use.dart | 0 lib/{modules/dictionary => }/models/word.dart | 0 5 files changed, 5 insertions(+), 25 deletions(-) rename lib/{modules/dictionary => }/models/entry.dart (100%) rename lib/{modules/dictionary => }/models/sample.dart (100%) rename lib/{modules/dictionary => }/models/use.dart (100%) rename lib/{modules/dictionary => }/models/word.dart (100%) diff --git a/lib/modules/dictionary/models/entry.dart b/lib/models/entry.dart similarity index 100% rename from lib/modules/dictionary/models/entry.dart rename to lib/models/entry.dart diff --git a/lib/models/language.dart b/lib/models/language.dart index bfc9b7cb..b0f61145 100644 --- a/lib/models/language.dart +++ b/lib/models/language.dart @@ -1,53 +1,33 @@ import 'package:avzag/shared/utils.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; -class LanguageStats { - final int dictionary; - - LanguageStats({ - this.dictionary = 0, - }); - - LanguageStats.fromJson(Map json) - : this( - dictionary: json['dictionary'] as int, - ); - - Map toJson() => { - 'dictionary': dictionary, - }; -} - class Language { final String name; final String endonym; - final String? flag; final String? contact; final List? aliases; final GeoPoint? location; - final LanguageStats? stats; + final int dictionary; const Language({ required this.name, required this.endonym, - this.flag, this.contact, this.aliases, this.location, - this.stats, + this.dictionary = 0, }); Language.fromJson(Map json) : this( name: json['name'] as String, endonym: json['endonym'] as String, - flag: json['flag'] as String?, contact: json['contact'] as String?, aliases: json2list(json['aliases']), location: json['location'] == null ? null : json['location'] as GeoPoint, - stats: json['stats'] == null - ? null - : LanguageStats.fromJson(json['stats'] as Map), + dictionary: + (json['stats'] as Map?)?['dictionary'] as int? ?? + 0, ); } diff --git a/lib/modules/dictionary/models/sample.dart b/lib/models/sample.dart similarity index 100% rename from lib/modules/dictionary/models/sample.dart rename to lib/models/sample.dart diff --git a/lib/modules/dictionary/models/use.dart b/lib/models/use.dart similarity index 100% rename from lib/modules/dictionary/models/use.dart rename to lib/models/use.dart diff --git a/lib/modules/dictionary/models/word.dart b/lib/models/word.dart similarity index 100% rename from lib/modules/dictionary/models/word.dart rename to lib/models/word.dart From 5b64d195e68df01b91a0dcaed2f2dba25cace477 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 17:23:29 +0300 Subject: [PATCH 04/34] Change account to settings --- lib/modules/account/account.dart | 61 ---------- .../services/credentials.dart | 0 .../services/crypto.dart | 0 .../services/signing.dart | 0 lib/modules/settings/settings.dart | 113 ++++++++++++++++++ .../widgets/account_tile.dart | 0 .../widgets/editor_languages.dart | 0 .../widgets/sign_in_buttons.dart | 0 8 files changed, 113 insertions(+), 61 deletions(-) delete mode 100644 lib/modules/account/account.dart rename lib/modules/{account => settings}/services/credentials.dart (100%) rename lib/modules/{account => settings}/services/crypto.dart (100%) rename lib/modules/{account => settings}/services/signing.dart (100%) create mode 100644 lib/modules/settings/settings.dart rename lib/modules/{account => settings}/widgets/account_tile.dart (100%) rename lib/modules/{account => settings}/widgets/editor_languages.dart (100%) rename lib/modules/{account => settings}/widgets/sign_in_buttons.dart (100%) diff --git a/lib/modules/account/account.dart b/lib/modules/account/account.dart deleted file mode 100644 index b2c4873f..00000000 --- a/lib/modules/account/account.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/language.dart'; -import 'package:avzag/modules/account/widgets/account_tile.dart'; -import 'package:avzag/modules/account/widgets/editor_languages.dart'; -import 'package:avzag/modules/navigation/services/router.gr.dart'; -import 'package:avzag/store.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; - -import 'widgets/sign_in_buttons.dart'; - -class AccountScreen extends StatefulWidget { - const AccountScreen({Key? key}) : super(key: key); - - @override - State createState() => _AccountScreenState(); -} - -class _AccountScreenState extends State { - User? get user => FirebaseAuth.instance.currentUser; - var language = EditorStore.language; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: const AutoLeadingButton(), - title: const Text('Account'), - ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.done_all_outlined), - onPressed: () { - EditorStore.language = language; - context.pushRoute(const RootRoute()); - }, - ), - body: ListView( - padding: const EdgeInsets.only(bottom: 76), - children: [ - if (user == null) - SignInButtons( - onSingIn: () => setState(() {}), - ) - else ...[ - AccountTile( - user!, - onSignOut: () => setState(() {}), - ), - EditorLanguages( - GlobalStore.languages.values.whereType(), - onTap: (l) => setState(() { - language = l == language ? null : l; - }), - selected: language, - ), - ], - ], - ), - ); - } -} diff --git a/lib/modules/account/services/credentials.dart b/lib/modules/settings/services/credentials.dart similarity index 100% rename from lib/modules/account/services/credentials.dart rename to lib/modules/settings/services/credentials.dart diff --git a/lib/modules/account/services/crypto.dart b/lib/modules/settings/services/crypto.dart similarity index 100% rename from lib/modules/account/services/crypto.dart rename to lib/modules/settings/services/crypto.dart diff --git a/lib/modules/account/services/signing.dart b/lib/modules/settings/services/signing.dart similarity index 100% rename from lib/modules/account/services/signing.dart rename to lib/modules/settings/services/signing.dart diff --git a/lib/modules/settings/settings.dart b/lib/modules/settings/settings.dart new file mode 100644 index 00000000..c12a09de --- /dev/null +++ b/lib/modules/settings/settings.dart @@ -0,0 +1,113 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:avzag/models/language.dart'; +import 'package:avzag/navigation/router.gr.dart'; +import 'package:avzag/shared/utils.dart'; +import 'package:avzag/shared/widgets/raxys.dart'; +import 'package:avzag/store.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +import 'widgets/account_tile.dart'; +import 'widgets/editor_languages.dart'; +import 'widgets/sign_in_buttons.dart'; + +class SettingsScreen extends StatefulWidget { + const SettingsScreen({Key? key}) : super(key: key); + + @override + State createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + User? get user => FirebaseAuth.instance.currentUser; + var language = EditorStore.language; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: const AutoLeadingButton(), + title: const Text('Settings'), + actions: const [ + Raxys(), + SizedBox(width: 4), + ], + ), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.done_all_outlined), + onPressed: () { + EditorStore.language = language; + context.pushRoute(const RootRoute()); + }, + ), + body: ListView( + padding: const EdgeInsets.only(bottom: 76), + children: [ + if (user == null) + SignInButtons( + onSingIn: () => setState(() {}), + ) + else ...[ + AccountTile( + user!, + onSignOut: () => setState(() {}), + ), + EditorLanguages( + GlobalStore.languages.values.whereType(), + onTap: (l) => setState(() { + language = l == language ? null : l; + }), + selected: language, + ), + ], + ListTile( + leading: const Icon(Icons.send_outlined), + title: const Text('Developer Contact'), + subtitle: const Text('Raxys Studios'), + onTap: () => openLink('https://t.me/raxysstudios'), + ), + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, status) { + final p = status.data; + return ListTile( + leading: const Icon(Icons.code_outlined), + title: const Text('GitHub Repository'), + subtitle: Text( + p == null ? '...' : 'v${p.version} • b${p.buildNumber}', + ), + onTap: () => openLink( + 'https://github.com/raxysstudios/avzag', + ), + ); + }, + ), + ListTile( + leading: const Icon(Icons.get_app_outlined), + title: Row( + children: [ + Expanded( + child: TextButton( + onPressed: () => openLink( + 'https://play.google.com/store/apps/details?id=com.alkaitagi.avzag', + ), + child: const Text('Google Play'), + ), + ), + Expanded( + child: TextButton( + onPressed: () => openLink( + 'https://apps.apple.com/app/avzag-languages-of-caucasus/id1603226004', + ), + child: const Text('App Store'), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/modules/account/widgets/account_tile.dart b/lib/modules/settings/widgets/account_tile.dart similarity index 100% rename from lib/modules/account/widgets/account_tile.dart rename to lib/modules/settings/widgets/account_tile.dart diff --git a/lib/modules/account/widgets/editor_languages.dart b/lib/modules/settings/widgets/editor_languages.dart similarity index 100% rename from lib/modules/account/widgets/editor_languages.dart rename to lib/modules/settings/widgets/editor_languages.dart diff --git a/lib/modules/account/widgets/sign_in_buttons.dart b/lib/modules/settings/widgets/sign_in_buttons.dart similarity index 100% rename from lib/modules/account/widgets/sign_in_buttons.dart rename to lib/modules/settings/widgets/sign_in_buttons.dart From bfa84248c8facfb4c48c269de27c577bd384115a Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 17:24:02 +0300 Subject: [PATCH 05/34] Refactor image widgets --- lib/shared/widgets/language_avatar.dart | 7 +++---- lib/shared/widgets/language_flag.dart | 5 +---- lib/shared/widgets/raxys.dart | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/shared/widgets/language_avatar.dart b/lib/shared/widgets/language_avatar.dart index 079080b1..88782553 100644 --- a/lib/shared/widgets/language_avatar.dart +++ b/lib/shared/widgets/language_avatar.dart @@ -1,4 +1,3 @@ -import 'package:avzag/store.dart'; import 'package:flutter/material.dart'; class LanguageAvatar extends StatelessWidget { @@ -15,11 +14,11 @@ class LanguageAvatar extends StatelessWidget { @override Widget build(BuildContext context) { - final url = this.url ?? GlobalStore.languages[language]?.flag; - if (url == null) return const Icon(Icons.flag_outlined); return CircleAvatar( radius: radius, - backgroundImage: NetworkImage(url), + foregroundImage: NetworkImage( + 'https://firebasestorage.googleapis.com/v0/b/avzagapp.appspot.com/o/flags%2F$language.png?alt=media', + ), backgroundColor: Colors.transparent, ); } diff --git a/lib/shared/widgets/language_flag.dart b/lib/shared/widgets/language_flag.dart index 56a1fc11..617df664 100644 --- a/lib/shared/widgets/language_flag.dart +++ b/lib/shared/widgets/language_flag.dart @@ -1,5 +1,4 @@ import 'dart:math'; -import 'package:avzag/store.dart'; import 'package:flutter/material.dart'; class LanguageFlag extends StatelessWidget { @@ -24,8 +23,6 @@ class LanguageFlag extends StatelessWidget { @override Widget build(BuildContext context) { - final url = this.url ?? GlobalStore.languages[language]?.flag; - if (url == null) return const SizedBox(); return Transform.translate( offset: offset, child: Transform.rotate( @@ -33,7 +30,7 @@ class LanguageFlag extends StatelessWidget { child: Transform.scale( scale: scale, child: Image.network( - url, + 'https://firebasestorage.googleapis.com/v0/b/avzagapp.appspot.com/o/flags%2F$language.png?alt=media', repeat: ImageRepeat.repeatX, fit: BoxFit.contain, width: width, diff --git a/lib/shared/widgets/raxys.dart b/lib/shared/widgets/raxys.dart index 4c01a4eb..f76d5fa0 100644 --- a/lib/shared/widgets/raxys.dart +++ b/lib/shared/widgets/raxys.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; class Raxys extends StatelessWidget { const Raxys({ this.size = 24, - this.opacity = 1, - this.scale = 1, + this.opacity = .1, + this.scale = 7, Key? key, }) : super(key: key); From bc423076e07a5e5a0c39600878e88f880c3bd896 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 17:24:08 +0300 Subject: [PATCH 06/34] Update rouring --- lib/main.dart | 4 +- lib/modules/dictionary/dictionary.dart | 23 +++++++++--- .../services/search_controller.dart | 3 +- lib/modules/dictionary/services/sharing.dart | 8 ++-- lib/modules/dictionary/services/word.dart | 3 +- .../dictionary/widgets/entry_group.dart | 37 ++++++++++--------- .../dictionary/widgets/entry_tile.dart | 3 +- .../dictionary/widgets/samples_column.dart | 2 +- .../dictionary/widgets/samples_editor.dart | 3 +- lib/modules/dictionary/widgets/word_view.dart | 2 +- lib/modules/dictionary/word.dart | 2 +- lib/modules/dictionary/word_editor.dart | 4 +- lib/modules/dictionary/word_loader.dart | 10 ++--- lib/modules/dictionary/words_diff.dart | 2 +- lib/modules/home/home.dart | 2 +- lib/modules/home/widgets/language_card.dart | 9 ++--- lib/modules/home/widgets/languages_bar.dart | 3 +- lib/modules/home/widgets/languages_map.dart | 5 +-- 18 files changed, 63 insertions(+), 62 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 66c39db5..6563efee 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,8 +8,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_strategy/url_strategy.dart'; import 'firebase_options.dart'; -import 'modules/navigation/services/root_guard.dart'; -import 'modules/navigation/services/router.gr.dart'; +import 'navigation/root_guard.dart'; +import 'navigation/router.gr.dart'; import 'theme_set.dart'; void main() async { diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 2ddd1b7f..17285cb1 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -1,17 +1,17 @@ import 'package:auto_route/auto_route.dart'; +import 'package:avzag/models/entry.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/modules/dictionary/widgets/entry_group.dart'; -import 'package:avzag/modules/navigation/navigation.dart'; -import 'package:avzag/modules/navigation/services/router.gr.dart'; +import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/modals/loading_dialog.dart'; import 'package:avzag/shared/modals/snackbar_manager.dart'; import 'package:avzag/shared/widgets/caption.dart'; import 'package:avzag/store.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:provider/provider.dart'; -import 'models/entry.dart'; -import 'models/word.dart'; import 'services/search_controller.dart'; import 'services/word.dart'; import 'widgets/search_toolbar.dart'; @@ -123,7 +123,6 @@ class DictionaryScreenState extends State { value: search, builder: (context, _) { return Scaffold( - drawer: const NavigationScreen(), floatingActionButton: EditorStore.editor ? FloatingActionButton( onPressed: edit, @@ -136,7 +135,15 @@ class DictionaryScreenState extends State { body: CustomScrollView( slivers: [ SliverAppBar( - title: const Text('Dictionary'), + leading: IconButton( + onPressed: () {}, + icon: const Icon(Icons.favorite_outline), + ), + centerTitle: true, + title: Text( + 'Avzag', + style: GoogleFonts.outfit(), + ), actions: [ if (EditorStore.admin) IconButton( @@ -152,6 +159,10 @@ class DictionaryScreenState extends State { ), tooltip: 'Unverified', ), + IconButton( + onPressed: () => context.pushRoute(const SettingsRoute()), + icon: const Icon(Icons.settings_outlined), + ), const SizedBox(width: 4), ], bottom: const PreferredSize( diff --git a/lib/modules/dictionary/services/search_controller.dart b/lib/modules/dictionary/services/search_controller.dart index 541199af..cce7d0ac 100644 --- a/lib/modules/dictionary/services/search_controller.dart +++ b/lib/modules/dictionary/services/search_controller.dart @@ -1,8 +1,7 @@ import 'package:algolia/algolia.dart'; +import 'package:avzag/models/entry.dart'; import 'package:flutter/material.dart'; -import '../models/entry.dart'; - class SearchController with ChangeNotifier { SearchController( this._languages, diff --git a/lib/modules/dictionary/services/sharing.dart b/lib/modules/dictionary/services/sharing.dart index cd16e2e4..96cb46cb 100644 --- a/lib/modules/dictionary/services/sharing.dart +++ b/lib/modules/dictionary/services/sharing.dart @@ -1,11 +1,9 @@ -import 'package:avzag/modules/dictionary/models/sample.dart'; +import 'package:avzag/models/sample.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:markdown/markdown.dart'; -import '../models/word.dart'; - -String _getWordLink(Word word) => - 'https://avzag.raxys.app/dictionary/${word.id}'; +String _getWordLink(Word word) => 'https://avzag.raxys.app/${word.id}'; String previewArticle(Word word) => ''' 🌄 Avzag • ${word.language.titled} diff --git a/lib/modules/dictionary/services/word.dart b/lib/modules/dictionary/services/word.dart index 2a4ddd86..9af427e1 100644 --- a/lib/modules/dictionary/services/word.dart +++ b/lib/modules/dictionary/services/word.dart @@ -1,4 +1,5 @@ import 'package:avzag/models/contribution.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/shared/modals/danger_dialog.dart'; import 'package:avzag/shared/modals/loading_dialog.dart'; import 'package:avzag/shared/modals/snackbar_manager.dart'; @@ -6,8 +7,6 @@ import 'package:avzag/store.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; -import '../models/word.dart'; - Future loadWord(String? id) async { if (id == null) return null; final doc = await FirebaseFirestore.instance diff --git a/lib/modules/dictionary/widgets/entry_group.dart b/lib/modules/dictionary/widgets/entry_group.dart index f8fa03c7..c9240ee9 100644 --- a/lib/modules/dictionary/widgets/entry_group.dart +++ b/lib/modules/dictionary/widgets/entry_group.dart @@ -1,6 +1,5 @@ -import 'package:avzag/modules/dictionary/models/entry.dart'; +import 'package:avzag/models/entry.dart'; import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; import 'package:flutter/material.dart'; import 'entry_tile.dart'; @@ -28,7 +27,7 @@ class EntryGroup extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 4), + padding: const EdgeInsets.fromLTRB(20, 10, 20, 4), child: Row( textBaseline: TextBaseline.alphabetic, crossAxisAlignment: CrossAxisAlignment.baseline, @@ -55,22 +54,24 @@ class EntryGroup extends StatelessWidget { ], ), ), - ColumnCard( + Card( + margin: const EdgeInsets.symmetric(horizontal: 4), elevation: .5, - margin: EdgeInsets.zero, - children: [ - for (final g in groups) - Column( - children: [ - for (var i = 0; i < g.length; i++) - EntryTile( - g[i], - showLanguage: showLanguage && i == 0, - onTap: () => onTap?.call(g[i]), - ), - ], - ) - ], + child: Column( + children: [ + for (final g in groups) + Column( + children: [ + for (var i = 0; i < g.length; i++) + EntryTile( + g[i], + showLanguage: showLanguage && i == 0, + onTap: () => onTap?.call(g[i]), + ), + ], + ) + ], + ), ), ], ); diff --git a/lib/modules/dictionary/widgets/entry_tile.dart b/lib/modules/dictionary/widgets/entry_tile.dart index d5f592c6..a05cbc5a 100644 --- a/lib/modules/dictionary/widgets/entry_tile.dart +++ b/lib/modules/dictionary/widgets/entry_tile.dart @@ -1,11 +1,10 @@ +import 'package:avzag/models/entry.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/span_icon.dart'; import 'package:flutter/material.dart'; -import '../models/entry.dart'; - class EntryTile extends StatelessWidget { final Entry hit; final bool showLanguage; diff --git a/lib/modules/dictionary/widgets/samples_column.dart b/lib/modules/dictionary/widgets/samples_column.dart index bd052753..ec75693c 100644 --- a/lib/modules/dictionary/widgets/samples_column.dart +++ b/lib/modules/dictionary/widgets/samples_column.dart @@ -1,6 +1,6 @@ +import 'package:avzag/models/sample.dart'; import 'package:avzag/shared/utils.dart'; import 'package:flutter/material.dart'; -import '../models/sample.dart'; class SamplesColumn extends StatelessWidget { const SamplesColumn( diff --git a/lib/modules/dictionary/widgets/samples_editor.dart b/lib/modules/dictionary/widgets/samples_editor.dart index 3dd49996..13442506 100644 --- a/lib/modules/dictionary/widgets/samples_editor.dart +++ b/lib/modules/dictionary/widgets/samples_editor.dart @@ -1,8 +1,7 @@ +import 'package:avzag/models/sample.dart'; import 'package:avzag/shared/widgets/compact_input.dart'; import 'package:flutter/material.dart'; -import '../models/sample.dart'; - class SamplesEditor extends StatefulWidget { const SamplesEditor( this.title, diff --git a/lib/modules/dictionary/widgets/word_view.dart b/lib/modules/dictionary/widgets/word_view.dart index 7390fb7c..27bf70c7 100644 --- a/lib/modules/dictionary/widgets/word_view.dart +++ b/lib/modules/dictionary/widgets/word_view.dart @@ -1,4 +1,4 @@ -import 'package:avzag/modules/dictionary/models/word.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/caption.dart'; diff --git a/lib/modules/dictionary/word.dart b/lib/modules/dictionary/word.dart index da1f4341..e24d246a 100644 --- a/lib/modules/dictionary/word.dart +++ b/lib/modules/dictionary/word.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/modules/dictionary/services/sharing.dart'; import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/language_title.dart'; @@ -6,7 +7,6 @@ import 'package:avzag/shared/widgets/options_button.dart'; import 'package:flutter/material.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; -import 'models/word.dart'; import 'widgets/word_view.dart'; class WordScreen extends StatelessWidget { diff --git a/lib/modules/dictionary/word_editor.dart b/lib/modules/dictionary/word_editor.dart index fd6a3dde..68c79ff1 100644 --- a/lib/modules/dictionary/word_editor.dart +++ b/lib/modules/dictionary/word_editor.dart @@ -1,4 +1,6 @@ import 'package:auto_route/auto_route.dart'; +import 'package:avzag/models/use.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/modules/dictionary/widgets/samples_editor.dart'; import 'package:avzag/shared/modals/danger_dialog.dart'; import 'package:avzag/shared/widgets/column_card.dart'; @@ -9,8 +11,6 @@ import 'package:avzag/store.dart'; import 'package:flutter/material.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; -import 'models/use.dart'; -import 'models/word.dart'; import 'services/word.dart'; class WordEditorScreen extends StatefulWidget { diff --git a/lib/modules/dictionary/word_loader.dart b/lib/modules/dictionary/word_loader.dart index f573f3bb..63340923 100644 --- a/lib/modules/dictionary/word_loader.dart +++ b/lib/modules/dictionary/word_loader.dart @@ -1,8 +1,8 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/modules/dictionary/models/word.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/modules/dictionary/services/word.dart'; -import 'package:avzag/modules/navigation/loader.dart'; -import 'package:avzag/modules/navigation/services/router.gr.dart'; +import 'package:avzag/navigation/loader.dart'; +import 'package:avzag/navigation/router.gr.dart'; import 'package:flutter/material.dart'; class WordLoaderScreen extends StatelessWidget { @@ -26,7 +26,7 @@ class WordLoaderScreen extends StatelessWidget { then: (context, word) async { final router = context.router; if (word == null) { - router.navigate(const DictionaryRootRoute()); + router.navigate(const RootRoute()); } else { final router = context.router; await router.replace( @@ -37,7 +37,7 @@ class WordLoaderScreen extends StatelessWidget { ), ); if (router.stack.length < 2) { - router.navigate(const DictionaryRootRoute()); + router.navigate(const RootRoute()); } } }, diff --git a/lib/modules/dictionary/words_diff.dart b/lib/modules/dictionary/words_diff.dart index 24854431..80593771 100644 --- a/lib/modules/dictionary/words_diff.dart +++ b/lib/modules/dictionary/words_diff.dart @@ -1,11 +1,11 @@ import 'package:auto_route/auto_route.dart'; +import 'package:avzag/models/word.dart'; import 'package:avzag/modules/dictionary/widgets/word_view.dart'; import 'package:avzag/shared/widgets/caption.dart'; import 'package:avzag/shared/widgets/language_title.dart'; import 'package:avzag/shared/widgets/options_button.dart'; import 'package:flutter/material.dart'; -import 'models/word.dart'; import 'services/word.dart'; class WordsDiffScreen extends StatelessWidget { diff --git a/lib/modules/home/home.dart b/lib/modules/home/home.dart index f0a6d952..3a7c8015 100644 --- a/lib/modules/home/home.dart +++ b/lib/modules/home/home.dart @@ -1,7 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:avzag/models/language.dart'; import 'package:avzag/modules/home/widgets/languages_bar.dart'; -import 'package:avzag/modules/navigation/services/router.gr.dart'; +import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:avzag/shared/widgets/options_button.dart'; import 'package:avzag/store.dart'; diff --git a/lib/modules/home/widgets/language_card.dart b/lib/modules/home/widgets/language_card.dart index 0ce781a5..31a60b3e 100644 --- a/lib/modules/home/widgets/language_card.dart +++ b/lib/modules/home/widgets/language_card.dart @@ -29,10 +29,9 @@ class LanguageCard extends StatelessWidget { children: [ AnimatedOpacity( opacity: selected ? 1 : .5, - duration:duration200, + duration: duration200, child: LanguageFlag( - null, - url: language.flag, + language.name, offset: const Offset(20, 0), ), ), @@ -54,7 +53,7 @@ class LanguageCard extends StatelessWidget { language.name.titled, style: const TextStyle(fontWeight: FontWeight.w500), ), - if (language.stats != null) + if (language.dictionary > 0) Padding( padding: const EdgeInsets.only(top: 2), child: RichText( @@ -68,7 +67,7 @@ class LanguageCard extends StatelessWidget { child: SpanIcon(Icons.book_outlined), ), TextSpan( - text: language.stats!.dictionary.toString(), + text: language.dictionary.toString(), ), ], ), diff --git a/lib/modules/home/widgets/languages_bar.dart b/lib/modules/home/widgets/languages_bar.dart index 058133ab..79a123e3 100644 --- a/lib/modules/home/widgets/languages_bar.dart +++ b/lib/modules/home/widgets/languages_bar.dart @@ -57,8 +57,7 @@ class _LanguagesBarState extends State { padding: const EdgeInsets.only(right: 4), child: InputChip( avatar: LanguageAvatar( - null, - url: language.flag, + language.name, radius: 12, ), label: Text( diff --git a/lib/modules/home/widgets/languages_map.dart b/lib/modules/home/widgets/languages_map.dart index 4627347d..8ca2c325 100644 --- a/lib/modules/home/widgets/languages_map.dart +++ b/lib/modules/home/widgets/languages_map.dart @@ -78,10 +78,7 @@ class LanguagesMap extends StatelessWidget { onTap: () => onToggle(language), child: Padding( padding: const EdgeInsets.all(2), - child: LanguageAvatar( - null, - url: language.flag, - ), + child: LanguageAvatar(language.name), ), ), ), From 4a0df6f9730c4f1d68f124e46e4cc13c1f59eb99 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 20:47:24 +0300 Subject: [PATCH 07/34] Refactor settings --- lib/modules/dictionary/dictionary.dart | 19 +++- .../dictionary/widgets/search_toolbar.dart | 13 ++- .../settings/services/credentials.dart | 20 +++- lib/modules/settings/services/crypto.dart | 18 ---- lib/modules/settings/services/signing.dart | 8 +- lib/modules/settings/settings.dart | 83 ++++---------- .../settings/widgets/account_tile.dart | 102 +++++++++++++++--- .../settings/widgets/editor_languages.dart | 52 --------- .../settings/widgets/editor_mode_card.dart | 100 +++++++++++++++++ .../settings/widgets/sign_in_buttons.dart | 76 ------------- lib/shared/utils.dart | 3 +- lib/shared/widgets/language_title.dart | 6 +- 12 files changed, 259 insertions(+), 241 deletions(-) delete mode 100644 lib/modules/settings/services/crypto.dart delete mode 100644 lib/modules/settings/widgets/editor_languages.dart create mode 100644 lib/modules/settings/widgets/editor_mode_card.dart delete mode 100644 lib/modules/settings/widgets/sign_in_buttons.dart diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 17285cb1..677b8366 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -5,10 +5,10 @@ import 'package:avzag/modules/dictionary/widgets/entry_group.dart'; import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/modals/loading_dialog.dart'; import 'package:avzag/shared/modals/snackbar_manager.dart'; +import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/caption.dart'; import 'package:avzag/store.dart'; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:provider/provider.dart'; @@ -136,13 +136,18 @@ class DictionaryScreenState extends State { slivers: [ SliverAppBar( leading: IconButton( - onPressed: () {}, - icon: const Icon(Icons.favorite_outline), + onPressed: () => showSnackbar( + context, + icon: Icons.info_outlined, + text: 'Bookmarks are coming soon', + ), + icon: const Icon(Icons.bookmarks_outlined), + tooltip: 'Bookmarks', ), centerTitle: true, title: Text( 'Avzag', - style: GoogleFonts.outfit(), + style: styleTitle, ), actions: [ if (EditorStore.admin) @@ -160,8 +165,12 @@ class DictionaryScreenState extends State { tooltip: 'Unverified', ), IconButton( - onPressed: () => context.pushRoute(const SettingsRoute()), + onPressed: () async { + await context.pushRoute(const SettingsRoute()); + setState(() {}); + }, icon: const Icon(Icons.settings_outlined), + tooltip: 'Settings', ), const SizedBox(width: 4), ], diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index 0201921e..1bca5726 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:auto_route/auto_route.dart'; +import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:avzag/shared/widgets/language_avatar.dart'; import 'package:avzag/shared/widgets/options_button.dart'; @@ -93,7 +95,16 @@ class SearchToolbarState extends State { style: const TextStyle(fontWeight: FontWeight.w500), ), onTap: () => setLanguage(l), - ) + ), + OptionItem.divider(), + OptionItem.simple( + Icons.add_circle_outline_outlined, + 'Add', + onTap: () async { + await context.pushRoute(const HomeRoute()); + setState(() {}); + }, + ), ], icon: Builder(builder: (context) { if (search.language.isEmpty) { diff --git a/lib/modules/settings/services/credentials.dart b/lib/modules/settings/services/credentials.dart index e1158293..e665e80d 100644 --- a/lib/modules/settings/services/credentials.dart +++ b/lib/modules/settings/services/credentials.dart @@ -1,10 +1,12 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'package:crypto/crypto.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart' as apple; -import 'crypto.dart'; - Future getGoogleCredentials() async { final user = await GoogleSignIn().signIn(); if (user != null) { @@ -48,3 +50,17 @@ Future getAppleCredentials() async { } return null; } + +String generateNonce([int length = 32]) { + const charset = + '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; + final random = Random.secure(); + return List.generate(length, (_) => charset[random.nextInt(charset.length)]) + .join(); +} + +String sha256ofString(String input) { + final bytes = utf8.encode(input); + final digest = sha256.convert(bytes); + return digest.toString(); +} diff --git a/lib/modules/settings/services/crypto.dart b/lib/modules/settings/services/crypto.dart deleted file mode 100644 index ffa6c75e..00000000 --- a/lib/modules/settings/services/crypto.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:convert'; -import 'dart:math'; - -import 'package:crypto/crypto.dart'; - -String generateNonce([int length = 32]) { - const charset = - '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; - final random = Random.secure(); - return List.generate(length, (_) => charset[random.nextInt(charset.length)]) - .join(); -} - -String sha256ofString(String input) { - final bytes = utf8.encode(input); - final digest = sha256.convert(bytes); - return digest.toString(); -} diff --git a/lib/modules/settings/services/signing.dart b/lib/modules/settings/services/signing.dart index bfc644d5..1127ae38 100644 --- a/lib/modules/settings/services/signing.dart +++ b/lib/modules/settings/services/signing.dart @@ -10,11 +10,9 @@ Future signIn( return true; } -Future signOut([User? user]) async { - final provider = (user ?? FirebaseAuth.instance.currentUser) - ?.providerData - .first - .providerId; +Future signOut() async { + final provider = + FirebaseAuth.instance.currentUser?.providerData.first.providerId; if (provider == null) return false; await FirebaseAuth.instance.signOut(); diff --git a/lib/modules/settings/settings.dart b/lib/modules/settings/settings.dart index c12a09de..779a6b3c 100644 --- a/lib/modules/settings/settings.dart +++ b/lib/modules/settings/settings.dart @@ -1,16 +1,11 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/language.dart'; -import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/raxys.dart'; -import 'package:avzag/store.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'widgets/account_tile.dart'; -import 'widgets/editor_languages.dart'; -import 'widgets/sign_in_buttons.dart'; +import 'widgets/editor_mode_card.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({Key? key}) : super(key: key); @@ -20,50 +15,33 @@ class SettingsScreen extends StatefulWidget { } class _SettingsScreenState extends State { - User? get user => FirebaseAuth.instance.currentUser; - var language = EditorStore.language; - @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: const AutoLeadingButton(), - title: const Text('Settings'), - actions: const [ - Raxys(), - SizedBox(width: 4), + title: Text( + 'Settings', + style: styleTitle, + ), + centerTitle: true, + actions: [ + IconButton( + onPressed: () => openLink('https://raxys.app'), + tooltip: 'Made with honor in North Caucasus', + icon: const Raxys(), + ), + const SizedBox(width: 4), ], ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.done_all_outlined), - onPressed: () { - EditorStore.language = language; - context.pushRoute(const RootRoute()); - }, - ), body: ListView( padding: const EdgeInsets.only(bottom: 76), children: [ - if (user == null) - SignInButtons( - onSingIn: () => setState(() {}), - ) - else ...[ - AccountTile( - user!, - onSignOut: () => setState(() {}), - ), - EditorLanguages( - GlobalStore.languages.values.whereType(), - onTap: (l) => setState(() { - language = l == language ? null : l; - }), - selected: language, - ), - ], + const AccountTile(), + const EditorModeCard(), ListTile( leading: const Icon(Icons.send_outlined), - title: const Text('Developer Contact'), + title: const Text('Developer contact'), subtitle: const Text('Raxys Studios'), onTap: () => openLink('https://t.me/raxysstudios'), ), @@ -73,9 +51,11 @@ class _SettingsScreenState extends State { final p = status.data; return ListTile( leading: const Icon(Icons.code_outlined), - title: const Text('GitHub Repository'), + title: const Text('GitHub repository'), subtitle: Text( - p == null ? '...' : 'v${p.version} • b${p.buildNumber}', + p == null + ? 'Loading...' + : 'v${p.version} • b${p.buildNumber}', ), onTap: () => openLink( 'https://github.com/raxysstudios/avzag', @@ -83,29 +63,6 @@ class _SettingsScreenState extends State { ); }, ), - ListTile( - leading: const Icon(Icons.get_app_outlined), - title: Row( - children: [ - Expanded( - child: TextButton( - onPressed: () => openLink( - 'https://play.google.com/store/apps/details?id=com.alkaitagi.avzag', - ), - child: const Text('Google Play'), - ), - ), - Expanded( - child: TextButton( - onPressed: () => openLink( - 'https://apps.apple.com/app/avzag-languages-of-caucasus/id1603226004', - ), - child: const Text('App Store'), - ), - ), - ], - ), - ), ], ), ); diff --git a/lib/modules/settings/widgets/account_tile.dart b/lib/modules/settings/widgets/account_tile.dart index a702014a..a40ad7f8 100644 --- a/lib/modules/settings/widgets/account_tile.dart +++ b/lib/modules/settings/widgets/account_tile.dart @@ -1,20 +1,92 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:avzag/shared/modals/snackbar_manager.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import '../services/credentials.dart'; import '../services/signing.dart'; -class AccountTile extends StatelessWidget { - const AccountTile( - this.user, { - this.onSignOut, - Key? key, - }) : super(key: key); +class AccountTile extends StatefulWidget { + const AccountTile({Key? key}) : super(key: key); + + @override + State createState() => _AccountTileState(); +} - final User user; - final VoidCallback? onSignOut; +class _AccountTileState extends State { + late final StreamSubscription _authStream; + var loading = false; + + @override + void initState() { + super.initState(); + _authStream = FirebaseAuth.instance.authStateChanges().listen( + (_) => setState(() {}), + ); + } + + @override + void dispose() { + _authStream.cancel(); + super.dispose(); + } + + Future _signIn( + Future Function() credentialsGetter, + ) async { + setState(() { + loading = true; + }); + try { + await signIn(credentialsGetter); + } catch (e) { + showSnackbar(context); + } + setState(() { + loading = false; + }); + } @override Widget build(BuildContext context) { + final user = FirebaseAuth.instance.currentUser; + if (user == null) { + return loading + ? const Padding( + padding: EdgeInsets.all(16), + child: Center( + child: SizedBox.square( + dimension: 24, + child: CircularProgressIndicator(), + ), + ), + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: TextButton.icon( + onPressed: () => _signIn(getGoogleCredentials), + icon: const Icon(Icons.login_outlined), + label: const Text('Sign in with Google'), + ), + ), + if (kIsWeb || Platform.isIOS) + Padding( + padding: const EdgeInsets.all(8), + child: TextButton.icon( + onPressed: () => _signIn(getAppleCredentials), + icon: const Icon(Icons.login_outlined), + label: const Text('Sign in with Apple'), + ), + ), + ], + ); + } return ListTile( leading: CircleAvatar( backgroundImage: @@ -23,15 +95,11 @@ class AccountTile extends StatelessWidget { ), title: Text(user.displayName ?? '[no name]'), subtitle: Text(user.email ?? '[no email]'), - trailing: onSignOut == null - ? null - : IconButton( - onPressed: () async { - if (await signOut(user) == true) onSignOut?.call(); - }, - icon: const Icon(Icons.logout_outlined), - tooltip: 'Sign out', - ), + trailing: const IconButton( + onPressed: signOut, + icon: Icon(Icons.logout_outlined), + tooltip: 'Sign out', + ), ); } } diff --git a/lib/modules/settings/widgets/editor_languages.dart b/lib/modules/settings/widgets/editor_languages.dart deleted file mode 100644 index 3505f391..00000000 --- a/lib/modules/settings/widgets/editor_languages.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:avzag/models/language.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; -import 'package:avzag/shared/widgets/language_avatar.dart'; -import 'package:flutter/material.dart'; - -class EditorLanguages extends StatelessWidget { - const EditorLanguages( - this.languages, { - required this.onTap, - this.selected, - Key? key, - }) : super(key: key); - - final Iterable languages; - final String? selected; - final ValueSetter onTap; - - @override - Widget build(BuildContext context) { - return ColumnCard( - children: [ - for (final l in languages) - ListTile( - leading: LanguageAvatar(l.name), - title: Text( - l.name.titled, - style: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 18, - ), - ), - onTap: () => onTap(l.name), - selected: l.name == selected, - trailing: Builder( - builder: (context) { - final contact = l.contact; - return contact == null - ? const SizedBox() - : IconButton( - onPressed: () => openLink(contact), - icon: const Icon(Icons.send_outlined), - tooltip: 'Contact admin', - ); - }, - ), - ), - ], - ); - } -} diff --git a/lib/modules/settings/widgets/editor_mode_card.dart b/lib/modules/settings/widgets/editor_mode_card.dart new file mode 100644 index 00000000..bfe9ee4b --- /dev/null +++ b/lib/modules/settings/widgets/editor_mode_card.dart @@ -0,0 +1,100 @@ +import 'dart:async'; + +import 'package:avzag/models/language.dart'; +import 'package:avzag/shared/extensions.dart'; +import 'package:avzag/shared/utils.dart'; +import 'package:avzag/shared/widgets/column_card.dart'; +import 'package:avzag/shared/widgets/language_avatar.dart'; +import 'package:avzag/shared/widgets/span_icon.dart'; +import 'package:avzag/store.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; + +class EditorModeCard extends StatefulWidget { + const EditorModeCard({Key? key}) : super(key: key); + + @override + State createState() => _EditorModeCardState(); +} + +class _EditorModeCardState extends State { + late final StreamSubscription _authStream; + var adminable = []; + + @override + void initState() { + super.initState(); + _authStream = FirebaseAuth.instance.authStateChanges().listen( + (_) => updateAdminable(), + ); + } + + @override + void dispose() { + _authStream.cancel(); + super.dispose(); + } + + void updateAdminable() async { + adminable = await EditorStore.getAdminable(); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return ColumnCard( + margin: const EdgeInsets.symmetric(vertical: 12), + children: [ + ListTile( + leading: const Icon(Icons.edit_outlined), + title: const Text('Editor mode'), + subtitle: Row( + children: [ + if (EditorStore.language == null) + Text( + EditorStore.user == null + ? 'Sign in to contribute' + : 'Select language below', + style: const TextStyle(fontStyle: FontStyle.italic), + ) + else ...[ + if (EditorStore.admin) + const SpanIcon(Icons.verified_user_outlined), + Text(EditorStore.language!.titled), + ] + ], + ), + ), + if (EditorStore.user != null) + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (final l + in GlobalStore.languages.values.whereType()) + InputChip( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + avatar: LanguageAvatar(l.name), + label: Text(l.name.titled), + onDeleted: + l.contact == null ? null : () => openLink(l.contact!), + deleteButtonTooltipMessage: 'Contact', + deleteIcon: const Icon( + Icons.help_outlined, + size: 18, + ), + tooltip: l.endonym.titled, + selected: l.name == EditorStore.language, + onSelected: (s) => setState(() { + EditorStore.language = s ? l.name : null; + }), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/modules/settings/widgets/sign_in_buttons.dart b/lib/modules/settings/widgets/sign_in_buttons.dart deleted file mode 100644 index 611e5050..00000000 --- a/lib/modules/settings/widgets/sign_in_buttons.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:avzag/shared/modals/snackbar_manager.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import '../services/credentials.dart'; -import '../services/signing.dart' as signing; - -class SignInButtons extends StatefulWidget { - const SignInButtons({ - this.onSingIn, - Key? key, - }) : super(key: key); - - final FutureOr Function()? onSingIn; - - @override - State createState() => _SignInButtonsState(); -} - -class _SignInButtonsState extends State { - var loading = false; - - Future signIn( - Future Function() credentialsGetter, - ) async { - setState(() { - loading = true; - }); - try { - if (await signing.signIn(credentialsGetter) == true) { - await widget.onSingIn?.call(); - } - } catch (e) { - showSnackbar(context); - } - setState(() { - loading = false; - }); - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: loading - ? const Center( - child: SizedBox.square( - dimension: 24, - child: CircularProgressIndicator(), - ), - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ElevatedButton.icon( - onPressed: () => signIn(getGoogleCredentials), - icon: const Icon(Icons.login_outlined), - label: const Text('Sign in with Google'), - ), - if (kIsWeb || Platform.isIOS) ...[ - const SizedBox(height: 16), - ElevatedButton.icon( - onPressed: () => signIn(getAppleCredentials), - icon: const Icon(Icons.login_outlined), - label: const Text('Sign in with Apple'), - ), - ], - ], - ), - ); - } -} diff --git a/lib/shared/utils.dart b/lib/shared/utils.dart index 5288a0cf..80477c28 100644 --- a/lib/shared/utils.dart +++ b/lib/shared/utils.dart @@ -25,7 +25,8 @@ void openLink(String url) { } const duration200 = Duration(milliseconds: 200); -final styleNative = GoogleFonts.bitter(fontWeight: FontWeight.w600); +final styleNative = GoogleFonts.bitter(fontWeight: FontWeight.w500); +final styleTitle = GoogleFonts.outfit(fontWeight: FontWeight.w500); List? json2list(Object? array) { return (array as Iterable?) diff --git a/lib/shared/widgets/language_title.dart b/lib/shared/widgets/language_title.dart index 6a52d545..ca0f4f64 100644 --- a/lib/shared/widgets/language_title.dart +++ b/lib/shared/widgets/language_title.dart @@ -1,6 +1,7 @@ import 'package:avzag/shared/extensions.dart'; import 'package:flutter/material.dart'; +import '../utils.dart'; import 'language_flag.dart'; class LanguageTitle extends StatelessWidget { @@ -23,7 +24,10 @@ class LanguageTitle extends StatelessWidget { child: LanguageFlag(language), ), ), - Text(language.titled), + Text( + language.titled, + style: styleTitle, + ), ], ); } From 63bd9d77ec66a96877f5a3600ebba2d76b2f7568 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 22:00:48 +0300 Subject: [PATCH 08/34] Revert typing --- lib/modules/dictionary/dictionary.dart | 6 +---- lib/modules/dictionary/widgets/word_view.dart | 24 ++++++++++++------- lib/modules/settings/settings.dart | 6 +---- lib/shared/utils.dart | 3 +-- lib/shared/widgets/language_title.dart | 4 +--- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 677b8366..d60ed21a 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -5,7 +5,6 @@ import 'package:avzag/modules/dictionary/widgets/entry_group.dart'; import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/modals/loading_dialog.dart'; import 'package:avzag/shared/modals/snackbar_manager.dart'; -import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/caption.dart'; import 'package:avzag/store.dart'; import 'package:flutter/material.dart'; @@ -145,10 +144,7 @@ class DictionaryScreenState extends State { tooltip: 'Bookmarks', ), centerTitle: true, - title: Text( - 'Avzag', - style: styleTitle, - ), + title: const Text('Avzag'), actions: [ if (EditorStore.admin) IconButton( diff --git a/lib/modules/dictionary/widgets/word_view.dart b/lib/modules/dictionary/widgets/word_view.dart index 27bf70c7..a63bedf3 100644 --- a/lib/modules/dictionary/widgets/word_view.dart +++ b/lib/modules/dictionary/widgets/word_view.dart @@ -71,21 +71,27 @@ class WordView extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + '${word.uses.indexOf(u) + 1}', + style: theme.headline6?.copyWith( + color: theme.caption?.color, + ), + ), Expanded( - child: Text( - u.term.titled, - style: theme.headline6, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + u.term.titled, + style: theme.headline6, + ), ), ), if (u.aliases.isNotEmpty) Tooltip( message: u.aliases.join(' • ').titled, - child: Padding( - padding: const EdgeInsets.only(left: 8), - child: Icon( - Icons.label_outlined, - color: Theme.of(context).textTheme.caption?.color, - ), + child: Icon( + Icons.label_outlined, + color: Theme.of(context).textTheme.caption?.color, ), ) ], diff --git a/lib/modules/settings/settings.dart b/lib/modules/settings/settings.dart index 779a6b3c..52c56a21 100644 --- a/lib/modules/settings/settings.dart +++ b/lib/modules/settings/settings.dart @@ -20,11 +20,7 @@ class _SettingsScreenState extends State { return Scaffold( appBar: AppBar( leading: const AutoLeadingButton(), - title: Text( - 'Settings', - style: styleTitle, - ), - centerTitle: true, + title: const Text('Settings'), actions: [ IconButton( onPressed: () => openLink('https://raxys.app'), diff --git a/lib/shared/utils.dart b/lib/shared/utils.dart index 80477c28..5288a0cf 100644 --- a/lib/shared/utils.dart +++ b/lib/shared/utils.dart @@ -25,8 +25,7 @@ void openLink(String url) { } const duration200 = Duration(milliseconds: 200); -final styleNative = GoogleFonts.bitter(fontWeight: FontWeight.w500); -final styleTitle = GoogleFonts.outfit(fontWeight: FontWeight.w500); +final styleNative = GoogleFonts.bitter(fontWeight: FontWeight.w600); List? json2list(Object? array) { return (array as Iterable?) diff --git a/lib/shared/widgets/language_title.dart b/lib/shared/widgets/language_title.dart index ca0f4f64..af117602 100644 --- a/lib/shared/widgets/language_title.dart +++ b/lib/shared/widgets/language_title.dart @@ -1,7 +1,6 @@ import 'package:avzag/shared/extensions.dart'; import 'package:flutter/material.dart'; -import '../utils.dart'; import 'language_flag.dart'; class LanguageTitle extends StatelessWidget { @@ -25,8 +24,7 @@ class LanguageTitle extends StatelessWidget { ), ), Text( - language.titled, - style: styleTitle, + language.titled ), ], ); From 6b955cb00bef202bd6f9ba8326a9739002f8e7cd Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 22:10:50 +0300 Subject: [PATCH 09/34] Improve settings back button --- lib/modules/settings/settings.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/modules/settings/settings.dart b/lib/modules/settings/settings.dart index 52c56a21..c80adde8 100644 --- a/lib/modules/settings/settings.dart +++ b/lib/modules/settings/settings.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/raxys.dart'; import 'package:flutter/material.dart'; @@ -19,7 +20,10 @@ class _SettingsScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - leading: const AutoLeadingButton(), + leading: IconButton( + onPressed: () => context.navigateTo(const RootRoute()), + icon: const Icon(Icons.arrow_back_outlined), + ), title: const Text('Settings'), actions: [ IconButton( From 81146693916c64bc748eca8e049f49249ba83336 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 22:23:42 +0300 Subject: [PATCH 10/34] Refactor Home ordering --- lib/modules/home/home.dart | 94 ++++++++++++-------------------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/lib/modules/home/home.dart b/lib/modules/home/home.dart index 3a7c8015..703c6b69 100644 --- a/lib/modules/home/home.dart +++ b/lib/modules/home/home.dart @@ -2,8 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:avzag/models/language.dart'; import 'package:avzag/modules/home/widgets/languages_bar.dart'; import 'package:avzag/navigation/router.gr.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/widgets/options_button.dart'; import 'package:avzag/store.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; @@ -18,60 +16,34 @@ class HomeScreen extends StatefulWidget { State createState() => _HomeScreenState(); } -class _LanguageOrdering { - final bool descending; - final String text; - final IconData icon; - late final String field; - - _LanguageOrdering( - this.icon, - this.text, { - String? field, - this.descending = false, - }) { - this.field = field ?? text; - } -} - class _HomeScreenState extends State { var catalogue = []; var tags = {}; var languages = []; var selected = {}; - final inputController = TextEditingController(); - var isLoading = false; - var isMap = false; - - final orderings = [ - _LanguageOrdering(Icons.label_outlined, 'name'), - _LanguageOrdering( - Icons.book_outlined, - 'dictionary', - field: 'stats.dictionary', - descending: true, - ), - ]; - late var ordering = orderings.first; + final _input = TextEditingController(); + var loading = false; + var map = false; + var alpha = true; @override void initState() { super.initState(); - inputController.addListener(filterLanguages); + _input.addListener(filterLanguages); load(); } Future load() async { setState(() { - isLoading = true; + loading = true; }); - var query = FirebaseFirestore.instance + catalogue = await FirebaseFirestore.instance .collection('languages') - .orderBy(ordering.field, descending: ordering.descending); - if (ordering.field != 'name') query = query.orderBy('name'); - - catalogue = await query + .orderBy( + alpha ? 'name' : 'stats.dictionary', + descending: !alpha, + ) .withConverter( fromFirestore: (snapshot, _) => Language.fromJson(snapshot.data()!), toFirestore: (__, _) => {}, @@ -91,17 +63,14 @@ class _HomeScreenState extends State { ].join(' ') }; - isLoading = false; + loading = false; filterLanguages(); } void filterLanguages() { - if (isLoading) return; - final query = inputController.text - .trim() - .toLowerCase() - .split(' ') - .where((s) => s.isNotEmpty); + if (loading) return; + final query = + _input.text.trim().toLowerCase().split(' ').where((s) => s.isNotEmpty); setState(() { languages ..clear() @@ -131,35 +100,30 @@ class _HomeScreenState extends State { titleSpacing: 0, centerTitle: true, leading: IconButton( - tooltip: isMap ? 'Show list' : 'Show map', + tooltip: map ? 'Show list' : 'Show map', icon: Icon( - isMap ? Icons.view_list_outlined : Icons.map_outlined, + map ? Icons.view_list_outlined : Icons.map_outlined, ), onPressed: () => setState(() { - isMap = !isMap; + map = !map; }), ), title: TextField( - controller: inputController, + controller: _input, decoration: const InputDecoration( border: InputBorder.none, hintText: 'Search by names, tags, families', ), ), actions: [ - OptionsButton( - [ - for (final o in orderings) - OptionItem.simple( - o.icon, - o.text.titled, - onTap: () { - ordering = o; - load(); - }, - ), - ], - icon: const Icon(Icons.sort_outlined), + IconButton( + onPressed: () => setState(() { + alpha = !alpha; + load(); + }), + icon: Icon( + alpha ? Icons.sort_by_alpha_outlined : Icons.sort_outlined, + ), ), const SizedBox(width: 4), ], @@ -184,12 +148,12 @@ class _HomeScreenState extends State { ), body: Builder( builder: (context) { - if (isLoading) { + if (loading) { return const Center( child: CircularProgressIndicator(), ); } - if (isMap) { + if (map) { return LanguagesMap( onToggle: toggleLanguage, selected: selected, From 8713f89e402725eb3020da8e000506c80ed2475e Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 23:04:46 +0300 Subject: [PATCH 11/34] Refactor dictionary UI --- lib/modules/dictionary/dictionary.dart | 43 +++++++++++++------ .../services/search_controller.dart | 11 +++-- lib/shared/widgets/caption.dart | 5 ++- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index d60ed21a..70196527 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -146,20 +146,6 @@ class DictionaryScreenState extends State { centerTitle: true, title: const Text('Avzag'), actions: [ - if (EditorStore.admin) - IconButton( - onPressed: () => setState(() { - search.unverified = !search.unverified; - search.updateQuery(); - }), - icon: Icon( - Icons.unpublished_outlined, - color: search.unverified - ? Theme.of(context).colorScheme.primary - : null, - ), - tooltip: 'Unverified', - ), IconButton( onPressed: () async { await context.pushRoute(const SettingsRoute()); @@ -179,6 +165,33 @@ class DictionaryScreenState extends State { floating: true, forceElevated: true, ), + SliverList( + delegate: SliverChildListDelegate([ + if (EditorStore.admin) ...[ + SwitchListTile( + value: context.read().unverified, + secondary: const Icon(Icons.unpublished_outlined), + title: const Text('Only unverified'), + onChanged: (v) => setState(() { + search.unverified = v; + search.updateQuery(); + }), + ), + const Divider(), + ], + Caption( + 'Found ${context.watch().snapshot?.nbHits ?? 0} entries', + icon: Icons.search_outlined, + padding: const EdgeInsets.only( + right: 20, + top: 16, + bottom: 4, + left: 20, + ), + centered: false, + ), + ]), + ), PagedSliverList( pagingController: paging, builderDelegate: PagedChildBuilderDelegate( @@ -210,6 +223,8 @@ class DictionaryScreenState extends State { ? 'End of the results' : 'Showing the first 50 entries', icon: Icons.done_all_outlined, + centered: false, + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), ); } } diff --git a/lib/modules/dictionary/services/search_controller.dart b/lib/modules/dictionary/services/search_controller.dart index cce7d0ac..e67ebe90 100644 --- a/lib/modules/dictionary/services/search_controller.dart +++ b/lib/modules/dictionary/services/search_controller.dart @@ -20,7 +20,6 @@ class SearchController with ChangeNotifier { set language(String value) { _language = value; updateQuery(); - notifyListeners(); } bool _unverified = false; @@ -28,13 +27,15 @@ class SearchController with ChangeNotifier { set unverified(bool value) { _unverified = value; updateQuery(); - notifyListeners(); } bool get monolingual => language.isNotEmpty && language != '_'; int get length => _tags.length; + AlgoliaQuerySnapshot? _snapshot; + AlgoliaQuerySnapshot? get snapshot => _snapshot; + final Map> _tags = {}; Set getTags(String id) => _tags[id] ?? {}; @@ -66,10 +67,14 @@ class SearchController with ChangeNotifier { _tags.clear(); _hits.clear(); _onSearch?.call(); + notifyListeners(); } Future> fetchHits(int page) async { - var hits = await _query.setPage(page).getObjects().then((s) => s.hits); + _snapshot = await _query.setPage(page).getObjects(); + notifyListeners(); + + var hits = snapshot!.hits; if (hits.isNotEmpty && language == '_') { final original = { for (final hit in hits) hit.objectID: hit, diff --git a/lib/shared/widgets/caption.dart b/lib/shared/widgets/caption.dart index a657269e..774c8ebb 100644 --- a/lib/shared/widgets/caption.dart +++ b/lib/shared/widgets/caption.dart @@ -7,19 +7,22 @@ class Caption extends StatelessWidget { this.text, { this.icon, this.padding = const EdgeInsets.all(16), + this.centered = true, Key? key, }) : super(key: key); final String text; final IconData? icon; final EdgeInsets padding; + final bool centered; @override Widget build(BuildContext context) { return Padding( padding: padding, child: Row( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: + centered ? MainAxisAlignment.center : MainAxisAlignment.start, children: [ if (icon != null) SpanIcon( From 03743dfb11bc5a75dbdaec5eb75237438e500de2 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 23:06:26 +0300 Subject: [PATCH 12/34] Remove end caption on 0 hits --- lib/modules/dictionary/dictionary.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 70196527..18c86bea 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -218,6 +218,9 @@ class DictionaryScreenState extends State { } Widget _endCaption(BuildContext context) { + if (context.watch().snapshot?.nbHits == 0) { + return const SizedBox(); + } return Caption( search.monolingual ? 'End of the results' From 98a59d2a8fd7b336c31edb7d1f475a522845c93e Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 23:25:08 +0300 Subject: [PATCH 13/34] Fix languages switch --- .../services/search_controller.dart | 13 +++-- .../dictionary/widgets/search_toolbar.dart | 56 +++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/lib/modules/dictionary/services/search_controller.dart b/lib/modules/dictionary/services/search_controller.dart index e67ebe90..b16e20fb 100644 --- a/lib/modules/dictionary/services/search_controller.dart +++ b/lib/modules/dictionary/services/search_controller.dart @@ -11,16 +11,13 @@ class SearchController with ChangeNotifier { updateQuery(); } - final Iterable _languages; + Iterable _languages; + final AlgoliaIndexReference _index; final VoidCallback? _onSearch; String _language = ''; String get language => _language; - set language(String value) { - _language = value; - updateQuery(); - } bool _unverified = false; bool get unverified => _unverified; @@ -48,6 +45,12 @@ class SearchController with ChangeNotifier { late AlgoliaQuery _query = _index.query(''); + void updateLanguage(String language, [Iterable? languages]) { + _language = language; + _languages = languages ?? _languages; + updateQuery(); + } + void updateQuery([String text = '']) { final parsed = _parseQuery(text); final languages = _generateFilter( diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index 1bca5726..37ec5f1d 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -19,20 +19,20 @@ class SearchToolbar extends StatefulWidget { } class SearchToolbarState extends State { - final inputController = TextEditingController(); + final _input = TextEditingController(); Timer timer = Timer(Duration.zero, () {}); String lastText = ''; @override void initState() { super.initState(); - inputController.addListener(() { - if (lastText == inputController.text) return; + _input.addListener(() { + if (lastText == _input.text) return; setState(() { - lastText = inputController.text; + lastText = _input.text; }); timer.cancel(); - if (inputController.text.isEmpty) { + if (_input.text.isEmpty) { search(); } else { timer = Timer( @@ -49,16 +49,15 @@ class SearchToolbarState extends State { @override void dispose() { - inputController.dispose(); + _input.dispose(); super.dispose(); } - void search() => - context.read().updateQuery(inputController.text); + void search() => context.read().updateQuery(_input.text); void setLanguage(String language) { - context.read().language = language; - inputController.clear(); + context.read().updateLanguage(language); + _input.clear(); } @override @@ -102,19 +101,30 @@ class SearchToolbarState extends State { 'Add', onTap: () async { await context.pushRoute(const HomeRoute()); - setState(() {}); + final l = search.language; + final ls = GlobalStore.languages.keys; + search.updateLanguage( + l.isEmpty + ? '' + : ls.contains(l) + ? l + : ls.first, + ls, + ); }, ), ], - icon: Builder(builder: (context) { - if (search.language.isEmpty) { - return const Icon(Icons.language_outlined); - } - if (search.language == '_') { - return const Icon(Icons.layers_outlined); - } - return LanguageAvatar(search.language); - }), + icon: Builder( + builder: (context) { + if (search.language.isEmpty) { + return const Icon(Icons.language_outlined); + } + if (search.language == '_') { + return const Icon(Icons.layers_outlined); + } + return LanguageAvatar(search.language); + }, + ), ), Expanded( child: Padding( @@ -126,7 +136,7 @@ class SearchToolbarState extends State { ? 'forms in ${search.language.titled}' : '${search.language.isEmpty ? 'over' : 'across'} the languages'; return TextField( - controller: inputController, + controller: _input, decoration: InputDecoration( border: InputBorder.none, labelText: label, @@ -136,9 +146,9 @@ class SearchToolbarState extends State { ), ), ), - if (inputController.text.isNotEmpty) + if (_input.text.isNotEmpty) IconButton( - onPressed: inputController.clear, + onPressed: _input.clear, icon: const Icon(Icons.clear_outlined), ), ], From 55c8c6aa34fddef2e910ae235b573c516fbcea5a Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sat, 9 Jul 2022 23:26:04 +0300 Subject: [PATCH 14/34] Bump --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index c77beb57..7e2d9e28 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: avzag description: Extensible parallel language compendium publish_to: "none" -version: 1.0.6+69 +version: 1.1.0+70 environment: sdk: ">=2.13.0 <3.0.0" From 203fe246fdd0cfae5f493736fca023a26b30a324 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 01:17:44 +0300 Subject: [PATCH 15/34] Lint --- lib/shared/widgets/language_title.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/shared/widgets/language_title.dart b/lib/shared/widgets/language_title.dart index af117602..6a52d545 100644 --- a/lib/shared/widgets/language_title.dart +++ b/lib/shared/widgets/language_title.dart @@ -23,9 +23,7 @@ class LanguageTitle extends StatelessWidget { child: LanguageFlag(language), ), ), - Text( - language.titled - ), + Text(language.titled), ], ); } From 1f1041078e82aca0a23afcd6b9644333fb37dcbe Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 01:45:07 +0300 Subject: [PATCH 16/34] Update description --- pubspec.yaml | 2 +- web/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 7e2d9e28..bc6d82d6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: avzag -description: Extensible parallel language compendium +description: Extensible parallel dictionary publish_to: "none" version: 1.1.0+70 diff --git a/web/index.html b/web/index.html index 09e2a80c..b0ef8900 100644 --- a/web/index.html +++ b/web/index.html @@ -7,7 +7,7 @@ From 6e20b9005ecab916666504a6fb5fc722c24e98ca Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 13:58:36 +0300 Subject: [PATCH 17/34] Update AppBars --- lib/modules/dictionary/dictionary.dart | 41 +++++++++++++++---- .../dictionary/widgets/entry_group.dart | 32 +++++++-------- .../dictionary/widgets/search_toolbar.dart | 21 +--------- lib/shared/widgets/column_card.dart | 4 +- lib/shared/widgets/options_button.dart | 3 ++ 5 files changed, 56 insertions(+), 45 deletions(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 18c86bea..87e69d3d 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -135,17 +135,42 @@ class DictionaryScreenState extends State { slivers: [ SliverAppBar( leading: IconButton( - onPressed: () => showSnackbar( - context, - icon: Icons.info_outlined, - text: 'Bookmarks are coming soon', - ), - icon: const Icon(Icons.bookmarks_outlined), - tooltip: 'Bookmarks', + onPressed: () async { + await context.pushRoute(const HomeRoute()); + final l = search.language; + final ls = GlobalStore.languages.keys; + search.updateLanguage( + l.isEmpty + ? '' + : ls.contains(l) + ? l + : ls.first, + ls, + ); + }, + tooltip: 'Home', + icon: const Icon(Icons.landscape_outlined), ), - centerTitle: true, title: const Text('Avzag'), actions: [ + IconButton( + onPressed: () => showSnackbar( + context, + icon: Icons.info_outlined, + text: 'Bookmarks are coming soon', + ), + icon: const Icon(Icons.bookmarks_outlined), + tooltip: 'Bookmarks', + ), + IconButton( + onPressed: () => showSnackbar( + context, + icon: Icons.info_outlined, + text: 'History is coming soon', + ), + icon: const Icon(Icons.history_outlined), + tooltip: 'History', + ), IconButton( onPressed: () async { await context.pushRoute(const SettingsRoute()); diff --git a/lib/modules/dictionary/widgets/entry_group.dart b/lib/modules/dictionary/widgets/entry_group.dart index c9240ee9..ada61a80 100644 --- a/lib/modules/dictionary/widgets/entry_group.dart +++ b/lib/modules/dictionary/widgets/entry_group.dart @@ -1,5 +1,6 @@ import 'package:avzag/models/entry.dart'; import 'package:avzag/shared/extensions.dart'; +import 'package:avzag/shared/widgets/column_card.dart'; import 'package:flutter/material.dart'; import 'entry_tile.dart'; @@ -54,24 +55,23 @@ class EntryGroup extends StatelessWidget { ], ), ), - Card( + ColumnCard( margin: const EdgeInsets.symmetric(horizontal: 4), elevation: .5, - child: Column( - children: [ - for (final g in groups) - Column( - children: [ - for (var i = 0; i < g.length; i++) - EntryTile( - g[i], - showLanguage: showLanguage && i == 0, - onTap: () => onTap?.call(g[i]), - ), - ], - ) - ], - ), + shape: null, + children: [ + for (final g in groups) + Column( + children: [ + for (var i = 0; i < g.length; i++) + EntryTile( + g[i], + showLanguage: showLanguage && i == 0, + onTap: () => onTap?.call(g[i]), + ), + ], + ), + ], ), ], ); diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index 37ec5f1d..ec2eb0ed 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:auto_route/auto_route.dart'; -import 'package:avzag/navigation/router.gr.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:avzag/shared/widgets/language_avatar.dart'; import 'package:avzag/shared/widgets/options_button.dart'; @@ -95,25 +93,8 @@ class SearchToolbarState extends State { ), onTap: () => setLanguage(l), ), - OptionItem.divider(), - OptionItem.simple( - Icons.add_circle_outline_outlined, - 'Add', - onTap: () async { - await context.pushRoute(const HomeRoute()); - final l = search.language; - final ls = GlobalStore.languages.keys; - search.updateLanguage( - l.isEmpty - ? '' - : ls.contains(l) - ? l - : ls.first, - ls, - ); - }, - ), ], + tooltip: 'Search mode', icon: Builder( builder: (context) { if (search.language.isEmpty) { diff --git a/lib/shared/widgets/column_card.dart b/lib/shared/widgets/column_card.dart index ecb3ef99..2658b8e0 100644 --- a/lib/shared/widgets/column_card.dart +++ b/lib/shared/widgets/column_card.dart @@ -10,12 +10,14 @@ class ColumnCard extends StatelessWidget { this.divider = const Divider(), this.margin = const EdgeInsets.only(top: 12), this.padding = EdgeInsets.zero, + this.shape = const RoundedRectangleBorder(), Key? key, }) : super(key: key); final EdgeInsets margin; final EdgeInsets padding; final double? elevation; + final ShapeBorder? shape; final String? title; final String? subtitle; final Widget? divider; @@ -27,7 +29,7 @@ class ColumnCard extends StatelessWidget { return Card( elevation: elevation, margin: margin, - shape: const RoundedRectangleBorder(), + shape: shape, child: Padding( padding: padding, child: Column( diff --git a/lib/shared/widgets/options_button.dart b/lib/shared/widgets/options_button.dart index 896d50fe..58231da2 100644 --- a/lib/shared/widgets/options_button.dart +++ b/lib/shared/widgets/options_button.dart @@ -49,10 +49,12 @@ class OptionsButton extends StatelessWidget { const OptionsButton( this.options, { this.icon = const Icon(Icons.more_vert_outlined), + this.tooltip, Key? key, }) : super(key: key); final Widget icon; + final String? tooltip; final List options; PopupMenuEntry _getMenuEntry(int i) { @@ -71,6 +73,7 @@ class OptionsButton extends StatelessWidget { data: const ListTileThemeData(horizontalTitleGap: 0), child: PopupMenuButton( icon: icon, + tooltip: tooltip, onSelected: (i) => options[i].onTap?.call(), itemBuilder: (context) { return [ From de13947820b3849e7282a1dabbcc52a3cda61b7e Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 14:01:22 +0300 Subject: [PATCH 18/34] Improve aliases tooltip --- lib/modules/dictionary/widgets/word_view.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/modules/dictionary/widgets/word_view.dart b/lib/modules/dictionary/widgets/word_view.dart index a63bedf3..2e735053 100644 --- a/lib/modules/dictionary/widgets/word_view.dart +++ b/lib/modules/dictionary/widgets/word_view.dart @@ -88,6 +88,8 @@ class WordView extends StatelessWidget { ), if (u.aliases.isNotEmpty) Tooltip( + waitDuration: Duration.zero, + triggerMode: TooltipTriggerMode.tap, message: u.aliases.join(' • ').titled, child: Icon( Icons.label_outlined, From 2080066735aeefd0b2381611022196f33840c305 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 14:06:44 +0300 Subject: [PATCH 19/34] Improve copying --- lib/modules/dictionary/services/sharing.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/modules/dictionary/services/sharing.dart b/lib/modules/dictionary/services/sharing.dart index 96cb46cb..e11289a3 100644 --- a/lib/modules/dictionary/services/sharing.dart +++ b/lib/modules/dictionary/services/sharing.dart @@ -3,12 +3,12 @@ import 'package:avzag/models/word.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:markdown/markdown.dart'; -String _getWordLink(Word word) => 'https://avzag.raxys.app/${word.id}'; +String _getLink(Word word) => 'https://avzag.raxys.app/${word.id}'; String previewArticle(Word word) => ''' 🌄 Avzag • ${word.language.titled} 🔖 ${word.headword.titled} — ${word.uses.map((u) => u.term.titled).join(', ')} -${_getWordLink(word)}'''; +${_getLink(word)}'''; String _cleanMarkdown(String md) => markdownToHtml(md, inlineOnly: true) .replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), '') @@ -26,7 +26,7 @@ String textifyArticle(Word word) { final article = [ '🌄 Avzag • ${word.language.titled}', - _getWordLink(word), + _getLink(word), '\n🔖 ${word.headword.titled}', if (word.ipa != null) '🔉 ${word.ipa}', if (word.tags.isNotEmpty) tags(word.tags), @@ -34,7 +34,7 @@ String textifyArticle(Word word) { if (word.forms.isNotEmpty) ...samples(word.forms), if (word.uses.isNotEmpty) for (final use in word.uses) ...[ - '\n💡 ${use.term.titled}', + '\n💡${word.uses.indexOf(use) + 1} ${use.term.titled}', if (use.tags.isNotEmpty) tags(use.tags), if (use.note?.isNotEmpty ?? false) note(use.note!), if (use.examples.isNotEmpty) ...samples(use.examples), From f94726d844d8c2001e695698a42c307f224fe61b Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 14:32:52 +0300 Subject: [PATCH 20/34] Update search toolbar --- lib/modules/dictionary/dictionary.dart | 73 +++++++++++-------- .../services/search_controller.dart | 45 ++++-------- .../dictionary/widgets/search_toolbar.dart | 73 +++++++++---------- 3 files changed, 92 insertions(+), 99 deletions(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 87e69d3d..3024d427 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -39,10 +39,10 @@ class DictionaryScreenState extends State { super.initState(); paging.addPageRequestListener( (page) async { - final terms = await search.fetchHits(page); + final terms = await search.fetch(page); if (terms.isEmpty) { paging.appendLastPage([]); - } else if (search.monolingual) { + } else if (search.global) { paging.appendPage(terms, page + 1); } else { paging.appendLastPage(terms); @@ -116,6 +116,19 @@ class DictionaryScreenState extends State { } } + void updateLanguages() { + final l = search.language; + final ls = GlobalStore.languages.keys; + search.setLanguage( + l.isEmpty + ? '' + : ls.contains(l) + ? l + : ls.first, + ls, + ); + } + @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( @@ -137,16 +150,7 @@ class DictionaryScreenState extends State { leading: IconButton( onPressed: () async { await context.pushRoute(const HomeRoute()); - final l = search.language; - final ls = GlobalStore.languages.keys; - search.updateLanguage( - l.isEmpty - ? '' - : ls.contains(l) - ? l - : ls.first, - ls, - ); + updateLanguages(); }, tooltip: 'Home', icon: const Icon(Icons.landscape_outlined), @@ -182,8 +186,11 @@ class DictionaryScreenState extends State { const SizedBox(width: 4), ], bottom: const PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight + 3), - child: SearchToolbar(), + preferredSize: Size.fromHeight(kToolbarHeight), + child: SizedBox( + height: kToolbarHeight, + child: SearchToolbar(), + ), ), pinned: true, snap: true, @@ -199,21 +206,29 @@ class DictionaryScreenState extends State { title: const Text('Only unverified'), onChanged: (v) => setState(() { search.unverified = v; - search.updateQuery(); + search.query(); }), ), const Divider(), ], - Caption( - 'Found ${context.watch().snapshot?.nbHits ?? 0} entries', - icon: Icons.search_outlined, - padding: const EdgeInsets.only( - right: 20, - top: 16, - bottom: 4, - left: 20, - ), - centered: false, + Builder( + builder: (context) { + final snapshot = + context.watch().snapshot; + return Caption( + snapshot == null + ? 'Searching...' + : 'Found ${snapshot.nbHits} entries', + icon: Icons.search_outlined, + padding: const EdgeInsets.only( + right: 20, + top: 16, + bottom: 4, + left: 20, + ), + centered: false, + ); + }, ), ]), ), @@ -224,8 +239,8 @@ class DictionaryScreenState extends State { return EntryGroup( search.getHits(id), onTap: open, - showLanguage: GlobalStore.languages.length > 1 && - !search.monolingual, + showLanguage: + GlobalStore.languages.length > 1 && !search.global, ); }, noItemsFoundIndicatorBuilder: _endCaption, @@ -247,9 +262,7 @@ class DictionaryScreenState extends State { return const SizedBox(); } return Caption( - search.monolingual - ? 'End of the results' - : 'Showing the first 50 entries', + search.global ? 'End of the results' : 'Showing the first 50 entries', icon: Icons.done_all_outlined, centered: false, padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), diff --git a/lib/modules/dictionary/services/search_controller.dart b/lib/modules/dictionary/services/search_controller.dart index b16e20fb..6624fdc1 100644 --- a/lib/modules/dictionary/services/search_controller.dart +++ b/lib/modules/dictionary/services/search_controller.dart @@ -8,7 +8,7 @@ class SearchController with ChangeNotifier { this._index, [ this._onSearch, ]) { - updateQuery(); + query(); } Iterable _languages; @@ -23,11 +23,10 @@ class SearchController with ChangeNotifier { bool get unverified => _unverified; set unverified(bool value) { _unverified = value; - updateQuery(); + query(); } - bool get monolingual => language.isNotEmpty && language != '_'; - + bool get global => language.isNotEmpty; int get length => _tags.length; AlgoliaQuerySnapshot? _snapshot; @@ -45,16 +44,16 @@ class SearchController with ChangeNotifier { late AlgoliaQuery _query = _index.query(''); - void updateLanguage(String language, [Iterable? languages]) { + void setLanguage(String language, [Iterable? languages]) { _language = language; _languages = languages ?? _languages; - updateQuery(); + query(); } - void updateQuery([String text = '']) { + void query([String text = '']) { final parsed = _parseQuery(text); final languages = _generateFilter( - monolingual ? [language] : _languages, + global ? [language] : _languages, 'language', ); _query = _index.query(parsed[0]).filters( @@ -63,45 +62,29 @@ class SearchController with ChangeNotifier { if (unverified) { _query = _query.facetFilter('unverified:true'); } - _query = monolingual + _query = global ? _query.setRestrictSearchableAttributes(['forms']) : _query.setHitsPerPage(50); + _snapshot = null; _tags.clear(); _hits.clear(); _onSearch?.call(); notifyListeners(); } - Future> fetchHits(int page) async { + Future> fetch(int page) async { _snapshot = await _query.setPage(page).getObjects(); notifyListeners(); - - var hits = snapshot!.hits; - if (hits.isNotEmpty && language == '_') { - final original = { - for (final hit in hits) hit.objectID: hit, - }; - final terms = _generateFilter( - hits.map((hit) => hit.data['term'] as String), - 'term', - ); - final languages = _generateFilter(_languages, 'language'); - hits = await _index - .filters('($languages) AND ($terms)') - .getObjects() - .then((s) => s.hits.map((h) => original[h.objectID] ?? h)) - .then((h) => h.toList()); - } - return _organizeHits( - hits.map((h) => Entry.fromAlgoliaHit(h)), + return _organize( + snapshot!.hits.map((h) => Entry.fromAlgoliaHit(h)), ); } - List _organizeHits(Iterable hits) { + List _organize(Iterable hits) { final newIds = []; for (final hit in hits) { - final id = monolingual ? hit.objectID : hit.term; + final id = global ? hit.objectID : hit.term; _hits.putIfAbsent(id, () { _tags[id] = {}; newIds.add(id); diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index ec2eb0ed..a8691d29 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -51,32 +51,30 @@ class SearchToolbarState extends State { super.dispose(); } - void search() => context.read().updateQuery(_input.text); + void search() => context.read().query(_input.text); void setLanguage(String language) { - context.read().updateLanguage(language); + context.read().setLanguage(language); _input.clear(); } @override Widget build(BuildContext context) { final search = context.watch(); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Row( - children: [ + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(width: 4), + if (GlobalStore.languages.length == 1) + LanguageAvatar(GlobalStore.languages.keys.first) + else OptionsButton( [ OptionItem.simple( Icons.language_outlined, - GlobalStore.languages.length == 1 ? 'English' : 'Multilingual', + 'Global', onTap: () => setLanguage(''), ), - OptionItem.simple( - Icons.layers_outlined, - 'Cross-lingual', - onTap: () => setLanguage('_'), - ), OptionItem.divider(), for (final l in GlobalStore.languages.keys) OptionItem.tile( @@ -107,33 +105,32 @@ class SearchToolbarState extends State { }, ), ), - Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 20, right: 4), - child: Builder( - builder: (context) { - var label = 'Search '; - label += search.monolingual - ? 'forms in ${search.language.titled}' - : '${search.language.isEmpty ? 'over' : 'across'} the languages'; - return TextField( - controller: _input, - decoration: InputDecoration( - border: InputBorder.none, - labelText: label, - ), - ); - }, - ), - ), + const SizedBox(width: 20), + Expanded( + child: Builder( + builder: (context) { + var label = 'Search '; + label += search.global + ? 'forms in ${search.language.titled}' + : '${search.language.isEmpty ? 'over' : 'across'} the languages'; + return TextField( + controller: _input, + decoration: InputDecoration( + border: InputBorder.none, + hintText: label, + ), + ); + }, ), - if (_input.text.isNotEmpty) - IconButton( - onPressed: _input.clear, - icon: const Icon(Icons.clear_outlined), - ), - ], - ), + ), + const SizedBox(width: 4), + if (_input.text.isNotEmpty) + IconButton( + onPressed: _input.clear, + icon: const Icon(Icons.clear_outlined), + ), + const SizedBox(width: 4), + ], ); } } From e98eea804be1bbf7749c24ff3f47a4cb1544ee96 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 14:34:03 +0300 Subject: [PATCH 21/34] Bump --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index bc6d82d6..ae1763d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: avzag description: Extensible parallel dictionary publish_to: "none" -version: 1.1.0+70 +version: 1.1.1+71 environment: sdk: ">=2.13.0 <3.0.0" From 4647960cd738f4ab0cd28cd0641c78b3d6a92278 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 14:49:35 +0300 Subject: [PATCH 22/34] Clean up search toolbar --- lib/modules/dictionary/dictionary.dart | 7 +-- .../dictionary/widgets/search_toolbar.dart | 56 ++++++++----------- lib/shared/widgets/expandable_tile.dart | 39 ------------- 3 files changed, 25 insertions(+), 77 deletions(-) delete mode 100644 lib/shared/widgets/expandable_tile.dart diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 3024d427..71e39eba 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -117,14 +117,9 @@ class DictionaryScreenState extends State { } void updateLanguages() { - final l = search.language; final ls = GlobalStore.languages.keys; search.setLanguage( - l.isEmpty - ? '' - : ls.contains(l) - ? l - : ls.first, + ls.length == 1 ? ls.first : '', ls, ); } diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index a8691d29..1de0fbb7 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -65,46 +65,37 @@ class SearchToolbarState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(width: 4), - if (GlobalStore.languages.length == 1) - LanguageAvatar(GlobalStore.languages.keys.first) - else - OptionsButton( - [ + OptionsButton( + [ + if (GlobalStore.languages.length > 1) ...[ OptionItem.simple( Icons.language_outlined, 'Global', onTap: () => setLanguage(''), ), OptionItem.divider(), - for (final l in GlobalStore.languages.keys) - OptionItem.tile( - Transform.scale( - scale: 1.25, - child: LanguageAvatar( - l, - radius: 12, - ), - ), - Text( - l.titled, - style: const TextStyle(fontWeight: FontWeight.w500), + ], + for (final l in GlobalStore.languages.keys) + OptionItem.tile( + Transform.scale( + scale: 1.25, + child: LanguageAvatar( + l, + radius: 12, ), - onTap: () => setLanguage(l), ), - ], - tooltip: 'Search mode', - icon: Builder( - builder: (context) { - if (search.language.isEmpty) { - return const Icon(Icons.language_outlined); - } - if (search.language == '_') { - return const Icon(Icons.layers_outlined); - } - return LanguageAvatar(search.language); - }, - ), - ), + Text( + l.titled, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onTap: () => setLanguage(l), + ), + ], + tooltip: 'Search mode', + icon: search.language.isEmpty + ? const Icon(Icons.language_outlined) + : LanguageAvatar(search.language), + ), const SizedBox(width: 20), Expanded( child: Builder( @@ -127,6 +118,7 @@ class SearchToolbarState extends State { if (_input.text.isNotEmpty) IconButton( onPressed: _input.clear, + tooltip: 'Clear', icon: const Icon(Icons.clear_outlined), ), const SizedBox(width: 4), diff --git a/lib/shared/widgets/expandable_tile.dart b/lib/shared/widgets/expandable_tile.dart deleted file mode 100644 index 80c1568c..00000000 --- a/lib/shared/widgets/expandable_tile.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; - -class ExpandableTile extends StatefulWidget { - const ExpandableTile({ - required this.header, - required this.body, - Key? key, - }) : super(key: key); - - final Widget header; - final Widget body; - - @override - State createState() => _ExpandableTileState(); -} - -class _ExpandableTileState extends State { - var isExpanded = false; - - @override - Widget build(BuildContext context) { - return ExpansionPanelList( - elevation: 0, - expansionCallback: (index, value) => setState(() { - isExpanded = !isExpanded; - }), - expandedHeaderPadding: EdgeInsets.zero, - children: [ - ExpansionPanel( - backgroundColor: Colors.transparent, - isExpanded: isExpanded, - canTapOnHeader: true, - headerBuilder: (_, __) => widget.header, - body: widget.body, - ), - ], - ); - } -} From f53a2e05f3c83309c5bc2c5bd0185aad28757f8f Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Sun, 10 Jul 2022 19:12:02 +0300 Subject: [PATCH 23/34] Update flag display --- lib/modules/home/widgets/language_card.dart | 53 +++++---------------- lib/shared/widgets/language_title.dart | 2 +- 2 files changed, 12 insertions(+), 43 deletions(-) diff --git a/lib/modules/home/widgets/language_card.dart b/lib/modules/home/widgets/language_card.dart index 31a60b3e..827c6e19 100644 --- a/lib/modules/home/widgets/language_card.dart +++ b/lib/modules/home/widgets/language_card.dart @@ -2,7 +2,6 @@ import 'package:avzag/models/language.dart'; import 'package:avzag/shared/extensions.dart'; import 'package:avzag/shared/utils.dart'; import 'package:avzag/shared/widgets/language_flag.dart'; -import 'package:avzag/shared/widgets/span_icon.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -24,19 +23,6 @@ class LanguageCard extends StatelessWidget { child: InkWell( onTap: onTap, child: ListTile( - trailing: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedOpacity( - opacity: selected ? 1 : .5, - duration: duration200, - child: LanguageFlag( - language.name, - offset: const Offset(20, 0), - ), - ), - ], - ), selected: selected, minVerticalPadding: 16, title: Text( @@ -46,34 +32,17 @@ class LanguageCard extends StatelessWidget { fontWeight: FontWeight.w600, ), ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - language.name.titled, - style: const TextStyle(fontWeight: FontWeight.w500), - ), - if (language.dictionary > 0) - Padding( - padding: const EdgeInsets.only(top: 2), - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: Theme.of(context).textTheme.caption?.copyWith( - fontSize: 14, - ), - children: [ - const WidgetSpan( - child: SpanIcon(Icons.book_outlined), - ), - TextSpan( - text: language.dictionary.toString(), - ), - ], - ), - ), - ), - ], + subtitle: Text( + '${language.name.titled} • ${language.dictionary}', + style: const TextStyle(fontWeight: FontWeight.w500), + ), + trailing: Center( + widthFactor: .4, + child: AnimatedOpacity( + opacity: selected ? 1 : .4, + duration: duration200, + child: LanguageFlag(language.name), + ), ), ), ), diff --git a/lib/shared/widgets/language_title.dart b/lib/shared/widgets/language_title.dart index 6a52d545..103c8387 100644 --- a/lib/shared/widgets/language_title.dart +++ b/lib/shared/widgets/language_title.dart @@ -19,7 +19,7 @@ class LanguageTitle extends StatelessWidget { Align( alignment: Alignment.centerRight, child: Opacity( - opacity: .5, + opacity: .4, child: LanguageFlag(language), ), ), From c43ca3a29b80baadf7ddc067de691531622beef1 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 02:22:02 +0300 Subject: [PATCH 24/34] Update app name --- README.md | 13 ++++++++----- lib/main.dart | 4 ++-- lib/models/entry.dart | 2 +- lib/models/language.dart | 2 +- lib/models/use.dart | 2 +- lib/models/word.dart | 4 ++-- lib/modules/dictionary/dictionary.dart | 18 +++++++++--------- .../dictionary/services/search_controller.dart | 2 +- lib/modules/dictionary/services/sharing.dart | 12 ++++++------ lib/modules/dictionary/services/word.dart | 12 ++++++------ .../dictionary/widgets/entry_group.dart | 6 +++--- lib/modules/dictionary/widgets/entry_tile.dart | 8 ++++---- .../dictionary/widgets/samples_column.dart | 4 ++-- .../dictionary/widgets/samples_editor.dart | 4 ++-- .../dictionary/widgets/search_toolbar.dart | 8 ++++---- lib/modules/dictionary/widgets/word_view.dart | 12 ++++++------ lib/modules/dictionary/word.dart | 10 +++++----- lib/modules/dictionary/word_editor.dart | 18 +++++++++--------- lib/modules/dictionary/word_loader.dart | 8 ++++---- lib/modules/dictionary/words_diff.dart | 10 +++++----- lib/modules/home/home.dart | 8 ++++---- lib/modules/home/widgets/language_card.dart | 8 ++++---- lib/modules/home/widgets/languages_bar.dart | 8 ++++---- lib/modules/home/widgets/languages_map.dart | 10 +++++----- lib/modules/settings/settings.dart | 8 ++++---- lib/modules/settings/widgets/account_tile.dart | 2 +- .../settings/widgets/editor_mode_card.dart | 14 +++++++------- lib/navigation/loader.dart | 2 +- lib/navigation/root_guard.dart | 2 +- lib/navigation/router.dart | 14 +++++++------- lib/shared/utils.dart | 2 +- lib/shared/widgets/language_title.dart | 2 +- pubspec.yaml | 2 +- web/index.html | 4 ++-- web/manifest.json | 4 ++-- 35 files changed, 126 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index bf8c24f4..a12064e0 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,32 @@ # Andax -Expandable parallel reference to the languages of the Caucasus in English. Currently includes Dictionary, with more modules to come soon. Legacy experimental implementation with 4 modules is available at https://ex.avzag.app/. +Expandable parallel dictionary for the languages of the Caucasus in English. -Google Play: https://play.google.com/store/apps/details?id=com.alkaitagi.avzag -Web application: https://avzag.app/. +Google Play: +Web application: . Built with Flutter & Firebase & Algolia. ## Roadmap ### 2021 + - [x] September: start of beta-test. - [x] November: release of crowdsourcing functionality. - [x] December: general availability on Android & Web. ### 2022 + - [x] Spring: Home-screen map, iOS release. - [x] Summer: Deep-linking, word sharing. - [ ] Autumn: Word of the day, search UI improvements. - [ ] Winter: Material 3, UI & UX overhaul. ## Getting Started + ```sh -> git clone https://github.com/raxysstudios/avzag.git -> cd avzag +> git clone https://github.com/raxysstudios/bazur.git +> cd bazur > pub get > flutter run -d chrome --web-port 80 ``` diff --git a/lib/main.dart b/lib/main.dart index 6563efee..2e1ab023 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,5 @@ import 'package:algolia/algolia.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/store.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; @@ -41,7 +41,7 @@ class App extends StatelessWidget { Widget build(context) { final theme = ThemeSet(Theme.of(context).colorScheme); return MaterialApp.router( - title: 'Avzag', + title: 'Bazur', theme: theme.light, darkTheme: theme.dark, routerDelegate: _appRouter.delegate(), diff --git a/lib/models/entry.dart b/lib/models/entry.dart index 38672c4d..e5f4aa47 100644 --- a/lib/models/entry.dart +++ b/lib/models/entry.dart @@ -1,5 +1,5 @@ import 'package:algolia/algolia.dart'; -import 'package:avzag/shared/utils.dart'; +import 'package:bazur/shared/utils.dart'; class Entry { final String entryID; diff --git a/lib/models/language.dart b/lib/models/language.dart index b0f61145..6511034b 100644 --- a/lib/models/language.dart +++ b/lib/models/language.dart @@ -1,4 +1,4 @@ -import 'package:avzag/shared/utils.dart'; +import 'package:bazur/shared/utils.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; class Language { diff --git a/lib/models/use.dart b/lib/models/use.dart index 3d755b97..180074f5 100644 --- a/lib/models/use.dart +++ b/lib/models/use.dart @@ -1,4 +1,4 @@ -import 'package:avzag/shared/utils.dart'; +import 'package:bazur/shared/utils.dart'; import 'sample.dart'; diff --git a/lib/models/word.dart b/lib/models/word.dart index d9362f93..8748cddc 100644 --- a/lib/models/word.dart +++ b/lib/models/word.dart @@ -1,5 +1,5 @@ -import 'package:avzag/models/contribution.dart'; -import 'package:avzag/shared/utils.dart'; +import 'package:bazur/models/contribution.dart'; +import 'package:bazur/shared/utils.dart'; import 'sample.dart'; import 'use.dart'; diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 71e39eba..14be31e6 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -1,12 +1,12 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/entry.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/modules/dictionary/widgets/entry_group.dart'; -import 'package:avzag/navigation/router.gr.dart'; -import 'package:avzag/shared/modals/loading_dialog.dart'; -import 'package:avzag/shared/modals/snackbar_manager.dart'; -import 'package:avzag/shared/widgets/caption.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/models/entry.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/modules/dictionary/widgets/entry_group.dart'; +import 'package:bazur/navigation/router.gr.dart'; +import 'package:bazur/shared/modals/loading_dialog.dart'; +import 'package:bazur/shared/modals/snackbar_manager.dart'; +import 'package:bazur/shared/widgets/caption.dart'; +import 'package:bazur/store.dart'; import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:provider/provider.dart'; @@ -150,7 +150,7 @@ class DictionaryScreenState extends State { tooltip: 'Home', icon: const Icon(Icons.landscape_outlined), ), - title: const Text('Avzag'), + title: const Text('Bazur'), actions: [ IconButton( onPressed: () => showSnackbar( diff --git a/lib/modules/dictionary/services/search_controller.dart b/lib/modules/dictionary/services/search_controller.dart index 6624fdc1..1c509bdb 100644 --- a/lib/modules/dictionary/services/search_controller.dart +++ b/lib/modules/dictionary/services/search_controller.dart @@ -1,5 +1,5 @@ import 'package:algolia/algolia.dart'; -import 'package:avzag/models/entry.dart'; +import 'package:bazur/models/entry.dart'; import 'package:flutter/material.dart'; class SearchController with ChangeNotifier { diff --git a/lib/modules/dictionary/services/sharing.dart b/lib/modules/dictionary/services/sharing.dart index e11289a3..37f37b40 100644 --- a/lib/modules/dictionary/services/sharing.dart +++ b/lib/modules/dictionary/services/sharing.dart @@ -1,12 +1,12 @@ -import 'package:avzag/models/sample.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/shared/extensions.dart'; +import 'package:bazur/models/sample.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/shared/extensions.dart'; import 'package:markdown/markdown.dart'; -String _getLink(Word word) => 'https://avzag.raxys.app/${word.id}'; +String _getLink(Word word) => 'https://bazur.raxys.app/${word.id}'; String previewArticle(Word word) => ''' -🌄 Avzag • ${word.language.titled} +🌄 Bazur • ${word.language.titled} 🔖 ${word.headword.titled} — ${word.uses.map((u) => u.term.titled).join(', ')} ${_getLink(word)}'''; @@ -25,7 +25,7 @@ String textifyArticle(Word word) { String note(String n) => '📝 ${_cleanMarkdown(n)}'; final article = [ - '🌄 Avzag • ${word.language.titled}', + '🌄 Bazur • ${word.language.titled}', _getLink(word), '\n🔖 ${word.headword.titled}', if (word.ipa != null) '🔉 ${word.ipa}', diff --git a/lib/modules/dictionary/services/word.dart b/lib/modules/dictionary/services/word.dart index 9af427e1..716f69a7 100644 --- a/lib/modules/dictionary/services/word.dart +++ b/lib/modules/dictionary/services/word.dart @@ -1,9 +1,9 @@ -import 'package:avzag/models/contribution.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/shared/modals/danger_dialog.dart'; -import 'package:avzag/shared/modals/loading_dialog.dart'; -import 'package:avzag/shared/modals/snackbar_manager.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/models/contribution.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/shared/modals/danger_dialog.dart'; +import 'package:bazur/shared/modals/loading_dialog.dart'; +import 'package:bazur/shared/modals/snackbar_manager.dart'; +import 'package:bazur/store.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; diff --git a/lib/modules/dictionary/widgets/entry_group.dart b/lib/modules/dictionary/widgets/entry_group.dart index ada61a80..77dbbf69 100644 --- a/lib/modules/dictionary/widgets/entry_group.dart +++ b/lib/modules/dictionary/widgets/entry_group.dart @@ -1,6 +1,6 @@ -import 'package:avzag/models/entry.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; +import 'package:bazur/models/entry.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/widgets/column_card.dart'; import 'package:flutter/material.dart'; import 'entry_tile.dart'; diff --git a/lib/modules/dictionary/widgets/entry_tile.dart b/lib/modules/dictionary/widgets/entry_tile.dart index a05cbc5a..7b98aa1f 100644 --- a/lib/modules/dictionary/widgets/entry_tile.dart +++ b/lib/modules/dictionary/widgets/entry_tile.dart @@ -1,7 +1,7 @@ -import 'package:avzag/models/entry.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/span_icon.dart'; +import 'package:bazur/models/entry.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/span_icon.dart'; import 'package:flutter/material.dart'; diff --git a/lib/modules/dictionary/widgets/samples_column.dart b/lib/modules/dictionary/widgets/samples_column.dart index ec75693c..6891f170 100644 --- a/lib/modules/dictionary/widgets/samples_column.dart +++ b/lib/modules/dictionary/widgets/samples_column.dart @@ -1,5 +1,5 @@ -import 'package:avzag/models/sample.dart'; -import 'package:avzag/shared/utils.dart'; +import 'package:bazur/models/sample.dart'; +import 'package:bazur/shared/utils.dart'; import 'package:flutter/material.dart'; class SamplesColumn extends StatelessWidget { diff --git a/lib/modules/dictionary/widgets/samples_editor.dart b/lib/modules/dictionary/widgets/samples_editor.dart index 13442506..9e7b8d1e 100644 --- a/lib/modules/dictionary/widgets/samples_editor.dart +++ b/lib/modules/dictionary/widgets/samples_editor.dart @@ -1,5 +1,5 @@ -import 'package:avzag/models/sample.dart'; -import 'package:avzag/shared/widgets/compact_input.dart'; +import 'package:bazur/models/sample.dart'; +import 'package:bazur/shared/widgets/compact_input.dart'; import 'package:flutter/material.dart'; class SamplesEditor extends StatefulWidget { diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index 1de0fbb7..6627b21a 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/widgets/language_avatar.dart'; -import 'package:avzag/shared/widgets/options_button.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/widgets/language_avatar.dart'; +import 'package:bazur/shared/widgets/options_button.dart'; +import 'package:bazur/store.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/lib/modules/dictionary/widgets/word_view.dart b/lib/modules/dictionary/widgets/word_view.dart index 2e735053..9e1dd8ae 100644 --- a/lib/modules/dictionary/widgets/word_view.dart +++ b/lib/modules/dictionary/widgets/word_view.dart @@ -1,9 +1,9 @@ -import 'package:avzag/models/word.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/caption.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; -import 'package:avzag/shared/widgets/markdown_text.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/caption.dart'; +import 'package:bazur/shared/widgets/column_card.dart'; +import 'package:bazur/shared/widgets/markdown_text.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/lib/modules/dictionary/word.dart b/lib/modules/dictionary/word.dart index e24d246a..c6bf1a50 100644 --- a/lib/modules/dictionary/word.dart +++ b/lib/modules/dictionary/word.dart @@ -1,9 +1,9 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/modules/dictionary/services/sharing.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/language_title.dart'; -import 'package:avzag/shared/widgets/options_button.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/modules/dictionary/services/sharing.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/language_title.dart'; +import 'package:bazur/shared/widgets/options_button.dart'; import 'package:flutter/material.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; diff --git a/lib/modules/dictionary/word_editor.dart b/lib/modules/dictionary/word_editor.dart index 68c79ff1..692ec593 100644 --- a/lib/modules/dictionary/word_editor.dart +++ b/lib/modules/dictionary/word_editor.dart @@ -1,13 +1,13 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/use.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/modules/dictionary/widgets/samples_editor.dart'; -import 'package:avzag/shared/modals/danger_dialog.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; -import 'package:avzag/shared/widgets/compact_input.dart'; -import 'package:avzag/shared/widgets/language_title.dart'; -import 'package:avzag/shared/widgets/options_button.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/models/use.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/modules/dictionary/widgets/samples_editor.dart'; +import 'package:bazur/shared/modals/danger_dialog.dart'; +import 'package:bazur/shared/widgets/column_card.dart'; +import 'package:bazur/shared/widgets/compact_input.dart'; +import 'package:bazur/shared/widgets/language_title.dart'; +import 'package:bazur/shared/widgets/options_button.dart'; +import 'package:bazur/store.dart'; import 'package:flutter/material.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; diff --git a/lib/modules/dictionary/word_loader.dart b/lib/modules/dictionary/word_loader.dart index 63340923..55ea3453 100644 --- a/lib/modules/dictionary/word_loader.dart +++ b/lib/modules/dictionary/word_loader.dart @@ -1,8 +1,8 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/modules/dictionary/services/word.dart'; -import 'package:avzag/navigation/loader.dart'; -import 'package:avzag/navigation/router.gr.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/modules/dictionary/services/word.dart'; +import 'package:bazur/navigation/loader.dart'; +import 'package:bazur/navigation/router.gr.dart'; import 'package:flutter/material.dart'; class WordLoaderScreen extends StatelessWidget { diff --git a/lib/modules/dictionary/words_diff.dart b/lib/modules/dictionary/words_diff.dart index 80593771..4a895f8b 100644 --- a/lib/modules/dictionary/words_diff.dart +++ b/lib/modules/dictionary/words_diff.dart @@ -1,9 +1,9 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/word.dart'; -import 'package:avzag/modules/dictionary/widgets/word_view.dart'; -import 'package:avzag/shared/widgets/caption.dart'; -import 'package:avzag/shared/widgets/language_title.dart'; -import 'package:avzag/shared/widgets/options_button.dart'; +import 'package:bazur/models/word.dart'; +import 'package:bazur/modules/dictionary/widgets/word_view.dart'; +import 'package:bazur/shared/widgets/caption.dart'; +import 'package:bazur/shared/widgets/language_title.dart'; +import 'package:bazur/shared/widgets/options_button.dart'; import 'package:flutter/material.dart'; import 'services/word.dart'; diff --git a/lib/modules/home/home.dart b/lib/modules/home/home.dart index 703c6b69..2fb69db6 100644 --- a/lib/modules/home/home.dart +++ b/lib/modules/home/home.dart @@ -1,8 +1,8 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/models/language.dart'; -import 'package:avzag/modules/home/widgets/languages_bar.dart'; -import 'package:avzag/navigation/router.gr.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/models/language.dart'; +import 'package:bazur/modules/home/widgets/languages_bar.dart'; +import 'package:bazur/navigation/router.gr.dart'; +import 'package:bazur/store.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; diff --git a/lib/modules/home/widgets/language_card.dart b/lib/modules/home/widgets/language_card.dart index 827c6e19..95bf5e9d 100644 --- a/lib/modules/home/widgets/language_card.dart +++ b/lib/modules/home/widgets/language_card.dart @@ -1,7 +1,7 @@ -import 'package:avzag/models/language.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/language_flag.dart'; +import 'package:bazur/models/language.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/language_flag.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/lib/modules/home/widgets/languages_bar.dart b/lib/modules/home/widgets/languages_bar.dart index 79a123e3..e69cf39e 100644 --- a/lib/modules/home/widgets/languages_bar.dart +++ b/lib/modules/home/widgets/languages_bar.dart @@ -1,7 +1,7 @@ -import 'package:avzag/models/language.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/language_avatar.dart'; +import 'package:bazur/models/language.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/language_avatar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; diff --git a/lib/modules/home/widgets/languages_map.dart b/lib/modules/home/widgets/languages_map.dart index 8ca2c325..25e3f78d 100644 --- a/lib/modules/home/widgets/languages_map.dart +++ b/lib/modules/home/widgets/languages_map.dart @@ -1,8 +1,8 @@ -import 'package:avzag/models/language.dart'; -import 'package:avzag/modules/home/services/mapbox.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/language_avatar.dart'; +import 'package:bazur/models/language.dart'; +import 'package:bazur/modules/home/services/mapbox.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/language_avatar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart'; diff --git a/lib/modules/settings/settings.dart b/lib/modules/settings/settings.dart index c80adde8..537caae0 100644 --- a/lib/modules/settings/settings.dart +++ b/lib/modules/settings/settings.dart @@ -1,7 +1,7 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/navigation/router.gr.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/raxys.dart'; +import 'package:bazur/navigation/router.gr.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/raxys.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -58,7 +58,7 @@ class _SettingsScreenState extends State { : 'v${p.version} • b${p.buildNumber}', ), onTap: () => openLink( - 'https://github.com/raxysstudios/avzag', + 'https://github.com/raxysstudios/bazur', ), ); }, diff --git a/lib/modules/settings/widgets/account_tile.dart b/lib/modules/settings/widgets/account_tile.dart index a40ad7f8..61d8079f 100644 --- a/lib/modules/settings/widgets/account_tile.dart +++ b/lib/modules/settings/widgets/account_tile.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:avzag/shared/modals/snackbar_manager.dart'; +import 'package:bazur/shared/modals/snackbar_manager.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/lib/modules/settings/widgets/editor_mode_card.dart b/lib/modules/settings/widgets/editor_mode_card.dart index bfe9ee4b..40ac2f66 100644 --- a/lib/modules/settings/widgets/editor_mode_card.dart +++ b/lib/modules/settings/widgets/editor_mode_card.dart @@ -1,12 +1,12 @@ import 'dart:async'; -import 'package:avzag/models/language.dart'; -import 'package:avzag/shared/extensions.dart'; -import 'package:avzag/shared/utils.dart'; -import 'package:avzag/shared/widgets/column_card.dart'; -import 'package:avzag/shared/widgets/language_avatar.dart'; -import 'package:avzag/shared/widgets/span_icon.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/models/language.dart'; +import 'package:bazur/shared/extensions.dart'; +import 'package:bazur/shared/utils.dart'; +import 'package:bazur/shared/widgets/column_card.dart'; +import 'package:bazur/shared/widgets/language_avatar.dart'; +import 'package:bazur/shared/widgets/span_icon.dart'; +import 'package:bazur/store.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; diff --git a/lib/navigation/loader.dart b/lib/navigation/loader.dart index e661b8f9..85403611 100644 --- a/lib/navigation/loader.dart +++ b/lib/navigation/loader.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; -import 'package:avzag/shared/modals/loading_dialog.dart'; +import 'package:bazur/shared/modals/loading_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; diff --git a/lib/navigation/root_guard.dart b/lib/navigation/root_guard.dart index 2d7cfa63..c1dea8aa 100644 --- a/lib/navigation/root_guard.dart +++ b/lib/navigation/root_guard.dart @@ -1,5 +1,5 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/store.dart'; +import 'package:bazur/store.dart'; import 'router.gr.dart'; diff --git a/lib/navigation/router.dart b/lib/navigation/router.dart index bc9e8ce9..b551b2a4 100644 --- a/lib/navigation/router.dart +++ b/lib/navigation/router.dart @@ -1,11 +1,11 @@ import 'package:auto_route/auto_route.dart'; -import 'package:avzag/modules/dictionary/dictionary.dart'; -import 'package:avzag/modules/dictionary/word.dart'; -import 'package:avzag/modules/dictionary/word_editor.dart'; -import 'package:avzag/modules/dictionary/word_loader.dart'; -import 'package:avzag/modules/dictionary/words_diff.dart'; -import 'package:avzag/modules/home/home.dart'; -import 'package:avzag/modules/settings/settings.dart'; +import 'package:bazur/modules/dictionary/dictionary.dart'; +import 'package:bazur/modules/dictionary/word.dart'; +import 'package:bazur/modules/dictionary/word_editor.dart'; +import 'package:bazur/modules/dictionary/word_loader.dart'; +import 'package:bazur/modules/dictionary/words_diff.dart'; +import 'package:bazur/modules/home/home.dart'; +import 'package:bazur/modules/settings/settings.dart'; import 'root_guard.dart'; import 'route_builders.dart'; diff --git a/lib/shared/utils.dart b/lib/shared/utils.dart index 5288a0cf..b4172379 100644 --- a/lib/shared/utils.dart +++ b/lib/shared/utils.dart @@ -1,4 +1,4 @@ -import 'package:avzag/shared/modals/snackbar_manager.dart'; +import 'package:bazur/shared/modals/snackbar_manager.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/lib/shared/widgets/language_title.dart b/lib/shared/widgets/language_title.dart index 103c8387..d42e9e6d 100644 --- a/lib/shared/widgets/language_title.dart +++ b/lib/shared/widgets/language_title.dart @@ -1,4 +1,4 @@ -import 'package:avzag/shared/extensions.dart'; +import 'package:bazur/shared/extensions.dart'; import 'package:flutter/material.dart'; import 'language_flag.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index ae1763d4..1b76c4a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,4 +1,4 @@ -name: avzag +name: bazur description: Extensible parallel dictionary publish_to: "none" version: 1.1.1+71 diff --git a/web/index.html b/web/index.html index b0ef8900..72986742 100644 --- a/web/index.html +++ b/web/index.html @@ -14,7 +14,7 @@ - + - Avzag + Bazur diff --git a/web/manifest.json b/web/manifest.json index d00d6130..2e253173 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -1,6 +1,6 @@ { - "name": "Avzag", - "short_name": "Avzag", + "name": "Bazur", + "short_name": "Bazur", "start_url": ".", "display": "standalone", "background_color": "#303030", From f366339e51be26b21d59bf883e31914e41a7408d Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 14:45:32 +0300 Subject: [PATCH 25/34] Revert search controller global mode display --- lib/modules/dictionary/dictionary.dart | 10 +--------- lib/modules/dictionary/widgets/search_toolbar.dart | 14 ++++++-------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 14be31e6..6149dab3 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -116,14 +116,6 @@ class DictionaryScreenState extends State { } } - void updateLanguages() { - final ls = GlobalStore.languages.keys; - search.setLanguage( - ls.length == 1 ? ls.first : '', - ls, - ); - } - @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( @@ -145,7 +137,7 @@ class DictionaryScreenState extends State { leading: IconButton( onPressed: () async { await context.pushRoute(const HomeRoute()); - updateLanguages(); + search.setLanguage(''); }, tooltip: 'Home', icon: const Icon(Icons.landscape_outlined), diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index 6627b21a..9ef7a570 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -67,14 +67,12 @@ class SearchToolbarState extends State { const SizedBox(width: 4), OptionsButton( [ - if (GlobalStore.languages.length > 1) ...[ - OptionItem.simple( - Icons.language_outlined, - 'Global', - onTap: () => setLanguage(''), - ), - OptionItem.divider(), - ], + OptionItem.simple( + Icons.language_outlined, + 'Global', + onTap: () => setLanguage(''), + ), + OptionItem.divider(), for (final l in GlobalStore.languages.keys) OptionItem.tile( Transform.scale( From 3b89604e313e1ba77921aed42f99dfb0e92a50f1 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 14:53:53 +0300 Subject: [PATCH 26/34] Make word screen text selectable --- .../dictionary/widgets/samples_column.dart | 46 ++++++++----------- lib/modules/dictionary/widgets/word_view.dart | 6 +-- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/lib/modules/dictionary/widgets/samples_column.dart b/lib/modules/dictionary/widgets/samples_column.dart index 6891f170..2a6b0e2b 100644 --- a/lib/modules/dictionary/widgets/samples_column.dart +++ b/lib/modules/dictionary/widgets/samples_column.dart @@ -23,37 +23,27 @@ class SamplesColumn extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ for (final s in samples) - InkWell( - onLongPress: () { - final text = [ - s.text, - if (s.meaning != null) - inline ? s.meaning!.toUpperCase() : s.meaning - ].join(inline ? ' ' : '\n'); - copyText(context, text); - }, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 4, - ), - child: RichText( - text: TextSpan( - style: theme.bodyText2, - children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + child: SelectableText.rich( + TextSpan( + style: theme.bodyText2, + children: [ + TextSpan( + text: s.text, + style: styleNative, + ), + if (s.meaning != null) ...[ + TextSpan(text: inline ? ' ' : '\n'), TextSpan( - text: s.text, - style: styleNative, + text: inline ? s.meaning!.toUpperCase() : s.meaning, + style: TextStyle(color: theme.caption?.color), ), - if (s.meaning != null) ...[ - TextSpan(text: inline ? ' ' : '\n'), - TextSpan( - text: inline ? s.meaning!.toUpperCase() : s.meaning, - style: TextStyle(color: theme.caption?.color), - ), - ], ], - ), + ], ), ), ), diff --git a/lib/modules/dictionary/widgets/word_view.dart b/lib/modules/dictionary/widgets/word_view.dart index 9e1dd8ae..acb72f45 100644 --- a/lib/modules/dictionary/widgets/word_view.dart +++ b/lib/modules/dictionary/widgets/word_view.dart @@ -33,14 +33,14 @@ class WordView extends StatelessWidget { vertical: 8, ), children: [ - Text( + SelectableText( word.headword.titled, style: styleNative.copyWith( fontSize: theme.headline5?.fontSize, ), ), if (word.ipa != null) - Text( + SelectableText( '[${word.ipa!}]', style: GoogleFonts.notoSans( textStyle: theme.bodyText1, @@ -80,7 +80,7 @@ class WordView extends StatelessWidget { Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( + child: SelectableText( u.term.titled, style: theme.headline6, ), From 5dd0129b37ebee2469ecbeceaf282b45beddbf59 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 14:54:00 +0300 Subject: [PATCH 27/34] Fix language changing --- lib/modules/dictionary/dictionary.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 6149dab3..6ed079c8 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -137,7 +137,7 @@ class DictionaryScreenState extends State { leading: IconButton( onPressed: () async { await context.pushRoute(const HomeRoute()); - search.setLanguage(''); + search.setLanguage('', GlobalStore.languages.keys); }, tooltip: 'Home', icon: const Icon(Icons.landscape_outlined), From c32a5d2c56ad6f86dd08fadbee19a76850fc5adc Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 17:04:39 +0300 Subject: [PATCH 28/34] Rename app --- android/app/src/debug/res/values/string.xml | 2 +- android/app/src/main/res/values/string.xml | 2 +- ios/Runner/Info.plist | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/src/debug/res/values/string.xml b/android/app/src/debug/res/values/string.xml index 6d209371..3d2aeeb5 100644 --- a/android/app/src/debug/res/values/string.xml +++ b/android/app/src/debug/res/values/string.xml @@ -1,4 +1,4 @@ - 🛠️ Avzag + 🛠️ Bazur diff --git a/android/app/src/main/res/values/string.xml b/android/app/src/main/res/values/string.xml index 41b1d27c..bd05eef4 100644 --- a/android/app/src/main/res/values/string.xml +++ b/android/app/src/main/res/values/string.xml @@ -1,4 +1,4 @@ - Avzag + Bazur diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index ba7337c1..ceec8823 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -11,7 +11,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - avzag + bazur CFBundlePackageType APPL CFBundleShortVersionString @@ -60,7 +60,7 @@ CFBundleTypeRole Editor CFBundleURLName - avzag.raxys.app + bazur.raxys.app CFBundleURLSchemes customscheme From 9b61400c9d8f4a52604e929326bfd62bf6d42d08 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 17:43:39 +0300 Subject: [PATCH 29/34] Update models --- lib/models/entry.dart | 6 +-- lib/models/language.dart | 4 +- lib/models/use.dart | 14 +++---- lib/models/word.dart | 12 +++--- lib/modules/dictionary/dictionary.dart | 2 +- .../services/search_controller.dart | 2 +- lib/modules/dictionary/services/sharing.dart | 15 ++++--- lib/modules/dictionary/services/word.dart | 4 +- .../dictionary/widgets/entry_group.dart | 2 +- lib/modules/dictionary/widgets/word_view.dart | 18 ++++----- lib/modules/dictionary/word_editor.dart | 40 +++++++++---------- 11 files changed, 58 insertions(+), 61 deletions(-) diff --git a/lib/models/entry.dart b/lib/models/entry.dart index e5f4aa47..78bcd63f 100644 --- a/lib/models/entry.dart +++ b/lib/models/entry.dart @@ -7,7 +7,7 @@ class Entry { final String headword; final String? form; final String language; - final String term; + final String translation; final bool unverified; final List? tags; @@ -17,7 +17,7 @@ class Entry { required this.headword, this.form, required this.language, - required this.term, + required this.translation, this.tags, this.unverified = false, }); @@ -37,7 +37,7 @@ class Entry { headword: json['headword'] as String, form: form >= 0 ? json2list(json['forms'])![form] : null, language: json['language'] as String, - term: json['term'] as String, + translation: json['translation'] as String, unverified: json['unverified'] as bool? ?? false, tags: json2list(json['tags']), ); diff --git a/lib/models/language.dart b/lib/models/language.dart index 6511034b..7df54d03 100644 --- a/lib/models/language.dart +++ b/lib/models/language.dart @@ -26,8 +26,6 @@ class Language { aliases: json2list(json['aliases']), location: json['location'] == null ? null : json['location'] as GeoPoint, - dictionary: - (json['stats'] as Map?)?['dictionary'] as int? ?? - 0, + dictionary: json['dictionary'] as int? ?? 0, ); } diff --git a/lib/models/use.dart b/lib/models/use.dart index 180074f5..c0db2f5e 100644 --- a/lib/models/use.dart +++ b/lib/models/use.dart @@ -2,24 +2,24 @@ import 'package:bazur/shared/utils.dart'; import 'sample.dart'; -class Use { - String term; +class Definition { + String translation; List aliases; List tags; String? note; List examples; - Use( - this.term, { + Definition( + this.translation, { required this.aliases, required this.tags, this.note, required this.examples, }); - Use.fromJson(Map json) + Definition.fromJson(Map json) : this( - json['term'] as String, + json['translation'] as String, aliases: json2list(json['aliases']) ?? [], tags: json2list(json['tags']) ?? [], note: json['note'] as String?, @@ -32,7 +32,7 @@ class Use { Map toJson() { final data = {}; - data['term'] = term; + data['translation'] = translation; if (aliases.isNotEmpty) data['aliases'] = aliases; if (tags.isNotEmpty) data['tags'] = tags; if (note?.isNotEmpty ?? false) data['note'] = note; diff --git a/lib/models/word.dart b/lib/models/word.dart index 8748cddc..cf138770 100644 --- a/lib/models/word.dart +++ b/lib/models/word.dart @@ -12,7 +12,7 @@ class Word { String language; List tags; String? note; - List uses; + List definitions; Contribution? contribution; Word( @@ -20,7 +20,7 @@ class Word { required this.headword, this.ipa, required this.language, - required this.uses, + required this.definitions, required this.forms, required this.tags, this.contribution, @@ -38,9 +38,9 @@ class Word { ) ?? [], language: json['language'] as String, - uses: listFromJson( - json['uses'], - (dynamic j) => Use.fromJson(j as Map), + definitions: listFromJson( + json['definitions'], + (dynamic j) => Definition.fromJson(j as Map), )!, tags: json2list(json['tags']) ?? [], note: json['note'] as String?, @@ -60,7 +60,7 @@ class Word { data['language'] = language; if (tags.isNotEmpty) data['tags'] = tags; if (note?.isNotEmpty ?? false) data['note'] = note; - data['uses'] = uses.map((v) => v.toJson()).toList(); + data['definitions'] = definitions.map((v) => v.toJson()).toList(); if (contribution != null) data['contribution'] = contribution!.toJson(); return data; } diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 6ed079c8..2b48e4e2 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -64,7 +64,7 @@ class DictionaryScreenState extends State { editing ??= Word( null, headword: '', - uses: [], + definitions: [], language: EditorStore.language!, tags: [], forms: [], diff --git a/lib/modules/dictionary/services/search_controller.dart b/lib/modules/dictionary/services/search_controller.dart index 1c509bdb..7ecb158d 100644 --- a/lib/modules/dictionary/services/search_controller.dart +++ b/lib/modules/dictionary/services/search_controller.dart @@ -84,7 +84,7 @@ class SearchController with ChangeNotifier { List _organize(Iterable hits) { final newIds = []; for (final hit in hits) { - final id = global ? hit.objectID : hit.term; + final id = global ? hit.objectID : hit.translation; _hits.putIfAbsent(id, () { _tags[id] = {}; newIds.add(id); diff --git a/lib/modules/dictionary/services/sharing.dart b/lib/modules/dictionary/services/sharing.dart index 37f37b40..58dcbc9b 100644 --- a/lib/modules/dictionary/services/sharing.dart +++ b/lib/modules/dictionary/services/sharing.dart @@ -7,7 +7,7 @@ String _getLink(Word word) => 'https://bazur.raxys.app/${word.id}'; String previewArticle(Word word) => ''' 🌄 Bazur • ${word.language.titled} -🔖 ${word.headword.titled} — ${word.uses.map((u) => u.term.titled).join(', ')} +🔖 ${word.headword.titled} — ${word.definitions.map((d) => d.translation.titled).join(', ')} ${_getLink(word)}'''; String _cleanMarkdown(String md) => markdownToHtml(md, inlineOnly: true) @@ -32,13 +32,12 @@ String textifyArticle(Word word) { if (word.tags.isNotEmpty) tags(word.tags), if (word.note?.isNotEmpty ?? false) note(word.note!), if (word.forms.isNotEmpty) ...samples(word.forms), - if (word.uses.isNotEmpty) - for (final use in word.uses) ...[ - '\n💡${word.uses.indexOf(use) + 1} ${use.term.titled}', - if (use.tags.isNotEmpty) tags(use.tags), - if (use.note?.isNotEmpty ?? false) note(use.note!), - if (use.examples.isNotEmpty) ...samples(use.examples), - ], + for (final d in word.definitions) ...[ + '\n💡${word.definitions.indexOf(d) + 1} ${d.translation.titled}', + if (d.tags.isNotEmpty) tags(d.tags), + if (d.note?.isNotEmpty ?? false) note(d.note!), + if (d.examples.isNotEmpty) ...samples(d.examples), + ], ]; return article.join('\n'); } diff --git a/lib/modules/dictionary/services/word.dart b/lib/modules/dictionary/services/word.dart index 716f69a7..87de945b 100644 --- a/lib/modules/dictionary/services/word.dart +++ b/lib/modules/dictionary/services/word.dart @@ -47,10 +47,10 @@ void submitWord( Word word, { VoidCallback? after, }) async { - if (word.uses.isEmpty) { + if (word.definitions.isEmpty) { return showSnackbar( context, - text: 'Must have at least one use', + text: 'Must have at least one definition', ); } String? id = word.id; diff --git a/lib/modules/dictionary/widgets/entry_group.dart b/lib/modules/dictionary/widgets/entry_group.dart index 77dbbf69..cd2ed561 100644 --- a/lib/modules/dictionary/widgets/entry_group.dart +++ b/lib/modules/dictionary/widgets/entry_group.dart @@ -34,7 +34,7 @@ class EntryGroup extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.baseline, children: [ Text( - groups.first.first.term.titled, + groups.first.first.translation.titled, style: TextStyle( color: theme.caption?.color, fontWeight: FontWeight.w500, diff --git a/lib/modules/dictionary/widgets/word_view.dart b/lib/modules/dictionary/widgets/word_view.dart index acb72f45..286bad29 100644 --- a/lib/modules/dictionary/widgets/word_view.dart +++ b/lib/modules/dictionary/widgets/word_view.dart @@ -60,7 +60,7 @@ class WordView extends StatelessWidget { word.forms, inline: true, ), - for (final u in word.uses) ...[ + for (final d in word.definitions) ...[ ColumnCard( divider: const SizedBox(height: 4), padding: const EdgeInsets.symmetric( @@ -72,7 +72,7 @@ class WordView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${word.uses.indexOf(u) + 1}', + '${word.definitions.indexOf(d) + 1}', style: theme.headline6?.copyWith( color: theme.caption?.color, ), @@ -81,16 +81,16 @@ class WordView extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: SelectableText( - u.term.titled, + d.translation.titled, style: theme.headline6, ), ), ), - if (u.aliases.isNotEmpty) + if (d.aliases.isNotEmpty) Tooltip( waitDuration: Duration.zero, triggerMode: TooltipTriggerMode.tap, - message: u.aliases.join(' • ').titled, + message: d.aliases.join(' • ').titled, child: Icon( Icons.label_outlined, color: Theme.of(context).textTheme.caption?.color, @@ -98,15 +98,15 @@ class WordView extends StatelessWidget { ) ], ), - if (u.tags.isNotEmpty) + if (d.tags.isNotEmpty) Text( - u.tags.join(', '), + d.tags.join(', '), style: theme.caption, ), - if (u.note != null) MarkdownText(u.note!), + if (d.note != null) MarkdownText(d.note!), ], ), - if (u.examples.isNotEmpty) SamplesColumn(u.examples), + if (d.examples.isNotEmpty) SamplesColumn(d.examples), ], if (word.contribution != null) const Caption( diff --git a/lib/modules/dictionary/word_editor.dart b/lib/modules/dictionary/word_editor.dart index 692ec593..526791e5 100644 --- a/lib/modules/dictionary/word_editor.dart +++ b/lib/modules/dictionary/word_editor.dart @@ -88,7 +88,7 @@ class _WordEditorScreenState extends State { divider: null, children: [ CompactInput( - Icons.bookmark_border_outlined, + Icons.label_important_outlined, 'Headword', word.headword, (s) => word.headword = s, @@ -107,7 +107,7 @@ class _WordEditorScreenState extends State { (s) => word.tags = s.split(' '), ), CompactInput( - Icons.note_outlined, + Icons.sticky_note_2_outlined, 'General note', word.note, (s) => word.note = s, @@ -117,24 +117,24 @@ class _WordEditorScreenState extends State { ], ), SamplesEditor('Add a form', word.forms), - for (final u in word.uses) ...[ + for (final d in word.definitions) ...[ ColumnCard( - key: ObjectKey(u), + key: ObjectKey(d), divider: null, children: [ CompactInput( Icons.lightbulb_outlined, - 'Term', - u.term, - (s) => u.term = s, + 'Translation', + d.translation, + (s) => d.translation = s, noEmpty: true, trailing: IconButton( onPressed: () => showDangerDialog( context, () => setState(() { - word.uses.remove(u); + word.definitions.remove(d); }), - 'Delete the use?', + 'Delete the definition?', ), icon: const Icon(Icons.delete_outlined), ), @@ -142,33 +142,33 @@ class _WordEditorScreenState extends State { CompactInput( Icons.label_outlined, 'Aliases', - u.aliases.join(' '), - (s) => u.aliases = s.split(' '), + d.aliases.join(' '), + (s) => d.aliases = s.split(' '), ), CompactInput( Icons.tag_outlined, 'Semantic tags', - u.tags.join(' '), - (s) => u.tags = s.split(' '), + d.tags.join(' '), + (s) => d.tags = s.split(' '), ), CompactInput( - Icons.note_outlined, + Icons.sticky_note_2_outlined, 'Usage note', - u.note, - (s) => u.note = s, + d.note, + (s) => d.note = s, lowercase: false, multiline: true, ), ], ), - SamplesEditor('Add an example', u.examples), + SamplesEditor('Add an example', d.examples), ], Padding( padding: const EdgeInsets.all(8), child: ElevatedButton.icon( onPressed: () => setState(() { - word.uses.add( - Use( + word.definitions.add( + Definition( '', aliases: [], tags: [], @@ -177,7 +177,7 @@ class _WordEditorScreenState extends State { ); }), icon: const Icon(Icons.add_outlined), - label: const Text('Add a use'), + label: const Text('Add a definition'), ), ) ], From ad9f301e1ac6bc811e5ba65e9a1394843c66395b Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 18:51:51 +0300 Subject: [PATCH 30/34] Update indexing --- functions/src/dictionary.ts | 17 +++++++++++------ functions/src/stats.ts | 18 +++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/functions/src/dictionary.ts b/functions/src/dictionary.ts index 6116137b..507ee489 100644 --- a/functions/src/dictionary.ts +++ b/functions/src/dictionary.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import * as functions from "firebase-functions"; import algoliasearch from "algoliasearch"; +import {firestore} from "firebase-admin"; const dictionary = algoliasearch( functions.config().algolia.app, @@ -25,6 +26,7 @@ export const indexDictionary = functions entryID, headword: entry.headword, language: entry.language, + lastUpdated: firestore.Timestamp.now(), forms: [ entry.headword, ...(entry.forms?.map((s: any) => s.text) ?? []), @@ -36,13 +38,16 @@ export const indexDictionary = functions const records = []; - for (const use of entry.uses) { - const record = Object.assign({term: use.term}, base) as any; - if (use.tags?.length || entry.tags?.length) { - record.tags = (use.tags ?? []).concat(entry.tags ?? []); + for (const definition of entry.definitions) { + const record = Object.assign({ + translation: definition.translation, + rand: Math.random(), + }, base) as any; + if (definition.tags?.length || entry.tags?.length) { + record.tags = (definition.tags ?? []).concat(entry.tags ?? []); } - if (use.aliases?.length) { - record.aliases = use.aliases; + if (definition.aliases?.length) { + record.aliases = definition.aliases; } records.push(record); } diff --git a/functions/src/stats.ts b/functions/src/stats.ts index d06f62a7..44a16d4c 100644 --- a/functions/src/stats.ts +++ b/functions/src/stats.ts @@ -9,23 +9,23 @@ const dictionary = algoliasearch( export const collectStats = functions .region("europe-central2") - .pubsub.schedule("every 6 hours") + .pubsub.schedule("every 24 hours") .timeZone("Europe/Moscow") .onRun(async () => { const db = admin.firestore(); const langs = await db - .collection("languages").get() + .collection("languages") + .get() .then((d) => d.docs.map((l) => l.id)); for (const lang of langs) { await db.doc("languages/" + lang).update({ - stats: { - dictionary: await dictionary - .search("", { - facetFilters: ["language:" + lang], - hitsPerPage: 0, - }).then((s) => s.nbHits), - }, + dictionary: await dictionary + .search("", { + facetFilters: ["language:" + lang], + hitsPerPage: 0, + }) + .then((s) => s.nbHits), }); } }); From 7e95f84cb3e196c2f758e4b912070aa822e7d664 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 19:18:34 +0300 Subject: [PATCH 31/34] Update indexing --- functions/src/dictionary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/src/dictionary.ts b/functions/src/dictionary.ts index 507ee489..e152c263 100644 --- a/functions/src/dictionary.ts +++ b/functions/src/dictionary.ts @@ -26,6 +26,7 @@ export const indexDictionary = functions entryID, headword: entry.headword, language: entry.language, + rand: Math.random(), lastUpdated: firestore.Timestamp.now(), forms: [ entry.headword, @@ -41,7 +42,6 @@ export const indexDictionary = functions for (const definition of entry.definitions) { const record = Object.assign({ translation: definition.translation, - rand: Math.random(), }, base) as any; if (definition.tags?.length || entry.tags?.length) { record.tags = (definition.tags ?? []).concat(entry.tags ?? []); From eae5ec02d13f98ccd3f2abd8488ac5535a22012a Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 19:48:04 +0300 Subject: [PATCH 32/34] Update indexing --- functions/src/dictionary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/src/dictionary.ts b/functions/src/dictionary.ts index e152c263..5682f0e0 100644 --- a/functions/src/dictionary.ts +++ b/functions/src/dictionary.ts @@ -27,7 +27,7 @@ export const indexDictionary = functions headword: entry.headword, language: entry.language, rand: Math.random(), - lastUpdated: firestore.Timestamp.now(), + lastUpdated: firestore.Timestamp.now().toDate().valueOf(), forms: [ entry.headword, ...(entry.forms?.map((s: any) => s.text) ?? []), From 26082a79f1a94e22ad3fa836d08e5fbc93f5dd36 Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 19:52:12 +0300 Subject: [PATCH 33/34] Simplify language model fromJson --- lib/models/language.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/models/language.dart b/lib/models/language.dart index 7df54d03..aad1fe3f 100644 --- a/lib/models/language.dart +++ b/lib/models/language.dart @@ -24,8 +24,7 @@ class Language { endonym: json['endonym'] as String, contact: json['contact'] as String?, aliases: json2list(json['aliases']), - location: - json['location'] == null ? null : json['location'] as GeoPoint, + location: json['location'] as GeoPoint?, dictionary: json['dictionary'] as int? ?? 0, ); } From 07f255ec576f572fd5976227f6afee32dc16ba2a Mon Sep 17 00:00:00 2001 From: alkaitagi Date: Mon, 11 Jul 2022 20:08:24 +0300 Subject: [PATCH 34/34] Lazy load languages --- lib/modules/dictionary/dictionary.dart | 4 +- .../dictionary/widgets/search_toolbar.dart | 2 +- lib/modules/home/home.dart | 4 +- .../settings/widgets/editor_mode_card.dart | 24 ++++++++++-- lib/store.dart | 39 ++++--------------- 5 files changed, 33 insertions(+), 40 deletions(-) diff --git a/lib/modules/dictionary/dictionary.dart b/lib/modules/dictionary/dictionary.dart index 2b48e4e2..0c11c220 100644 --- a/lib/modules/dictionary/dictionary.dart +++ b/lib/modules/dictionary/dictionary.dart @@ -29,7 +29,7 @@ class DictionaryScreenState extends State { firstPageKey: 0, ); late final search = SearchController( - GlobalStore.languages.keys, + GlobalStore.languages, algolia.index('dictionary'), paging.refresh, ); @@ -137,7 +137,7 @@ class DictionaryScreenState extends State { leading: IconButton( onPressed: () async { await context.pushRoute(const HomeRoute()); - search.setLanguage('', GlobalStore.languages.keys); + search.setLanguage('', GlobalStore.languages); }, tooltip: 'Home', icon: const Icon(Icons.landscape_outlined), diff --git a/lib/modules/dictionary/widgets/search_toolbar.dart b/lib/modules/dictionary/widgets/search_toolbar.dart index 9ef7a570..21726ce5 100644 --- a/lib/modules/dictionary/widgets/search_toolbar.dart +++ b/lib/modules/dictionary/widgets/search_toolbar.dart @@ -73,7 +73,7 @@ class SearchToolbarState extends State { onTap: () => setLanguage(''), ), OptionItem.divider(), - for (final l in GlobalStore.languages.keys) + for (final l in GlobalStore.languages) OptionItem.tile( Transform.scale( scale: 1.25, diff --git a/lib/modules/home/home.dart b/lib/modules/home/home.dart index 2fb69db6..35e98d16 100644 --- a/lib/modules/home/home.dart +++ b/lib/modules/home/home.dart @@ -51,7 +51,7 @@ class _HomeScreenState extends State { .get() .then((r) => r.docs.map((d) => d.data()).toList()); - selected = GlobalStore.languages.keys + selected = GlobalStore.languages .map((n) => catalogue.firstWhere((l) => l.name == n)) .toSet(); tags = { @@ -134,7 +134,7 @@ class _HomeScreenState extends State { ? null : FloatingActionButton( onPressed: () { - GlobalStore.set(objects: selected); + GlobalStore.set(selected.map((l) => l.name).toList()); context.navigateTo(const RootRoute()); }, child: const Icon(Icons.done_all_outlined), diff --git a/lib/modules/settings/widgets/editor_mode_card.dart b/lib/modules/settings/widgets/editor_mode_card.dart index 40ac2f66..4a787e5d 100644 --- a/lib/modules/settings/widgets/editor_mode_card.dart +++ b/lib/modules/settings/widgets/editor_mode_card.dart @@ -7,6 +7,7 @@ import 'package:bazur/shared/widgets/column_card.dart'; import 'package:bazur/shared/widgets/language_avatar.dart'; import 'package:bazur/shared/widgets/span_icon.dart'; import 'package:bazur/store.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; @@ -20,6 +21,7 @@ class EditorModeCard extends StatefulWidget { class _EditorModeCardState extends State { late final StreamSubscription _authStream; var adminable = []; + List? languages; @override void initState() { @@ -36,6 +38,23 @@ class _EditorModeCardState extends State { } void updateAdminable() async { + if (languages == null) { + languages = []; + for (final l in GlobalStore.languages) { + await FirebaseFirestore.instance + .doc('languages/$l') + .withConverter( + fromFirestore: (snapshot, _) => + Language.fromJson(snapshot.data()!), + toFirestore: (_, __) => {}, + ) + .get() + .then((d) { + final l = d.data(); + if (l != null) languages!.add(l); + }); + } + } adminable = await EditorStore.getAdminable(); setState(() {}); } @@ -65,15 +84,14 @@ class _EditorModeCardState extends State { ], ), ), - if (EditorStore.user != null) + if (EditorStore.user != null && languages != null) Padding( padding: const EdgeInsets.all(8), child: Wrap( spacing: 8, runSpacing: 8, children: [ - for (final l - in GlobalStore.languages.values.whereType()) + for (final l in languages!) InputChip( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, avatar: LanguageAvatar(l.name), diff --git a/lib/store.dart b/lib/store.dart index 7072df53..2e85aa21 100644 --- a/lib/store.dart +++ b/lib/store.dart @@ -1,8 +1,6 @@ import 'package:algolia/algolia.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'models/language.dart'; import 'shared/utils.dart'; late final Algolia algolia; @@ -48,39 +46,16 @@ class EditorStore { } class GlobalStore { - static Map languages = {}; + static var languages = []; - static void set({ - Iterable? names, - Iterable? objects, - }) { - if (objects != null) { - languages = {for (final l in objects) l.name: l}; - } else if (names != null) { - languages = {for (final l in names) l: null}; - } - if (!languages.containsKey(EditorStore.language)) { + static void set(List languages) { + GlobalStore.languages = [...languages]; + if (!languages.contains(EditorStore.language)) { EditorStore.language = null; } - prefs.setStringList('languages', languages.keys.toList()); + prefs.setStringList('languages', languages); } - static void init([List? names]) { - set( - names: names ?? prefs.getStringList('languages') ?? ['aghul'], - ); - for (final l in languages.keys) { - FirebaseFirestore.instance - .doc('languages/$l') - .withConverter( - fromFirestore: (snapshot, _) => Language.fromJson(snapshot.data()!), - toFirestore: (_, __) => {}, - ) - .get() - .then((r) { - final l = r.data(); - if (l != null) languages[l.name] = l; - }); - } - } + static void init([List? names]) => + set(prefs.getStringList('languages') ?? ['aghul']); }