From fe48597b1608a056fd8d723b073d05edf68de0bd Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Fri, 2 Aug 2024 23:33:49 +0300 Subject: [PATCH 01/19] powersync prototype (WIP) --- lib/api_client.dart | 60 ++++++++ lib/app_config.dart | 4 + lib/main.dart | 21 +++ lib/models/schema.dart | 54 +++++++ lib/models/todo_item.dart | 50 +++++++ lib/models/todo_list.dart | 96 +++++++++++++ lib/powersync.dart | 132 ++++++++++++++++++ linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 82 ++++++++++- pubspec.yaml | 3 + 12 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 lib/api_client.dart create mode 100644 lib/app_config.dart create mode 100644 lib/models/schema.dart create mode 100644 lib/models/todo_item.dart create mode 100644 lib/models/todo_list.dart create mode 100644 lib/powersync.dart diff --git a/lib/api_client.dart b/lib/api_client.dart new file mode 100644 index 000000000..d6ff9c40a --- /dev/null +++ b/lib/api_client.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:logging/logging.dart'; + +final log = Logger('powersync-test'); + +class ApiClient { + final String baseUrl; + + ApiClient(this.baseUrl); + + Future> authenticate(String username, String password) async { + final response = await http.post( + Uri.parse('$baseUrl/api/auth/'), + headers: {'Content-Type': 'application/json'}, + body: json.encode({'username': username, 'password': password}), + ); + if (response.statusCode == 200) { + return json.decode(response.body); + } else { + throw Exception('Failed to authenticate'); + } + } + + Future> getToken(String userId) async { + final response = await http.get( + Uri.parse('$baseUrl/api/get_powersync_token/'), + headers: {'Content-Type': 'application/json'}, + ); + if (response.statusCode == 200) { + return json.decode(response.body); + } else { + throw Exception('Failed to fetch token'); + } + } + + Future upsert(Map record) async { + await http.put( + Uri.parse('$baseUrl/api/upload_data/'), + headers: {'Content-Type': 'application/json'}, + body: json.encode(record), + ); + } + + Future update(Map record) async { + await http.patch( + Uri.parse('$baseUrl/api/upload_data/'), + headers: {'Content-Type': 'application/json'}, + body: json.encode(record), + ); + } + + Future delete(Map record) async { + await http.delete( + Uri.parse('$baseUrl/api/upload_data/'), + headers: {'Content-Type': 'application/json'}, + body: json.encode(record), + ); + } +} diff --git a/lib/app_config.dart b/lib/app_config.dart new file mode 100644 index 000000000..e751f6945 --- /dev/null +++ b/lib/app_config.dart @@ -0,0 +1,4 @@ +class AppConfig { + static const String djangoUrl = 'http://192.168.2.223:6061'; + static const String powersyncUrl = 'http://192.168.2.223:8080'; +} diff --git a/lib/main.dart b/lib/main.dart index ac4abfe1a..ba514036f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,10 +16,12 @@ * along with this program. If not, see . */ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/core/locator.dart'; +import 'package:wger/powersync.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/providers/base_provider.dart'; import 'package:wger/providers/body_weight.dart'; @@ -52,15 +54,34 @@ import 'package:wger/screens/workout_plans_screen.dart'; import 'package:wger/theme/theme.dart'; import 'package:wger/widgets/core/about.dart'; import 'package:wger/widgets/core/settings.dart'; +import 'package:logging/logging.dart'; import 'providers/auth.dart'; void main() async { //zx.setLogEnabled(kDebugMode); + Logger.root.level = Level.INFO; + Logger.root.onRecord.listen((record) { + if (kDebugMode) { + print('[${record.loggerName}] ${record.level.name}: ${record.time}: ${record.message}'); + + if (record.error != null) { + print(record.error); + } + if (record.stackTrace != null) { + print(record.stackTrace); + } + } + }); // Needs to be called before runApp WidgetsFlutterBinding.ensureInitialized(); + await openDatabase(); + + final loggedIn = await isLoggedIn(); + print('is logged in $loggedIn'); + // Locator to initialize exerciseDB await ServiceLocator().configure(); // Application diff --git a/lib/models/schema.dart b/lib/models/schema.dart new file mode 100644 index 000000000..b85c5f103 --- /dev/null +++ b/lib/models/schema.dart @@ -0,0 +1,54 @@ +import 'package:powersync/powersync.dart'; + +const todosTable = 'todos'; + +// these are the same ones as in postgres, except for 'id' +Schema schema = const Schema(([ + Table(todosTable, [ + Column.text('list_id'), + Column.text('created_at'), + Column.text('completed_at'), + Column.text('description'), + Column.integer('completed'), + Column.text('created_by'), + Column.text('completed_by'), + ], indexes: [ + // Index to allow efficient lookup within a list + Index('list', [IndexedColumn('list_id')]) + ]), + Table('lists', + [Column.text('created_at'), Column.text('name'), Column.text('owner_id')]) +])); + +// post gres columns: +// todos: +// id | created_at | completed_at | description | completed | created_by | completed_by | list_id +// lists: +// id | created_at | name | owner_id + +// diagnostics app: +/* +new Schema([ + new Table({ + name: 'lists', // same as flutter + columns: [ + new Column({ name: 'created_at', type: ColumnType.TEXT }), + new Column({ name: 'name', type: ColumnType.TEXT }), + new Column({ name: 'owner_id', type: ColumnType.TEXT }) + ] + }), + new Table({ + name: 'todos', // misses completed_at and completed_by, until these actually get populated with something + columns: [ + new Column({ name: 'created_at', type: ColumnType.TEXT }), + new Column({ name: 'description', type: ColumnType.TEXT }), + new Column({ name: 'completed', type: ColumnType.INTEGER }), + new Column({ name: 'created_by', type: ColumnType.TEXT }), + new Column({ name: 'list_id', type: ColumnType.TEXT }) + ] + }) +]) + + Column.text('completed_at'), + Column.text('completed_by'), +*/ diff --git a/lib/models/todo_item.dart b/lib/models/todo_item.dart new file mode 100644 index 000000000..8bd10864d --- /dev/null +++ b/lib/models/todo_item.dart @@ -0,0 +1,50 @@ +import 'package:wger/models/schema.dart'; + +import '../powersync.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; + +/// TodoItem represents a result row of a query on "todos". +/// +/// This class is immutable - methods on this class do not modify the instance +/// directly. Instead, watch or re-query the data to get the updated item. +/// confirm how the watch works. this seems like a weird pattern +class TodoItem { + final String id; + final String description; + final String? photoId; + final bool completed; + + TodoItem( + {required this.id, + required this.description, + required this.completed, + required this.photoId}); + + factory TodoItem.fromRow(sqlite.Row row) { + return TodoItem( + id: row['id'], + description: row['description'], + photoId: row['photo_id'], + completed: row['completed'] == 1); + } + + Future toggle() async { + if (completed) { + await db.execute( + 'UPDATE $todosTable SET completed = FALSE, completed_by = NULL, completed_at = NULL WHERE id = ?', + [id]); + } else { + await db.execute( + 'UPDATE $todosTable SET completed = TRUE, completed_by = ?, completed_at = datetime() WHERE id = ?', + [await getUserId(), id]); + } + } + + Future delete() async { + await db.execute('DELETE FROM $todosTable WHERE id = ?', [id]); + } + + static Future addPhoto(String photoId, String id) async { + await db.execute('UPDATE $todosTable SET photo_id = ? WHERE id = ?', [photoId, id]); + } +} diff --git a/lib/models/todo_list.dart b/lib/models/todo_list.dart new file mode 100644 index 000000000..17e848b2c --- /dev/null +++ b/lib/models/todo_list.dart @@ -0,0 +1,96 @@ +import 'package:powersync/sqlite3.dart' as sqlite; + +import 'todo_item.dart'; +import '../powersync.dart'; + +/// TodoList represents a result row of a query on "lists". +/// +/// This class is immutable - methods on this class do not modify the instance +/// directly. Instead, watch or re-query the data to get the updated list. +class TodoList { + /// List id (UUID). + final String id; + + /// Descriptive name. + final String name; + + /// Number of completed todos in this list. + final int? completedCount; + + /// Number of pending todos in this list. + final int? pendingCount; + + TodoList({required this.id, required this.name, this.completedCount, this.pendingCount}); + + factory TodoList.fromRow(sqlite.Row row) { + return TodoList( + id: row['id'], + name: row['name'], + completedCount: row['completed_count'], + pendingCount: row['pending_count']); + } + + /// Watch all lists. + static Stream> watchLists() { + // This query is automatically re-run when data in "lists" or "todos" is modified. + return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { + return results.map(TodoList.fromRow).toList(growable: false); + }); + } + + /// Watch all lists, with [completedCount] and [pendingCount] populated. + static Stream> watchListsWithStats() { + // This query is automatically re-run when data in "lists" or "todos" is modified. + return db.watch(''' + SELECT + *, + (SELECT count() FROM todos WHERE list_id = lists.id AND completed = TRUE) as completed_count, + (SELECT count() FROM todos WHERE list_id = lists.id AND completed = FALSE) as pending_count + FROM lists + ORDER BY created_at + ''').map((results) { + return results.map(TodoList.fromRow).toList(growable: false); + }); + } + + /// Create a new list + static Future create(String name) async { + final results = await db.execute(''' + INSERT INTO + lists(id, created_at, name, owner_id) + VALUES(uuid(), datetime(), ?, ?) + RETURNING * + ''', [name, await getUserId()]); + return TodoList.fromRow(results.first); + } + + /// Watch items within this list. + Stream> watchItems() { + return db.watch('SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC, id', + parameters: [id]).map((event) { + return event.map(TodoItem.fromRow).toList(growable: false); + }); + } + + /// Delete this list. + Future delete() async { + await db.execute('DELETE FROM lists WHERE id = ?', [id]); + } + + /// Find list item. + static Future find(id) async { + final results = await db.get('SELECT * FROM lists WHERE id = ?', [id]); + return TodoList.fromRow(results); + } + + /// Add a new todo item to this list. + Future add(String description) async { + final results = await db.execute(''' + INSERT INTO + todos(id, created_at, completed, list_id, description, created_by) + VALUES(uuid(), datetime(), FALSE, ?, ?, ?) + RETURNING * + ''', [id, description, await getUserId()]); + return TodoItem.fromRow(results.first); + } +} diff --git a/lib/powersync.dart b/lib/powersync.dart new file mode 100644 index 000000000..0eaa6c7b8 --- /dev/null +++ b/lib/powersync.dart @@ -0,0 +1,132 @@ +// This file performs setup of the PowerSync database +import 'package:logging/logging.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:powersync/powersync.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:wger/api_client.dart'; + +import './app_config.dart'; +import './models/schema.dart'; + +final log = Logger('powersync-django'); + +/// Postgres Response codes that we cannot recover from by retrying. +final List fatalResponseCodes = [ + // Class 22 — Data Exception + // Examples include data type mismatch. + RegExp(r'^22...$'), + // Class 23 — Integrity Constraint Violation. + // Examples include NOT NULL, FOREIGN KEY and UNIQUE violations. + RegExp(r'^23...$'), + // INSUFFICIENT PRIVILEGE - typically a row-level security violation + RegExp(r'^42501$'), +]; + +class DjangoConnector extends PowerSyncBackendConnector { + PowerSyncDatabase db; + + DjangoConnector(this.db); + + final ApiClient apiClient = ApiClient(AppConfig.djangoUrl); + + /// Get a token to authenticate against the PowerSync instance. + @override + Future fetchCredentials() async { + final prefs = await SharedPreferences.getInstance(); + final userId = prefs.getString('id'); + if (userId == null) { + throw Exception('User does not have session'); + } + // Somewhat contrived to illustrate usage, see auth docs here: + // https://docs.powersync.com/usage/installation/authentication-setup/custom + final session = await apiClient.getToken(userId); + return PowerSyncCredentials(endpoint: AppConfig.powersyncUrl, token: session['token']); + } + + // Upload pending changes to Postgres via Django backend + // this is generic. on the django side we inspect the request and do model-specific operations + // would it make sense to do api calls here specific to the relevant model? (e.g. put to a todo-specific endpoint) + @override + Future uploadData(PowerSyncDatabase database) async { + final transaction = await database.getNextCrudTransaction(); + + if (transaction == null) { + return; + } + + try { + for (var op in transaction.crud) { + final record = { + 'table': op.table, + 'data': {'id': op.id, ...?op.opData}, + }; + + switch (op.op) { + case UpdateType.put: + await apiClient.upsert(record); + break; + case UpdateType.patch: + await apiClient.update(record); + break; + case UpdateType.delete: + await apiClient.delete(record); + break; + } + } + await transaction.complete(); + } on Exception catch (e) { + log.severe('Error uploading data', e); + // Error may be retryable - e.g. network error or temporary server error. + // Throwing an error here causes this call to be retried after a delay. + rethrow; + } + } +} + +/// Global reference to the database +late final PowerSyncDatabase db; + +// Hacky flag to ensure the database is only initialized once, better to do this with listeners +bool _dbInitialized = false; + +Future isLoggedIn() async { + final prefs = await SharedPreferences.getInstance(); // Initialize SharedPreferences + final userId = prefs.getString('id'); + return userId != null; +} + +Future getDatabasePath() async { + final dir = await getApplicationSupportDirectory(); + return join(dir.path, 'powersync-demo.db'); +} + +// opens the database and connects if logged in +Future openDatabase() async { + // Open the local database + if (!_dbInitialized) { + db = PowerSyncDatabase(schema: schema, path: await getDatabasePath(), logger: attachedLogger); + await db.initialize(); + _dbInitialized = true; + } + + DjangoConnector? currentConnector; + + if (await isLoggedIn()) { + // If the user is already logged in, connect immediately. + // Otherwise, connect once logged in. + currentConnector = DjangoConnector(db); + db.connect(connector: currentConnector); + } +} + +/// Explicit sign out - clear database and log out. +Future logout() async { + await db.disconnectAndClear(); +} + +/// id of the user currently logged in +Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('id'); +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 9f99dda79..e1098f6ba 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include @@ -14,6 +15,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) powersync_flutter_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "PowersyncFlutterLibsPlugin"); + powersync_flutter_libs_plugin_register_with_registrar(powersync_flutter_libs_registrar); g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 74369f251..296e5b2f2 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux + powersync_flutter_libs sqlite3_flutter_libs url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e5d0d3081..20619c357 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import file_selector_macos import package_info_plus import path_provider_foundation +import powersync_flutter_libs import rive_common import shared_preferences_foundation import sqlite3_flutter_libs @@ -18,6 +19,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + PowersyncFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "PowersyncFlutterLibsPlugin")) RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 7f1ca41d8..cf227c53d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -329,6 +329,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + fetch_api: + dependency: transitive + description: + name: fetch_api + sha256: "97f46c25b480aad74f7cc2ad7ccba2c5c6f08d008e68f95c1077286ce243d0e6" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + fetch_client: + dependency: transitive + description: + name: fetch_client + sha256: "9666ee14536778474072245ed5cba07db81ae8eb5de3b7bf4a2d1e2c49696092" + url: "https://pub.dev" + source: hosted + version: "1.1.2" ffi: dependency: transitive description: @@ -821,7 +837,7 @@ packages: source: hosted version: "1.0.2" logging: - dependency: transitive + dependency: "direct main" description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" @@ -892,6 +908,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.3" + mutex: + dependency: transitive + description: + name: mutex + sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2" + url: "https://pub.dev" + source: hosted + version: "3.1.0" nested: dependency: transitive description: @@ -1060,6 +1084,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + powersync: + dependency: "direct main" + description: + name: powersync + sha256: c6975007493617fdfc5945c3fab24ea2e6999ae300dd4d19d739713a4f2bcd96 + url: "https://pub.dev" + source: hosted + version: "1.6.3" + powersync_flutter_libs: + dependency: transitive + description: + name: powersync_flutter_libs + sha256: "449063aa4956c6be215ea7dfb9cc61255188e82cf7bc3f75621796fcc6615b70" + url: "https://pub.dev" + source: hosted + version: "0.1.0" process: dependency: transitive description: @@ -1233,6 +1273,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqlite3: dependency: transitive description: @@ -1249,6 +1297,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.24" + sqlite3_web: + dependency: transitive + description: + name: sqlite3_web + sha256: "51fec34757577841cc72d79086067e3651c434669d5af557a5c106787198a76f" + url: "https://pub.dev" + source: hosted + version: "0.1.2-wip" + sqlite_async: + dependency: "direct main" + description: + name: sqlite_async + sha256: "79e636c857ed43f6cd5e5be72b36967a29f785daa63ff5b078bd34f74f44cb54" + url: "https://pub.dev" + source: hosted + version: "0.8.1" sqlparser: dependency: transitive description: @@ -1337,6 +1401,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" url_launcher: dependency: "direct main" description: @@ -1401,6 +1473,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" + url: "https://pub.dev" + source: hosted + version: "4.4.2" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3e27b5f71..1ec936fb8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,6 +33,7 @@ dependencies: sdk: flutter android_metadata: ^0.2.1 + powersync: ^1.5.5 collection: ^1.17.0 cupertino_icons: ^1.0.8 equatable: ^2.0.5 @@ -70,6 +71,8 @@ dependencies: freezed_annotation: ^2.4.1 clock: ^1.1.1 flutter_svg_icons: ^0.0.1 + sqlite_async: ^0.8.1 + logging: ^1.2.0 dependency_overrides: intl: ^0.19.0 From 6faba1f188500cc3b3f50dfd314ce52969a20cd2 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sun, 8 Sep 2024 20:57:05 +0200 Subject: [PATCH 02/19] Fetch powersync keys --- Gemfile.lock | 34 +++++++++---------- fastlane/report.xml | 4 +-- lib/api_client.dart | 53 +++++++++++++++++++++++------- lib/app_config.dart | 4 +-- lib/models/muscle.dart | 37 +++++++++++++++++++++ lib/models/schema.dart | 51 +++++++++++++++++++--------- lib/models/todo_item.dart | 25 +++++++------- lib/models/todo_list.dart | 18 ++++++---- lib/powersync.dart | 10 ++---- lib/screens/home_tabs_screen.dart | 31 ++++++++++------- lib/widgets/dashboard/widgets.dart | 6 +++- pubspec.lock | 32 +++++++++--------- pubspec.yaml | 2 +- 13 files changed, 202 insertions(+), 105 deletions(-) create mode 100644 lib/models/muscle.dart diff --git a/Gemfile.lock b/Gemfile.lock index 86dccb02f..b4b74e3ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,25 +5,25 @@ GEM base64 nkf rexml - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.944.0) - aws-sdk-core (3.197.0) + aws-partitions (1.960.0) + aws-sdk-core (3.201.3) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.84.0) - aws-sdk-core (~> 3, >= 3.197.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.3) - aws-sdk-core (~> 3, >= 3.197.0) + aws-sdk-kms (1.88.0) + aws-sdk-core (~> 3, >= 3.201.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.156.0) + aws-sdk-core (~> 3, >= 3.201.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.9.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) @@ -38,7 +38,7 @@ GEM domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.110.0) + excon (0.111.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -60,7 +60,7 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) @@ -68,7 +68,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.220.0) + fastlane (2.222.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -153,9 +153,9 @@ GEM httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.1) + jwt (2.8.2) base64 - mini_magick (4.13.1) + mini_magick (4.13.2) mini_mime (1.1.5) multi_json (1.15.0) multipart-post (2.4.1) @@ -165,7 +165,7 @@ GEM optparse (0.5.0) os (1.1.4) plist (3.7.1) - public_suffix (5.1.0) + public_suffix (6.0.1) rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) diff --git a/fastlane/report.xml b/fastlane/report.xml index 87898c060..31b384403 100644 --- a/fastlane/report.xml +++ b/fastlane/report.xml @@ -5,12 +5,12 @@ - + - + diff --git a/lib/api_client.dart b/lib/api_client.dart index d6ff9c40a..d7b0d1521 100644 --- a/lib/api_client.dart +++ b/lib/api_client.dart @@ -1,42 +1,71 @@ import 'dart:convert'; +import 'dart:io'; + import 'package:http/http.dart' as http; import 'package:logging/logging.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'helpers/consts.dart'; final log = Logger('powersync-test'); class ApiClient { final String baseUrl; - ApiClient(this.baseUrl); + const ApiClient(this.baseUrl); Future> authenticate(String username, String password) async { final response = await http.post( - Uri.parse('$baseUrl/api/auth/'), + Uri.parse('$baseUrl/api/v2/login/'), headers: {'Content-Type': 'application/json'}, body: json.encode({'username': username, 'password': password}), ); if (response.statusCode == 200) { + log.log(Level.ALL, response.body); return json.decode(response.body); - } else { - throw Exception('Failed to authenticate'); } + throw Exception('Failed to authenticate'); } - Future> getToken(String userId) async { + Future> getWgerJWTToken() async { + final response = await http.post( + Uri.parse('$baseUrl/api/v2/token'), + headers: {HttpHeaders.contentTypeHeader: 'application/json'}, + body: json.encode({'username': 'admin', 'password': 'adminadmin'}), + ); + if (response.statusCode == 200) { + log.log(Level.ALL, response.body); + return json.decode(response.body); + } + throw Exception('Failed to fetch token'); + } + + /// Returns a powersync JWT token token + /// + /// Note that at the moment we use the permanent API token for authentication + /// but this should be probably changed to the wger API JWT tokens in the + /// future since they are not permanent and could be easily revoked. + Future> getPowersyncToken() async { + final prefs = await SharedPreferences.getInstance(); + final apiData = json.decode(prefs.getString(PREFS_USER)!); + final response = await http.get( - Uri.parse('$baseUrl/api/get_powersync_token/'), - headers: {'Content-Type': 'application/json'}, + Uri.parse('$baseUrl/api/v2/powersync-token'), + headers: { + HttpHeaders.contentTypeHeader: 'application/json', + HttpHeaders.authorizationHeader: 'Token ${apiData["token"]}', + }, ); if (response.statusCode == 200) { + log.log(Level.ALL, response.body); return json.decode(response.body); - } else { - throw Exception('Failed to fetch token'); } + throw Exception('Failed to fetch token'); } Future upsert(Map record) async { await http.put( - Uri.parse('$baseUrl/api/upload_data/'), + Uri.parse('$baseUrl/api/upload-powersync-data'), headers: {'Content-Type': 'application/json'}, body: json.encode(record), ); @@ -44,7 +73,7 @@ class ApiClient { Future update(Map record) async { await http.patch( - Uri.parse('$baseUrl/api/upload_data/'), + Uri.parse('$baseUrl/api/upload-powersync-data'), headers: {'Content-Type': 'application/json'}, body: json.encode(record), ); @@ -52,7 +81,7 @@ class ApiClient { Future delete(Map record) async { await http.delete( - Uri.parse('$baseUrl/api/upload_data/'), + Uri.parse('$baseUrl/api/v2/upload-powersync-data'), headers: {'Content-Type': 'application/json'}, body: json.encode(record), ); diff --git a/lib/app_config.dart b/lib/app_config.dart index e751f6945..78dc420ad 100644 --- a/lib/app_config.dart +++ b/lib/app_config.dart @@ -1,4 +1,4 @@ class AppConfig { - static const String djangoUrl = 'http://192.168.2.223:6061'; - static const String powersyncUrl = 'http://192.168.2.223:8080'; + static const String djangoUrl = 'http://10.0.2.2:8000'; + static const String powersyncUrl = 'http://10.0.2.2:8080'; } diff --git a/lib/models/muscle.dart b/lib/models/muscle.dart new file mode 100644 index 000000000..b910341f8 --- /dev/null +++ b/lib/models/muscle.dart @@ -0,0 +1,37 @@ +import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/models/schema.dart'; +import 'package:wger/powersync.dart'; + +class Muscle { + final String id; + final String name; + final String nameEn; + final bool isFront; + + const Muscle({ + required this.id, + required this.name, + required this.nameEn, + required this.isFront, + }); + + factory Muscle.fromRow(sqlite.Row row) { + return Muscle( + id: row['id'], + name: row['name'], + nameEn: row['name_en'], + isFront: row['is_front'] == 1, + ); + } + + Future delete() async { + await db.execute('DELETE FROM $musclesTable WHERE id = ?', [id]); + } + + /// Watch all lists. + static Stream> watchMuscles() { + return db.watch('SELECT * FROM muscles ORDER BY id').map((results) { + return results.map(Muscle.fromRow).toList(growable: false); + }); + } +} diff --git a/lib/models/schema.dart b/lib/models/schema.dart index b85c5f103..b4c037847 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -1,24 +1,43 @@ import 'package:powersync/powersync.dart'; const todosTable = 'todos'; +const musclesTable = 'muscles'; // these are the same ones as in postgres, except for 'id' -Schema schema = const Schema(([ - Table(todosTable, [ - Column.text('list_id'), - Column.text('created_at'), - Column.text('completed_at'), - Column.text('description'), - Column.integer('completed'), - Column.text('created_by'), - Column.text('completed_by'), - ], indexes: [ - // Index to allow efficient lookup within a list - Index('list', [IndexedColumn('list_id')]) - ]), - Table('lists', - [Column.text('created_at'), Column.text('name'), Column.text('owner_id')]) -])); +Schema schema = const Schema([ + Table( + todosTable, + [ + Column.text('list_id'), + Column.text('created_at'), + Column.text('completed_at'), + Column.text('description'), + Column.integer('completed'), + Column.text('created_by'), + Column.text('completed_by'), + ], + indexes: [ + // Index to allow efficient lookup within a list + Index('list', [IndexedColumn('list_id')]), + ], + ), + Table( + 'lists', + [ + Column.text('created_at'), + Column.text('name'), + Column.text('owner_id'), + ], + ), + Table( + 'muscles', + [ + Column.text('name'), + Column.text('name_en'), + Column.text('is_front'), + ], + ), +]); // post gres columns: // todos: diff --git a/lib/models/todo_item.dart b/lib/models/todo_item.dart index 8bd10864d..1b6e6e4eb 100644 --- a/lib/models/todo_item.dart +++ b/lib/models/todo_item.dart @@ -1,7 +1,6 @@ -import 'package:wger/models/schema.dart'; - -import '../powersync.dart'; import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/models/schema.dart'; +import 'package:wger/powersync.dart'; /// TodoItem represents a result row of a query on "todos". /// @@ -14,18 +13,20 @@ class TodoItem { final String? photoId; final bool completed; - TodoItem( - {required this.id, - required this.description, - required this.completed, - required this.photoId}); + TodoItem({ + required this.id, + required this.description, + required this.completed, + required this.photoId, + }); factory TodoItem.fromRow(sqlite.Row row) { return TodoItem( - id: row['id'], - description: row['description'], - photoId: row['photo_id'], - completed: row['completed'] == 1); + id: row['id'], + description: row['description'], + photoId: row['photo_id'], + completed: row['completed'] == 1, + ); } Future toggle() async { diff --git a/lib/models/todo_list.dart b/lib/models/todo_list.dart index 17e848b2c..4d965eb4c 100644 --- a/lib/models/todo_list.dart +++ b/lib/models/todo_list.dart @@ -1,7 +1,7 @@ import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/powersync.dart'; import 'todo_item.dart'; -import '../powersync.dart'; /// TodoList represents a result row of a query on "lists". /// @@ -24,10 +24,11 @@ class TodoList { factory TodoList.fromRow(sqlite.Row row) { return TodoList( - id: row['id'], - name: row['name'], - completedCount: row['completed_count'], - pendingCount: row['pending_count']); + id: row['id'], + name: row['name'], + completedCount: row['completed_count'], + pendingCount: row['pending_count'], + ); } /// Watch all lists. @@ -55,12 +56,15 @@ class TodoList { /// Create a new list static Future create(String name) async { - final results = await db.execute(''' + final results = await db.execute( + ''' INSERT INTO lists(id, created_at, name, owner_id) VALUES(uuid(), datetime(), ?, ?) RETURNING * - ''', [name, await getUserId()]); + ''', + [name, await getUserId()], + ); return TodoList.fromRow(results.first); } diff --git a/lib/powersync.dart b/lib/powersync.dart index 0eaa6c7b8..bdfcdfc95 100644 --- a/lib/powersync.dart +++ b/lib/powersync.dart @@ -28,19 +28,15 @@ class DjangoConnector extends PowerSyncBackendConnector { DjangoConnector(this.db); - final ApiClient apiClient = ApiClient(AppConfig.djangoUrl); + final ApiClient apiClient = const ApiClient(AppConfig.djangoUrl); /// Get a token to authenticate against the PowerSync instance. @override Future fetchCredentials() async { - final prefs = await SharedPreferences.getInstance(); - final userId = prefs.getString('id'); - if (userId == null) { - throw Exception('User does not have session'); - } // Somewhat contrived to illustrate usage, see auth docs here: // https://docs.powersync.com/usage/installation/authentication-setup/custom - final session = await apiClient.getToken(userId); + // final wgerSession = await apiClient.getWgerJWTToken(); + final session = await apiClient.getPowersyncToken(); return PowerSyncCredentials(endpoint: AppConfig.powersyncUrl, token: session['token']); } diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 765703be5..96faff819 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -16,13 +16,13 @@ * along with this program. If not, see . */ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:rive/rive.dart'; +import 'package:wger/powersync.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/exercises.dart'; @@ -39,6 +39,7 @@ import 'package:wger/screens/workout_plans_screen.dart'; class HomeTabsScreen extends StatefulWidget { const HomeTabsScreen(); + static const routeName = '/dashboard2'; @override @@ -72,6 +73,12 @@ class _HomeTabsScreenState extends State with SingleTickerProvid /// Load initial data from the server Future _loadEntries() async { + final connector = DjangoConnector(db); + final credentials = await connector.fetchCredentials(); + print('----------'); + print(credentials); + print('----------'); + final authProvider = context.read(); if (!authProvider.dataInit) { @@ -84,7 +91,7 @@ class _HomeTabsScreenState extends State with SingleTickerProvid final userProvider = context.read(); // Base data - log('Loading base data'); + log.log(Level.FINER, Level.FINER, 'Loading base data'); try { await Future.wait([ authProvider.setServerVersion(), @@ -94,12 +101,12 @@ class _HomeTabsScreenState extends State with SingleTickerProvid exercisesProvider.fetchAndSetInitialData(), ]); } catch (e) { - log('Exception loading base data'); - log(e.toString()); + log.log(Level.FINER, 'Exception loading base data'); + log.log(Level.FINER, e.toString()); } // Plans, weight and gallery - log('Loading plans, weight, measurements and gallery'); + log.log(Level.FINER, 'Loading plans, weight, measurements and gallery'); try { await Future.wait([ galleryProvider.fetchAndSetGallery(), @@ -109,24 +116,24 @@ class _HomeTabsScreenState extends State with SingleTickerProvid measurementProvider.fetchAndSetAllCategoriesAndEntries(), ]); } catch (e) { - log('Exception loading plans, weight, measurements and gallery'); - log(e.toString()); + log.log(Level.FINER, 'Exception loading plans, weight, measurements and gallery'); + log.log(Level.FINER, e.toString()); } // Current nutritional plan - log('Loading current nutritional plan'); + log.log(Level.FINER, 'Loading current nutritional plan'); try { if (nutritionPlansProvider.currentPlan != null) { final plan = nutritionPlansProvider.currentPlan!; await nutritionPlansProvider.fetchAndSetPlanFull(plan.id!); } } catch (e) { - log('Exception loading current nutritional plan'); - log(e.toString()); + log.log(Level.FINER, 'Exception loading current nutritional plan'); + log.log(Level.FINER, e.toString()); } // Current workout plan - log('Loading current workout plan'); + log.log(Level.FINER, 'Loading current workout plan'); if (workoutPlansProvider.activePlan != null) { final planId = workoutPlansProvider.activePlan!.id!; await workoutPlansProvider.fetchAndSetWorkoutPlanFull(planId); diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index 855d6f227..65ee1c39c 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -49,6 +49,7 @@ import 'package:wger/widgets/workouts/forms.dart'; class DashboardNutritionWidget extends StatefulWidget { const DashboardNutritionWidget(); + @override _DashboardNutritionWidgetState createState() => _DashboardNutritionWidgetState(); } @@ -149,6 +150,7 @@ class _DashboardNutritionWidgetState extends State { class DashboardWeightWidget extends StatefulWidget { const DashboardWeightWidget(); + @override _DashboardWeightWidgetState createState() => _DashboardWeightWidgetState(); } @@ -235,13 +237,14 @@ class _DashboardWeightWidgetState extends State { class DashboardMeasurementWidget extends StatefulWidget { const DashboardMeasurementWidget(); + @override _DashboardMeasurementWidgetState createState() => _DashboardMeasurementWidgetState(); } class _DashboardMeasurementWidgetState extends State { int _current = 0; - final CarouselController _controller = CarouselController(); + final _controller = CarouselSliderController(); @override Widget build(BuildContext context) { @@ -346,6 +349,7 @@ class _DashboardMeasurementWidgetState extends State class DashboardWorkoutWidget extends StatefulWidget { const DashboardWorkoutWidget(); + @override _DashboardWorkoutWidgetState createState() => _DashboardWorkoutWidgetState(); } diff --git a/pubspec.lock b/pubspec.lock index cf227c53d..3a67c0793 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -173,10 +173,10 @@ packages: dependency: "direct main" description: name: carousel_slider - sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42" + sha256: "7b006ec356205054af5beaef62e2221160ea36b90fb70a35e4deacd49d0349ae" url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "5.0.0" change: dependency: transitive description: @@ -800,18 +800,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -872,18 +872,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: @@ -1032,10 +1032,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -1381,10 +1381,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" timing: dependency: transitive description: @@ -1573,10 +1573,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1ec936fb8..cdda63e0d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,7 +56,7 @@ dependencies: flutter_barcode_scanner: ^2.0.0 video_player: ^2.8.6 flutter_staggered_grid_view: ^0.7.0 - carousel_slider: ^4.2.1 + carousel_slider: ^5.0.0 multi_select_flutter: ^4.1.3 flutter_svg: ^2.0.10+1 fl_chart: ^0.68.0 From cea3ae15a67f5c55ab247e5ace36238f6cf6a4e4 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Tue, 10 Sep 2024 21:08:45 +0300 Subject: [PATCH 03/19] wip --- lib/models/nutrition/log_powersync.dart | 68 +++++++++++++++++++++++++ lib/models/schema.dart | 40 ++++++++++++--- lib/models/todo_item.dart | 51 ------------------- lib/powersync.dart | 20 ++++---- 4 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 lib/models/nutrition/log_powersync.dart delete mode 100644 lib/models/todo_item.dart diff --git a/lib/models/nutrition/log_powersync.dart b/lib/models/nutrition/log_powersync.dart new file mode 100644 index 000000000..258191521 --- /dev/null +++ b/lib/models/nutrition/log_powersync.dart @@ -0,0 +1,68 @@ +import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/models/schema.dart'; + +import '../../powersync.dart'; + +/// TodoItem represents a result row of a query on "todos". +/// +/// This class is immutable - methods on this class do not modify the instance +/// directly. Instead, watch or re-query the data to get the updated item. +/// confirm how the watch works. this seems like a weird pattern +class TodoItem { + final String id; + final String description; + final String? photoId; + final bool completed; + + TodoItem( + {required this.id, + required this.description, + required this.completed, + required this.photoId}); + + factory TodoItem.fromRow(sqlite.Row row) { + return TodoItem( + id: row['id'], + description: row['description'], + photoId: row['photo_id'], + completed: row['completed'] == 1); + } + + Future toggle() async { + if (completed) { + await db.execute( + 'UPDATE $logItemsTable SET completed = FALSE, completed_by = NULL, completed_at = NULL WHERE id = ?', + [id]); + } else { + await db.execute( + 'UPDATE $logItemsTable SET completed = TRUE, completed_by = ?, completed_at = datetime() WHERE id = ?', + [await getUserId(), id]); + } + } + + Future delete() async { + await db.execute('DELETE FROM $logItemsTable WHERE id = ?', [id]); + } + + static Future addPhoto(String photoId, String id) async { + await db.execute('UPDATE $logItemsTable SET photo_id = ? WHERE id = ?', [photoId, id]); + } +} +/* + static Stream> watchLists() { + // This query is automatically re-run when data in "lists" or "todos" is modified. + return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { + return results.map(TodoList.fromRow).toList(growable: false); + }); + } + + static Future create(String name) async { + final results = await db.execute(''' + INSERT INTO + lists(id, created_at, name, owner_id) + VALUES(uuid(), datetime(), ?, ?) + RETURNING * + ''', [name, await getUserId()]); + return TodoList.fromRow(results.first); + } + */ \ No newline at end of file diff --git a/lib/models/schema.dart b/lib/models/schema.dart index b4c037847..f8f68ea20 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -2,9 +2,41 @@ import 'package:powersync/powersync.dart'; const todosTable = 'todos'; const musclesTable = 'muscles'; +const logItemsTable = 'nutrition_logitem'; -// these are the same ones as in postgres, except for 'id' +/* +Postgres: +wger@localhost:wger> \d nutrition_logitem; ++----------------+--------------------------+--------------------------------------------+ +| Column | Type | Modifiers | +|----------------+--------------------------+--------------------------------------------| +| id | integer | not null generated by default as identity | +| datetime | timestamp with time zone | not null | +| comment | text | | +| amount | numeric(6,2) | not null | +| ingredient_id | integer | not null | +| plan_id | integer | not null | +| weight_unit_id | integer | | +| meal_id | integer | | ++----------------+--------------------------+--------------------------------------------+ +*/ +// these are the same ones as in postgres, except for 'id', because that is implied Schema schema = const Schema([ + Table( + logItemsTable, + [ + Column.text('datetime'), + Column.text('comment'), + Column.integer('amount'), + Column.integer('ingredient_id'), + Column.integer('plan_id'), + Column.integer('weight_unit_id'), + Column.integer('meal_id'), + ], + indexes: [ + // Index('plan', [IndexedColumn('plan_id')]) + ], + ), Table( todosTable, [ @@ -31,11 +63,7 @@ Schema schema = const Schema([ ), Table( 'muscles', - [ - Column.text('name'), - Column.text('name_en'), - Column.text('is_front'), - ], + [Column.text('name'), Column.text('name_en'), Column.text('is_front')], ), ]); diff --git a/lib/models/todo_item.dart b/lib/models/todo_item.dart deleted file mode 100644 index 1b6e6e4eb..000000000 --- a/lib/models/todo_item.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:powersync/sqlite3.dart' as sqlite; -import 'package:wger/models/schema.dart'; -import 'package:wger/powersync.dart'; - -/// TodoItem represents a result row of a query on "todos". -/// -/// This class is immutable - methods on this class do not modify the instance -/// directly. Instead, watch or re-query the data to get the updated item. -/// confirm how the watch works. this seems like a weird pattern -class TodoItem { - final String id; - final String description; - final String? photoId; - final bool completed; - - TodoItem({ - required this.id, - required this.description, - required this.completed, - required this.photoId, - }); - - factory TodoItem.fromRow(sqlite.Row row) { - return TodoItem( - id: row['id'], - description: row['description'], - photoId: row['photo_id'], - completed: row['completed'] == 1, - ); - } - - Future toggle() async { - if (completed) { - await db.execute( - 'UPDATE $todosTable SET completed = FALSE, completed_by = NULL, completed_at = NULL WHERE id = ?', - [id]); - } else { - await db.execute( - 'UPDATE $todosTable SET completed = TRUE, completed_by = ?, completed_at = datetime() WHERE id = ?', - [await getUserId(), id]); - } - } - - Future delete() async { - await db.execute('DELETE FROM $todosTable WHERE id = ?', [id]); - } - - static Future addPhoto(String photoId, String id) async { - await db.execute('UPDATE $todosTable SET photo_id = ? WHERE id = ?', [photoId, id]); - } -} diff --git a/lib/powersync.dart b/lib/powersync.dart index bdfcdfc95..df457aa58 100644 --- a/lib/powersync.dart +++ b/lib/powersync.dart @@ -52,7 +52,7 @@ class DjangoConnector extends PowerSyncBackendConnector { } try { - for (var op in transaction.crud) { + for (final op in transaction.crud) { final record = { 'table': op.table, 'data': {'id': op.id, ...?op.opData}, @@ -86,9 +86,14 @@ late final PowerSyncDatabase db; // Hacky flag to ensure the database is only initialized once, better to do this with listeners bool _dbInitialized = false; +/// id of the user currently logged in +Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('id'); +} + Future isLoggedIn() async { - final prefs = await SharedPreferences.getInstance(); // Initialize SharedPreferences - final userId = prefs.getString('id'); + final userId = await getUserId(); return userId != null; } @@ -113,6 +118,9 @@ Future openDatabase() async { // Otherwise, connect once logged in. currentConnector = DjangoConnector(db); db.connect(connector: currentConnector); + + // TODO: should we respond to login state changing? like here: + // https://www.powersync.com/blog/flutter-tutorial-building-an-offline-first-chat-app-with-supabase-and-powersync#implement-auth-methods } } @@ -120,9 +128,3 @@ Future openDatabase() async { Future logout() async { await db.disconnectAndClear(); } - -/// id of the user currently logged in -Future getUserId() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('id'); -} From c3b213e2d0139f2c03d9bc3c65836a45da788045 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Tue, 10 Sep 2024 21:47:58 +0300 Subject: [PATCH 04/19] auth debug --- lib/api_client.dart | 3 ++- lib/app_config.dart | 4 ++-- lib/main.dart | 1 + lib/screens/home_tabs_screen.dart | 12 ++++++++---- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/api_client.dart b/lib/api_client.dart index d7b0d1521..0dcbefd40 100644 --- a/lib/api_client.dart +++ b/lib/api_client.dart @@ -48,7 +48,7 @@ class ApiClient { Future> getPowersyncToken() async { final prefs = await SharedPreferences.getInstance(); final apiData = json.decode(prefs.getString(PREFS_USER)!); - + print('posting our token "${apiData["token"]}" to $baseUrl/api/v2/powersync-token'); final response = await http.get( Uri.parse('$baseUrl/api/v2/powersync-token'), headers: { @@ -56,6 +56,7 @@ class ApiClient { HttpHeaders.authorizationHeader: 'Token ${apiData["token"]}', }, ); + print('response: status ${response.statusCode}, body ${response.body}'); if (response.statusCode == 200) { log.log(Level.ALL, response.body); return json.decode(response.body); diff --git a/lib/app_config.dart b/lib/app_config.dart index 78dc420ad..49d9477bf 100644 --- a/lib/app_config.dart +++ b/lib/app_config.dart @@ -1,4 +1,4 @@ class AppConfig { - static const String djangoUrl = 'http://10.0.2.2:8000'; - static const String powersyncUrl = 'http://10.0.2.2:8080'; + static const String djangoUrl = 'http://192.168.1.195:8000'; + static const String powersyncUrl = 'http://192.168.1.195:8080'; } diff --git a/lib/main.dart b/lib/main.dart index ba514036f..15430ed14 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -84,6 +84,7 @@ void main() async { // Locator to initialize exerciseDB await ServiceLocator().configure(); + print('running myapp'); // Application runApp(const MyApp()); } diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 96faff819..127bfe136 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -74,10 +74,14 @@ class _HomeTabsScreenState extends State with SingleTickerProvid /// Load initial data from the server Future _loadEntries() async { final connector = DjangoConnector(db); - final credentials = await connector.fetchCredentials(); - print('----------'); - print(credentials); - print('----------'); + try { + final credentials = await connector.fetchCredentials(); + print('----------'); + print(credentials); + print('----------'); + } catch (e) { + print('fail' + e.toString()); + } final authProvider = context.read(); From a1295e564918c5545983d2e1467b29baea093dbf Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Tue, 10 Sep 2024 22:06:24 +0300 Subject: [PATCH 05/19] wip logitem --- lib/models/nutrition/log.dart | 43 ++++++++++++++++ lib/models/nutrition/log_powersync.dart | 68 ------------------------- 2 files changed, 43 insertions(+), 68 deletions(-) delete mode 100644 lib/models/nutrition/log_powersync.dart diff --git a/lib/models/nutrition/log.dart b/lib/models/nutrition/log.dart index ac4aa8457..8799d6ce0 100644 --- a/lib/models/nutrition/log.dart +++ b/lib/models/nutrition/log.dart @@ -17,11 +17,14 @@ */ import 'package:json_annotation/json_annotation.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; import 'package:wger/helpers/json.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/ingredient_weight_unit.dart'; import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; +import 'package:wger/models/schema.dart'; +import 'package:wger/powersync.dart'; part 'log.g.dart'; @@ -75,6 +78,19 @@ class Log { amount = mealItem.amount; } + factory Log.fromRow(sqlite.Row row) { + return Log( + id: row['id'], + mealId: row['meal_id'], + ingredientId: row['ingredient_id'], + weightUnitId: row['weight_unit_id'], + amount: row['amount'], + planId: row['plan_id'], + datetime: row['datetime'], + comment: row['comment'], + ); + } + // Boilerplate factory Log.fromJson(Map json) => _$LogFromJson(json); @@ -89,4 +105,31 @@ class Log { return ingredient.nutritionalValues / (100 / weight); } +/* + Future delete() async { + await db.execute('DELETE FROM $logItemsTable WHERE id = ?', [id]); + } + + static Future addPhoto(String photoId, String id) async { + await db.execute('UPDATE $logItemsTable SET photo_id = ? WHERE id = ?', [photoId, id]); + } +} + + static Stream> watchLists() { + // This query is automatically re-run when data in "lists" or "todos" is modified. + return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { + return results.map(TodoList.fromRow).toList(growable: false); + }); + } + + static Future create(String name) async { + final results = await db.execute(''' + INSERT INTO + lists(id, created_at, name, owner_id) + VALUES(uuid(), datetime(), ?, ?) + RETURNING * + ''', [name, await getUserId()]); + return TodoList.fromRow(results.first); + } + */ } diff --git a/lib/models/nutrition/log_powersync.dart b/lib/models/nutrition/log_powersync.dart deleted file mode 100644 index 258191521..000000000 --- a/lib/models/nutrition/log_powersync.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:powersync/sqlite3.dart' as sqlite; -import 'package:wger/models/schema.dart'; - -import '../../powersync.dart'; - -/// TodoItem represents a result row of a query on "todos". -/// -/// This class is immutable - methods on this class do not modify the instance -/// directly. Instead, watch or re-query the data to get the updated item. -/// confirm how the watch works. this seems like a weird pattern -class TodoItem { - final String id; - final String description; - final String? photoId; - final bool completed; - - TodoItem( - {required this.id, - required this.description, - required this.completed, - required this.photoId}); - - factory TodoItem.fromRow(sqlite.Row row) { - return TodoItem( - id: row['id'], - description: row['description'], - photoId: row['photo_id'], - completed: row['completed'] == 1); - } - - Future toggle() async { - if (completed) { - await db.execute( - 'UPDATE $logItemsTable SET completed = FALSE, completed_by = NULL, completed_at = NULL WHERE id = ?', - [id]); - } else { - await db.execute( - 'UPDATE $logItemsTable SET completed = TRUE, completed_by = ?, completed_at = datetime() WHERE id = ?', - [await getUserId(), id]); - } - } - - Future delete() async { - await db.execute('DELETE FROM $logItemsTable WHERE id = ?', [id]); - } - - static Future addPhoto(String photoId, String id) async { - await db.execute('UPDATE $logItemsTable SET photo_id = ? WHERE id = ?', [photoId, id]); - } -} -/* - static Stream> watchLists() { - // This query is automatically re-run when data in "lists" or "todos" is modified. - return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { - return results.map(TodoList.fromRow).toList(growable: false); - }); - } - - static Future create(String name) async { - final results = await db.execute(''' - INSERT INTO - lists(id, created_at, name, owner_id) - VALUES(uuid(), datetime(), ?, ?) - RETURNING * - ''', [name, await getUserId()]); - return TodoList.fromRow(results.first); - } - */ \ No newline at end of file From 9332bb1bb2a317d44268be7b9808b8bee1610550 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Fri, 13 Sep 2024 21:48:21 +0300 Subject: [PATCH 06/19] wip --- lib/api_client.dart | 2 +- lib/app_config.dart | 1 + lib/main.dart | 2 +- lib/screens/home_tabs_screen.dart | 7 ++++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/api_client.dart b/lib/api_client.dart index 0dcbefd40..b882cd1d0 100644 --- a/lib/api_client.dart +++ b/lib/api_client.dart @@ -31,7 +31,7 @@ class ApiClient { final response = await http.post( Uri.parse('$baseUrl/api/v2/token'), headers: {HttpHeaders.contentTypeHeader: 'application/json'}, - body: json.encode({'username': 'admin', 'password': 'adminadmin'}), + body: json.encode({'username': 'admin', 'password': 'adminadmin'}), // FIXME ); if (response.statusCode == 200) { log.log(Level.ALL, response.body); diff --git a/lib/app_config.dart b/lib/app_config.dart index 49d9477bf..58d87fdd0 100644 --- a/lib/app_config.dart +++ b/lib/app_config.dart @@ -1,4 +1,5 @@ class AppConfig { + // TODO: base this off of base URL returned by auth provider static const String djangoUrl = 'http://192.168.1.195:8000'; static const String powersyncUrl = 'http://192.168.1.195:8080'; } diff --git a/lib/main.dart b/lib/main.dart index 15430ed14..f58bc5498 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -80,7 +80,7 @@ void main() async { await openDatabase(); final loggedIn = await isLoggedIn(); - print('is logged in $loggedIn'); + print('main(): is logged in $loggedIn'); // Locator to initialize exerciseDB await ServiceLocator().configure(); diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 127bfe136..11ca031b7 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -53,6 +53,10 @@ class _HomeTabsScreenState extends State with SingleTickerProvid @override void initState() { super.initState(); + + final authProvider = context.read(); + print('auth provider says surverurl is ${authProvider.serverUrl}'); + // Loading data here, since the build method can be called more than once _initialData = _loadEntries(); } @@ -82,7 +86,8 @@ class _HomeTabsScreenState extends State with SingleTickerProvid } catch (e) { print('fail' + e.toString()); } - + final loggedIn = await isLoggedIn(); + print('_loadEntries(): is logged in $loggedIn'); final authProvider = context.read(); if (!authProvider.dataInit) { From bb679754b9eea0cfdd0fe24d88b2c0195f5e872e Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Sat, 14 Sep 2024 13:22:10 +0300 Subject: [PATCH 07/19] fix/refactor/simplify powersync auth setup --- lib/api_client.dart | 26 -------------------------- lib/main.dart | 5 ----- lib/powersync.dart | 21 ++++----------------- lib/providers/auth.dart | 1 + lib/screens/home_tabs_screen.dart | 20 ++++++++++++++------ 5 files changed, 19 insertions(+), 54 deletions(-) diff --git a/lib/api_client.dart b/lib/api_client.dart index b882cd1d0..bde351c5c 100644 --- a/lib/api_client.dart +++ b/lib/api_client.dart @@ -14,32 +14,6 @@ class ApiClient { const ApiClient(this.baseUrl); - Future> authenticate(String username, String password) async { - final response = await http.post( - Uri.parse('$baseUrl/api/v2/login/'), - headers: {'Content-Type': 'application/json'}, - body: json.encode({'username': username, 'password': password}), - ); - if (response.statusCode == 200) { - log.log(Level.ALL, response.body); - return json.decode(response.body); - } - throw Exception('Failed to authenticate'); - } - - Future> getWgerJWTToken() async { - final response = await http.post( - Uri.parse('$baseUrl/api/v2/token'), - headers: {HttpHeaders.contentTypeHeader: 'application/json'}, - body: json.encode({'username': 'admin', 'password': 'adminadmin'}), // FIXME - ); - if (response.statusCode == 200) { - log.log(Level.ALL, response.body); - return json.decode(response.body); - } - throw Exception('Failed to fetch token'); - } - /// Returns a powersync JWT token token /// /// Note that at the moment we use the permanent API token for authentication diff --git a/lib/main.dart b/lib/main.dart index f58bc5498..a7827d918 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -77,11 +77,6 @@ void main() async { // Needs to be called before runApp WidgetsFlutterBinding.ensureInitialized(); - await openDatabase(); - - final loggedIn = await isLoggedIn(); - print('main(): is logged in $loggedIn'); - // Locator to initialize exerciseDB await ServiceLocator().configure(); print('running myapp'); diff --git a/lib/powersync.dart b/lib/powersync.dart index df457aa58..5bb7e1b9c 100644 --- a/lib/powersync.dart +++ b/lib/powersync.dart @@ -3,7 +3,6 @@ import 'package:logging/logging.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:powersync/powersync.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:wger/api_client.dart'; import './app_config.dart'; @@ -37,6 +36,7 @@ class DjangoConnector extends PowerSyncBackendConnector { // https://docs.powersync.com/usage/installation/authentication-setup/custom // final wgerSession = await apiClient.getWgerJWTToken(); final session = await apiClient.getPowersyncToken(); + // note: we don't set userId and expires property here. not sure if needed return PowerSyncCredentials(endpoint: AppConfig.powersyncUrl, token: session['token']); } @@ -86,24 +86,13 @@ late final PowerSyncDatabase db; // Hacky flag to ensure the database is only initialized once, better to do this with listeners bool _dbInitialized = false; -/// id of the user currently logged in -Future getUserId() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('id'); -} - -Future isLoggedIn() async { - final userId = await getUserId(); - return userId != null; -} - Future getDatabasePath() async { final dir = await getApplicationSupportDirectory(); return join(dir.path, 'powersync-demo.db'); } // opens the database and connects if logged in -Future openDatabase() async { +Future openDatabase(bool connect) async { // Open the local database if (!_dbInitialized) { db = PowerSyncDatabase(schema: schema, path: await getDatabasePath(), logger: attachedLogger); @@ -111,12 +100,10 @@ Future openDatabase() async { _dbInitialized = true; } - DjangoConnector? currentConnector; - - if (await isLoggedIn()) { + if (connect) { // If the user is already logged in, connect immediately. // Otherwise, connect once logged in. - currentConnector = DjangoConnector(db); + final currentConnector = DjangoConnector(db); db.connect(connector: currentConnector); // TODO: should we respond to login state changing? like here: diff --git a/lib/providers/auth.dart b/lib/providers/auth.dart index 61e25617d..fa07f0d9f 100644 --- a/lib/providers/auth.dart +++ b/lib/providers/auth.dart @@ -182,6 +182,7 @@ class AuthProvider with ChangeNotifier { } // Log user in + // should we update the backend to just include a powersync token also? token = responseData['token']; notifyListeners(); diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 11ca031b7..8c0f96709 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -54,8 +54,8 @@ class _HomeTabsScreenState extends State with SingleTickerProvid void initState() { super.initState(); - final authProvider = context.read(); - print('auth provider says surverurl is ${authProvider.serverUrl}'); + // do we need to await this? or if it's async, how do we handle failures? + _setupPowersync(); // Loading data here, since the build method can be called more than once _initialData = _loadEntries(); @@ -75,19 +75,27 @@ class _HomeTabsScreenState extends State with SingleTickerProvid const GalleryScreen(), ]; - /// Load initial data from the server - Future _loadEntries() async { + Future _setupPowersync() async { + final authProvider = context.read(); + print('auth provider says surverurl is ${authProvider.serverUrl}'); + await openDatabase(false); + final connector = DjangoConnector(db); try { + // TODO: should we cache these credentials? that's what their demo does? + // we could maybe get the initial token from the /api/v2/login call final credentials = await connector.fetchCredentials(); print('----------'); print(credentials); print('----------'); + await openDatabase(true); } catch (e) { print('fail' + e.toString()); } - final loggedIn = await isLoggedIn(); - print('_loadEntries(): is logged in $loggedIn'); + } + + /// Load initial data from the server + Future _loadEntries() async { final authProvider = context.read(); if (!authProvider.dataInit) { From 770ea64a23767ab6df6d90693b602db22faf1f11 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Sun, 15 Sep 2024 12:25:54 +0300 Subject: [PATCH 08/19] fix tablename --- lib/models/muscle.dart | 2 +- lib/models/schema.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/models/muscle.dart b/lib/models/muscle.dart index b910341f8..d76238fe5 100644 --- a/lib/models/muscle.dart +++ b/lib/models/muscle.dart @@ -30,7 +30,7 @@ class Muscle { /// Watch all lists. static Stream> watchMuscles() { - return db.watch('SELECT * FROM muscles ORDER BY id').map((results) { + return db.watch('SELECT * FROM $musclesTable ORDER BY id').map((results) { return results.map(Muscle.fromRow).toList(growable: false); }); } diff --git a/lib/models/schema.dart b/lib/models/schema.dart index f8f68ea20..6bc8e8083 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -1,7 +1,7 @@ import 'package:powersync/powersync.dart'; const todosTable = 'todos'; -const musclesTable = 'muscles'; +const musclesTable = 'exercises_muscle'; const logItemsTable = 'nutrition_logitem'; /* @@ -62,7 +62,7 @@ Schema schema = const Schema([ ], ), Table( - 'muscles', + 'exercises_muscle', [Column.text('name'), Column.text('name_en'), Column.text('is_front')], ), ]); From 4fef0b052d37107ac6c74acb3d675b7be4c54629 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Sun, 15 Sep 2024 12:26:33 +0300 Subject: [PATCH 09/19] figure out django/powersync url's from authProvider --- lib/app_config.dart | 5 ----- lib/powersync.dart | 17 ++++++++++------- lib/screens/home_tabs_screen.dart | 10 ++++++---- 3 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 lib/app_config.dart diff --git a/lib/app_config.dart b/lib/app_config.dart deleted file mode 100644 index 58d87fdd0..000000000 --- a/lib/app_config.dart +++ /dev/null @@ -1,5 +0,0 @@ -class AppConfig { - // TODO: base this off of base URL returned by auth provider - static const String djangoUrl = 'http://192.168.1.195:8000'; - static const String powersyncUrl = 'http://192.168.1.195:8080'; -} diff --git a/lib/powersync.dart b/lib/powersync.dart index 5bb7e1b9c..4532ae0f7 100644 --- a/lib/powersync.dart +++ b/lib/powersync.dart @@ -5,7 +5,6 @@ import 'package:path_provider/path_provider.dart'; import 'package:powersync/powersync.dart'; import 'package:wger/api_client.dart'; -import './app_config.dart'; import './models/schema.dart'; final log = Logger('powersync-django'); @@ -24,10 +23,13 @@ final List fatalResponseCodes = [ class DjangoConnector extends PowerSyncBackendConnector { PowerSyncDatabase db; + String baseUrl; + String powersyncUrl; + late ApiClient apiClient; - DjangoConnector(this.db); - - final ApiClient apiClient = const ApiClient(AppConfig.djangoUrl); + DjangoConnector(this.db, this.baseUrl, this.powersyncUrl) { + apiClient = ApiClient(baseUrl); + } /// Get a token to authenticate against the PowerSync instance. @override @@ -37,7 +39,7 @@ class DjangoConnector extends PowerSyncBackendConnector { // final wgerSession = await apiClient.getWgerJWTToken(); final session = await apiClient.getPowersyncToken(); // note: we don't set userId and expires property here. not sure if needed - return PowerSyncCredentials(endpoint: AppConfig.powersyncUrl, token: session['token']); + return PowerSyncCredentials(endpoint: this.powersyncUrl, token: session['token']); } // Upload pending changes to Postgres via Django backend @@ -92,7 +94,7 @@ Future getDatabasePath() async { } // opens the database and connects if logged in -Future openDatabase(bool connect) async { +Future openDatabase(bool connect, String baseUrl, String powersyncUrl) async { // Open the local database if (!_dbInitialized) { db = PowerSyncDatabase(schema: schema, path: await getDatabasePath(), logger: attachedLogger); @@ -103,7 +105,8 @@ Future openDatabase(bool connect) async { if (connect) { // If the user is already logged in, connect immediately. // Otherwise, connect once logged in. - final currentConnector = DjangoConnector(db); + + final currentConnector = DjangoConnector(db, baseUrl, powersyncUrl); db.connect(connector: currentConnector); // TODO: should we respond to login state changing? like here: diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 8c0f96709..b5a29b05a 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -77,10 +77,12 @@ class _HomeTabsScreenState extends State with SingleTickerProvid Future _setupPowersync() async { final authProvider = context.read(); - print('auth provider says surverurl is ${authProvider.serverUrl}'); - await openDatabase(false); + final baseUrl = authProvider.serverUrl!; + final powerSyncUrl = baseUrl.replaceAll(':8000', ':8080'); - final connector = DjangoConnector(db); + await openDatabase(false, baseUrl, powerSyncUrl); + + final connector = DjangoConnector(db, baseUrl, powerSyncUrl); try { // TODO: should we cache these credentials? that's what their demo does? // we could maybe get the initial token from the /api/v2/login call @@ -88,7 +90,7 @@ class _HomeTabsScreenState extends State with SingleTickerProvid print('----------'); print(credentials); print('----------'); - await openDatabase(true); + await openDatabase(true, baseUrl, powerSyncUrl); } catch (e) { print('fail' + e.toString()); } From e02bf6b31fffc0accd838b14870dfe85b072b264 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Sun, 15 Sep 2024 19:37:21 +0300 Subject: [PATCH 10/19] powersync based muscles --- lib/models/muscle.dart | 5 +- lib/models/schema.dart | 112 ++++++++++-------------------- lib/providers/nutrition.dart | 3 + lib/screens/dashboard.dart | 47 +++++++++++++ lib/screens/home_tabs_screen.dart | 6 +- 5 files changed, 92 insertions(+), 81 deletions(-) diff --git a/lib/models/muscle.dart b/lib/models/muscle.dart index d76238fe5..1b5b9788b 100644 --- a/lib/models/muscle.dart +++ b/lib/models/muscle.dart @@ -25,12 +25,13 @@ class Muscle { } Future delete() async { - await db.execute('DELETE FROM $musclesTable WHERE id = ?', [id]); + await db.execute('DELETE FROM $tableMuscles WHERE id = ?', [id]); } /// Watch all lists. static Stream> watchMuscles() { - return db.watch('SELECT * FROM $musclesTable ORDER BY id').map((results) { + return db.watch('SELECT * FROM $tableMuscles ORDER BY id').map((results) { + print('watchMuscles triggered' + results.toString()); return results.map(Muscle.fromRow).toList(growable: false); }); } diff --git a/lib/models/schema.dart b/lib/models/schema.dart index 6bc8e8083..b964c4c61 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -1,29 +1,33 @@ import 'package:powersync/powersync.dart'; -const todosTable = 'todos'; -const musclesTable = 'exercises_muscle'; -const logItemsTable = 'nutrition_logitem'; +const tableMuscles = 'exercises_muscle'; +const tableLogItems = 'nutrition_logitem'; +const tableNutritionPlans = 'nutrition_nutritionplan'; +const tableMeals = 'nutrition_meal'; +const tableMealItems = 'nutrition_mealitem'; -/* -Postgres: -wger@localhost:wger> \d nutrition_logitem; -+----------------+--------------------------+--------------------------------------------+ -| Column | Type | Modifiers | -|----------------+--------------------------+--------------------------------------------| -| id | integer | not null generated by default as identity | -| datetime | timestamp with time zone | not null | -| comment | text | | -| amount | numeric(6,2) | not null | -| ingredient_id | integer | not null | -| plan_id | integer | not null | -| weight_unit_id | integer | | -| meal_id | integer | | -+----------------+--------------------------+--------------------------------------------+ -*/ -// these are the same ones as in postgres, except for 'id', because that is implied Schema schema = const Schema([ Table( - logItemsTable, + tableMuscles, + [Column.text('name'), Column.text('name_en'), Column.text('is_front')], + ), + Table( + tableNutritionPlans, + [ + Column.text('creation_date'), + Column.text('description'), + Column.integer('has_goal_calories'), + Column.integer('user_id'), + Column.integer('only_logging'), + Column.integer('goal_carbohydrates'), + Column.integer('goal_energy'), + Column.integer('goal_fat'), + Column.integer('goal_protein'), + Column.integer('goal_fiber'), + ], + ), + Table( + tableLogItems, [ Column.text('datetime'), Column.text('comment'), @@ -31,71 +35,29 @@ Schema schema = const Schema([ Column.integer('ingredient_id'), Column.integer('plan_id'), Column.integer('weight_unit_id'), - Column.integer('meal_id'), + Column.integer('meal_id'), // optional ], indexes: [ // Index('plan', [IndexedColumn('plan_id')]) ], ), Table( - todosTable, + tableMeals, [ - Column.text('list_id'), - Column.text('created_at'), - Column.text('completed_at'), - Column.text('description'), - Column.integer('completed'), - Column.text('created_by'), - Column.text('completed_by'), - ], - indexes: [ - // Index to allow efficient lookup within a list - Index('list', [IndexedColumn('list_id')]), + Column.integer('order'), + Column.text('time'), + Column.integer('plan_id'), + Column.text('name'), ], ), Table( - 'lists', + tableMealItems, [ - Column.text('created_at'), - Column.text('name'), - Column.text('owner_id'), + Column.integer('order'), + Column.integer('amount'), + Column.integer('ingredient_id'), + Column.integer('meal_id'), + Column.integer('weight_unit_id'), ], ), - Table( - 'exercises_muscle', - [Column.text('name'), Column.text('name_en'), Column.text('is_front')], - ), ]); - -// post gres columns: -// todos: -// id | created_at | completed_at | description | completed | created_by | completed_by | list_id -// lists: -// id | created_at | name | owner_id - -// diagnostics app: -/* -new Schema([ - new Table({ - name: 'lists', // same as flutter - columns: [ - new Column({ name: 'created_at', type: ColumnType.TEXT }), - new Column({ name: 'name', type: ColumnType.TEXT }), - new Column({ name: 'owner_id', type: ColumnType.TEXT }) - ] - }), - new Table({ - name: 'todos', // misses completed_at and completed_by, until these actually get populated with something - columns: [ - new Column({ name: 'created_at', type: ColumnType.TEXT }), - new Column({ name: 'description', type: ColumnType.TEXT }), - new Column({ name: 'completed', type: ColumnType.INTEGER }), - new Column({ name: 'created_by', type: ColumnType.TEXT }), - new Column({ name: 'list_id', type: ColumnType.TEXT }) - ] - }) -]) - - Column.text('completed_at'), - Column.text('completed_by'), -*/ diff --git a/lib/providers/nutrition.dart b/lib/providers/nutrition.dart index 9c88eee72..6a5e11c6d 100644 --- a/lib/providers/nutrition.dart +++ b/lib/providers/nutrition.dart @@ -125,6 +125,9 @@ class NutritionPlansProvider with ChangeNotifier { } /// Fetches a plan fully, i.e. with all corresponding child objects + /// + + Future fetchAndSetPlanFull(int planId) async { NutritionalPlan plan; try { diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 02d524f3c..43a0fbde6 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -16,8 +16,11 @@ * along with this program. If not, see . */ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/models/muscle.dart'; import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/dashboard/calendar.dart'; import 'package:wger/widgets/dashboard/widgets.dart'; @@ -39,6 +42,7 @@ class _DashboardScreenState extends State { padding: EdgeInsets.all(10), child: Column( children: [ + DashboardMuscleWidget(), DashboardWorkoutWidget(), DashboardNutritionWidget(), DashboardWeightWidget(), @@ -50,3 +54,46 @@ class _DashboardScreenState extends State { ); } } + +class DashboardMuscleWidget extends StatefulWidget { + const DashboardMuscleWidget({super.key}); + + @override + _DashboardMuscleWidgetState createState() => _DashboardMuscleWidgetState(); +} + +class _DashboardMuscleWidgetState extends State { + List _data = []; + StreamSubscription? _subscription; + + _DashboardMuscleWidgetState(); + + @override + void initState() { + super.initState(); + final stream = Muscle.watchMuscles(); + _subscription = stream.listen((data) { + if (!context.mounted) { + return; + } + setState(() { + _data = data; + }); + }); + } + + @override + void dispose() { + _subscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.brown, + child: Column( + children: [Text('muscles'), ..._data.map((e) => Text(e.name)).toList()], + )); + } +} diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index b5a29b05a..0d026ede5 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -87,12 +87,10 @@ class _HomeTabsScreenState extends State with SingleTickerProvid // TODO: should we cache these credentials? that's what their demo does? // we could maybe get the initial token from the /api/v2/login call final credentials = await connector.fetchCredentials(); - print('----------'); - print(credentials); - print('----------'); + print('fetched credentials' + credentials.toString()); await openDatabase(true, baseUrl, powerSyncUrl); } catch (e) { - print('fail' + e.toString()); + print('failed to fetchCredentials()' + e.toString()); } } From 96b164f612edfdc4a6a1391042979fbb4a7f54f1 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Sun, 15 Sep 2024 19:37:44 +0300 Subject: [PATCH 11/19] powersync based nutrition --- lib/models/muscle.dart | 1 - lib/models/nutrition/log.dart | 19 ++- lib/models/nutrition/meal.dart | 22 +++ lib/models/nutrition/nutritional_plan.dart | 113 +++++++++++++ lib/models/todo_list.dart | 100 ------------ lib/providers/nutrition.dart | 53 +------ lib/screens/home_tabs_screen.dart | 15 +- lib/screens/nutritional_plan_screen.dart | 6 +- lib/widgets/dashboard/widgets.dart | 22 ++- .../nutrition/nutritional_plans_list.dart | 148 +++++++++--------- 10 files changed, 245 insertions(+), 254 deletions(-) delete mode 100644 lib/models/todo_list.dart diff --git a/lib/models/muscle.dart b/lib/models/muscle.dart index 1b5b9788b..2c86437e5 100644 --- a/lib/models/muscle.dart +++ b/lib/models/muscle.dart @@ -31,7 +31,6 @@ class Muscle { /// Watch all lists. static Stream> watchMuscles() { return db.watch('SELECT * FROM $tableMuscles ORDER BY id').map((results) { - print('watchMuscles triggered' + results.toString()); return results.map(Muscle.fromRow).toList(growable: false); }); } diff --git a/lib/models/nutrition/log.dart b/lib/models/nutrition/log.dart index 8799d6ce0..5b56fe8b1 100644 --- a/lib/models/nutrition/log.dart +++ b/lib/models/nutrition/log.dart @@ -25,6 +25,7 @@ import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; import 'package:wger/models/schema.dart'; import 'package:wger/powersync.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; part 'log.g.dart'; @@ -80,13 +81,13 @@ class Log { factory Log.fromRow(sqlite.Row row) { return Log( - id: row['id'], - mealId: row['meal_id'], - ingredientId: row['ingredient_id'], - weightUnitId: row['weight_unit_id'], + id: int.parse(row['id']), + mealId: int.parse(row['meal_id']), + ingredientId: int.parse(row['ingredient_id']), + weightUnitId: int.parse(row['weight_unit_id']), amount: row['amount'], - planId: row['plan_id'], - datetime: row['datetime'], + planId: int.parse(row['plan_id']), + datetime: DateTime.parse(row['datetime']), comment: row['comment'], ); } @@ -105,6 +106,12 @@ class Log { return ingredient.nutritionalValues / (100 / weight); } + + static Future> readByPlanId(int planId) async { + final results = await db.getAll('SELECT * FROM $tableLogItems WHERE plan_id = ?', [planId]); + return results.map((r) => Log.fromRow(r)).toList(); + } + /* Future delete() async { await db.execute('DELETE FROM $logItemsTable WHERE id = ?', [id]); diff --git a/lib/models/nutrition/meal.dart b/lib/models/nutrition/meal.dart index ecb3ca3f5..ea7080f8c 100644 --- a/lib/models/nutrition/meal.dart +++ b/lib/models/nutrition/meal.dart @@ -24,6 +24,9 @@ import 'package:wger/helpers/misc.dart'; import 'package:wger/models/nutrition/log.dart'; import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/models/schema.dart'; +import 'package:wger/powersync.dart'; part 'meal.g.dart'; @@ -87,6 +90,15 @@ class Meal { // Boilerplate factory Meal.fromJson(Map json) => _$MealFromJson(json); + factory Meal.fromRow(sqlite.Row row) { + return Meal( + id: int.parse(row['id']), + plan: int.parse(row['plan']), + time: stringToTime(row['time']), + name: row['name'], + ); + } + Map toJson() => _$MealToJson(this); Meal copyWith({ @@ -106,4 +118,14 @@ class Meal { diaryEntries: diaryEntries ?? this.diaryEntries, ); } + + static Future read(int id) async { + final results = await db.get('SELECT * FROM $tableMeals WHERE id = ?', [id]); + return Meal.fromRow(results); + } + + static Future> readByPlanId(int planId) async { + final results = await db.getAll('SELECT * FROM $tableMeals WHERE plan_id = ?', [planId]); + return results.map((r) => Meal.fromRow(r)).toList(); + } } diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index a0f678397..fe635aa19 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -20,6 +20,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/models/nutrition/log.dart'; @@ -27,6 +28,8 @@ import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_goals.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; +import 'package:wger/models/schema.dart'; +import 'package:wger/powersync.dart'; part 'nutritional_plan.g.dart'; @@ -82,6 +85,55 @@ class NutritionalPlan { this.diaryEntries = diaryEntries ?? []; } + factory NutritionalPlan.fromRow(sqlite.Row row) { + return NutritionalPlan( + id: int.parse(row['id']), + description: row['description'], + creationDate: DateTime.parse(row['creation_date']), + onlyLogging: row['only_logging'] == 1, + goalEnergy: row['goal_energy'], + goalProtein: row['goal_protein'], + goalCarbohydrates: row['goal_carbohydrates'], + goalFat: row['goal_fat'], + goalFiber: row['goal_fiber'], + ); + } + + NutritionalPlan copyWith({ + int? id, + String? description, + DateTime? creationDate, + bool? onlyLogging, + num? goalEnergy, + num? goalProtein, + num? goalCarbohydrates, + num? goalFat, + num? goalFiber, + List? meals, + List? diaryEntries, + }) { + return NutritionalPlan( + id: id ?? this.id, + description: description ?? this.description, + creationDate: creationDate ?? this.creationDate, + onlyLogging: onlyLogging ?? this.onlyLogging, + goalEnergy: goalEnergy ?? this.goalEnergy, + goalProtein: goalProtein ?? this.goalProtein, + goalCarbohydrates: goalCarbohydrates ?? this.goalCarbohydrates, + goalFat: goalFat ?? this.goalFat, + goalFiber: goalFiber ?? this.goalFiber, + meals: meals ?? this.meals, + diaryEntries: diaryEntries ?? this.diaryEntries, + ); + } + + Future loadChildren() async { + return copyWith( + diaryEntries: await Log.readByPlanId(id!), + meals: await Meal.readByPlanId(id!), + ); + } + NutritionalPlan.empty() { creationDate = DateTime.now(); description = ''; @@ -246,4 +298,65 @@ class NutritionalPlan { diaryEntries: diaryEntries.where((e) => e.mealId == null).toList(), ); } + + static Future read(int id) async { + final row = await db.get('SELECT * FROM $tableNutritionPlans WHERE id = ?', [id]); + return NutritionalPlan.fromRow(row).loadChildren(); + } + +// this is a bit complicated. +// what we need at the end of the day, is a stream of List, where +// a new value is emitted any time a plan is changed. But the plan is not just the plan record +// we need to load data for Logs and Meals corresponding to the plan also. +// so our options are: +// 1) db.watch with a select query on plans; and extra dart code to load the logs/meals stuff, +// but this only triggers for updates on the plans table, and misses logs/meals updates +// 2) db.watch with a huge join query across all tables from which we need info, +// so we have all the data in our resultset to create the datastructures with, but: +// - this creates long rows with lots of duplicated data (e.g. all the plan data) for every row +// which would only differ for e.g. the meal or the log item +// - it would probably get a bit messy to parse the resultset into the datastructures +// 3) the best of both worlds: load the data we need in dart at runtime, but explicitly +// trigger our code execution when *any* of the relevant tables changes +// + static Stream> watchNutritionPlans() { + return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { + final data = await db.getAll('SELECT * FROM $tableNutritionPlans ORDER BY creation_date'); + final futures = Future.wait(data.map((row) => NutritionalPlan.fromRow(row).loadChildren())); + return (await futures).toList(growable: false); + }); + } + + static Stream watchNutritionPlan(int id) { + return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { + final row = await db.get('SELECT * FROM $tableNutritionPlans WHERE id = ?', [id]); + return NutritionalPlan.fromRow(row).loadChildren(); + }); + } + + static Stream watchNutritionPlanLast() { + return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { + final row = + await db.get('SELECT * FROM $tableNutritionPlans ORDER BY creation_date DESC LIMIT 1'); + return NutritionalPlan.fromRow(row).loadChildren(); + }); + } +/* + static Stream> watchNutritionPlan(int id) { + return db + .watch('SELECT * FROM $tableNutritionPlans WHERE id = ?', parameters: [id]).map((results) { + return results.map(NutritionalPlan.fromRow).toList(growable: false); + }); + } + + static Stream> watchNutritionPlans() { + return db.watch('SELECT * FROM $tableNutritionPlans ORDER BY creation_date').map((results) { + return results.map(NutritionalPlan.fromRow).toList(growable: false); + }); + } + */ + + Future delete() async { + await db.execute('DELETE FROM $tableNutritionPlans WHERE id = ?', [id]); + } } diff --git a/lib/models/todo_list.dart b/lib/models/todo_list.dart deleted file mode 100644 index 4d965eb4c..000000000 --- a/lib/models/todo_list.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:powersync/sqlite3.dart' as sqlite; -import 'package:wger/powersync.dart'; - -import 'todo_item.dart'; - -/// TodoList represents a result row of a query on "lists". -/// -/// This class is immutable - methods on this class do not modify the instance -/// directly. Instead, watch or re-query the data to get the updated list. -class TodoList { - /// List id (UUID). - final String id; - - /// Descriptive name. - final String name; - - /// Number of completed todos in this list. - final int? completedCount; - - /// Number of pending todos in this list. - final int? pendingCount; - - TodoList({required this.id, required this.name, this.completedCount, this.pendingCount}); - - factory TodoList.fromRow(sqlite.Row row) { - return TodoList( - id: row['id'], - name: row['name'], - completedCount: row['completed_count'], - pendingCount: row['pending_count'], - ); - } - - /// Watch all lists. - static Stream> watchLists() { - // This query is automatically re-run when data in "lists" or "todos" is modified. - return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { - return results.map(TodoList.fromRow).toList(growable: false); - }); - } - - /// Watch all lists, with [completedCount] and [pendingCount] populated. - static Stream> watchListsWithStats() { - // This query is automatically re-run when data in "lists" or "todos" is modified. - return db.watch(''' - SELECT - *, - (SELECT count() FROM todos WHERE list_id = lists.id AND completed = TRUE) as completed_count, - (SELECT count() FROM todos WHERE list_id = lists.id AND completed = FALSE) as pending_count - FROM lists - ORDER BY created_at - ''').map((results) { - return results.map(TodoList.fromRow).toList(growable: false); - }); - } - - /// Create a new list - static Future create(String name) async { - final results = await db.execute( - ''' - INSERT INTO - lists(id, created_at, name, owner_id) - VALUES(uuid(), datetime(), ?, ?) - RETURNING * - ''', - [name, await getUserId()], - ); - return TodoList.fromRow(results.first); - } - - /// Watch items within this list. - Stream> watchItems() { - return db.watch('SELECT * FROM todos WHERE list_id = ? ORDER BY created_at DESC, id', - parameters: [id]).map((event) { - return event.map(TodoItem.fromRow).toList(growable: false); - }); - } - - /// Delete this list. - Future delete() async { - await db.execute('DELETE FROM lists WHERE id = ?', [id]); - } - - /// Find list item. - static Future find(id) async { - final results = await db.get('SELECT * FROM lists WHERE id = ?', [id]); - return TodoList.fromRow(results); - } - - /// Add a new todo item to this list. - Future add(String description) async { - final results = await db.execute(''' - INSERT INTO - todos(id, created_at, completed, list_id, description, created_by) - VALUES(uuid(), datetime(), FALSE, ?, ?, ?) - RETURNING * - ''', [id, description, await getUserId()]); - return TodoItem.fromRow(results.first); - } -} diff --git a/lib/providers/nutrition.dart b/lib/providers/nutrition.dart index 6a5e11c6d..cda549ef0 100644 --- a/lib/providers/nutrition.dart +++ b/lib/providers/nutrition.dart @@ -88,59 +88,10 @@ class NutritionPlansProvider with ChangeNotifier { return null; } - /// Fetches and sets all plans sparsely, i.e. only with the data on the plan - /// object itself and no child attributes - Future fetchAndSetAllPlansSparse() async { - final data = await baseProvider.fetchPaginated( - baseProvider.makeUrl(_nutritionalPlansPath, query: {'limit': '1000'}), - ); - _plans = []; - for (final planData in data) { - final plan = NutritionalPlan.fromJson(planData); - _plans.add(plan); - _plans.sort((a, b) => b.creationDate.compareTo(a.creationDate)); - } - notifyListeners(); - } - - /// Fetches and sets all plans fully, i.e. with all corresponding child objects - Future fetchAndSetAllPlansFull() async { - final data = await baseProvider.fetchPaginated(baseProvider.makeUrl(_nutritionalPlansPath)); - await Future.wait(data.map((e) => fetchAndSetPlanFull(e['id'])).toList()); - } - - /// Fetches and sets the given nutritional plan - /// - /// This method only loads the data on the nutritional plan object itself, - /// no meals, etc. - Future fetchAndSetPlanSparse(int planId) async { - final url = baseProvider.makeUrl(_nutritionalPlansPath, id: planId); - final planData = await baseProvider.fetch(url); - final plan = NutritionalPlan.fromJson(planData); - _plans.add(plan); - _plans.sort((a, b) => b.creationDate.compareTo(a.creationDate)); - - notifyListeners(); - return plan; - } - /// Fetches a plan fully, i.e. with all corresponding child objects /// - - +/* Future fetchAndSetPlanFull(int planId) async { - NutritionalPlan plan; - try { - plan = findById(planId); - } on NoSuchEntryException { - // TODO: remove this useless call, because we will fetch all details below - plan = await fetchAndSetPlanSparse(planId); - } - - // Plan - final url = baseProvider.makeUrl(_nutritionalPlansInfoPath, id: planId); - final fullPlanData = await baseProvider.fetch(url); - // Meals final List meals = []; for (final mealData in fullPlanData['meals']) { @@ -173,7 +124,9 @@ class NutritionPlansProvider with ChangeNotifier { // ... and done notifyListeners(); return plan; + } + */ Future addPlan(NutritionalPlan planData) async { final data = await baseProvider.post( diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 0d026ede5..a2b79217b 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -123,11 +123,10 @@ class _HomeTabsScreenState extends State with SingleTickerProvid } // Plans, weight and gallery - log.log(Level.FINER, 'Loading plans, weight, measurements and gallery'); + log.log(Level.FINER, 'Loading workouts, weight, measurements and gallery'); try { await Future.wait([ galleryProvider.fetchAndSetGallery(), - nutritionPlansProvider.fetchAndSetAllPlansSparse(), workoutPlansProvider.fetchAndSetAllPlansSparse(), weightProvider.fetchAndSetEntries(), measurementProvider.fetchAndSetAllCategoriesAndEntries(), @@ -137,18 +136,6 @@ class _HomeTabsScreenState extends State with SingleTickerProvid log.log(Level.FINER, e.toString()); } - // Current nutritional plan - log.log(Level.FINER, 'Loading current nutritional plan'); - try { - if (nutritionPlansProvider.currentPlan != null) { - final plan = nutritionPlansProvider.currentPlan!; - await nutritionPlansProvider.fetchAndSetPlanFull(plan.id!); - } - } catch (e) { - log.log(Level.FINER, 'Exception loading current nutritional plan'); - log.log(Level.FINER, e.toString()); - } - // Current workout plan log.log(Level.FINER, 'Loading current workout plan'); if (workoutPlansProvider.activePlan != null) { diff --git a/lib/screens/nutritional_plan_screen.dart b/lib/screens/nutritional_plan_screen.dart index bb13be6cd..5459276f8 100644 --- a/lib/screens/nutritional_plan_screen.dart +++ b/lib/screens/nutritional_plan_screen.dart @@ -36,10 +36,6 @@ class NutritionalPlanScreen extends StatelessWidget { const NutritionalPlanScreen(); static const routeName = '/nutritional-plan-detail'; - Future _loadFullPlan(BuildContext context, int planId) { - return Provider.of(context, listen: false).fetchAndSetPlanFull(planId); - } - @override Widget build(BuildContext context) { const appBarForeground = Colors.white; @@ -158,7 +154,7 @@ class NutritionalPlanScreen extends StatelessWidget { ), ), FutureBuilder( - future: _loadFullPlan(context, nutritionalPlan.id!), + future: NutritionalPlan.read(nutritionalPlan.id!), builder: (context, AsyncSnapshot snapshot) => snapshot.connectionState == ConnectionState.waiting ? SliverList( diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index 65ee1c39c..b49ee5100 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import 'dart:async'; + import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -56,13 +58,29 @@ class DashboardNutritionWidget extends StatefulWidget { class _DashboardNutritionWidgetState extends State { NutritionalPlan? _plan; + StreamSubscription? _subscription; + bool _hasContent = false; @override void initState() { super.initState(); - _plan = Provider.of(context, listen: false).currentPlan; - _hasContent = _plan != null; + final stream = NutritionalPlan.watchNutritionPlanLast(); + _subscription = stream.listen((plan) { + if (!context.mounted) { + return; + } + setState(() { + _plan = plan; + _hasContent = _plan != null; + }); + }); + } + + @override + void dispose() { + _subscription?.cancel(); + super.dispose(); } @override diff --git a/lib/widgets/nutrition/nutritional_plans_list.dart b/lib/widgets/nutrition/nutritional_plans_list.dart index d1a6a48e0..15beca293 100644 --- a/lib/widgets/nutrition/nutritional_plans_list.dart +++ b/lib/widgets/nutrition/nutritional_plans_list.dart @@ -30,87 +30,83 @@ class NutritionalPlansList extends StatelessWidget { @override Widget build(BuildContext context) { - return RefreshIndicator( - onRefresh: () => _nutritionProvider.fetchAndSetAllPlansSparse(), - child: _nutritionProvider.items.isEmpty - ? const TextPrompt() - : ListView.builder( - padding: const EdgeInsets.all(10.0), - itemCount: _nutritionProvider.items.length, - itemBuilder: (context, index) { - final currentPlan = _nutritionProvider.items[index]; - return Card( - child: ListTile( - onTap: () { - Navigator.of(context).pushNamed( - NutritionalPlanScreen.routeName, - arguments: currentPlan, - ); - }, - title: Text(currentPlan.getLabel(context)), - subtitle: Text( - DateFormat.yMd( - Localizations.localeOf(context).languageCode, - ).format(currentPlan.creationDate), - ), - trailing: Row(mainAxisSize: MainAxisSize.min, children: [ - const VerticalDivider(), - IconButton( - icon: const Icon(Icons.delete), - tooltip: AppLocalizations.of(context).delete, - onPressed: () async { - // Delete the plan from DB - await showDialog( - context: context, - builder: (BuildContext contextDialog) { - return AlertDialog( - content: Text( - AppLocalizations.of(context) - .confirmDelete(currentPlan.description), + return _nutritionProvider.items.isEmpty + ? const TextPrompt() + : ListView.builder( + padding: const EdgeInsets.all(10.0), + itemCount: _nutritionProvider.items.length, + itemBuilder: (context, index) { + final currentPlan = _nutritionProvider.items[index]; + return Card( + child: ListTile( + onTap: () { + Navigator.of(context).pushNamed( + NutritionalPlanScreen.routeName, + arguments: currentPlan, + ); + }, + title: Text(currentPlan.getLabel(context)), + subtitle: Text( + DateFormat.yMd( + Localizations.localeOf(context).languageCode, + ).format(currentPlan.creationDate), + ), + trailing: Row(mainAxisSize: MainAxisSize.min, children: [ + const VerticalDivider(), + IconButton( + icon: const Icon(Icons.delete), + tooltip: AppLocalizations.of(context).delete, + onPressed: () async { + // Delete the plan from DB + await showDialog( + context: context, + builder: (BuildContext contextDialog) { + return AlertDialog( + content: Text( + AppLocalizations.of(context).confirmDelete(currentPlan.description), + ), + actions: [ + TextButton( + child: Text( + MaterialLocalizations.of(context).cancelButtonLabel, + ), + onPressed: () => Navigator.of(contextDialog).pop(), ), - actions: [ - TextButton( - child: Text( - MaterialLocalizations.of(context).cancelButtonLabel, + TextButton( + child: Text( + AppLocalizations.of(context).delete, + style: TextStyle( + color: Theme.of(context).colorScheme.error, ), - onPressed: () => Navigator.of(contextDialog).pop(), ), - TextButton( - child: Text( - AppLocalizations.of(context).delete, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - ), - onPressed: () { - // Confirmed, delete the plan - _nutritionProvider.deletePlan(currentPlan.id!); + onPressed: () { + // Confirmed, delete the plan + _nutritionProvider.deletePlan(currentPlan.id!); - // Close the popup - Navigator.of(contextDialog).pop(); + // Close the popup + Navigator.of(contextDialog).pop(); - // and inform the user - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, - ), + // and inform the user + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, ), - ); - }, - ), - ], - ); - }, - ); - }, - ), - ]), - ), - ); - }, - ), - ); + ), + ); + }, + ), + ], + ); + }, + ); + }, + ), + ]), + ), + ); + }, + ); } } From ea60e18f18f2ccb20bda0b9466cb704c9361960b Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Wed, 18 Sep 2024 10:33:06 +0300 Subject: [PATCH 12/19] WIP fixes --- lib/models/nutrition/log.dart | 44 ++++++++++++++++------------------ lib/models/nutrition/meal.dart | 6 +++-- lib/models/schema.dart | 15 ++++++++++++ lib/providers/nutrition.dart | 36 ++-------------------------- 4 files changed, 42 insertions(+), 59 deletions(-) diff --git a/lib/models/nutrition/log.dart b/lib/models/nutrition/log.dart index 5b56fe8b1..98a7952d9 100644 --- a/lib/models/nutrition/log.dart +++ b/lib/models/nutrition/log.dart @@ -25,7 +25,7 @@ import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; import 'package:wger/models/schema.dart'; import 'package:wger/powersync.dart'; -import 'package:powersync/sqlite3.dart' as sqlite; +import 'package:wger/providers/nutrition.dart'; part 'log.g.dart'; @@ -82,11 +82,11 @@ class Log { factory Log.fromRow(sqlite.Row row) { return Log( id: int.parse(row['id']), - mealId: int.parse(row['meal_id']), - ingredientId: int.parse(row['ingredient_id']), - weightUnitId: int.parse(row['weight_unit_id']), + mealId: row['meal_id'], + ingredientId: row['ingredient_id'], + weightUnitId: row['weight_unit_id'], amount: row['amount'], - planId: int.parse(row['plan_id']), + planId: row['plan_id'], datetime: DateTime.parse(row['datetime']), comment: row['comment'], ); @@ -109,7 +109,22 @@ class Log { static Future> readByPlanId(int planId) async { final results = await db.getAll('SELECT * FROM $tableLogItems WHERE plan_id = ?', [planId]); - return results.map((r) => Log.fromRow(r)).toList(); + return results.map((r) { + final log = Log.fromRow(r); + // TODO: + // need to find a way to set ingredients. since we don't use powersync for it, we need to fetch + // but this needs a context, therofere this needs a context, and all callers do, so we should probably + // move all that stuff into the nutritionprovider, so we keep context out of the models + // however, still unsolved: + // mealItem stuff then? + // nutrition image + // nutrition_ingredientcategory + // nutrition_ingredientweightunit + // nutrition_weightunit; + // nutrition_mealitem + log.ingredient = Provider.of(context, listen: false).fetchIngredient(id), + return log; + }).toList(); } /* @@ -121,22 +136,5 @@ class Log { await db.execute('UPDATE $logItemsTable SET photo_id = ? WHERE id = ?', [photoId, id]); } } - - static Stream> watchLists() { - // This query is automatically re-run when data in "lists" or "todos" is modified. - return db.watch('SELECT * FROM lists ORDER BY created_at, id').map((results) { - return results.map(TodoList.fromRow).toList(growable: false); - }); - } - - static Future create(String name) async { - final results = await db.execute(''' - INSERT INTO - lists(id, created_at, name, owner_id) - VALUES(uuid(), datetime(), ?, ?) - RETURNING * - ''', [name, await getUserId()]); - return TodoList.fromRow(results.first); - } */ } diff --git a/lib/models/nutrition/meal.dart b/lib/models/nutrition/meal.dart index ea7080f8c..76128b09b 100644 --- a/lib/models/nutrition/meal.dart +++ b/lib/models/nutrition/meal.dart @@ -18,13 +18,13 @@ import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/helpers/misc.dart'; import 'package:wger/models/nutrition/log.dart'; import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; -import 'package:powersync/sqlite3.dart' as sqlite; import 'package:wger/models/schema.dart'; import 'package:wger/powersync.dart'; @@ -93,7 +93,7 @@ class Meal { factory Meal.fromRow(sqlite.Row row) { return Meal( id: int.parse(row['id']), - plan: int.parse(row['plan']), + plan: row['plan'], time: stringToTime(row['time']), name: row['name'], ); @@ -125,7 +125,9 @@ class Meal { } static Future> readByPlanId(int planId) async { + print('Meal.readByPlanId: SELECT * FROM $tableMeals WHERE plan_id = $planId'); final results = await db.getAll('SELECT * FROM $tableMeals WHERE plan_id = ?', [planId]); + print(results.rows.length); return results.map((r) => Meal.fromRow(r)).toList(); } } diff --git a/lib/models/schema.dart b/lib/models/schema.dart index b964c4c61..0b5b72c76 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -1,5 +1,20 @@ import 'package:powersync/powersync.dart'; +/* nutrition tables in postgres: +| public | nutrition_image | table> +| public | nutrition_ingredient | table> * # millions of ingredients +| public | nutrition_ingredientcategory | table> +| public | nutrition_ingredientweightunit | table> +| public | nutrition_logitem | table> * OK +| public | nutrition_meal | table> * OK +| public | nutrition_mealitem | table> * +| public | nutrition_nutritionplan | table> * OK +| public | nutrition_weightunit | table> + +assumptions: nutrition_ingredientcategory, nutrition_weightunit, nutrition_ingredientweightunit globals? +*/ + +// User,NutritionPlan,Meal,LogItem,MealItem,Ingredient const tableMuscles = 'exercises_muscle'; const tableLogItems = 'nutrition_logitem'; const tableNutritionPlans = 'nutrition_nutritionplan'; diff --git a/lib/providers/nutrition.dart b/lib/providers/nutrition.dart index cda549ef0..0bda89434 100644 --- a/lib/providers/nutrition.dart +++ b/lib/providers/nutrition.dart @@ -34,6 +34,7 @@ import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/base_provider.dart'; class NutritionPlansProvider with ChangeNotifier { + // TODO: should be able to delete many of these paths and their corresponding code static const _nutritionalPlansPath = 'nutritionplan'; static const _nutritionalPlansInfoPath = 'nutritionplaninfo'; static const _mealPath = 'meal'; @@ -88,44 +89,11 @@ class NutritionPlansProvider with ChangeNotifier { return null; } - /// Fetches a plan fully, i.e. with all corresponding child objects - /// /* - Future fetchAndSetPlanFull(int planId) async { - // Meals - final List meals = []; - for (final mealData in fullPlanData['meals']) { - final List mealItems = []; - final meal = Meal.fromJson(mealData); - - // TODO: we should add these ingredients to the ingredient cache - for (final mealItemData in mealData['meal_items']) { - final mealItem = MealItem.fromJson(mealItemData); - - final ingredient = Ingredient.fromJson(mealItemData['ingredient_obj']); - if (mealItemData['image'] != null) { - final image = IngredientImage.fromJson(mealItemData['image']); +TODO implement: ingredient.image = image; - } mealItem.ingredient = ingredient; - mealItems.add(mealItem); - } - meal.mealItems = mealItems; - meals.add(meal); - } - plan.meals = meals; - - // Logs - await fetchAndSetLogs(plan); - for (final meal in meals) { - meal.diaryEntries = plan.diaryEntries.where((e) => e.mealId == meal.id).toList(); - } - // ... and done - notifyListeners(); - return plan; - - } */ Future addPlan(NutritionalPlan planData) async { From 51ec089d76b30e34a5a35f13ecd5a33a2d7704dc Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Wed, 16 Oct 2024 17:55:15 +0300 Subject: [PATCH 13/19] progress reading data, using ivm tables --- lib/models/nutrition/log.dart | 22 +- lib/models/nutrition/meal.dart | 16 +- lib/models/nutrition/meal_item.dart | 18 ++ lib/models/nutrition/nutritional_plan.dart | 6 +- lib/models/schema.dart | 6 +- lib/providers/nutrition.dart | 61 ++++ lib/screens/dashboard.dart | 1 - lib/screens/nutritional_diary_screen.dart | 39 ++- lib/screens/nutritional_plan_screen.dart | 284 ++++++++++-------- lib/screens/nutritional_plans_screen.dart | 2 +- lib/widgets/dashboard/widgets.dart | 5 +- lib/widgets/nutrition/forms.dart | 2 +- .../nutrition/nutritional_diary_table.dart | 2 +- .../nutrition/nutritional_plans_list.dart | 53 +++- test/nutrition/nutrition_provider_test.dart | 2 +- 15 files changed, 344 insertions(+), 175 deletions(-) diff --git a/lib/models/nutrition/log.dart b/lib/models/nutrition/log.dart index 98a7952d9..0d3a61a66 100644 --- a/lib/models/nutrition/log.dart +++ b/lib/models/nutrition/log.dart @@ -107,24 +107,14 @@ class Log { return ingredient.nutritionalValues / (100 / weight); } + static Future> readByMealId(int mealId) async { + final results = await db.getAll('SELECT * FROM $tableLogItems WHERE meal_id = ?', [mealId]); + return results.map((r) => Log.fromRow(r)).toList(); + } + static Future> readByPlanId(int planId) async { final results = await db.getAll('SELECT * FROM $tableLogItems WHERE plan_id = ?', [planId]); - return results.map((r) { - final log = Log.fromRow(r); - // TODO: - // need to find a way to set ingredients. since we don't use powersync for it, we need to fetch - // but this needs a context, therofere this needs a context, and all callers do, so we should probably - // move all that stuff into the nutritionprovider, so we keep context out of the models - // however, still unsolved: - // mealItem stuff then? - // nutrition image - // nutrition_ingredientcategory - // nutrition_ingredientweightunit - // nutrition_weightunit; - // nutrition_mealitem - log.ingredient = Provider.of(context, listen: false).fetchIngredient(id), - return log; - }).toList(); + return results.map((r) => Log.fromRow(r)).toList(); } /* diff --git a/lib/models/nutrition/meal.dart b/lib/models/nutrition/meal.dart index 76128b09b..3057220b7 100644 --- a/lib/models/nutrition/meal.dart +++ b/lib/models/nutrition/meal.dart @@ -93,7 +93,7 @@ class Meal { factory Meal.fromRow(sqlite.Row row) { return Meal( id: int.parse(row['id']), - plan: row['plan'], + plan: row['plan_id'], time: stringToTime(row['time']), name: row['name'], ); @@ -103,7 +103,7 @@ class Meal { Meal copyWith({ int? id, - int? planId, + int? plan, TimeOfDay? time, String? name, List? mealItems, @@ -111,7 +111,7 @@ class Meal { }) { return Meal( id: id ?? this.id, - plan: planId ?? this.planId, + plan: plan ?? planId, time: time ?? this.time, name: name ?? this.name, mealItems: mealItems ?? this.mealItems, @@ -119,6 +119,14 @@ class Meal { ); } + Future loadChildren() async { + print('loadChildren called. plan is $planId'); + return copyWith( + mealItems: await MealItem.readByMealId(id!), + diaryEntries: await Log.readByMealId(id!), + ); + } + static Future read(int id) async { final results = await db.get('SELECT * FROM $tableMeals WHERE id = ?', [id]); return Meal.fromRow(results); @@ -128,6 +136,6 @@ class Meal { print('Meal.readByPlanId: SELECT * FROM $tableMeals WHERE plan_id = $planId'); final results = await db.getAll('SELECT * FROM $tableMeals WHERE plan_id = ?', [planId]); print(results.rows.length); - return results.map((r) => Meal.fromRow(r)).toList(); + return Future.wait(results.map((r) => Meal.fromRow(r).loadChildren())); } } diff --git a/lib/models/nutrition/meal_item.dart b/lib/models/nutrition/meal_item.dart index 410e43064..811f86e99 100644 --- a/lib/models/nutrition/meal_item.dart +++ b/lib/models/nutrition/meal_item.dart @@ -17,10 +17,14 @@ */ import 'package:json_annotation/json_annotation.dart'; +import 'package:powersync/sqlite3.dart' as sqlite; + import 'package:wger/helpers/json.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/ingredient_weight_unit.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; +import 'package:wger/models/schema.dart'; +import 'package:wger/powersync.dart'; part 'meal_item.g.dart'; @@ -71,6 +75,15 @@ class MealItem { Map toJson() => _$MealItemToJson(this); + factory MealItem.fromRow(sqlite.Row row) { + return MealItem( + amount: row['amount'], + weightUnitId: row['weight_unit_id'], + mealId: row['meal_id'], + ingredientId: row['ingredient_id'], + ); + } + /// Calculations /// TODO why does this not consider weightUnitObj ? should we do the same as Log.nutritionalValues here? NutritionalValues get nutritionalValues { @@ -112,4 +125,9 @@ class MealItem { m.weightUnitObj = weightUnitObj ?? this.weightUnitObj; return m; } + + static Future> readByMealId(int mealId) async { + final results = await db.getAll('SELECT * FROM $tableMealItems WHERE meal_id = ?', [mealId]); + return results.map((r) => MealItem.fromRow(r)).toList(); + } } diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index fe635aa19..213fdfd4b 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -327,10 +327,10 @@ class NutritionalPlan { }); } - static Stream watchNutritionPlan(int id) { + static Stream watchNutritionPlan(int id) { return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { - final row = await db.get('SELECT * FROM $tableNutritionPlans WHERE id = ?', [id]); - return NutritionalPlan.fromRow(row).loadChildren(); + final row = await db.getOptional('SELECT * FROM $tableNutritionPlans WHERE id = ?', [id]); + return row == null ? null : NutritionalPlan.fromRow(row).loadChildren(); }); } diff --git a/lib/models/schema.dart b/lib/models/schema.dart index 0b5b72c76..551e1a019 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -16,10 +16,10 @@ assumptions: nutrition_ingredientcategory, nutrition_weightunit, nutrition_ingre // User,NutritionPlan,Meal,LogItem,MealItem,Ingredient const tableMuscles = 'exercises_muscle'; -const tableLogItems = 'nutrition_logitem'; +const tableLogItems = 'ivm_nutrition_logitem'; const tableNutritionPlans = 'nutrition_nutritionplan'; -const tableMeals = 'nutrition_meal'; -const tableMealItems = 'nutrition_mealitem'; +const tableMeals = 'ivm_nutrition_meal'; +const tableMealItems = 'ivm_nutrition_mealitem'; Schema schema = const Schema([ Table( diff --git a/lib/providers/nutrition.dart b/lib/providers/nutrition.dart index 9eec56a4b..4e8261190 100644 --- a/lib/providers/nutrition.dart +++ b/lib/providers/nutrition.dart @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import 'dart:async'; import 'dart:convert'; import 'dart:developer'; @@ -91,6 +92,66 @@ class NutritionPlansProvider with ChangeNotifier { return null; } + Future _enrichPlan(NutritionalPlan plan) async { + // TODO: set up ingredient images + + final List diaryEntries = []; + for (final diaryEntry in plan.diaryEntries) { + diaryEntry.ingredient = await fetchIngredient(diaryEntry.ingredientId); + diaryEntries.add(diaryEntry); + } + + final List meals = []; + for (final meal in plan.meals) { + final List mealItems = []; + for (final mealItem in meal.mealItems) { + mealItem.ingredient = await fetchIngredient(mealItem.ingredientId); + mealItems.add(mealItem); + } + meal.mealItems = mealItems; + meal.diaryEntries = diaryEntries.where((d) => d.mealId == meal.id).toList(); + meals.add(meal); + } + + plan.meals = meals; + plan.diaryEntries = diaryEntries; + + return plan; + } + + Stream watchNutritionPlan(int id) { + return NutritionalPlan.watchNutritionPlan(id).transform( + StreamTransformer.fromHandlers( + handleData: (plan, sink) async { + if (plan == null) { + sink.add(plan); + return; + } + sink.add(await _enrichPlan(plan)); + }, + ), + ); + } + + Stream watchNutritionPlanLast() { + return NutritionalPlan.watchNutritionPlanLast().transform( + StreamTransformer.fromHandlers( + handleData: (plan, sink) async { + sink.add(await _enrichPlan(plan)); + }, + ), + ); + } + + Stream> watchNutritionPlans() { + return NutritionalPlan.watchNutritionPlans().transform( + StreamTransformer.fromHandlers( + handleData: (plans, sink) async { + sink.add(await Future.wait(plans.map((plan) => _enrichPlan(plan)))); + }, + ), + ); + } /* TODO implement: ingredient.image = image; diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 43a0fbde6..35ab9f52e 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -42,7 +42,6 @@ class _DashboardScreenState extends State { padding: EdgeInsets.all(10), child: Column( children: [ - DashboardMuscleWidget(), DashboardWorkoutWidget(), DashboardNutritionWidget(), DashboardWeightWidget(), diff --git a/lib/screens/nutritional_diary_screen.dart b/lib/screens/nutritional_diary_screen.dart index 4e60f44f0..1ea1c15c4 100644 --- a/lib/screens/nutritional_diary_screen.dart +++ b/lib/screens/nutritional_diary_screen.dart @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -26,7 +28,7 @@ import 'package:wger/widgets/nutrition/nutritional_diary_detail.dart'; /// Arguments passed to the form screen class NutritionalDiaryArguments { /// Nutritional plan - final NutritionalPlan plan; + final int plan; /// Date to show data for final DateTime date; @@ -34,23 +36,50 @@ class NutritionalDiaryArguments { const NutritionalDiaryArguments(this.plan, this.date); } -class NutritionalDiaryScreen extends StatelessWidget { +class NutritionalDiaryScreen extends StatefulWidget { const NutritionalDiaryScreen(); static const routeName = '/nutritional-diary'; @override - Widget build(BuildContext context) { + State createState() => _NutritionalDiaryScreenState(); +} + +class _NutritionalDiaryScreenState extends State { + NutritionalPlan? _plan; + late DateTime date; + StreamSubscription? _subscription; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); final args = ModalRoute.of(context)!.settings.arguments as NutritionalDiaryArguments; + date = args.date; + + final stream = + Provider.of(context, listen: false).watchNutritionPlan(args.plan); + _subscription = stream.listen((plan) { + if (!context.mounted) { + return; + } + setState(() { + _plan = plan; + }); + }); + } + @override + Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(DateFormat.yMd(Localizations.localeOf(context).languageCode).format(args.date)), + title: Text(DateFormat.yMd(Localizations.localeOf(context).languageCode).format(date)), ), body: Consumer( builder: (context, nutritionProvider, child) => SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(8.0), - child: NutritionalDiaryDetailWidget(args.plan, args.date), + child: _plan == null + ? const Text('plan not found') + : NutritionalDiaryDetailWidget(_plan!, date), ), ), ), diff --git a/lib/screens/nutritional_plan_screen.dart b/lib/screens/nutritional_plan_screen.dart index 0849d2cd9..6201433d8 100644 --- a/lib/screens/nutritional_plan_screen.dart +++ b/lib/screens/nutritional_plan_screen.dart @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg_icons/flutter_svg_icons.dart'; @@ -32,153 +34,189 @@ enum NutritionalPlanOptions { delete, } -class NutritionalPlanScreen extends StatelessWidget { +class NutritionalPlanScreen extends StatefulWidget { const NutritionalPlanScreen(); static const routeName = '/nutritional-plan-detail'; + @override + _NutritionalPlanScreenState createState() => _NutritionalPlanScreenState(); +} + +class _NutritionalPlanScreenState extends State { + NutritionalPlan? _plan; + StreamSubscription? _subscription; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final id = ModalRoute.of(context)!.settings.arguments as int; + //final id = 111; + + final stream = + Provider.of(context, listen: false).watchNutritionPlan(id); + _subscription = stream.listen((plan) { + if (!context.mounted) { + return; + } + setState(() { + _plan = plan; + }); + }); + } + + @override + void dispose() { + _subscription?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { const appBarForeground = Colors.white; - final nutritionalPlan = ModalRoute.of(context)!.settings.arguments as NutritionalPlan; return Scaffold( //appBar: getAppBar(nutritionalPlan), - floatingActionButton: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - FloatingActionButton( - heroTag: null, - tooltip: AppLocalizations.of(context).logIngredient, - onPressed: () { - Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).logIngredient, - IngredientLogForm(nutritionalPlan), - hasListView: true, - ), - ); - }, - child: const SvgIcon( - icon: SvgIconData('assets/icons/ingredient-diary.svg'), - color: Colors.white, - ), - ), - const SizedBox(width: 8), - FloatingActionButton( - heroTag: null, - tooltip: AppLocalizations.of(context).logMeal, - onPressed: () { - Navigator.of(context).pushNamed( - LogMealsScreen.routeName, - arguments: nutritionalPlan, - ); - }, - child: const SvgIcon( - icon: SvgIconData('assets/icons/meal-diary.svg'), - color: Colors.white, - ), - ), - ], - ), - body: CustomScrollView( - slivers: [ - SliverAppBar( - foregroundColor: appBarForeground, - pinned: true, - iconTheme: const IconThemeData(color: appBarForeground), - actions: [ - if (!nutritionalPlan.onlyLogging) - IconButton( - icon: const SvgIcon( - icon: SvgIconData('assets/icons/meal-add.svg'), - ), + floatingActionButton: _plan == null + ? const Offstage() + : Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FloatingActionButton( + heroTag: null, + tooltip: AppLocalizations.of(context).logIngredient, onPressed: () { Navigator.pushNamed( context, FormScreen.routeName, arguments: FormScreenArguments( - AppLocalizations.of(context).addMeal, - MealForm(nutritionalPlan.id!), + AppLocalizations.of(context).logIngredient, + IngredientLogForm(_plan!), + hasListView: true, ), ); }, + child: const SvgIcon( + icon: SvgIconData('assets/icons/ingredient-diary.svg'), + color: Colors.white, + ), ), - PopupMenuButton( - icon: const Icon(Icons.more_vert, color: appBarForeground), - onSelected: (value) { - switch (value) { - case NutritionalPlanOptions.edit: - Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).edit, - PlanForm(nutritionalPlan), - hasListView: true, + const SizedBox(width: 8), + FloatingActionButton( + heroTag: null, + tooltip: AppLocalizations.of(context).logMeal, + onPressed: () { + Navigator.of(context).pushNamed( + LogMealsScreen.routeName, + arguments: _plan, + ); + }, + child: const SvgIcon( + icon: SvgIconData('assets/icons/meal-diary.svg'), + color: Colors.white, + ), + ), + ], + ), + body: _plan == null + ? const Text('plan not found') + : CustomScrollView( + slivers: [ + SliverAppBar( + foregroundColor: appBarForeground, + pinned: true, + iconTheme: const IconThemeData(color: appBarForeground), + actions: [ + if (!_plan!.onlyLogging) + IconButton( + icon: const SvgIcon( + icon: SvgIconData('assets/icons/meal-add.svg'), ), - ); - break; - case NutritionalPlanOptions.delete: - Provider.of(context, listen: false) - .deletePlan(nutritionalPlan.id!); - Navigator.of(context).pop(); - break; - } - }, - itemBuilder: (BuildContext context) { - return [ - PopupMenuItem( - value: NutritionalPlanOptions.edit, - child: ListTile( - leading: const Icon(Icons.edit), - title: Text(AppLocalizations.of(context).edit), + onPressed: () { + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).addMeal, + MealForm(_plan!.id!), + ), + ); + }, ), + PopupMenuButton( + icon: const Icon(Icons.more_vert, color: appBarForeground), + onSelected: (value) { + switch (value) { + case NutritionalPlanOptions.edit: + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).edit, + PlanForm(_plan), + hasListView: true, + ), + ); + break; + case NutritionalPlanOptions.delete: + Provider.of(context, listen: false) + .deletePlan(_plan!.id!); + Navigator.of(context).pop(); + break; + } + }, + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + value: NutritionalPlanOptions.edit, + child: ListTile( + leading: const Icon(Icons.edit), + title: Text(AppLocalizations.of(context).edit), + ), + ), + const PopupMenuDivider(), + PopupMenuItem( + value: NutritionalPlanOptions.delete, + child: ListTile( + leading: const Icon(Icons.delete), + title: Text(AppLocalizations.of(context).delete), + ), + ), + ]; + }, ), - const PopupMenuDivider(), - PopupMenuItem( - value: NutritionalPlanOptions.delete, - child: ListTile( - leading: const Icon(Icons.delete), - title: Text(AppLocalizations.of(context).delete), - ), + ], + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16), + title: Text( + _plan!.getLabel(context), + style: + Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground), ), - ]; - }, - ), - ], - flexibleSpace: FlexibleSpaceBar( - titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16), - title: Text( - nutritionalPlan.getLabel(context), - style: Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground), - ), - ), - ), - FutureBuilder( - future: NutritionalPlan.read(nutritionalPlan.id!), - builder: (context, AsyncSnapshot snapshot) => - snapshot.connectionState == ConnectionState.waiting - ? SliverList( - delegate: SliverChildListDelegate( - [ - const SizedBox( - height: 200, - child: Center( - child: CircularProgressIndicator(), + ), + ), + FutureBuilder( + future: NutritionalPlan.read(_plan!.id!), + builder: (context, AsyncSnapshot snapshot) => + snapshot.connectionState == ConnectionState.waiting + ? SliverList( + delegate: SliverChildListDelegate( + [ + const SizedBox( + height: 200, + child: Center( + child: CircularProgressIndicator(), + ), + ), + ], ), + ) + : Consumer( + builder: (context, value, child) => + NutritionalPlanDetailWidget(_plan!), ), - ], - ), - ) - : Consumer( - builder: (context, value, child) => - NutritionalPlanDetailWidget(nutritionalPlan), - ), - ), - ], - ), + ), + ], + ), ); } } diff --git a/lib/screens/nutritional_plans_screen.dart b/lib/screens/nutritional_plans_screen.dart index 43ce024c4..d6c9e706f 100644 --- a/lib/screens/nutritional_plans_screen.dart +++ b/lib/screens/nutritional_plans_screen.dart @@ -48,7 +48,7 @@ class NutritionalPlansScreen extends StatelessWidget { }, ), body: Consumer( - builder: (context, nutritionProvider, child) => NutritionalPlansList(nutritionProvider), + builder: (context, nutritionProvider, child) => NutritionalPlansList(), ), ); } diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index e93d8ef5b..e0709bd19 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -65,7 +65,8 @@ class _DashboardNutritionWidgetState extends State { @override void initState() { super.initState(); - final stream = NutritionalPlan.watchNutritionPlanLast(); + final stream = + Provider.of(context, listen: false).watchNutritionPlanLast(); _subscription = stream.listen((plan) { if (!context.mounted) { return; @@ -127,7 +128,7 @@ class _DashboardNutritionWidgetState extends State { onPressed: () { Navigator.of(context).pushNamed( NutritionalPlanScreen.routeName, - arguments: _plan, + arguments: _plan!.id, ); }, ), diff --git a/lib/widgets/nutrition/forms.dart b/lib/widgets/nutrition/forms.dart index ec336f376..785e9bacb 100644 --- a/lib/widgets/nutrition/forms.dart +++ b/lib/widgets/nutrition/forms.dart @@ -672,7 +672,7 @@ class _PlanFormState extends State { if (context.mounted) { Navigator.of(context).pushReplacementNamed( NutritionalPlanScreen.routeName, - arguments: widget._plan, + arguments: widget._plan.id, ); } } diff --git a/lib/widgets/nutrition/nutritional_diary_table.dart b/lib/widgets/nutrition/nutritional_diary_table.dart index ec7dddc87..f69bb814d 100644 --- a/lib/widgets/nutrition/nutritional_diary_table.dart +++ b/lib/widgets/nutrition/nutritional_diary_table.dart @@ -117,7 +117,7 @@ class NutritionalDiaryTable extends StatelessWidget { return GestureDetector( onTap: () => Navigator.of(context).pushNamed( NutritionalDiaryScreen.routeName, - arguments: NutritionalDiaryArguments(plan, date), + arguments: NutritionalDiaryArguments(plan.id!, date), ), child: element, ); diff --git a/lib/widgets/nutrition/nutritional_plans_list.dart b/lib/widgets/nutrition/nutritional_plans_list.dart index 15beca293..16449e288 100644 --- a/lib/widgets/nutrition/nutritional_plans_list.dart +++ b/lib/widgets/nutrition/nutritional_plans_list.dart @@ -16,33 +16,64 @@ * along with this program. If not, see . */ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/nutritional_plan_screen.dart'; import 'package:wger/widgets/core/text_prompt.dart'; -class NutritionalPlansList extends StatelessWidget { - final NutritionPlansProvider _nutritionProvider; +class NutritionalPlansList extends StatefulWidget { + @override + _NutritionalPlansListState createState() => _NutritionalPlansListState(); +} + +class _NutritionalPlansListState extends State { + List _plans = []; + StreamSubscription? _subscription; + + @override + void initState() { + super.initState(); + final stream = + Provider.of(context, listen: false).watchNutritionPlans(); + _subscription = stream.listen((plans) { + if (!context.mounted) { + return; + } + setState(() { + _plans = plans; + }); + }); + } - const NutritionalPlansList(this._nutritionProvider); + @override + void dispose() { + _subscription?.cancel(); + super.dispose(); + } @override Widget build(BuildContext context) { - return _nutritionProvider.items.isEmpty + final provider = Provider.of(context, listen: false); + + return _plans.isEmpty ? const TextPrompt() : ListView.builder( padding: const EdgeInsets.all(10.0), - itemCount: _nutritionProvider.items.length, + itemCount: _plans.length, itemBuilder: (context, index) { - final currentPlan = _nutritionProvider.items[index]; + final currentPlan = _plans[index]; return Card( child: ListTile( onTap: () { Navigator.of(context).pushNamed( NutritionalPlanScreen.routeName, - arguments: currentPlan, + arguments: currentPlan.id, ); }, title: Text(currentPlan.getLabel(context)), @@ -57,7 +88,6 @@ class NutritionalPlansList extends StatelessWidget { icon: const Icon(Icons.delete), tooltip: AppLocalizations.of(context).delete, onPressed: () async { - // Delete the plan from DB await showDialog( context: context, builder: (BuildContext contextDialog) { @@ -80,13 +110,8 @@ class NutritionalPlansList extends StatelessWidget { ), ), onPressed: () { - // Confirmed, delete the plan - _nutritionProvider.deletePlan(currentPlan.id!); - - // Close the popup + provider.deletePlan(currentPlan.id!); Navigator.of(contextDialog).pop(); - - // and inform the user ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( diff --git a/test/nutrition/nutrition_provider_test.dart b/test/nutrition/nutrition_provider_test.dart index 2b8fb842a..d7922df24 100644 --- a/test/nutrition/nutrition_provider_test.dart +++ b/test/nutrition/nutrition_provider_test.dart @@ -103,7 +103,7 @@ void main() { group('fetchAndSetPlanFull', () { test('should correctly load a full nutritional plan', () async { // arrange - await nutritionProvider.fetchAndSetPlanFull(1); + // await nutritionProvider.fetchAndSetPlanFull(1); // assert expect(nutritionProvider.items.isEmpty, false); From d041b06bb2feb54a6fcca55ea6d30c008744acde Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Wed, 16 Oct 2024 22:06:03 +0300 Subject: [PATCH 14/19] flutter says we need to upgrade the kotlin gradle plugin --- android/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/settings.gradle b/android/settings.gradle index ef03c12b4..1c38bbdf1 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.3.1" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "org.jetbrains.kotlin.android" version "2.0.21" apply false } include ":app" From 5019ba398d678322e7c015adf82e8b2fcd81c085 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Thu, 17 Oct 2024 11:28:54 +0300 Subject: [PATCH 15/19] fix version conflict --- pubspec.lock | 18 +++++++++--------- pubspec.yaml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 94914a2ef..f7c40298b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1088,18 +1088,18 @@ packages: dependency: "direct main" description: name: powersync - sha256: c6975007493617fdfc5945c3fab24ea2e6999ae300dd4d19d739713a4f2bcd96 + sha256: "7f1d2f38a936d3afd82447c4aee3b103929c6987beeae8353ccb135fe7490534" url: "https://pub.dev" source: hosted - version: "1.6.3" + version: "1.8.6" powersync_flutter_libs: dependency: transitive description: name: powersync_flutter_libs - sha256: "449063aa4956c6be215ea7dfb9cc61255188e82cf7bc3f75621796fcc6615b70" + sha256: "9cddbbc91a5887eb54297fc8f189aff76ca5f70988eaf702cf46d1ab2bdb3b72" url: "https://pub.dev" source: hosted - version: "0.1.0" + version: "0.4.0" process: dependency: transitive description: @@ -1301,18 +1301,18 @@ packages: dependency: transitive description: name: sqlite3_web - sha256: "51fec34757577841cc72d79086067e3651c434669d5af557a5c106787198a76f" + sha256: b4043336e74cac54d3ca44c90434a3c310550b9a80851b09ad1af282af0df6d4 url: "https://pub.dev" source: hosted - version: "0.1.2-wip" + version: "0.1.3" sqlite_async: dependency: "direct main" description: name: sqlite_async - sha256: "79e636c857ed43f6cd5e5be72b36967a29f785daa63ff5b078bd34f74f44cb54" + sha256: c5c57b025133d0869cce6a647f99b378ab42cc26488ff22ff942ae9588201af0 url: "https://pub.dev" source: hosted - version: "0.8.1" + version: "0.9.0" sqlparser: dependency: transitive description: @@ -1650,5 +1650,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index d1bcf7af8..aaee00e47 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,7 +71,7 @@ dependencies: freezed_annotation: ^2.4.4 clock: ^1.1.1 flutter_svg_icons: ^0.0.1 - sqlite_async: ^0.8.1 + sqlite_async: ^0.9.0 logging: ^1.2.0 dependency_overrides: From 3daf2f1bdf83a830ee79bb5300910c6203d75c38 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Thu, 17 Oct 2024 14:53:39 +0300 Subject: [PATCH 16/19] ivm aliases --- lib/models/schema.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/models/schema.dart b/lib/models/schema.dart index 551e1a019..0b5b72c76 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -16,10 +16,10 @@ assumptions: nutrition_ingredientcategory, nutrition_weightunit, nutrition_ingre // User,NutritionPlan,Meal,LogItem,MealItem,Ingredient const tableMuscles = 'exercises_muscle'; -const tableLogItems = 'ivm_nutrition_logitem'; +const tableLogItems = 'nutrition_logitem'; const tableNutritionPlans = 'nutrition_nutritionplan'; -const tableMeals = 'ivm_nutrition_meal'; -const tableMealItems = 'ivm_nutrition_mealitem'; +const tableMeals = 'nutrition_meal'; +const tableMealItems = 'nutrition_mealitem'; Schema schema = const Schema([ Table( From 5c446785574b4baa4494741c63f9f2fa7cf60c15 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Wed, 23 Oct 2024 21:32:47 +0300 Subject: [PATCH 17/19] WIP: use new string id's for nutrition stuff so we can use powersync --- lib/helpers/consts.dart | 2 +- lib/models/nutrition/log.dart | 35 +++++--- lib/models/nutrition/log.g.dart | 6 +- lib/models/nutrition/meal.dart | 16 ++-- lib/models/nutrition/meal.g.dart | 4 +- lib/models/nutrition/meal_item.dart | 8 +- lib/models/nutrition/meal_item.g.dart | 2 +- lib/models/nutrition/nutritional_plan.dart | 23 ++--- lib/models/nutrition/nutritional_plan.g.dart | 3 +- lib/models/schema.dart | 12 ++- lib/powersync.dart | 2 + lib/providers/nutrition.dart | 67 +++++++-------- lib/screens/nutritional_diary_screen.dart | 2 +- lib/screens/nutritional_plan_screen.dart | 2 +- lib/widgets/nutrition/forms.dart | 4 +- test/core/settings_test.mocks.dart | 85 +++++-------------- .../nutrition/nutritional_meal_form_test.dart | 2 +- .../nutritional_meal_form_test.mocks.dart | 85 +++++-------------- .../nutrition/nutritional_plan_form_test.dart | 2 +- .../nutritional_plan_form_test.mocks.dart | 85 +++++-------------- .../nutritional_plans_screen_test.dart | 4 +- test_data/nutritional_plans.dart | 32 +++---- 22 files changed, 182 insertions(+), 301 deletions(-) diff --git a/lib/helpers/consts.dart b/lib/helpers/consts.dart index 2b2ebc7e0..6482ec3a7 100644 --- a/lib/helpers/consts.dart +++ b/lib/helpers/consts.dart @@ -109,7 +109,7 @@ enum EXERCISE_IMAGE_ART_STYLE { } /// Dummy ID for pseudo meals -const PSEUDO_MEAL_ID = -1; +const PSEUDO_MEAL_ID = 'deadbeef'; /// Colors used for muscles const COLOR_MAIN_MUSCLES = Colors.red; diff --git a/lib/models/nutrition/log.dart b/lib/models/nutrition/log.dart index 0d3a61a66..362edf05a 100644 --- a/lib/models/nutrition/log.dart +++ b/lib/models/nutrition/log.dart @@ -17,6 +17,7 @@ */ import 'package:json_annotation/json_annotation.dart'; +import 'package:powersync/powersync.dart'; import 'package:powersync/sqlite3.dart' as sqlite; import 'package:wger/helpers/json.dart'; import 'package:wger/models/nutrition/ingredient.dart'; @@ -25,20 +26,19 @@ import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; import 'package:wger/models/schema.dart'; import 'package:wger/powersync.dart'; -import 'package:wger/providers/nutrition.dart'; part 'log.g.dart'; @JsonSerializable() class Log { @JsonKey(required: true) - int? id; + String? id; @JsonKey(required: false, name: 'meal') - int? mealId; + String? mealId; @JsonKey(required: true, name: 'plan') - int planId; + String planId; @JsonKey(required: true) late DateTime datetime; @@ -81,7 +81,7 @@ class Log { factory Log.fromRow(sqlite.Row row) { return Log( - id: int.parse(row['id']), + id: row['id'], mealId: row['meal_id'], ingredientId: row['ingredient_id'], weightUnitId: row['weight_unit_id'], @@ -107,12 +107,12 @@ class Log { return ingredient.nutritionalValues / (100 / weight); } - static Future> readByMealId(int mealId) async { + static Future> readByMealId(String mealId) async { final results = await db.getAll('SELECT * FROM $tableLogItems WHERE meal_id = ?', [mealId]); return results.map((r) => Log.fromRow(r)).toList(); } - static Future> readByPlanId(int planId) async { + static Future> readByPlanId(String planId) async { final results = await db.getAll('SELECT * FROM $tableLogItems WHERE plan_id = ?', [planId]); return results.map((r) => Log.fromRow(r)).toList(); } @@ -121,10 +121,23 @@ class Log { Future delete() async { await db.execute('DELETE FROM $logItemsTable WHERE id = ?', [id]); } + */ - static Future addPhoto(String photoId, String id) async { - await db.execute('UPDATE $logItemsTable SET photo_id = ? WHERE id = ?', [photoId, id]); + Future log() async { + print('DIETER Log.log called id=$id, planId=$planId'); + await db.execute( + 'INSERT INTO $tableLogItems (id, meal_id, ingredient_id, weight_unit_id, amount, plan_id, datetime, comment) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [ + // generate an id using uuid + uuid.v4(), + mealId, + ingredientId, + weightUnitId, + amount, + planId, + datetime.toIso8601String(), + comment, + ], + ); } -} - */ } diff --git a/lib/models/nutrition/log.g.dart b/lib/models/nutrition/log.g.dart index 72e0a6aeb..11e5eb547 100644 --- a/lib/models/nutrition/log.g.dart +++ b/lib/models/nutrition/log.g.dart @@ -19,12 +19,12 @@ Log _$LogFromJson(Map json) { ], ); return Log( - id: (json['id'] as num?)?.toInt(), - mealId: (json['meal'] as num?)?.toInt(), + id: json['id'] as String?, + mealId: json['meal'] as String?, ingredientId: (json['ingredient'] as num).toInt(), weightUnitId: (json['weight_unit'] as num?)?.toInt(), amount: stringToNum(json['amount'] as String?), - planId: (json['plan'] as num).toInt(), + planId: json['plan'] as String, datetime: DateTime.parse(json['datetime'] as String), comment: json['comment'] as String?, ); diff --git a/lib/models/nutrition/meal.dart b/lib/models/nutrition/meal.dart index 3057220b7..10b6109c8 100644 --- a/lib/models/nutrition/meal.dart +++ b/lib/models/nutrition/meal.dart @@ -33,10 +33,10 @@ part 'meal.g.dart'; @JsonSerializable() class Meal { @JsonKey(required: false) - late int? id; + late String? id; @JsonKey(name: 'plan') - late int planId; + late String planId; @JsonKey(toJson: timeToString, fromJson: stringToTime) TimeOfDay? time; @@ -55,7 +55,7 @@ class Meal { Meal({ this.id, - int? plan, + String? plan, this.time, String? name, List? mealItems, @@ -92,7 +92,7 @@ class Meal { factory Meal.fromRow(sqlite.Row row) { return Meal( - id: int.parse(row['id']), + id: row['id'], plan: row['plan_id'], time: stringToTime(row['time']), name: row['name'], @@ -102,8 +102,8 @@ class Meal { Map toJson() => _$MealToJson(this); Meal copyWith({ - int? id, - int? plan, + String? id, + String? plan, TimeOfDay? time, String? name, List? mealItems, @@ -127,12 +127,12 @@ class Meal { ); } - static Future read(int id) async { + static Future read(String id) async { final results = await db.get('SELECT * FROM $tableMeals WHERE id = ?', [id]); return Meal.fromRow(results); } - static Future> readByPlanId(int planId) async { + static Future> readByPlanId(String planId) async { print('Meal.readByPlanId: SELECT * FROM $tableMeals WHERE plan_id = $planId'); final results = await db.getAll('SELECT * FROM $tableMeals WHERE plan_id = ?', [planId]); print(results.rows.length); diff --git a/lib/models/nutrition/meal.g.dart b/lib/models/nutrition/meal.g.dart index 2258775c8..187537b4d 100644 --- a/lib/models/nutrition/meal.g.dart +++ b/lib/models/nutrition/meal.g.dart @@ -7,10 +7,10 @@ part of 'meal.dart'; // ************************************************************************** Meal _$MealFromJson(Map json) => Meal( - id: (json['id'] as num?)?.toInt(), + id: json['id'] as String?, time: stringToTime(json['time'] as String?), name: json['name'] as String?, - )..planId = (json['plan'] as num).toInt(); + )..planId = json['plan'] as String; Map _$MealToJson(Meal instance) => { 'id': instance.id, diff --git a/lib/models/nutrition/meal_item.dart b/lib/models/nutrition/meal_item.dart index 811f86e99..e740d4c2c 100644 --- a/lib/models/nutrition/meal_item.dart +++ b/lib/models/nutrition/meal_item.dart @@ -34,7 +34,7 @@ class MealItem { int? id; @JsonKey(required: false, name: 'meal') - late int mealId; + late String mealId; @JsonKey(required: false, name: 'ingredient') late int ingredientId; @@ -53,7 +53,7 @@ class MealItem { MealItem({ this.id, - int? mealId, + String? mealId, required this.ingredientId, this.weightUnitId, required this.amount, @@ -107,7 +107,7 @@ class MealItem { MealItem copyWith({ int? id, - int? mealId, + String? mealId, int? ingredientId, int? weightUnitId, num? amount, @@ -126,7 +126,7 @@ class MealItem { return m; } - static Future> readByMealId(int mealId) async { + static Future> readByMealId(String mealId) async { final results = await db.getAll('SELECT * FROM $tableMealItems WHERE meal_id = ?', [mealId]); return results.map((r) => MealItem.fromRow(r)).toList(); } diff --git a/lib/models/nutrition/meal_item.g.dart b/lib/models/nutrition/meal_item.g.dart index 256f9cce7..e503d3fc9 100644 --- a/lib/models/nutrition/meal_item.g.dart +++ b/lib/models/nutrition/meal_item.g.dart @@ -13,7 +13,7 @@ MealItem _$MealItemFromJson(Map json) { ); return MealItem( id: (json['id'] as num?)?.toInt(), - mealId: (json['meal'] as num?)?.toInt(), + mealId: json['meal'] as String?, ingredientId: (json['ingredient'] as num).toInt(), weightUnitId: (json['weight_unit'] as num?)?.toInt(), amount: stringToNum(json['amount'] as String?), diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index 213fdfd4b..a8cbbef64 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -35,8 +35,8 @@ part 'nutritional_plan.g.dart'; @JsonSerializable(explicitToJson: true) class NutritionalPlan { - @JsonKey(required: true) - int? id; + @JsonKey(required: false) + String? id; @JsonKey(required: true) late String description; @@ -87,7 +87,7 @@ class NutritionalPlan { factory NutritionalPlan.fromRow(sqlite.Row row) { return NutritionalPlan( - id: int.parse(row['id']), + id: row['id'], description: row['description'], creationDate: DateTime.parse(row['creation_date']), onlyLogging: row['only_logging'] == 1, @@ -100,7 +100,7 @@ class NutritionalPlan { } NutritionalPlan copyWith({ - int? id, + String? id, String? description, DateTime? creationDate, bool? onlyLogging, @@ -299,7 +299,7 @@ class NutritionalPlan { ); } - static Future read(int id) async { + static Future read(String id) async { final row = await db.get('SELECT * FROM $tableNutritionPlans WHERE id = ?', [id]); return NutritionalPlan.fromRow(row).loadChildren(); } @@ -327,18 +327,21 @@ class NutritionalPlan { }); } - static Stream watchNutritionPlan(int id) { + static Stream watchNutritionPlan(String id) { return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { final row = await db.getOptional('SELECT * FROM $tableNutritionPlans WHERE id = ?', [id]); return row == null ? null : NutritionalPlan.fromRow(row).loadChildren(); }); } - static Stream watchNutritionPlanLast() { + static Stream watchNutritionPlanLast() { return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { - final row = - await db.get('SELECT * FROM $tableNutritionPlans ORDER BY creation_date DESC LIMIT 1'); - return NutritionalPlan.fromRow(row).loadChildren(); + final res = + await db.getAll('SELECT * FROM $tableNutritionPlans ORDER BY creation_date DESC LIMIT 1'); + if (res.isEmpty) { + return null; + } + return NutritionalPlan.fromRow(res.first).loadChildren(); }); } /* diff --git a/lib/models/nutrition/nutritional_plan.g.dart b/lib/models/nutrition/nutritional_plan.g.dart index b4a8bbe73..934459424 100644 --- a/lib/models/nutrition/nutritional_plan.g.dart +++ b/lib/models/nutrition/nutritional_plan.g.dart @@ -10,7 +10,6 @@ NutritionalPlan _$NutritionalPlanFromJson(Map json) { $checkKeys( json, requiredKeys: const [ - 'id', 'description', 'creation_date', 'only_logging', @@ -22,7 +21,7 @@ NutritionalPlan _$NutritionalPlanFromJson(Map json) { ], ); return NutritionalPlan( - id: (json['id'] as num?)?.toInt(), + id: json['id'] as String?, description: json['description'] as String, creationDate: DateTime.parse(json['creation_date'] as String), onlyLogging: json['only_logging'] as bool? ?? false, diff --git a/lib/models/schema.dart b/lib/models/schema.dart index 0b5b72c76..08ecd01b2 100644 --- a/lib/models/schema.dart +++ b/lib/models/schema.dart @@ -33,6 +33,7 @@ Schema schema = const Schema([ Column.text('description'), Column.integer('has_goal_calories'), Column.integer('user_id'), + Column.integer('remote_id'), Column.integer('only_logging'), Column.integer('goal_carbohydrates'), Column.integer('goal_energy'), @@ -47,10 +48,11 @@ Schema schema = const Schema([ Column.text('datetime'), Column.text('comment'), Column.integer('amount'), + Column.integer('remote_id'), Column.integer('ingredient_id'), - Column.integer('plan_id'), + Column.text('plan_id'), Column.integer('weight_unit_id'), - Column.integer('meal_id'), // optional + Column.text('meal_id'), // optional ], indexes: [ // Index('plan', [IndexedColumn('plan_id')]) @@ -60,8 +62,9 @@ Schema schema = const Schema([ tableMeals, [ Column.integer('order'), + Column.integer('remote_id'), Column.text('time'), - Column.integer('plan_id'), + Column.text('plan_id'), Column.text('name'), ], ), @@ -71,7 +74,8 @@ Schema schema = const Schema([ Column.integer('order'), Column.integer('amount'), Column.integer('ingredient_id'), - Column.integer('meal_id'), + Column.text('meal_id'), + Column.integer('remote_id'), Column.integer('weight_unit_id'), ], ), diff --git a/lib/powersync.dart b/lib/powersync.dart index 4532ae0f7..0a2122c97 100644 --- a/lib/powersync.dart +++ b/lib/powersync.dart @@ -60,6 +60,8 @@ class DjangoConnector extends PowerSyncBackendConnector { 'data': {'id': op.id, ...?op.opData}, }; + log.fine('DIETER Uploading record', record); + switch (op.op) { case UpdateType.put: await apiClient.upsert(record); diff --git a/lib/providers/nutrition.dart b/lib/providers/nutrition.dart index a092a6639..62c8ef3f3 100644 --- a/lib/providers/nutrition.dart +++ b/lib/providers/nutrition.dart @@ -74,13 +74,14 @@ class NutritionPlansProvider with ChangeNotifier { } return null; } - +/* NutritionalPlan findById(int id) { return _plans.firstWhere( (plan) => plan.id == id, orElse: () => throw const NoSuchEntryException(), ); } + Meal? findMealById(int id) { for (final plan in _plans) { @@ -91,6 +92,7 @@ class NutritionPlansProvider with ChangeNotifier { } return null; } + */ Future _enrichPlan(NutritionalPlan plan) async { // TODO: set up ingredient images @@ -119,7 +121,7 @@ class NutritionPlansProvider with ChangeNotifier { return plan; } - Stream watchNutritionPlan(int id) { + Stream watchNutritionPlan(String id) { return NutritionalPlan.watchNutritionPlan(id).transform( StreamTransformer.fromHandlers( handleData: (plan, sink) async { @@ -137,6 +139,9 @@ class NutritionPlansProvider with ChangeNotifier { return NutritionalPlan.watchNutritionPlanLast().transform( StreamTransformer.fromHandlers( handleData: (plan, sink) async { + if (plan == null) { + return; + } sink.add(await _enrichPlan(plan)); }, ), @@ -172,31 +177,16 @@ TODO implement: } Future editPlan(NutritionalPlan plan) async { - await baseProvider.patch( - plan.toJson(), - baseProvider.makeUrl(_nutritionalPlansPath, id: plan.id), - ); - notifyListeners(); +// TODO } - Future deletePlan(int id) async { - final existingPlanIndex = _plans.indexWhere((element) => element.id == id); - final existingPlan = _plans[existingPlanIndex]; - _plans.removeAt(existingPlanIndex); - notifyListeners(); - - final response = await baseProvider.deleteRequest(_nutritionalPlansPath, id); - - if (response.statusCode >= 400) { - _plans.insert(existingPlanIndex, existingPlan); - notifyListeners(); - throw WgerHttpException(response.body); - } - //existingPlan = null; + Future deletePlan(String id) async { +// TODO } /// Adds a meal to a plan - Future addMeal(Meal meal, int planId) async { + Future addMeal(Meal meal, String planId) async { + /* final plan = findById(planId); final data = await baseProvider.post( meal.toJson(), @@ -208,10 +198,13 @@ TODO implement: notifyListeners(); return meal; + */ + return meal; } /// Edits an existing meal Future editMeal(Meal meal) async { + /* final data = await baseProvider.patch( meal.toJson(), baseProvider.makeUrl(_mealPath, id: meal.id), @@ -219,11 +212,13 @@ TODO implement: meal = Meal.fromJson(data); notifyListeners(); + */ return meal; } /// Deletes a meal Future deleteMeal(Meal meal) async { + /* // Get the meal final plan = findById(meal.planId); final mealIndex = plan.meals.indexWhere((e) => e.id == meal.id); @@ -238,6 +233,8 @@ TODO implement: notifyListeners(); throw WgerHttpException(response.body); } + */ + return; } /// Adds a meal item to a meal @@ -257,6 +254,7 @@ TODO implement: /// Deletes a meal Future deleteMealItem(MealItem mealItem) async { + /* // Get the meal final meal = findMealById(mealItem.mealId)!; final mealItemIndex = meal.mealItems.indexWhere((e) => e.id == mealItem.id); @@ -271,6 +269,7 @@ TODO implement: notifyListeners(); throw WgerHttpException(response.body); } + */ } Future clearIngredientCache() async { @@ -380,6 +379,7 @@ TODO implement: /// Log meal to nutrition diary Future logMealToDiary(Meal meal) async { + /* for (final item in meal.mealItems) { final plan = findById(meal.planId); final Log log = Log.fromMealItem(item, plan.id!, meal.id); @@ -392,34 +392,29 @@ TODO implement: plan.diaryEntries.add(log); } notifyListeners(); + */ } /// Log custom ingredient to nutrition diary Future logIngredientToDiary( MealItem mealItem, - int planId, [ + String planId, [ DateTime? dateTime, - ]) async { - final plan = findById(planId); - mealItem.ingredient = await fetchIngredient(mealItem.ingredientId); - final Log log = Log.fromMealItem(mealItem, plan.id!, null, dateTime); - - final data = await baseProvider.post( - log.toJson(), - baseProvider.makeUrl(_nutritionDiaryPath), - ); - log.id = data['id']; - plan.diaryEntries.add(log); - notifyListeners(); + ]) { + print( + 'DIETER logIngredientToDiary called ingredient=${mealItem.ingredientId}, planId=$planId, dateTime=$dateTime'); + return Log.fromMealItem(mealItem, planId, null, dateTime).log(); } /// Deletes a log entry - Future deleteLog(int logId, int planId) async { + Future deleteLog(String logId, String planId) async { + /* await baseProvider.deleteRequest(_nutritionDiaryPath, logId); final plan = findById(planId); plan.diaryEntries.removeWhere((element) => element.id == logId); notifyListeners(); + */ } /// Load nutrition diary entries for plan diff --git a/lib/screens/nutritional_diary_screen.dart b/lib/screens/nutritional_diary_screen.dart index 1ea1c15c4..72d97daf8 100644 --- a/lib/screens/nutritional_diary_screen.dart +++ b/lib/screens/nutritional_diary_screen.dart @@ -28,7 +28,7 @@ import 'package:wger/widgets/nutrition/nutritional_diary_detail.dart'; /// Arguments passed to the form screen class NutritionalDiaryArguments { /// Nutritional plan - final int plan; + final String plan; /// Date to show data for final DateTime date; diff --git a/lib/screens/nutritional_plan_screen.dart b/lib/screens/nutritional_plan_screen.dart index 6201433d8..4f1c5ec95 100644 --- a/lib/screens/nutritional_plan_screen.dart +++ b/lib/screens/nutritional_plan_screen.dart @@ -49,7 +49,7 @@ class _NutritionalPlanScreenState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - final id = ModalRoute.of(context)!.settings.arguments as int; + final id = ModalRoute.of(context)!.settings.arguments as String; //final id = 111; final stream = diff --git a/lib/widgets/nutrition/forms.dart b/lib/widgets/nutrition/forms.dart index 785e9bacb..faad4a6f9 100644 --- a/lib/widgets/nutrition/forms.dart +++ b/lib/widgets/nutrition/forms.dart @@ -36,7 +36,7 @@ import 'package:wger/widgets/nutrition/widgets.dart'; class MealForm extends StatelessWidget { late final Meal _meal; - final int _planId; + final String _planId; final _form = GlobalKey(); final _timeController = TextEditingController(); @@ -128,7 +128,7 @@ Widget MealItemForm( ]) { return IngredientForm( // TODO we use planId 0 here cause we don't have one and we don't need it I think? - recent: recent.map((e) => Log.fromMealItem(e, 0, e.mealId)).toList(), + recent: recent.map((e) => Log.fromMealItem(e, "0", e.mealId)).toList(), onSave: (BuildContext context, MealItem mealItem, DateTime? dt) { mealItem.mealId = meal.id!; Provider.of(context, listen: false).addMealItem(mealItem, meal); diff --git a/test/core/settings_test.mocks.dart b/test/core/settings_test.mocks.dart index 303216098..5812e036b 100644 --- a/test/core/settings_test.mocks.dart +++ b/test/core/settings_test.mocks.dart @@ -711,79 +711,34 @@ class MockNutritionPlansProvider extends _i1.Mock ); @override - _i10.NutritionalPlan findById(int? id) => (super.noSuchMethod( + _i15.Stream<_i10.NutritionalPlan?> watchNutritionPlan(String? id) => + (super.noSuchMethod( Invocation.method( - #findById, + #watchNutritionPlan, [id], ), - returnValue: _FakeNutritionalPlan_8( - this, - Invocation.method( - #findById, - [id], - ), - ), - ) as _i10.NutritionalPlan); - - @override - _i11.Meal? findMealById(int? id) => (super.noSuchMethod(Invocation.method( - #findMealById, - [id], - )) as _i11.Meal?); - - @override - _i15.Future fetchAndSetAllPlansSparse() => (super.noSuchMethod( - Invocation.method( - #fetchAndSetAllPlansSparse, - [], - ), - returnValue: _i15.Future.value(), - returnValueForMissingStub: _i15.Future.value(), - ) as _i15.Future); - - @override - _i15.Future fetchAndSetAllPlansFull() => (super.noSuchMethod( - Invocation.method( - #fetchAndSetAllPlansFull, - [], - ), - returnValue: _i15.Future.value(), - returnValueForMissingStub: _i15.Future.value(), - ) as _i15.Future); + returnValue: _i15.Stream<_i10.NutritionalPlan?>.empty(), + ) as _i15.Stream<_i10.NutritionalPlan?>); @override - _i15.Future<_i10.NutritionalPlan> fetchAndSetPlanSparse(int? planId) => + _i15.Stream<_i10.NutritionalPlan> watchNutritionPlanLast() => (super.noSuchMethod( Invocation.method( - #fetchAndSetPlanSparse, - [planId], + #watchNutritionPlanLast, + [], ), - returnValue: - _i15.Future<_i10.NutritionalPlan>.value(_FakeNutritionalPlan_8( - this, - Invocation.method( - #fetchAndSetPlanSparse, - [planId], - ), - )), - ) as _i15.Future<_i10.NutritionalPlan>); + returnValue: _i15.Stream<_i10.NutritionalPlan>.empty(), + ) as _i15.Stream<_i10.NutritionalPlan>); @override - _i15.Future<_i10.NutritionalPlan> fetchAndSetPlanFull(int? planId) => + _i15.Stream> watchNutritionPlans() => (super.noSuchMethod( Invocation.method( - #fetchAndSetPlanFull, - [planId], + #watchNutritionPlans, + [], ), - returnValue: - _i15.Future<_i10.NutritionalPlan>.value(_FakeNutritionalPlan_8( - this, - Invocation.method( - #fetchAndSetPlanFull, - [planId], - ), - )), - ) as _i15.Future<_i10.NutritionalPlan>); + returnValue: _i15.Stream>.empty(), + ) as _i15.Stream>); @override _i15.Future<_i10.NutritionalPlan> addPlan(_i10.NutritionalPlan? planData) => @@ -813,7 +768,7 @@ class MockNutritionPlansProvider extends _i1.Mock ) as _i15.Future); @override - _i15.Future deletePlan(int? id) => (super.noSuchMethod( + _i15.Future deletePlan(String? id) => (super.noSuchMethod( Invocation.method( #deletePlan, [id], @@ -825,7 +780,7 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i15.Future<_i11.Meal> addMeal( _i11.Meal? meal, - int? planId, + String? planId, ) => (super.noSuchMethod( Invocation.method( @@ -991,7 +946,7 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i15.Future logIngredientToDiary( _i12.MealItem? mealItem, - int? planId, [ + String? planId, [ DateTime? dateTime, ]) => (super.noSuchMethod( @@ -1009,8 +964,8 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i15.Future deleteLog( - int? logId, - int? planId, + String? logId, + String? planId, ) => (super.noSuchMethod( Invocation.method( diff --git a/test/nutrition/nutritional_meal_form_test.dart b/test/nutrition/nutritional_meal_form_test.dart index fdb781f64..a053b19fa 100644 --- a/test/nutrition/nutritional_meal_form_test.dart +++ b/test/nutrition/nutritional_meal_form_test.dart @@ -60,7 +60,7 @@ void main() { localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, navigatorKey: key, - home: Scaffold(body: MealForm(1, meal)), + home: Scaffold(body: MealForm("1", meal)), routes: { NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(), }, diff --git a/test/nutrition/nutritional_meal_form_test.mocks.dart b/test/nutrition/nutritional_meal_form_test.mocks.dart index 3df6d7b22..08ce46e60 100644 --- a/test/nutrition/nutritional_meal_form_test.mocks.dart +++ b/test/nutrition/nutritional_meal_form_test.mocks.dart @@ -165,79 +165,34 @@ class MockNutritionPlansProvider extends _i1.Mock ); @override - _i4.NutritionalPlan findById(int? id) => (super.noSuchMethod( + _i9.Stream<_i4.NutritionalPlan?> watchNutritionPlan(String? id) => + (super.noSuchMethod( Invocation.method( - #findById, + #watchNutritionPlan, [id], ), - returnValue: _FakeNutritionalPlan_2( - this, - Invocation.method( - #findById, - [id], - ), - ), - ) as _i4.NutritionalPlan); - - @override - _i5.Meal? findMealById(int? id) => (super.noSuchMethod(Invocation.method( - #findMealById, - [id], - )) as _i5.Meal?); - - @override - _i9.Future fetchAndSetAllPlansSparse() => (super.noSuchMethod( - Invocation.method( - #fetchAndSetAllPlansSparse, - [], - ), - returnValue: _i9.Future.value(), - returnValueForMissingStub: _i9.Future.value(), - ) as _i9.Future); + returnValue: _i9.Stream<_i4.NutritionalPlan?>.empty(), + ) as _i9.Stream<_i4.NutritionalPlan?>); @override - _i9.Future fetchAndSetAllPlansFull() => (super.noSuchMethod( - Invocation.method( - #fetchAndSetAllPlansFull, - [], - ), - returnValue: _i9.Future.value(), - returnValueForMissingStub: _i9.Future.value(), - ) as _i9.Future); - - @override - _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanSparse(int? planId) => + _i9.Stream<_i4.NutritionalPlan> watchNutritionPlanLast() => (super.noSuchMethod( Invocation.method( - #fetchAndSetPlanSparse, - [planId], + #watchNutritionPlanLast, + [], ), - returnValue: - _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( - this, - Invocation.method( - #fetchAndSetPlanSparse, - [planId], - ), - )), - ) as _i9.Future<_i4.NutritionalPlan>); + returnValue: _i9.Stream<_i4.NutritionalPlan>.empty(), + ) as _i9.Stream<_i4.NutritionalPlan>); @override - _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanFull(int? planId) => + _i9.Stream> watchNutritionPlans() => (super.noSuchMethod( Invocation.method( - #fetchAndSetPlanFull, - [planId], + #watchNutritionPlans, + [], ), - returnValue: - _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( - this, - Invocation.method( - #fetchAndSetPlanFull, - [planId], - ), - )), - ) as _i9.Future<_i4.NutritionalPlan>); + returnValue: _i9.Stream>.empty(), + ) as _i9.Stream>); @override _i9.Future<_i4.NutritionalPlan> addPlan(_i4.NutritionalPlan? planData) => @@ -267,7 +222,7 @@ class MockNutritionPlansProvider extends _i1.Mock ) as _i9.Future); @override - _i9.Future deletePlan(int? id) => (super.noSuchMethod( + _i9.Future deletePlan(String? id) => (super.noSuchMethod( Invocation.method( #deletePlan, [id], @@ -279,7 +234,7 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i9.Future<_i5.Meal> addMeal( _i5.Meal? meal, - int? planId, + String? planId, ) => (super.noSuchMethod( Invocation.method( @@ -445,7 +400,7 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i9.Future logIngredientToDiary( _i6.MealItem? mealItem, - int? planId, [ + String? planId, [ DateTime? dateTime, ]) => (super.noSuchMethod( @@ -463,8 +418,8 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i9.Future deleteLog( - int? logId, - int? planId, + String? logId, + String? planId, ) => (super.noSuchMethod( Invocation.method( diff --git a/test/nutrition/nutritional_plan_form_test.dart b/test/nutrition/nutritional_plan_form_test.dart index e366ef51f..ce89c0fef 100644 --- a/test/nutrition/nutritional_plan_form_test.dart +++ b/test/nutrition/nutritional_plan_form_test.dart @@ -35,7 +35,7 @@ void main() { var mockNutrition = MockNutritionPlansProvider(); final plan1 = NutritionalPlan( - id: 1, + id: 'deadbeef', creationDate: DateTime(2021, 1, 1), description: 'test plan 1', ); diff --git a/test/nutrition/nutritional_plan_form_test.mocks.dart b/test/nutrition/nutritional_plan_form_test.mocks.dart index 3b48c5305..735d15379 100644 --- a/test/nutrition/nutritional_plan_form_test.mocks.dart +++ b/test/nutrition/nutritional_plan_form_test.mocks.dart @@ -165,79 +165,34 @@ class MockNutritionPlansProvider extends _i1.Mock ); @override - _i4.NutritionalPlan findById(int? id) => (super.noSuchMethod( + _i9.Stream<_i4.NutritionalPlan?> watchNutritionPlan(String? id) => + (super.noSuchMethod( Invocation.method( - #findById, + #watchNutritionPlan, [id], ), - returnValue: _FakeNutritionalPlan_2( - this, - Invocation.method( - #findById, - [id], - ), - ), - ) as _i4.NutritionalPlan); - - @override - _i5.Meal? findMealById(int? id) => (super.noSuchMethod(Invocation.method( - #findMealById, - [id], - )) as _i5.Meal?); - - @override - _i9.Future fetchAndSetAllPlansSparse() => (super.noSuchMethod( - Invocation.method( - #fetchAndSetAllPlansSparse, - [], - ), - returnValue: _i9.Future.value(), - returnValueForMissingStub: _i9.Future.value(), - ) as _i9.Future); + returnValue: _i9.Stream<_i4.NutritionalPlan?>.empty(), + ) as _i9.Stream<_i4.NutritionalPlan?>); @override - _i9.Future fetchAndSetAllPlansFull() => (super.noSuchMethod( - Invocation.method( - #fetchAndSetAllPlansFull, - [], - ), - returnValue: _i9.Future.value(), - returnValueForMissingStub: _i9.Future.value(), - ) as _i9.Future); - - @override - _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanSparse(int? planId) => + _i9.Stream<_i4.NutritionalPlan> watchNutritionPlanLast() => (super.noSuchMethod( Invocation.method( - #fetchAndSetPlanSparse, - [planId], + #watchNutritionPlanLast, + [], ), - returnValue: - _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( - this, - Invocation.method( - #fetchAndSetPlanSparse, - [planId], - ), - )), - ) as _i9.Future<_i4.NutritionalPlan>); + returnValue: _i9.Stream<_i4.NutritionalPlan>.empty(), + ) as _i9.Stream<_i4.NutritionalPlan>); @override - _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanFull(int? planId) => + _i9.Stream> watchNutritionPlans() => (super.noSuchMethod( Invocation.method( - #fetchAndSetPlanFull, - [planId], + #watchNutritionPlans, + [], ), - returnValue: - _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( - this, - Invocation.method( - #fetchAndSetPlanFull, - [planId], - ), - )), - ) as _i9.Future<_i4.NutritionalPlan>); + returnValue: _i9.Stream>.empty(), + ) as _i9.Stream>); @override _i9.Future<_i4.NutritionalPlan> addPlan(_i4.NutritionalPlan? planData) => @@ -267,7 +222,7 @@ class MockNutritionPlansProvider extends _i1.Mock ) as _i9.Future); @override - _i9.Future deletePlan(int? id) => (super.noSuchMethod( + _i9.Future deletePlan(String? id) => (super.noSuchMethod( Invocation.method( #deletePlan, [id], @@ -279,7 +234,7 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i9.Future<_i5.Meal> addMeal( _i5.Meal? meal, - int? planId, + String? planId, ) => (super.noSuchMethod( Invocation.method( @@ -445,7 +400,7 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i9.Future logIngredientToDiary( _i6.MealItem? mealItem, - int? planId, [ + String? planId, [ DateTime? dateTime, ]) => (super.noSuchMethod( @@ -463,8 +418,8 @@ class MockNutritionPlansProvider extends _i1.Mock @override _i9.Future deleteLog( - int? logId, - int? planId, + String? logId, + String? planId, ) => (super.noSuchMethod( Invocation.method( diff --git a/test/nutrition/nutritional_plans_screen_test.dart b/test/nutrition/nutritional_plans_screen_test.dart index 5a60b2503..eea8de89a 100644 --- a/test/nutrition/nutritional_plans_screen_test.dart +++ b/test/nutrition/nutritional_plans_screen_test.dart @@ -67,12 +67,12 @@ void main() { mockBaseProvider, [ NutritionalPlan( - id: 1, + id: 'deadbeefa', description: 'test plan 1', creationDate: DateTime(2021, 01, 01), ), NutritionalPlan( - id: 2, + id: 'deadbeefb', description: 'test plan 2', creationDate: DateTime(2021, 01, 10), ), diff --git a/test_data/nutritional_plans.dart b/test_data/nutritional_plans.dart index 1b3a42cc6..9a202bc4c 100644 --- a/test_data/nutritional_plans.dart +++ b/test_data/nutritional_plans.dart @@ -156,32 +156,32 @@ NutritionalPlan getNutritionalPlan() { mealItem3.ingredient = ingredient3; final meal1 = Meal( - id: 1, - plan: 1, + id: 'deadbeefa', + plan: '1', time: const TimeOfDay(hour: 17, minute: 0), name: 'Initial Name 1', ); meal1.mealItems = [mealItem1, mealItem2]; final meal2 = Meal( - id: 2, - plan: 1, + id: 'deadbeefb', + plan: '1', time: const TimeOfDay(hour: 22, minute: 5), name: 'Initial Name 2', ); meal2.mealItems = [mealItem3]; final NutritionalPlan plan = NutritionalPlan( - id: 1, + id: 'deadbeefc', description: 'Less fat, more protein', creationDate: DateTime(2021, 5, 23), ); plan.meals = [meal1, meal2]; // Add logs - plan.diaryEntries.add(Log.fromMealItem(mealItem1, 1, 1, DateTime(2021, 6, 1))); - plan.diaryEntries.add(Log.fromMealItem(mealItem2, 1, 1, DateTime(2021, 6, 1))); - plan.diaryEntries.add(Log.fromMealItem(mealItem3, 1, 1, DateTime(2021, 6, 10))); + plan.diaryEntries.add(Log.fromMealItem(mealItem1, '1', '1', DateTime(2021, 6, 1))); + plan.diaryEntries.add(Log.fromMealItem(mealItem2, '1', '1', DateTime(2021, 6, 1))); + plan.diaryEntries.add(Log.fromMealItem(mealItem3, '1', '1', DateTime(2021, 6, 10))); return plan; } @@ -194,32 +194,32 @@ NutritionalPlan getNutritionalPlanScreenshot() { final mealItem3 = MealItem(ingredientId: 3, amount: 100, ingredient: apple); final meal1 = Meal( - id: 1, - plan: 1, + id: 'deadbeefa', + plan: '1', time: const TimeOfDay(hour: 8, minute: 30), name: 'Breakfast', mealItems: [mealItem1, mealItem2], ); final meal2 = Meal( - id: 2, - plan: 1, + id: 'deadbeefb', + plan: '1', time: const TimeOfDay(hour: 11, minute: 0), name: 'Snack 1', mealItems: [mealItem3], ); final NutritionalPlan plan = NutritionalPlan( - id: 1, + id: '1', description: 'Diet', creationDate: DateTime(2021, 5, 23), meals: [meal1, meal2], ); // Add logs - plan.diaryEntries.add(Log.fromMealItem(mealItem1, 1, 1, DateTime.now())); - plan.diaryEntries.add(Log.fromMealItem(mealItem2, 1, 1, DateTime.now())); - plan.diaryEntries.add(Log.fromMealItem(mealItem3, 1, 1, DateTime.now())); + plan.diaryEntries.add(Log.fromMealItem(mealItem1, '1', '1', DateTime.now())); + plan.diaryEntries.add(Log.fromMealItem(mealItem2, '1', '1', DateTime.now())); + plan.diaryEntries.add(Log.fromMealItem(mealItem3, '1', '1', DateTime.now())); for (final i in plan.diaryEntries) { i.datetime = DateTime.now(); From 64dc10cdb20c6b97c144f0843ecf18b6954c597c Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sun, 19 Oct 2025 14:23:58 +0200 Subject: [PATCH 18/19] Fix import for localization strings --- integration_test/1_dashboard.dart | 27 +- integration_test/2_workout.dart | 6 +- integration_test/3_gym_mode.dart | 12 +- integration_test/4_measurements.dart | 6 +- integration_test/5_nutritional_plan.dart | 2 +- integration_test/6_weight.dart | 10 +- lib/helpers/exercises/forms.dart | 2 +- lib/helpers/i18n.dart | 2 +- lib/helpers/ui.dart | 15 +- lib/main.dart | 43 +-- lib/models/nutrition/nutritional_plan.dart | 49 ++- lib/screens/add_exercise_screen.dart | 23 +- lib/screens/auth_screen.dart | 164 ++++----- lib/screens/dashboard.dart | 8 +- lib/screens/exercises_screen.dart | 8 +- lib/screens/gallery_screen.dart | 2 +- lib/screens/home_tabs_screen.dart | 2 +- lib/screens/log_meal_screen.dart | 11 +- lib/screens/log_meals_screen.dart | 14 +- .../measurement_categories_screen.dart | 2 +- lib/screens/measurement_entries_screen.dart | 15 +- lib/screens/nutritional_plan_screen.dart | 58 ++-- lib/screens/nutritional_plans_screen.dart | 2 +- lib/screens/update_app_screen.dart | 2 +- lib/screens/weight_screen.dart | 2 +- lib/screens/workout_plan_screen.dart | 45 ++- lib/screens/workout_plans_screen.dart | 2 +- .../add_exercise/steps/step1basics.dart | 2 +- .../add_exercise/steps/step2variations.dart | 14 +- .../add_exercise/steps/step3description.dart | 2 +- .../add_exercise/steps/step4translations.dart | 9 +- .../add_exercise/steps/step5images.dart | 6 +- lib/widgets/core/about.dart | 20 +- lib/widgets/core/app_bar.dart | 10 +- lib/widgets/core/settings.dart | 6 +- lib/widgets/core/text_prompt.dart | 2 +- lib/widgets/dashboard/calendar.dart | 67 ++-- lib/widgets/dashboard/widgets.dart | 183 +++++----- lib/widgets/exercises/exercises.dart | 131 +++---- lib/widgets/exercises/filter_row.dart | 6 +- lib/widgets/gallery/forms.dart | 22 +- lib/widgets/gallery/overview.dart | 11 +- lib/widgets/measurements/categories_card.dart | 13 +- lib/widgets/measurements/charts.dart | 48 +-- lib/widgets/measurements/entries.dart | 134 ++++---- lib/widgets/measurements/forms.dart | 42 +-- lib/widgets/measurements/helpers.dart | 8 +- lib/widgets/nutrition/charts.dart | 321 ++++++++---------- lib/widgets/nutrition/forms.dart | 79 ++--- lib/widgets/nutrition/helpers.dart | 38 +-- lib/widgets/nutrition/ingredient_dialogs.dart | 11 +- .../nutrition/macro_nutrients_table.dart | 21 +- lib/widgets/nutrition/meal.dart | 87 +++-- lib/widgets/nutrition/nutrition_tiles.dart | 20 +- .../nutrition/nutritional_diary_detail.dart | 40 +-- .../nutrition/nutritional_diary_table.dart | 91 ++--- .../nutrition/nutritional_plan_detail.dart | 140 ++++---- .../nutrition/nutritional_plans_list.dart | 104 +++--- lib/widgets/nutrition/widgets.dart | 51 ++- lib/widgets/user/forms.dart | 6 +- lib/widgets/weight/forms.dart | 7 +- lib/widgets/weight/weight_overview.dart | 12 +- lib/widgets/workouts/app_bar.dart | 7 +- lib/widgets/workouts/charts.dart | 22 +- lib/widgets/workouts/day.dart | 72 ++-- lib/widgets/workouts/forms.dart | 126 +++---- lib/widgets/workouts/gym_mode.dart | 123 +++---- lib/widgets/workouts/workout_logs.dart | 12 +- lib/widgets/workouts/workout_plan_detail.dart | 12 +- lib/widgets/workouts/workout_plans_list.dart | 112 +++--- test/auth/auth_screen_test.dart | 99 +++--- test/core/settings_test.dart | 2 +- test/exercises/contribute_exercise_test.dart | 2 +- .../exercises_detail_widget_test.dart | 2 +- test/gallery/gallery_form_test.dart | 1 + test/gallery/gallery_screen_test.dart | 2 +- .../measurement_categories_screen_test.dart | 28 +- .../measurement_entries_screen_test.dart | 21 +- test/nutrition/nutritional_diary_test.dart | 2 +- .../nutrition/nutritional_meal_form_test.dart | 12 +- .../nutritional_meal_item_form_test.dart | 72 ++-- .../nutrition/nutritional_plan_form_test.dart | 6 +- .../nutritional_plan_screen_test.dart | 119 +++---- .../nutritional_plans_screen_test.dart | 39 +-- test/weight/weight_form_test.dart | 2 +- test/weight/weight_screen_test.dart | 2 +- test/workout/gym_mode_screen_test.dart | 13 +- .../repetition_unit_form_widget_test.dart | 6 +- .../workout/weight_unit_form_widget_test.dart | 6 +- test/workout/workout_day_form_test.dart | 2 +- test/workout/workout_form_test.dart | 17 +- test/workout/workout_plan_screen_test.dart | 6 +- test/workout/workout_plans_screen_test.dart | 20 +- test/workout/workout_set_form_test.dart | 21 +- 94 files changed, 1452 insertions(+), 1834 deletions(-) diff --git a/integration_test/1_dashboard.dart b/integration_test/1_dashboard.dart index 4550fea77..9c72ea595 100644 --- a/integration_test/1_dashboard.dart +++ b/integration_test/1_dashboard.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/measurement.dart'; import 'package:wger/providers/nutrition.dart'; @@ -42,8 +42,9 @@ Widget createDashboardScreen({locale = 'en'}) { final mockNutritionProvider = MockNutritionPlansProvider(); - when(mockNutritionProvider.currentPlan) - .thenAnswer((realInvocation) => getNutritionalPlanScreenshot()); + when( + mockNutritionProvider.currentPlan, + ).thenAnswer((realInvocation) => getNutritionalPlanScreenshot()); when(mockNutritionProvider.items).thenReturn([getNutritionalPlanScreenshot()]); final mockWeightProvider = MockBodyWeightProvider(); @@ -57,21 +58,11 @@ Widget createDashboardScreen({locale = 'en'}) { return MultiProvider( providers: [ - ChangeNotifierProvider( - create: (context) => mockUserProvider, - ), - ChangeNotifierProvider( - create: (context) => mockWorkoutProvider, - ), - ChangeNotifierProvider( - create: (context) => mockNutritionProvider, - ), - ChangeNotifierProvider( - create: (context) => mockWeightProvider, - ), - ChangeNotifierProvider( - create: (context) => mockMeasurementProvider, - ), + ChangeNotifierProvider(create: (context) => mockUserProvider), + ChangeNotifierProvider(create: (context) => mockWorkoutProvider), + ChangeNotifierProvider(create: (context) => mockNutritionProvider), + ChangeNotifierProvider(create: (context) => mockWeightProvider), + ChangeNotifierProvider(create: (context) => mockMeasurementProvider), ], child: MaterialApp( locale: Locale(locale), diff --git a/integration_test/2_workout.dart b/integration_test/2_workout.dart index 96bad963c..8e5fc12a9 100644 --- a/integration_test/2_workout.dart +++ b/integration_test/2_workout.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/workout_plan_screen.dart'; import 'package:wger/theme/theme.dart'; @@ -20,9 +20,7 @@ Widget createWorkoutDetailScreen({locale = 'en'}) { return MultiProvider( providers: [ - ChangeNotifierProvider( - create: (context) => mockWorkoutProvider, - ), + ChangeNotifierProvider(create: (context) => mockWorkoutProvider), ], child: MaterialApp( locale: Locale(locale), diff --git a/integration_test/3_gym_mode.dart b/integration_test/3_gym_mode.dart index 202b7c299..b6f469bdb 100644 --- a/integration_test/3_gym_mode.dart +++ b/integration_test/3_gym_mode.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/gym_mode.dart'; @@ -26,11 +26,7 @@ Widget createGymModeScreen({locale = 'en'}) { //when(mockExerciseProvider.findExerciseBaseById(3)).thenReturn(bases[2]); // dead lift return ChangeNotifierProvider( - create: (context) => WorkoutPlansProvider( - mockBaseProvider, - mockExerciseProvider, - [workout], - ), + create: (context) => WorkoutPlansProvider(mockBaseProvider, mockExerciseProvider, [workout]), child: ChangeNotifierProvider( create: (context) => mockExerciseProvider, child: MaterialApp( @@ -49,9 +45,7 @@ Widget createGymModeScreen({locale = 'en'}) { ), child: const SizedBox(), ), - routes: { - WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen(), - }, + routes: {WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen()}, ), ), ); diff --git a/integration_test/4_measurements.dart b/integration_test/4_measurements.dart index 8db33ba0d..9e5874c23 100644 --- a/integration_test/4_measurements.dart +++ b/integration_test/4_measurements.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/measurement.dart'; import 'package:wger/screens/measurement_categories_screen.dart'; import 'package:wger/theme/theme.dart'; @@ -15,9 +15,7 @@ Widget createMeasurementScreen({locale = 'en'}) { return MultiProvider( providers: [ - ChangeNotifierProvider( - create: (context) => mockMeasurementProvider, - ), + ChangeNotifierProvider(create: (context) => mockMeasurementProvider), ], child: MaterialApp( locale: Locale(locale), diff --git a/integration_test/5_nutritional_plan.dart b/integration_test/5_nutritional_plan.dart index b43965a1b..7381e39e1 100644 --- a/integration_test/5_nutritional_plan.dart +++ b/integration_test/5_nutritional_plan.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/nutritional_plan_screen.dart'; diff --git a/integration_test/6_weight.dart b/integration_test/6_weight.dart index 57f46609f..aaeb9abcd 100644 --- a/integration_test/6_weight.dart +++ b/integration_test/6_weight.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/user.dart'; import 'package:wger/screens/form_screen.dart'; @@ -22,12 +22,8 @@ Widget createWeightScreen({locale = 'en'}) { return MultiProvider( providers: [ - ChangeNotifierProvider( - create: (context) => mockUserProvider, - ), - ChangeNotifierProvider( - create: (context) => mockWeightProvider, - ), + ChangeNotifierProvider(create: (context) => mockUserProvider), + ChangeNotifierProvider(create: (context) => mockWeightProvider), ], child: MaterialApp( locale: Locale(locale), diff --git a/lib/helpers/exercises/forms.dart b/lib/helpers/exercises/forms.dart index 25d92bde4..d1d3e3b95 100644 --- a/lib/helpers/exercises/forms.dart +++ b/lib/helpers/exercises/forms.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; /// The amount of characters an exercise description needs to have const MIN_CHARS_DESCRIPTION = 40; diff --git a/lib/helpers/i18n.dart b/lib/helpers/i18n.dart index ebf02de8b..161bc110e 100644 --- a/lib/helpers/i18n.dart +++ b/lib/helpers/i18n.dart @@ -7,7 +7,7 @@ library; /// probably better ways to do this, but that's the way it is right now). import 'package:flutter/widgets.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; String getTranslation(String value, BuildContext context) { switch (value) { diff --git a/lib/helpers/ui.dart b/lib/helpers/ui.dart index 7e8823e56..7a2244bb9 100644 --- a/lib/helpers/ui.dart +++ b/lib/helpers/ui.dart @@ -19,9 +19,9 @@ import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/exercises/translation.dart'; import 'package:wger/models/workouts/log.dart'; @@ -50,10 +50,7 @@ void showErrorDialog(dynamic exception, BuildContext context) { ); } -void showHttpExceptionErrorDialog( - WgerHttpException exception, - BuildContext context, -) { +void showHttpExceptionErrorDialog(WgerHttpException exception, BuildContext context) { log('showHttpExceptionErrorDialog: '); log(exception.toString()); log('-------------------'); @@ -119,9 +116,7 @@ dynamic showDeleteDialog( context: context, builder: (BuildContext contextDialog) { return AlertDialog( - content: Text( - AppLocalizations.of(context).confirmDelete(confirmDeleteName), - ), + content: Text(AppLocalizations.of(context).confirmDelete(confirmDeleteName)), actions: [ TextButton( child: Text(MaterialLocalizations.of(context).cancelButtonLabel), @@ -134,9 +129,7 @@ dynamic showDeleteDialog( ), onPressed: () { exerciseData[exercise]!.removeWhere((el) => el.id == log.id); - Provider.of(context, listen: false).deleteLog( - log, - ); + Provider.of(context, listen: false).deleteLog(log); Navigator.of(contextDialog).pop(); diff --git a/lib/main.dart b/lib/main.dart index a7827d918..1a3a9bc13 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,6 +19,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:wger/core/locator.dart'; import 'package:wger/powersync.dart'; @@ -53,8 +54,7 @@ import 'package:wger/screens/workout_plan_screen.dart'; import 'package:wger/screens/workout_plans_screen.dart'; import 'package:wger/theme/theme.dart'; import 'package:wger/widgets/core/about.dart'; -import 'package:wger/widgets/core/settings.dart'; -import 'package:logging/logging.dart'; +import 'package:wger/widgets/core/settingsdart'; import 'providers/auth.dart'; @@ -92,9 +92,8 @@ class MyApp extends StatelessWidget { providers: [ ChangeNotifierProvider(create: (ctx) => AuthProvider()), ChangeNotifierProxyProvider( - create: (context) => ExercisesProvider( - WgerBaseProvider(Provider.of(context, listen: false)), - ), + create: (context) => + ExercisesProvider(WgerBaseProvider(Provider.of(context, listen: false))), update: (context, base, previous) => previous ?? ExercisesProvider(WgerBaseProvider(base)), ), @@ -108,44 +107,34 @@ class MyApp extends StatelessWidget { previous ?? WorkoutPlansProvider(WgerBaseProvider(auth), exercises, []), ), ChangeNotifierProxyProvider( - create: (context) => NutritionPlansProvider( - WgerBaseProvider(Provider.of(context, listen: false)), - [], - ), + create: (context) => + NutritionPlansProvider(WgerBaseProvider(Provider.of(context, listen: false)), []), update: (context, auth, previous) => previous ?? NutritionPlansProvider(WgerBaseProvider(auth), []), ), ChangeNotifierProxyProvider( - create: (context) => MeasurementProvider( - WgerBaseProvider(Provider.of(context, listen: false)), - ), + create: (context) => + MeasurementProvider(WgerBaseProvider(Provider.of(context, listen: false))), update: (context, base, previous) => previous ?? MeasurementProvider(WgerBaseProvider(base)), ), ChangeNotifierProxyProvider( - create: (context) => UserProvider( - WgerBaseProvider(Provider.of(context, listen: false)), - ), + create: (context) => UserProvider(WgerBaseProvider(Provider.of(context, listen: false))), update: (context, base, previous) => previous ?? UserProvider(WgerBaseProvider(base)), ), ChangeNotifierProxyProvider( - create: (context) => BodyWeightProvider( - WgerBaseProvider(Provider.of(context, listen: false)), - ), + create: (context) => + BodyWeightProvider(WgerBaseProvider(Provider.of(context, listen: false))), update: (context, base, previous) => previous ?? BodyWeightProvider(WgerBaseProvider(base)), ), ChangeNotifierProxyProvider( - create: (context) => GalleryProvider( - Provider.of(context, listen: false), - [], - ), + create: (context) => GalleryProvider(Provider.of(context, listen: false), []), update: (context, auth, previous) => previous ?? GalleryProvider(auth, []), ), ChangeNotifierProxyProvider( - create: (context) => AddExerciseProvider( - WgerBaseProvider(Provider.of(context, listen: false)), - ), + create: (context) => + AddExerciseProvider(WgerBaseProvider(Provider.of(context, listen: false))), update: (context, base, previous) => previous ?? AddExerciseProvider(WgerBaseProvider(base)), ), @@ -164,8 +153,8 @@ class MyApp extends StatelessWidget { future: auth.tryAutoLogin(), builder: (ctx, authResultSnapshot) => authResultSnapshot.connectionState == ConnectionState.waiting - ? const SplashScreen() - : const AuthScreen(), + ? const SplashScreen() + : const AuthScreen(), ), routes: { DashboardScreen.routeName: (ctx) => const DashboardScreen(), diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index a8cbbef64..9bd58c6aa 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -18,11 +18,11 @@ import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:powersync/sqlite3.dart' as sqlite; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/log.dart'; import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/models/nutrition/meal_item.dart'; @@ -128,10 +128,7 @@ class NutritionalPlan { } Future loadChildren() async { - return copyWith( - diaryEntries: await Log.readByPlanId(id!), - meals: await Meal.readByPlanId(id!), - ); + return copyWith(diaryEntries: await Log.readByPlanId(id!), meals: await Meal.readByPlanId(id!)); } NutritionalPlan.empty() { @@ -186,10 +183,7 @@ class NutritionalPlan { return NutritionalGoals(); } // otherwise, add up all the nutritional values of the meals and use that as goals - final sumValues = meals.fold( - NutritionalValues(), - (a, b) => a + b.plannedNutritionalValues, - ); + final sumValues = meals.fold(NutritionalValues(), (a, b) => a + b.plannedNutritionalValues); return NutritionalGoals( energy: sumValues.energy, fat: sumValues.fat, @@ -304,21 +298,21 @@ class NutritionalPlan { return NutritionalPlan.fromRow(row).loadChildren(); } -// this is a bit complicated. -// what we need at the end of the day, is a stream of List, where -// a new value is emitted any time a plan is changed. But the plan is not just the plan record -// we need to load data for Logs and Meals corresponding to the plan also. -// so our options are: -// 1) db.watch with a select query on plans; and extra dart code to load the logs/meals stuff, -// but this only triggers for updates on the plans table, and misses logs/meals updates -// 2) db.watch with a huge join query across all tables from which we need info, -// so we have all the data in our resultset to create the datastructures with, but: -// - this creates long rows with lots of duplicated data (e.g. all the plan data) for every row -// which would only differ for e.g. the meal or the log item -// - it would probably get a bit messy to parse the resultset into the datastructures -// 3) the best of both worlds: load the data we need in dart at runtime, but explicitly -// trigger our code execution when *any* of the relevant tables changes -// + // this is a bit complicated. + // what we need at the end of the day, is a stream of List, where + // a new value is emitted any time a plan is changed. But the plan is not just the plan record + // we need to load data for Logs and Meals corresponding to the plan also. + // so our options are: + // 1) db.watch with a select query on plans; and extra dart code to load the logs/meals stuff, + // but this only triggers for updates on the plans table, and misses logs/meals updates + // 2) db.watch with a huge join query across all tables from which we need info, + // so we have all the data in our resultset to create the datastructures with, but: + // - this creates long rows with lots of duplicated data (e.g. all the plan data) for every row + // which would only differ for e.g. the meal or the log item + // - it would probably get a bit messy to parse the resultset into the datastructures + // 3) the best of both worlds: load the data we need in dart at runtime, but explicitly + // trigger our code execution when *any* of the relevant tables changes + // static Stream> watchNutritionPlans() { return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { final data = await db.getAll('SELECT * FROM $tableNutritionPlans ORDER BY creation_date'); @@ -336,15 +330,16 @@ class NutritionalPlan { static Stream watchNutritionPlanLast() { return db.onChange([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap((event) async { - final res = - await db.getAll('SELECT * FROM $tableNutritionPlans ORDER BY creation_date DESC LIMIT 1'); + final res = await db.getAll( + 'SELECT * FROM $tableNutritionPlans ORDER BY creation_date DESC LIMIT 1', + ); if (res.isEmpty) { return null; } return NutritionalPlan.fromRow(res.first).loadChildren(); }); } -/* + /* static Stream> watchNutritionPlan(int id) { return db .watch('SELECT * FROM $tableNutritionPlans WHERE id = ?', parameters: [id]).map((results) { diff --git a/lib/screens/add_exercise_screen.dart b/lib/screens/add_exercise_screen.dart index 36695bb68..d8a44be2e 100644 --- a/lib/screens/add_exercise_screen.dart +++ b/lib/screens/add_exercise_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/user.dart'; @@ -76,9 +76,7 @@ class _AddExerciseStepperState extends State { final baseId = await addExerciseProvider.addExercise(); final base = await exerciseProvider.fetchAndSetExercise(baseId); final name = base - .getExercise( - Localizations.localeOf(context).languageCode, - ) + .getExercise(Localizations.localeOf(context).languageCode) .name; setState(() { @@ -91,9 +89,7 @@ class _AddExerciseStepperState extends State { builder: (BuildContext context) { return AlertDialog( title: Text(AppLocalizations.of(context).success), - content: Text( - AppLocalizations.of(context).cacheWarning, - ), + content: Text(AppLocalizations.of(context).cacheWarning), actions: [ TextButton( child: Text(name), @@ -112,11 +108,7 @@ class _AddExerciseStepperState extends State { ); }, child: _isLoading - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(), - ) + ? const SizedBox(height: 20, width: 20, child: CircularProgressIndicator()) : Text(AppLocalizations.of(context).save), ) else @@ -207,8 +199,11 @@ class EmailNotVerified extends StatelessWidget { ListTile( leading: const Icon(Icons.warning), title: Text(AppLocalizations.of(context).unVerifiedEmail), - subtitle: Text(AppLocalizations.of(context) - .contributeExerciseWarning(MIN_ACCOUNT_AGE.toString())), + subtitle: Text( + AppLocalizations.of( + context, + ).contributeExerciseWarning(MIN_ACCOUNT_AGE.toString()), + ), ), Row( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/screens/auth_screen.dart b/lib/screens/auth_screen.dart index ab096576d..2145d6a2e 100644 --- a/lib/screens/auth_screen.dart +++ b/lib/screens/auth_screen.dart @@ -19,21 +19,18 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/misc.dart'; import 'package:wger/helpers/ui.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/screens/update_app_screen.dart'; import 'package:wger/theme/theme.dart'; import '../providers/auth.dart'; -enum AuthMode { - Signup, - Login, -} +enum AuthMode { Signup, Login } class AuthScreen extends StatelessWidget { const AuthScreen(); @@ -50,10 +47,7 @@ class AuthScreen extends StatelessWidget { top: 0, right: 0, left: 0, - child: Container( - height: 0.55 * deviceSize.height, - color: wgerPrimaryColor, - ), + child: Container(height: 0.55 * deviceSize.height, color: wgerPrimaryColor), ), SingleChildScrollView( child: SizedBox( @@ -64,16 +58,10 @@ class AuthScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 0.15 * deviceSize.height), - const Image( - image: AssetImage('assets/images/logo-white.png'), - width: 85, - ), + const Image(image: AssetImage('assets/images/logo-white.png'), width: 85), Container( margin: const EdgeInsets.only(bottom: 20.0), - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 94.0, - ), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 94.0), child: const Text( 'wger', style: TextStyle( @@ -190,11 +178,10 @@ class _AuthCardState extends State { // Login existing user late Map res; if (_authMode == AuthMode.Login) { - res = await Provider.of(context, listen: false).login( - _authData['username']!, - _authData['password']!, - _authData['serverUrl']!, - ); + res = await Provider.of( + context, + listen: false, + ).login(_authData['username']!, _authData['password']!, _authData['serverUrl']!); // Register new user } else { @@ -210,9 +197,9 @@ class _AuthCardState extends State { // Check if update is required else continue normally if (res.containsKey('action')) { if (res['action'] == LoginActions.update && mounted) { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => const UpdateAppScreen()), - ); + Navigator.of( + context, + ).push(MaterialPageRoute(builder: (context) => const UpdateAppScreen())); return; } } @@ -260,16 +247,11 @@ class _AuthCardState extends State { Widget build(BuildContext context) { final deviceSize = MediaQuery.of(context).size; return Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), elevation: 8.0, child: Container( width: deviceSize.width * 0.9, - padding: EdgeInsets.symmetric( - horizontal: 15.0, - vertical: 0.025 * deviceSize.height, - ), + padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 0.025 * deviceSize.height), child: Form( key: _formKey, child: SingleChildScrollView( @@ -297,9 +279,7 @@ class _AuthCardState extends State { return null; }, - inputFormatters: [ - FilteringTextInputFormatter.deny(RegExp(r'\s\b|\b\s')), - ], + inputFormatters: [FilteringTextInputFormatter.deny(RegExp(r'\s\b|\b\s'))], onSaved: (value) { _authData['username'] = value!; }, @@ -327,63 +307,69 @@ class _AuthCardState extends State { _authData['email'] = value!; }, ), - StatefulBuilder(builder: (context, updateState) { - return TextFormField( - key: const Key('inputPassword'), - decoration: InputDecoration( - labelText: AppLocalizations.of(context).password, - prefixIcon: const Icon(Icons.password), - suffixIcon: IconButton( - icon: Icon(isObscure ? Icons.visibility_off : Icons.visibility), - onPressed: () { - isObscure = !isObscure; - updateState(() {}); - }, - ), - ), - autofillHints: const [AutofillHints.password], - obscureText: isObscure, - controller: _passwordController, - textInputAction: TextInputAction.next, - validator: (value) { - if (value!.isEmpty || value.length < 8) { - return AppLocalizations.of(context).passwordTooShort; - } - return null; - }, - onSaved: (value) { - _authData['password'] = value!; - }, - ); - }), - if (_authMode == AuthMode.Signup) - StatefulBuilder(builder: (context, updateState) { + StatefulBuilder( + builder: (context, updateState) { return TextFormField( - key: const Key('inputPassword2'), + key: const Key('inputPassword'), decoration: InputDecoration( - labelText: AppLocalizations.of(context).confirmPassword, + labelText: AppLocalizations.of(context).password, prefixIcon: const Icon(Icons.password), suffixIcon: IconButton( - icon: Icon(confirmIsObscure ? Icons.visibility_off : Icons.visibility), + icon: Icon(isObscure ? Icons.visibility_off : Icons.visibility), onPressed: () { - confirmIsObscure = !confirmIsObscure; + isObscure = !isObscure; updateState(() {}); }, ), ), - controller: _password2Controller, - enabled: _authMode == AuthMode.Signup, - obscureText: confirmIsObscure, - validator: _authMode == AuthMode.Signup - ? (value) { - if (value != _passwordController.text) { - return AppLocalizations.of(context).passwordsDontMatch; - } - return null; - } - : null, + autofillHints: const [AutofillHints.password], + obscureText: isObscure, + controller: _passwordController, + textInputAction: TextInputAction.next, + validator: (value) { + if (value!.isEmpty || value.length < 8) { + return AppLocalizations.of(context).passwordTooShort; + } + return null; + }, + onSaved: (value) { + _authData['password'] = value!; + }, ); - }), + }, + ), + if (_authMode == AuthMode.Signup) + StatefulBuilder( + builder: (context, updateState) { + return TextFormField( + key: const Key('inputPassword2'), + decoration: InputDecoration( + labelText: AppLocalizations.of(context).confirmPassword, + prefixIcon: const Icon(Icons.password), + suffixIcon: IconButton( + icon: Icon( + confirmIsObscure ? Icons.visibility_off : Icons.visibility, + ), + onPressed: () { + confirmIsObscure = !confirmIsObscure; + updateState(() {}); + }, + ), + ), + controller: _password2Controller, + enabled: _authMode == AuthMode.Signup, + obscureText: confirmIsObscure, + validator: _authMode == AuthMode.Signup + ? (value) { + if (value != _passwordController.text) { + return AppLocalizations.of(context).passwordsDontMatch; + } + return null; + } + : null, + ); + }, + ), // Off-stage widgets are kept in the tree, otherwise the server URL // would not be saved to _authData Offstage( @@ -426,8 +412,9 @@ class _AuthCardState extends State { IconButton( icon: const Icon(Icons.undo), onPressed: () { - _serverUrlController.text = - kDebugMode ? DEFAULT_SERVER_TEST : DEFAULT_SERVER_PROD; + _serverUrlController.text = kDebugMode + ? DEFAULT_SERVER_TEST + : DEFAULT_SERVER_PROD; }, ), Text(AppLocalizations.of(context).reset), @@ -485,14 +472,9 @@ class _AuthCardState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ + Text(text.substring(0, text.lastIndexOf('?') + 1)), Text( - text.substring(0, text.lastIndexOf('?') + 1), - ), - Text( - text.substring( - text.lastIndexOf('?') + 1, - text.length, - ), + text.substring(text.lastIndexOf('?') + 1, text.length), style: const TextStyle( //color: wgerPrimaryColor, fontWeight: FontWeight.w700, diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 35ab9f52e..5b50fdd8f 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -20,6 +20,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generatedapp_localizations.dart'; import 'package:wger/models/muscle.dart'; import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/dashboard/calendar.dart'; @@ -90,9 +91,8 @@ class _DashboardMuscleWidgetState extends State { @override Widget build(BuildContext context) { return Container( - color: Colors.brown, - child: Column( - children: [Text('muscles'), ..._data.map((e) => Text(e.name)).toList()], - )); + color: Colors.brown, + child: Column(children: [Text('muscles'), ..._data.map((e) => Text(e.name)).toList()]), + ); } } diff --git a/lib/screens/exercises_screen.dart b/lib/screens/exercises_screen.dart index ca6e33866..d12ce9bc3 100644 --- a/lib/screens/exercises_screen.dart +++ b/lib/screens/exercises_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/widgets/core/app_bar.dart'; @@ -43,11 +43,7 @@ class _ExercisesScreenState extends State { Expanded( child: exercisesList.isEmpty ? const Center( - child: SizedBox( - height: 100, - width: 100, - child: CircularProgressIndicator(), - ), + child: SizedBox(height: 100, width: 100, child: CircularProgressIndicator()), ) : _ExercisesList(exerciseBaseList: exercisesList), ), diff --git a/lib/screens/gallery_screen.dart b/lib/screens/gallery_screen.dart index a7b952792..bebe39865 100644 --- a/lib/screens/gallery_screen.dart +++ b/lib/screens/gallery_screen.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/platform.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/gallery.dart'; import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/gallery/forms.dart'; diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index a2b79217b..efacb4094 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:rive/rive.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/powersync.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/providers/body_weight.dart'; diff --git a/lib/screens/log_meal_screen.dart b/lib/screens/log_meal_screen.dart index d93fbe131..7cdef4877 100644 --- a/lib/screens/log_meal_screen.dart +++ b/lib/screens/log_meal_screen.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/widgets/nutrition/meal.dart'; @@ -59,10 +59,7 @@ class _LogMealScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), child: Column( children: [ - Text( - meal.name, - style: Theme.of(context).textTheme.headlineSmall, - ), + Text(meal.name, style: Theme.of(context).textTheme.headlineSmall), if (meal.mealItems.isEmpty) ListTile(title: Text(AppLocalizations.of(context).noIngredientsDefined)) else @@ -114,9 +111,7 @@ class _LogMealScreenState extends State { }, ), TextButton( - child: Text( - MaterialLocalizations.of(context).cancelButtonLabel, - ), + child: Text(MaterialLocalizations.of(context).cancelButtonLabel), onPressed: () => Navigator.of(context).pop(), ), ], diff --git a/lib/screens/log_meals_screen.dart b/lib/screens/log_meals_screen.dart index 74aea3897..c241d1d90 100644 --- a/lib/screens/log_meals_screen.dart +++ b/lib/screens/log_meals_screen.dart @@ -17,7 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/widgets/nutrition/meal.dart'; @@ -37,17 +37,11 @@ class _LogMealsScreenState extends State { final nutritionalPlan = ModalRoute.of(context)!.settings.arguments as NutritionalPlan; return Scaffold( - appBar: AppBar( - title: Text(AppLocalizations.of(context).selectMealToLog), - ), + appBar: AppBar(title: Text(AppLocalizations.of(context).selectMealToLog)), body: ListView.builder( itemCount: nutritionalPlan.meals.length, - itemBuilder: (context, index) => MealWidget( - nutritionalPlan.meals[index], - nutritionalPlan.dedupMealItems, - true, - true, - ), + itemBuilder: (context, index) => + MealWidget(nutritionalPlan.meals[index], nutritionalPlan.dedupMealItems, true, true), ), ); } diff --git a/lib/screens/measurement_categories_screen.dart b/lib/screens/measurement_categories_screen.dart index 29b33d0fa..e65fda111 100644 --- a/lib/screens/measurement_categories_screen.dart +++ b/lib/screens/measurement_categories_screen.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/measurement.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/widgets/measurements/categories.dart'; diff --git a/lib/screens/measurement_entries_screen.dart b/lib/screens/measurement_entries_screen.dart index 3c8e41889..492b8195f 100644 --- a/lib/screens/measurement_entries_screen.dart +++ b/lib/screens/measurement_entries_screen.dart @@ -17,17 +17,14 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/measurement.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/widgets/measurements/entries.dart'; import 'package:wger/widgets/measurements/forms.dart'; -enum MeasurementOptions { - edit, - delete, -} +enum MeasurementOptions { edit, delete } class MeasurementEntriesScreen extends StatelessWidget { const MeasurementEntriesScreen(); @@ -62,9 +59,7 @@ class MeasurementEntriesScreen extends StatelessWidget { context: context, builder: (BuildContext contextDialog) { return AlertDialog( - content: Text( - AppLocalizations.of(context).confirmDelete(category.name), - ), + content: Text(AppLocalizations.of(context).confirmDelete(category.name)), actions: [ TextButton( child: Text(MaterialLocalizations.of(context).cancelButtonLabel), @@ -73,9 +68,7 @@ class MeasurementEntriesScreen extends StatelessWidget { TextButton( child: Text( AppLocalizations.of(context).delete, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), + style: TextStyle(color: Theme.of(context).colorScheme.error), ), onPressed: () { // Confirmed, delete the workout diff --git a/lib/screens/nutritional_plan_screen.dart b/lib/screens/nutritional_plan_screen.dart index 4f1c5ec95..0cd36bd73 100644 --- a/lib/screens/nutritional_plan_screen.dart +++ b/lib/screens/nutritional_plan_screen.dart @@ -19,9 +19,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg_icons/flutter_svg_icons.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/form_screen.dart'; @@ -29,10 +29,7 @@ import 'package:wger/screens/log_meals_screen.dart'; import 'package:wger/widgets/nutrition/forms.dart'; import 'package:wger/widgets/nutrition/nutritional_plan_detail.dart'; -enum NutritionalPlanOptions { - edit, - delete, -} +enum NutritionalPlanOptions { edit, delete } class NutritionalPlanScreen extends StatefulWidget { const NutritionalPlanScreen(); @@ -52,8 +49,10 @@ class _NutritionalPlanScreenState extends State { final id = ModalRoute.of(context)!.settings.arguments as String; //final id = 111; - final stream = - Provider.of(context, listen: false).watchNutritionPlan(id); + final stream = Provider.of( + context, + listen: false, + ).watchNutritionPlan(id); _subscription = stream.listen((plan) { if (!context.mounted) { return; @@ -105,10 +104,7 @@ class _NutritionalPlanScreenState extends State { heroTag: null, tooltip: AppLocalizations.of(context).logMeal, onPressed: () { - Navigator.of(context).pushNamed( - LogMealsScreen.routeName, - arguments: _plan, - ); + Navigator.of(context).pushNamed(LogMealsScreen.routeName, arguments: _plan); }, child: const SvgIcon( icon: SvgIconData('assets/icons/meal-diary.svg'), @@ -128,9 +124,7 @@ class _NutritionalPlanScreenState extends State { actions: [ if (!_plan!.onlyLogging) IconButton( - icon: const SvgIcon( - icon: SvgIconData('assets/icons/meal-add.svg'), - ), + icon: const SvgIcon(icon: SvgIconData('assets/icons/meal-add.svg')), onPressed: () { Navigator.pushNamed( context, @@ -158,8 +152,10 @@ class _NutritionalPlanScreenState extends State { ); break; case NutritionalPlanOptions.delete: - Provider.of(context, listen: false) - .deletePlan(_plan!.id!); + Provider.of( + context, + listen: false, + ).deletePlan(_plan!.id!); Navigator.of(context).pop(); break; } @@ -189,8 +185,9 @@ class _NutritionalPlanScreenState extends State { titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16), title: Text( _plan!.getLabel(context), - style: - Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground), + style: Theme.of( + context, + ).textTheme.titleLarge?.copyWith(color: appBarForeground), ), ), ), @@ -198,22 +195,17 @@ class _NutritionalPlanScreenState extends State { future: NutritionalPlan.read(_plan!.id!), builder: (context, AsyncSnapshot snapshot) => snapshot.connectionState == ConnectionState.waiting - ? SliverList( - delegate: SliverChildListDelegate( - [ - const SizedBox( - height: 200, - child: Center( - child: CircularProgressIndicator(), - ), - ), - ], - ), - ) - : Consumer( - builder: (context, value, child) => - NutritionalPlanDetailWidget(_plan!), + ? SliverList( + delegate: SliverChildListDelegate([ + const SizedBox( + height: 200, + child: Center(child: CircularProgressIndicator()), ), + ]), + ) + : Consumer( + builder: (context, value, child) => NutritionalPlanDetailWidget(_plan!), + ), ), ], ), diff --git a/lib/screens/nutritional_plans_screen.dart b/lib/screens/nutritional_plans_screen.dart index d6c9e706f..f15592c8c 100644 --- a/lib/screens/nutritional_plans_screen.dart +++ b/lib/screens/nutritional_plans_screen.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/widgets/core/app_bar.dart'; diff --git a/lib/screens/update_app_screen.dart b/lib/screens/update_app_screen.dart index 52b76d6d6..2e2cc1567 100644 --- a/lib/screens/update_app_screen.dart +++ b/lib/screens/update_app_screen.dart @@ -17,7 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; class UpdateAppScreen extends StatelessWidget { const UpdateAppScreen(); diff --git a/lib/screens/weight_screen.dart b/lib/screens/weight_screen.dart index 5af357be2..64d481110 100644 --- a/lib/screens/weight_screen.dart +++ b/lib/screens/weight_screen.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/widgets/core/app_bar.dart'; diff --git a/lib/screens/workout_plan_screen.dart b/lib/screens/workout_plan_screen.dart index 61f898b7f..f07bdfc88 100644 --- a/lib/screens/workout_plan_screen.dart +++ b/lib/screens/workout_plan_screen.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/form_screen.dart'; @@ -27,15 +27,9 @@ import 'package:wger/widgets/workouts/forms.dart'; import 'package:wger/widgets/workouts/workout_logs.dart'; import 'package:wger/widgets/workouts/workout_plan_detail.dart'; -enum WorkoutScreenMode { - workout, - log, -} +enum WorkoutScreenMode { workout, log } -enum WorkoutOptions { - edit, - delete, -} +enum WorkoutOptions { edit, delete } class WorkoutPlanScreen extends StatefulWidget { const WorkoutPlanScreen(); @@ -55,8 +49,10 @@ class _WorkoutPlanScreenState extends State { } Future _loadFullWorkout(BuildContext context, int planId) { - return Provider.of(context, listen: false) - .fetchAndSetWorkoutPlanFull(planId); + return Provider.of( + context, + listen: false, + ).fetchAndSetWorkoutPlanFull(planId); } Widget getBody(WorkoutPlan plan) { @@ -105,8 +101,10 @@ class _WorkoutPlanScreenState extends State { // Delete } else if (value == WorkoutOptions.delete) { - Provider.of(context, listen: false) - .deleteWorkout(workoutPlan.id!); + Provider.of( + context, + listen: false, + ).deleteWorkout(workoutPlan.id!); Navigator.of(context).pop(); // Toggle Mode @@ -131,19 +129,14 @@ class _WorkoutPlanScreenState extends State { FutureBuilder( future: _loadFullWorkout(context, workoutPlan.id!), builder: (context, AsyncSnapshot snapshot) => SliverList( - delegate: SliverChildListDelegate( - [ - if (snapshot.connectionState == ConnectionState.waiting) - const SizedBox( - height: 200, - child: Center(child: CircularProgressIndicator()), - ) - else - Consumer( - builder: (context, value, child) => getBody(workoutPlan), - ), - ], - ), + delegate: SliverChildListDelegate([ + if (snapshot.connectionState == ConnectionState.waiting) + const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())) + else + Consumer( + builder: (context, value, child) => getBody(workoutPlan), + ), + ]), ), ), ], diff --git a/lib/screens/workout_plans_screen.dart b/lib/screens/workout_plans_screen.dart index 7f2490e9f..fe55020f3 100644 --- a/lib/screens/workout_plans_screen.dart +++ b/lib/screens/workout_plans_screen.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/form_screen.dart'; diff --git a/lib/widgets/add_exercise/steps/step1basics.dart b/lib/widgets/add_exercise/steps/step1basics.dart index 0d875947f..3c2a260e3 100644 --- a/lib/widgets/add_exercise/steps/step1basics.dart +++ b/lib/widgets/add_exercise/steps/step1basics.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/exercises/forms.dart'; import 'package:wger/helpers/i18n.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/equipment.dart'; import 'package:wger/models/exercises/muscle.dart'; diff --git a/lib/widgets/add_exercise/steps/step2variations.dart b/lib/widgets/add_exercise/steps/step2variations.dart index fb2fb5b7e..73d84fd7d 100644 --- a/lib/widgets/add_exercise/steps/step2variations.dart +++ b/lib/widgets/add_exercise/steps/step2variations.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/providers/exercises.dart'; @@ -40,9 +40,7 @@ class Step2Variations extends StatelessWidget { ...exerciseProvider.exerciseBasesByVariation[key]!.map( (base) => Text( base - .getExercise( - Localizations.localeOf(context).languageCode, - ) + .getExercise(Localizations.localeOf(context).languageCode) .name, overflow: TextOverflow.ellipsis, ), @@ -61,7 +59,9 @@ class Step2Variations extends StatelessWidget { ), ), // Exercise bases without variations - ...exerciseProvider.exercises.where((b) => b.variationId == null).map( + ...exerciseProvider.exercises + .where((b) => b.variationId == null) + .map( (base) => Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -72,9 +72,7 @@ class Step2Variations extends StatelessWidget { children: [ Text( base - .getExercise( - Localizations.localeOf(context).languageCode, - ) + .getExercise(Localizations.localeOf(context).languageCode) .name, overflow: TextOverflow.ellipsis, ), diff --git a/lib/widgets/add_exercise/steps/step3description.dart b/lib/widgets/add_exercise/steps/step3description.dart index fad46ba86..8df2b7434 100644 --- a/lib/widgets/add_exercise/steps/step3description.dart +++ b/lib/widgets/add_exercise/steps/step3description.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/exercises/forms.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/widgets/add_exercise/add_exercise_text_area.dart'; diff --git a/lib/widgets/add_exercise/steps/step4translations.dart b/lib/widgets/add_exercise/steps/step4translations.dart index 284ed62dd..876013c49 100644 --- a/lib/widgets/add_exercise/steps/step4translations.dart +++ b/lib/widgets/add_exercise/steps/step4translations.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/exercises/forms.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/language.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/providers/exercises.dart'; @@ -74,10 +74,9 @@ class _Step4TranslationState extends State { final names = alternateNames!.split('\n'); for (final name in names) { if (name.length < MIN_CHARS_NAME || name.length > MAX_CHARS_NAME) { - return AppLocalizations.of(context).enterCharacters( - MIN_CHARS_NAME, - MAX_CHARS_NAME, - ); + return AppLocalizations.of( + context, + ).enterCharacters(MIN_CHARS_NAME, MAX_CHARS_NAME); } } } diff --git a/lib/widgets/add_exercise/steps/step5images.dart b/lib/widgets/add_exercise/steps/step5images.dart index eb3b3bdd2..e029585ef 100644 --- a/lib/widgets/add_exercise/steps/step5images.dart +++ b/lib/widgets/add_exercise/steps/step5images.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/widgets/add_exercise/mixins/image_picker_mixin.dart'; import 'package:wger/widgets/add_exercise/preview_images.dart'; @@ -26,9 +26,7 @@ class _Step5ImagesState extends State with ExerciseImagePickerMixin ), Consumer( builder: (ctx, provider, __) => provider.exerciseImages.isNotEmpty - ? PreviewExerciseImages( - selectedImages: provider.exerciseImages, - ) + ? PreviewExerciseImages(selectedImages: provider.exerciseImages) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/widgets/core/about.dart b/lib/widgets/core/about.dart index 385690cb6..3adca94c0 100644 --- a/lib/widgets/core/about.dart +++ b/lib/widgets/core/about.dart @@ -17,10 +17,10 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/misc.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/auth.dart'; class AboutEntry extends StatelessWidget { @@ -66,9 +66,7 @@ class AboutPage extends StatelessWidget { final today = DateTime.now(); return Scaffold( - appBar: AppBar( - title: Text(AppLocalizations.of(context).aboutPageTitle), - ), + appBar: AppBar(title: Text(AppLocalizations.of(context).aboutPageTitle)), body: SingleChildScrollView( padding: const EdgeInsets.all(20.0), child: Column( @@ -89,13 +87,12 @@ class AboutPage extends StatelessWidget { children: [ const Text( 'Wger', - style: TextStyle( - fontSize: 23, - fontWeight: FontWeight.w600, - ), + style: TextStyle(fontSize: 23, fontWeight: FontWeight.w600), + ), + Text( + 'App: ${authProvider.applicationVersion!.version}\n' + 'Server: ${authProvider.serverVersion}', ), - Text('App: ${authProvider.applicationVersion!.version}\n' - 'Server: ${authProvider.serverVersion}'), ], ), ], @@ -163,7 +160,8 @@ class AboutPage extends StatelessWidget { showLicensePage( context: context, applicationName: 'wger', - applicationVersion: 'App: ${authProvider.applicationVersion!.version}\n' + applicationVersion: + 'App: ${authProvider.applicationVersion!.version}\n' 'Server: ${authProvider.serverVersion}', applicationLegalese: '\u{a9} 2020 - 2021 contributors', applicationIcon: Padding( diff --git a/lib/widgets/core/app_bar.dart b/lib/widgets/core/app_bar.dart index e82f242f7..0cb659bf5 100644 --- a/lib/widgets/core/app_bar.dart +++ b/lib/widgets/core/app_bar.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/gallery.dart'; @@ -50,9 +50,7 @@ class MainAppBar extends StatelessWidget implements PreferredSizeWidget { title: Text(AppLocalizations.of(context).optionsLabel), actions: [ TextButton( - child: Text( - MaterialLocalizations.of(context).closeButtonLabel, - ), + child: Text(MaterialLocalizations.of(context).closeButtonLabel), onPressed: () => Navigator.of(context).pop(), ), ], @@ -69,9 +67,7 @@ class MainAppBar extends StatelessWidget implements PreferredSizeWidget { FormScreen.routeName, arguments: FormScreenArguments( AppLocalizations.of(context).userProfile, - UserProfileForm( - context.read().profile!, - ), + UserProfileForm(context.read().profile!), ), ), ), diff --git a/lib/widgets/core/settings.dart b/lib/widgets/core/settings.dart index 78c8dfa9e..e036810d0 100644 --- a/lib/widgets/core/settings.dart +++ b/lib/widgets/core/settings.dart @@ -18,8 +18,8 @@ //import 'package:drift/drift.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/nutrition.dart'; @@ -34,9 +34,7 @@ class SettingsPage extends StatelessWidget { final nutritionProvider = Provider.of(context, listen: false); return Scaffold( - appBar: AppBar( - title: Text(AppLocalizations.of(context).settingsTitle), - ), + appBar: AppBar(title: Text(AppLocalizations.of(context).settingsTitle)), body: ListView( children: [ ListTile( diff --git a/lib/widgets/core/text_prompt.dart b/lib/widgets/core/text_prompt.dart index 397f3e1a8..72cc3ff72 100644 --- a/lib/widgets/core/text_prompt.dart +++ b/lib/widgets/core/text_prompt.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; class TextPrompt extends StatelessWidget { const TextPrompt(); diff --git a/lib/widgets/dashboard/calendar.dart b/lib/widgets/dashboard/calendar.dart index 9c1eb232e..4451cc97e 100644 --- a/lib/widgets/dashboard/calendar.dart +++ b/lib/widgets/dashboard/calendar.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/helpers/misc.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/session.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/measurement.dart'; @@ -31,12 +31,7 @@ import 'package:wger/providers/workout_plans.dart'; import 'package:wger/theme/theme.dart'; /// Types of events -enum EventType { - weight, - measurement, - session, - caloriesDiary, -} +enum EventType { weight, measurement, session, caloriesDiary } /// An event in the dashboard calendar class Event { @@ -84,8 +79,10 @@ class _DashboardCalendarWidgetState extends State void loadEvents() async { // Process weight entries - final BodyWeightProvider weightProvider = - Provider.of(context, listen: false); + final BodyWeightProvider weightProvider = Provider.of( + context, + listen: false, + ); for (final entry in weightProvider.items) { final date = DateFormatLists.format(entry.date); @@ -98,8 +95,10 @@ class _DashboardCalendarWidgetState extends State } // Process measurements - final MeasurementProvider measurementProvider = - Provider.of(context, listen: false); + final MeasurementProvider measurementProvider = Provider.of( + context, + listen: false, + ); for (final category in measurementProvider.categories) { for (final entry in category.entries) { final date = DateFormatLists.format(entry.date); @@ -108,10 +107,9 @@ class _DashboardCalendarWidgetState extends State _events[date] = []; } - _events[date]!.add(Event( - EventType.measurement, - '${category.name}: ${entry.value} ${category.unit}', - )); + _events[date]!.add( + Event(EventType.measurement, '${category.name}: ${entry.value} ${category.unit}'), + ); } } @@ -128,16 +126,20 @@ class _DashboardCalendarWidgetState extends State time = '(${timeToString(session.timeStart)} - ${timeToString(session.timeEnd)})'; // Add events to lists - _events[date]!.add(Event( - EventType.session, - '${AppLocalizations.of(context).impression}: ${session.impressionAsString} $time', - )); + _events[date]!.add( + Event( + EventType.session, + '${AppLocalizations.of(context).impression}: ${session.impressionAsString} $time', + ), + ); } }); // Process nutritional plans - final NutritionPlansProvider nutritionProvider = - Provider.of(context, listen: false); + final NutritionPlansProvider nutritionProvider = Provider.of( + context, + listen: false, + ); for (final plan in nutritionProvider.items) { for (final entry in plan.logEntriesValues.entries) { final date = DateFormatLists.format(entry.key); @@ -146,10 +148,9 @@ class _DashboardCalendarWidgetState extends State } // Add events to lists - _events[date]!.add(Event( - EventType.caloriesDiary, - '${entry.value.energy.toStringAsFixed(0)} kcal', - )); + _events[date]!.add( + Event(EventType.caloriesDiary, '${entry.value.energy.toStringAsFixed(0)} kcal'), + ); } } @@ -247,8 +248,10 @@ class _DashboardCalendarWidgetState extends State valueListenable: _selectedEvents, builder: (context, value, _) => Column( children: [ - ...value.map((event) => ListTile( - title: Text((() { + ...value.map( + (event) => ListTile( + title: Text( + (() { switch (event.type) { case EventType.caloriesDiary: return AppLocalizations.of(context).nutritionalDiary; @@ -262,10 +265,12 @@ class _DashboardCalendarWidgetState extends State case EventType.measurement: return AppLocalizations.of(context).measurement; } - })()), - subtitle: Text(event.description), - //onTap: () => print('$event tapped!'), - )), + })(), + ), + subtitle: Text(event.description), + //onTap: () => print('$event tapped!'), + ), + ), ], ), ), diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index 1397e1568..6140d2a7a 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -20,11 +20,11 @@ import 'dart:async'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg_icons/flutter_svg_icons.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/providers/body_weight.dart'; @@ -66,8 +66,10 @@ class _DashboardNutritionWidgetState extends State { @override void initState() { super.initState(); - final stream = - Provider.of(context, listen: false).watchNutritionPlanLast(); + final stream = Provider.of( + context, + listen: false, + ).watchNutritionPlanLast(); _subscription = stream.listen((plan) { if (!context.mounted) { return; @@ -97,8 +99,9 @@ class _DashboardNutritionWidgetState extends State { ), subtitle: Text( _hasContent - ? DateFormat.yMd(Localizations.localeOf(context).languageCode) - .format(_plan!.creationDate) + ? DateFormat.yMd( + Localizations.localeOf(context).languageCode, + ).format(_plan!.creationDate) : '', ), leading: Icon( @@ -127,17 +130,14 @@ class _DashboardNutritionWidgetState extends State { TextButton( child: Text(AppLocalizations.of(context).goToDetailPage), onPressed: () { - Navigator.of(context).pushNamed( - NutritionalPlanScreen.routeName, - arguments: _plan!.id, - ); + Navigator.of( + context, + ).pushNamed(NutritionalPlanScreen.routeName, arguments: _plan!.id); }, ), Expanded(child: Container()), IconButton( - icon: const SvgIcon( - icon: SvgIconData('assets/icons/ingredient-diary.svg'), - ), + icon: const SvgIcon(icon: SvgIconData('assets/icons/ingredient-diary.svg')), tooltip: AppLocalizations.of(context).logIngredient, onPressed: () { Navigator.pushNamed( @@ -152,9 +152,7 @@ class _DashboardNutritionWidgetState extends State { }, ), IconButton( - icon: const SvgIcon( - icon: SvgIconData('assets/icons/meal-diary.svg'), - ), + icon: const SvgIcon(icon: SvgIconData('assets/icons/meal-diary.svg')), tooltip: AppLocalizations.of(context).logMeal, onPressed: () { Navigator.of(context).pushNamed(LogMealsScreen.routeName, arguments: _plan); @@ -223,9 +221,7 @@ class _DashboardWeightWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton( - child: Text( - AppLocalizations.of(context).goToDetailPage, - ), + child: Text(AppLocalizations.of(context).goToDetailPage), onPressed: () { Navigator.of(context).pushNamed(WeightScreen.routeName); }, @@ -238,9 +234,12 @@ class _DashboardWeightWidgetState extends State { FormScreen.routeName, arguments: FormScreenArguments( AppLocalizations.of(context).newEntry, - WeightForm(weightProvider - .getNewestEntry() - ?.copyWith(id: null, date: DateTime.now())), + WeightForm( + weightProvider.getNewestEntry()?.copyWith( + id: null, + date: DateTime.now(), + ), + ), ), ); }, @@ -279,8 +278,9 @@ class _DashboardMeasurementWidgetState extends State Widget build(BuildContext context) { final provider = Provider.of(context, listen: false); - final items = - provider.categories.map((item) => CategoriesCard(item, elevation: 0)).toList(); + final items = provider.categories + .map((item) => CategoriesCard(item, elevation: 0)) + .toList(); if (items.isNotEmpty) { items.add( NothingFound( @@ -309,10 +309,8 @@ class _DashboardMeasurementWidgetState extends State // maybe we should just add a "Go to all" at the bottom of the widget trailing: IconButton( icon: const Icon(Icons.arrow_forward), - onPressed: () => Navigator.pushNamed( - context, - MeasurementCategoriesScreen.routeName, - ), + onPressed: () => + Navigator.pushNamed(context, MeasurementCategoriesScreen.routeName), ), ), Column( @@ -346,16 +344,11 @@ class _DashboardMeasurementWidgetState extends State child: Container( width: 12.0, height: 12.0, - margin: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 4.0, - ), + margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), decoration: BoxDecoration( shape: BoxShape.circle, - color: - Theme.of(context).textTheme.headlineSmall!.color!.withOpacity( - _current == entry.key ? 0.9 : 0.4, - ), + color: Theme.of(context).textTheme.headlineSmall!.color! + .withOpacity(_current == entry.key ? 0.9 : 0.4), ), ), ); @@ -407,61 +400,63 @@ class _DashboardWorkoutWidgetState extends State { } for (final day in _workoutPlan!.days) { - out.add(SizedBox( - width: double.infinity, - child: Row( - children: [ - Expanded( - child: Text( - day.description, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, + out.add( + SizedBox( + width: double.infinity, + child: Row( + children: [ + Expanded( + child: Text( + day.description, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), ), - ), - Expanded( - child: MutedText(day.getDaysText, textAlign: TextAlign.right), - ), - IconButton( - icon: const Icon(Icons.play_arrow), - color: wgerPrimaryButtonColor, - onPressed: () { - Navigator.of(context).pushNamed(GymModeScreen.routeName, arguments: day); - }, - ), - ], + Expanded(child: MutedText(day.getDaysText, textAlign: TextAlign.right)), + IconButton( + icon: const Icon(Icons.play_arrow), + color: wgerPrimaryButtonColor, + onPressed: () { + Navigator.of(context).pushNamed(GymModeScreen.routeName, arguments: day); + }, + ), + ], + ), ), - )); + ); for (final set in day.sets) { - out.add(SizedBox( - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...set.settingsFiltered.map((s) { - return _showDetail - ? Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(s.exerciseObj - .getExercise(Localizations.localeOf(context).languageCode) - .name), - const SizedBox(width: 10), - MutedText( - set.getSmartRepr(s.exerciseObj).join('\n'), - ), - ], - ), - const SizedBox(height: 10), - ], - ) - : Container(); - }), - ], + out.add( + SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...set.settingsFiltered.map((s) { + return _showDetail + ? Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + s.exerciseObj + .getExercise(Localizations.localeOf(context).languageCode) + .name, + ), + const SizedBox(width: 10), + MutedText(set.getSmartRepr(s.exerciseObj).join('\n')), + ], + ), + const SizedBox(height: 10), + ], + ) + : Container(); + }), + ], + ), ), - )); + ); } out.add(const Divider()); } @@ -481,8 +476,9 @@ class _DashboardWorkoutWidgetState extends State { ), subtitle: Text( _hasContent - ? DateFormat.yMd(Localizations.localeOf(context).languageCode) - .format(_workoutPlan!.creationDate) + ? DateFormat.yMd( + Localizations.localeOf(context).languageCode, + ).format(_workoutPlan!.creationDate) : '', ), leading: Icon( @@ -519,10 +515,9 @@ class _DashboardWorkoutWidgetState extends State { TextButton( child: Text(AppLocalizations.of(context).goToDetailPage), onPressed: () { - Navigator.of(context).pushNamed( - WorkoutPlanScreen.routeName, - arguments: _workoutPlan, - ); + Navigator.of( + context, + ).pushNamed(WorkoutPlanScreen.routeName, arguments: _workoutPlan); }, ), ], @@ -555,11 +550,7 @@ class NothingFound extends StatelessWidget { Navigator.pushNamed( context, FormScreen.routeName, - arguments: FormScreenArguments( - _titleForm, - hasListView: true, - _form, - ), + arguments: FormScreenArguments(_titleForm, hasListView: true, _form), ); }, ), diff --git a/lib/widgets/exercises/exercises.dart b/lib/widgets/exercises/exercises.dart index bfd515bf8..61147eb94 100644 --- a/lib/widgets/exercises/exercises.dart +++ b/lib/widgets/exercises/exercises.dart @@ -17,13 +17,13 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/i18n.dart'; import 'package:wger/helpers/platform.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/exercises/muscle.dart'; import 'package:wger/models/exercises/translation.dart'; @@ -83,18 +83,20 @@ class ExerciseDetail extends StatelessWidget { return out; } - out.add(Text( - AppLocalizations.of(context).variations, - style: Theme.of(context).textTheme.headlineSmall, - )); + out.add( + Text( + AppLocalizations.of(context).variations, + style: Theme.of(context).textTheme.headlineSmall, + ), + ); Provider.of(context, listen: false) .findExercisesByVariationId( - _exerciseBase.variationId!, - exerciseBaseIdToExclude: _exerciseBase.id, - ) + _exerciseBase.variationId!, + exerciseBaseIdToExclude: _exerciseBase.id, + ) .forEach((element) { - out.add(ExerciseListTile(exerciseBase: element)); - }); + out.add(ExerciseListTile(exerciseBase: element)); + }); out.add(const SizedBox(height: PADDING)); return out; @@ -103,10 +105,9 @@ class ExerciseDetail extends StatelessWidget { List getNotes(BuildContext context) { final List out = []; if (_exercise.notes.isNotEmpty) { - out.add(Text( - AppLocalizations.of(context).notes, - style: Theme.of(context).textTheme.headlineSmall, - )); + out.add( + Text(AppLocalizations.of(context).notes, style: Theme.of(context).textTheme.headlineSmall), + ); for (final e in _exercise.notes) { out.add(Text(e.comment)); } @@ -118,36 +119,37 @@ class ExerciseDetail extends StatelessWidget { List getMuscles(BuildContext context) { final List out = []; - out.add(Text( - AppLocalizations.of(context).muscles, - style: Theme.of(context).textTheme.headlineSmall, - )); - out.add(Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: PADDING), - child: MuscleWidget( - muscles: _exerciseBase.muscles, - musclesSecondary: _exerciseBase.musclesSecondary, - isFront: true, + out.add( + Text(AppLocalizations.of(context).muscles, style: Theme.of(context).textTheme.headlineSmall), + ); + out.add( + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: PADDING), + child: MuscleWidget( + muscles: _exerciseBase.muscles, + musclesSecondary: _exerciseBase.musclesSecondary, + isFront: true, + ), ), ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: PADDING), - child: MuscleWidget( - muscles: _exerciseBase.muscles, - musclesSecondary: _exerciseBase.musclesSecondary, - isFront: false, + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: PADDING), + child: MuscleWidget( + muscles: _exerciseBase.muscles, + musclesSecondary: _exerciseBase.musclesSecondary, + isFront: false, + ), ), ), - ), - ], - )); + ], + ), + ); out.add( Column( @@ -176,10 +178,12 @@ class ExerciseDetail extends StatelessWidget { List getDescription(BuildContext context) { final List out = []; - out.add(Text( - AppLocalizations.of(context).description, - style: Theme.of(context).textTheme.headlineSmall, - )); + out.add( + Text( + AppLocalizations.of(context).description, + style: Theme.of(context).textTheme.headlineSmall, + ), + ); out.add(Html(data: _exercise.description)); return out; @@ -212,14 +216,16 @@ class ExerciseDetail extends StatelessWidget { ); if (_exerciseBase.equipment.isNotEmpty) { _exerciseBase.equipment - .map((e) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Chip( - label: Text(getTranslation(e.name, context)), - padding: EdgeInsets.zero, - backgroundColor: theme.splashColor, - ), - )) + .map( + (e) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Chip( + label: Text(getTranslation(e.name, context)), + padding: EdgeInsets.zero, + backgroundColor: theme.splashColor, + ), + ), + ) .forEach((element) => out.add(element)); } out.add(const SizedBox(height: PADDING)); @@ -242,11 +248,13 @@ class ExerciseDetail extends StatelessWidget { List getAliases(BuildContext context) { final List out = []; if (_exercise.aliases.isNotEmpty) { - out.add(MutedText( - AppLocalizations.of(context).alsoKnownAs( - _exercise.aliases.map((e) => e.alias).toList().join(', '), + out.add( + MutedText( + AppLocalizations.of( + context, + ).alsoKnownAs(_exercise.aliases.map((e) => e.alias).toList().join(', ')), ), - )); + ); out.add(const SizedBox(height: PADDING)); } @@ -284,10 +292,7 @@ class MuscleRowWidget extends StatelessWidget { final List muscles; final List musclesSecondary; - const MuscleRowWidget({ - required this.muscles, - required this.musclesSecondary, - }); + const MuscleRowWidget({required this.muscles, required this.musclesSecondary}); @override Widget build(BuildContext context) { @@ -342,9 +347,9 @@ class MuscleWidget extends StatelessWidget { children: [ SvgPicture.asset('assets/images/muscles/$background.svg'), ...muscles.map((m) => SvgPicture.asset('assets/images/muscles/main/muscle-${m.id}.svg')), - ...musclesSecondary.map((m) => SvgPicture.asset( - 'assets/images/muscles/secondary/muscle-${m.id}.svg', - )), + ...musclesSecondary.map( + (m) => SvgPicture.asset('assets/images/muscles/secondary/muscle-${m.id}.svg'), + ), ], ); } diff --git a/lib/widgets/exercises/filter_row.dart b/lib/widgets/exercises/filter_row.dart index 807c530f0..0106f08d7 100644 --- a/lib/widgets/exercises/filter_row.dart +++ b/lib/widgets/exercises/filter_row.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/screens/add_exercise_screen.dart'; @@ -59,9 +59,7 @@ class _FilterRowState extends State { decoration: InputDecoration( hintText: '${AppLocalizations.of(context).exerciseName}...', contentPadding: const EdgeInsets.symmetric(horizontal: 10), - border: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black), - ), + border: const OutlineInputBorder(borderSide: BorderSide(color: Colors.black)), ), ), ), diff --git a/lib/widgets/gallery/forms.dart b/lib/widgets/gallery/forms.dart index 218a12976..418afedfa 100644 --- a/lib/widgets/gallery/forms.dart +++ b/lib/widgets/gallery/forms.dart @@ -19,11 +19,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:image_picker/image_picker.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/gallery/image.dart' as gallery; import 'package:wger/providers/gallery.dart'; @@ -126,9 +126,7 @@ class _ImageFormState extends State { _showPicker(ImageSource.gallery); }, leading: const Icon(Icons.photo_library), - title: Text( - AppLocalizations.of(context).chooseFromLibrary, - ), + title: Text(AppLocalizations.of(context).chooseFromLibrary), ), ], ), @@ -174,9 +172,7 @@ class _ImageFormState extends State { ), TextFormField( key: const Key('field-description'), - decoration: InputDecoration( - labelText: AppLocalizations.of(context).description, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).description), minLines: 3, maxLines: 10, controller: descriptionController, @@ -196,12 +192,16 @@ class _ImageFormState extends State { _form.currentState!.save(); if (widget._image.id == null) { - Provider.of(context, listen: false) - .addImage(widget._image, _file!); + Provider.of( + context, + listen: false, + ).addImage(widget._image, _file!); Navigator.of(context).pop(); } else { - Provider.of(context, listen: false) - .editImage(widget._image, _file); + Provider.of( + context, + listen: false, + ).editImage(widget._image, _file); Navigator.of(context).pop(); } }, diff --git a/lib/widgets/gallery/overview.dart b/lib/widgets/gallery/overview.dart index 0301b2c9a..468f4a29b 100644 --- a/lib/widgets/gallery/overview.dart +++ b/lib/widgets/gallery/overview.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/platform.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/gallery.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/widgets/core/text_prompt.dart'; @@ -58,13 +58,12 @@ class Gallery extends StatelessWidget { child: Column( children: [ Text( - DateFormat.yMd(Localizations.localeOf(context).languageCode) - .format(currentImage.date), + DateFormat.yMd( + Localizations.localeOf(context).languageCode, + ).format(currentImage.date), style: Theme.of(context).textTheme.headlineSmall, ), - Expanded( - child: Image.network(currentImage.url!), - ), + Expanded(child: Image.network(currentImage.url!)), Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text(currentImage.description), diff --git a/lib/widgets/measurements/categories_card.dart b/lib/widgets/measurements/categories_card.dart index f57c11a1a..c4a864657 100644 --- a/lib/widgets/measurements/categories_card.dart +++ b/lib/widgets/measurements/categories_card.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/measurement_entries_screen.dart'; import 'package:wger/widgets/measurements/helpers.dart'; + import 'charts.dart'; import 'forms.dart'; @@ -26,19 +26,12 @@ class CategoriesCard extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(top: 5), - child: Text( - currentCategory.name, - style: Theme.of(context).textTheme.titleLarge, - ), + child: Text(currentCategory.name, style: Theme.of(context).textTheme.titleLarge), ), Container( padding: const EdgeInsets.all(10), height: 220, - child: MeasurementChartWidgetFl( - entriesAll, - currentCategory.unit, - avgs: entries7dAvg, - ), + child: MeasurementChartWidgetFl(entriesAll, currentCategory.unit, avgs: entries7dAvg), ), if (entries7dAvg.isNotEmpty) MeasurementOverallChangeWidget( diff --git a/lib/widgets/measurements/charts.dart b/lib/widgets/measurements/charts.dart index e7ca60ce5..614684ce7 100644 --- a/lib/widgets/measurements/charts.dart +++ b/lib/widgets/measurements/charts.dart @@ -18,9 +18,9 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:wger/helpers/charts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; class MeasurementOverallChangeWidget extends StatelessWidget { final MeasurementChartEntry _first; @@ -34,8 +34,8 @@ class MeasurementOverallChangeWidget extends StatelessWidget { final prefix = delta > 0 ? '+' : delta < 0 - ? '-' - : ''; + ? '-' + : ''; return Text('overall change $prefix ${delta.abs().toStringAsFixed(1)} $_unit'); } @@ -61,10 +61,7 @@ class _MeasurementChartWidgetFlState extends State { Widget build(BuildContext context) { return AspectRatio( aspectRatio: 1.70, - child: Padding( - padding: const EdgeInsets.all(4), - child: LineChart(mainData()), - ), + child: Padding(padding: const EdgeInsets.all(4), child: LineChart(mainData())), ); } @@ -75,15 +72,13 @@ class _MeasurementChartWidgetFlState extends State { getTooltipItems: (touchedSpots) { return touchedSpots.map((touchedSpot) { final DateTime date = DateTime.fromMillisecondsSinceEpoch(touchedSpot.x.toInt()); - final dateStr = - DateFormat.Md(Localizations.localeOf(context).languageCode).format(date); + final dateStr = DateFormat.Md( + Localizations.localeOf(context).languageCode, + ).format(date); return LineTooltipItem( '$dateStr: ${touchedSpot.y.toStringAsFixed(1)} ${widget._unit}', - TextStyle( - color: touchedSpot.bar.color, - fontWeight: FontWeight.bold, - ), + TextStyle(color: touchedSpot.bar.color, fontWeight: FontWeight.bold), ); }).toList(); }, @@ -108,12 +103,8 @@ class _MeasurementChartWidgetFlState extends State { ), titlesData: FlTitlesData( show: true, - rightTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), - topTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, @@ -132,15 +123,10 @@ class _MeasurementChartWidgetFlState extends State { DateFormat.yMd(Localizations.localeOf(context).languageCode).format(date), ); } - return Text( - DateFormat.Md(Localizations.localeOf(context).languageCode).format(date), - ); + return Text(DateFormat.Md(Localizations.localeOf(context).languageCode).format(date)); }, interval: widget._entries.isNotEmpty - ? chartGetInterval( - widget._entries.last.date, - widget._entries.first.date, - ) + ? chartGetInterval(widget._entries.last.date, widget._entries.first.date) : 1000, ), ), @@ -168,10 +154,7 @@ class _MeasurementChartWidgetFlState extends State { lineBarsData: [ LineChartBarData( spots: widget._entries - .map((e) => FlSpot( - e.date.millisecondsSinceEpoch.toDouble(), - e.value.toDouble(), - )) + .map((e) => FlSpot(e.date.millisecondsSinceEpoch.toDouble(), e.value.toDouble())) .toList(), isCurved: false, color: Theme.of(context).colorScheme.primary, @@ -182,10 +165,7 @@ class _MeasurementChartWidgetFlState extends State { if (widget.avgs != null) LineChartBarData( spots: widget.avgs! - .map((e) => FlSpot( - e.date.millisecondsSinceEpoch.toDouble(), - e.value.toDouble(), - )) + .map((e) => FlSpot(e.date.millisecondsSinceEpoch.toDouble(), e.value.toDouble())) .toList(), isCurved: false, color: Theme.of(context).colorScheme.tertiary, diff --git a/lib/widgets/measurements/entries.dart b/lib/widgets/measurements/entries.dart index 68a3db34b..b20b623ac 100644 --- a/lib/widgets/measurements/entries.dart +++ b/lib/widgets/measurements/entries.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/providers/measurement.dart'; import 'package:wger/providers/nutrition.dart'; @@ -38,82 +38,80 @@ class EntriesList extends StatelessWidget { Widget build(BuildContext context) { final plan = Provider.of(context, listen: false).currentPlan; - final entriesAll = - _category.entries.map((e) => MeasurementChartEntry(e.value, e.date)).toList(); + final entriesAll = _category.entries + .map((e) => MeasurementChartEntry(e.value, e.date)) + .toList(); final entries7dAvg = moving7dAverage(entriesAll); - return Column(children: [ - ...getOverviewWidgetsSeries( - _category.name, - entriesAll, - entries7dAvg, - plan, - _category.unit, - context, - ), - SizedBox( - height: 300, - child: ListView.builder( - padding: const EdgeInsets.all(10.0), - itemCount: _category.entries.length, - itemBuilder: (context, index) { - final currentEntry = _category.entries[index]; - final provider = Provider.of(context, listen: false); + return Column( + children: [ + ...getOverviewWidgetsSeries( + _category.name, + entriesAll, + entries7dAvg, + plan, + _category.unit, + context, + ), + SizedBox( + height: 300, + child: ListView.builder( + padding: const EdgeInsets.all(10.0), + itemCount: _category.entries.length, + itemBuilder: (context, index) { + final currentEntry = _category.entries[index]; + final provider = Provider.of(context, listen: false); - return Card( - child: ListTile( - title: Text('${currentEntry.value} ${_category.unit}'), - subtitle: Text( - DateFormat.yMd(Localizations.localeOf(context).languageCode) - .format(currentEntry.date), - ), - trailing: PopupMenuButton( - itemBuilder: (BuildContext context) { - return [ - PopupMenuItem( - child: Text(AppLocalizations.of(context).edit), - onTap: () => Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).edit, - MeasurementEntryForm( - currentEntry.category, - currentEntry, + return Card( + child: ListTile( + title: Text('${currentEntry.value} ${_category.unit}'), + subtitle: Text( + DateFormat.yMd( + Localizations.localeOf(context).languageCode, + ).format(currentEntry.date), + ), + trailing: PopupMenuButton( + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + child: Text(AppLocalizations.of(context).edit), + onTap: () => Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).edit, + MeasurementEntryForm(currentEntry.category, currentEntry), ), ), ), - ), - PopupMenuItem( - child: Text(AppLocalizations.of(context).delete), - onTap: () async { - // Delete entry from DB - await provider.deleteEntry( - currentEntry.id!, - currentEntry.category, - ); + PopupMenuItem( + child: Text(AppLocalizations.of(context).delete), + onTap: () async { + // Delete entry from DB + await provider.deleteEntry(currentEntry.id!, currentEntry.category); - // and inform the user - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, + // and inform the user + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, + ), ), - ), - ); - } - }, - ), - ]; - }, + ); + } + }, + ), + ]; + }, + ), ), - ), - ); - }, + ); + }, + ), ), - ), - ]); + ], + ); } } diff --git a/lib/widgets/measurements/forms.dart b/lib/widgets/measurements/forms.dart index 57fa0d370..4beb7bc9d 100644 --- a/lib/widgets/measurements/forms.dart +++ b/lib/widgets/measurements/forms.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/helpers/ui.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/models/measurements/measurement_entry.dart'; import 'package:wger/providers/measurement.dart'; @@ -31,11 +31,7 @@ class MeasurementCategoryForm extends StatelessWidget { final nameController = TextEditingController(); final unitController = TextEditingController(); - final Map categoryData = { - 'id': null, - 'name': '', - 'unit': '', - }; + final Map categoryData = {'id': null, 'name': '', 'unit': ''}; MeasurementCategoryForm([MeasurementCategory? category]) { //this._category = category ?? MeasurementCategory(); @@ -103,20 +99,14 @@ class MeasurementCategoryForm extends StatelessWidget { // Save the entry on the server try { categoryData['id'] == null - ? await Provider.of( - context, - listen: false, - ).addCategory( + ? await Provider.of(context, listen: false).addCategory( MeasurementCategory( id: categoryData['id'], name: categoryData['name'], unit: categoryData['unit'], ), ) - : await Provider.of( - context, - listen: false, - ).editCategory( + : await Provider.of(context, listen: false).editCategory( categoryData['id'], categoryData['name'], categoryData['unit'], @@ -274,20 +264,16 @@ class MeasurementEntryForm extends StatelessWidget { // Save the entry on the server try { _entryData['id'] == null - ? await Provider.of( - context, - listen: false, - ).addEntry(MeasurementEntry( - id: _entryData['id'], - category: _entryData['category'], - date: _entryData['date'], - value: _entryData['value'], - notes: _entryData['notes'], - )) - : await Provider.of( - context, - listen: false, - ).editEntry( + ? await Provider.of(context, listen: false).addEntry( + MeasurementEntry( + id: _entryData['id'], + category: _entryData['category'], + date: _entryData['date'], + value: _entryData['value'], + notes: _entryData['notes'], + ), + ) + : await Provider.of(context, listen: false).editEntry( _entryData['id'], _entryData['category'], _entryData['value'], diff --git a/lib/widgets/measurements/helpers.dart b/lib/widgets/measurements/helpers.dart index 851394548..63dfd9a7e 100644 --- a/lib/widgets/measurements/helpers.dart +++ b/lib/widgets/measurements/helpers.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/widgets/measurements/charts.dart'; @@ -11,11 +11,7 @@ List getOverviewWidgets( BuildContext context, ) { return [ - Text( - title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleLarge, - ), + Text(title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge), Container( padding: const EdgeInsets.all(15), height: 220, diff --git a/lib/widgets/nutrition/charts.dart b/lib/widgets/nutrition/charts.dart index c429f0a83..a1463b430 100644 --- a/lib/widgets/nutrition/charts.dart +++ b/lib/widgets/nutrition/charts.dart @@ -20,8 +20,8 @@ import 'dart:math'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:wger/helpers/colors.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; import 'package:wger/widgets/measurements/charts.dart'; @@ -34,10 +34,8 @@ import 'package:wger/widgets/measurements/charts.dart'; // * here we draw our own simple gauges that can go beyond 100%, // and support multiple segments class FlNutritionalPlanGoalWidget extends StatelessWidget { - const FlNutritionalPlanGoalWidget({ - super.key, - required NutritionalPlan nutritionalPlan, - }) : _nutritionalPlan = nutritionalPlan; + const FlNutritionalPlanGoalWidget({super.key, required NutritionalPlan nutritionalPlan}) + : _nutritionalPlan = nutritionalPlan; final NutritionalPlan _nutritionalPlan; @@ -47,20 +45,12 @@ class FlNutritionalPlanGoalWidget extends StatelessWidget { // why don't we just handle this inside this function? because it might be // *another* gauge that's in surplus and we want to have consistent widths // between all gauges - Widget _diyGauge( - BuildContext context, - double normWidth, - double? plan, - double val, - ) { + Widget _diyGauge(BuildContext context, double normWidth, double? plan, double val) { Container segment(double width, Color color) { return Container( height: 16, width: width, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(15.0), - ), + decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(15.0)), ); } @@ -71,17 +61,21 @@ class FlNutritionalPlanGoalWidget extends StatelessWidget { // paint a surplus if (val > plan) { - return Stack(children: [ - segment(normWidth * val / plan, COLOR_SURPLUS), - segment(normWidth, LIST_OF_COLORS8[0]), - ]); + return Stack( + children: [ + segment(normWidth * val / plan, COLOR_SURPLUS), + segment(normWidth, LIST_OF_COLORS8[0]), + ], + ); } // paint a deficit - return Stack(children: [ - segment(normWidth, Theme.of(context).colorScheme.surface), - segment(normWidth * val / plan, LIST_OF_COLORS8[0]), - ]); + return Stack( + children: [ + segment(normWidth, Theme.of(context).colorScheme.surface), + segment(normWidth * val / plan, LIST_OF_COLORS8[0]), + ], + ); } @override @@ -90,87 +84,97 @@ class FlNutritionalPlanGoalWidget extends StatelessWidget { final goals = plan.nutritionalGoals; final today = plan.loggedNutritionalValuesToday; - return LayoutBuilder(builder: (context, constraints) { - // if any of the bars goes over 100%, find the one that goes over the most - // that one needs the most horizontal space to show how much it goes over, - // and therefore reduces the width of "100%" the most, and this width we want - // to be consistent for all other bars. - // if none goes over, 100% means fill all available space - final maxVal = [ - 1.0, - if (goals.energy != null && goals.energy! > 0) today.energy / goals.energy!, - if (goals.protein != null && goals.protein! > 0) today.protein / goals.protein!, - if (goals.carbohydrates != null && goals.carbohydrates! > 0) - today.carbohydrates / goals.carbohydrates!, - if (goals.fat != null && goals.fat! > 0) today.fat / goals.fat!, - if (goals.fiber != null && goals.fiber! > 0) today.fiber / goals.fiber!, - ].reduce(max); - - final normWidth = constraints.maxWidth / maxVal; - - String fmtMacro(String name, double today, double? goal, String unit) { - return '$name: ${today.toStringAsFixed(0)}${goal == null ? '' : ' / ${goal.toStringAsFixed(0)}'} $unit'; - } + return LayoutBuilder( + builder: (context, constraints) { + // if any of the bars goes over 100%, find the one that goes over the most + // that one needs the most horizontal space to show how much it goes over, + // and therefore reduces the width of "100%" the most, and this width we want + // to be consistent for all other bars. + // if none goes over, 100% means fill all available space + final maxVal = [ + 1.0, + if (goals.energy != null && goals.energy! > 0) today.energy / goals.energy!, + if (goals.protein != null && goals.protein! > 0) today.protein / goals.protein!, + if (goals.carbohydrates != null && goals.carbohydrates! > 0) + today.carbohydrates / goals.carbohydrates!, + if (goals.fat != null && goals.fat! > 0) today.fat / goals.fat!, + if (goals.fiber != null && goals.fiber! > 0) today.fiber / goals.fiber!, + ].reduce(max); + + final normWidth = constraints.maxWidth / maxVal; + + String fmtMacro(String name, double today, double? goal, String unit) { + return '$name: ${today.toStringAsFixed(0)}${goal == null ? '' : ' / ${goal.toStringAsFixed(0)}'} $unit'; + } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(fmtMacro( - AppLocalizations.of(context).energy, - today.energy, - goals.energy, - AppLocalizations.of(context).kcal, - )), - const SizedBox(height: 2), - _diyGauge(context, normWidth, goals.energy, today.energy), - const SizedBox(height: 8), - Text(fmtMacro( - AppLocalizations.of(context).protein, - today.protein, - goals.protein, - AppLocalizations.of(context).g, - )), - const SizedBox(height: 2), - _diyGauge(context, normWidth, goals.protein, today.protein), - const SizedBox(height: 8), - Text(fmtMacro( - AppLocalizations.of(context).carbohydrates, - today.carbohydrates, - goals.carbohydrates, - AppLocalizations.of(context).g, - )), - const SizedBox(height: 2), - _diyGauge( - context, - normWidth, - goals.carbohydrates, - today.carbohydrates, - ), - const SizedBox(height: 8), - Text(fmtMacro( - AppLocalizations.of(context).fat, - today.fat, - goals.fat, - AppLocalizations.of(context).g, - )), - const SizedBox(height: 2), - _diyGauge(context, normWidth, goals.fat, today.fat), - // optionally display the advanced macro goals: - if (goals.fiber != null) - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 8), - Text(fmtMacro( - AppLocalizations.of(context).fiber, - today.fiber, - goals.fiber, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + fmtMacro( + AppLocalizations.of(context).energy, + today.energy, + goals.energy, + AppLocalizations.of(context).kcal, + ), + ), + const SizedBox(height: 2), + _diyGauge(context, normWidth, goals.energy, today.energy), + const SizedBox(height: 8), + Text( + fmtMacro( + AppLocalizations.of(context).protein, + today.protein, + goals.protein, AppLocalizations.of(context).g, - )), - const SizedBox(height: 2), - _diyGauge(context, normWidth, goals.fiber, today.fiber), - ]), - ], - ); - }); + ), + ), + const SizedBox(height: 2), + _diyGauge(context, normWidth, goals.protein, today.protein), + const SizedBox(height: 8), + Text( + fmtMacro( + AppLocalizations.of(context).carbohydrates, + today.carbohydrates, + goals.carbohydrates, + AppLocalizations.of(context).g, + ), + ), + const SizedBox(height: 2), + _diyGauge(context, normWidth, goals.carbohydrates, today.carbohydrates), + const SizedBox(height: 8), + Text( + fmtMacro( + AppLocalizations.of(context).fat, + today.fat, + goals.fat, + AppLocalizations.of(context).g, + ), + ), + const SizedBox(height: 2), + _diyGauge(context, normWidth, goals.fat, today.fat), + // optionally display the advanced macro goals: + if (goals.fiber != null) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + Text( + fmtMacro( + AppLocalizations.of(context).fiber, + today.fiber, + goals.fiber, + AppLocalizations.of(context).g, + ), + ), + const SizedBox(height: 2), + _diyGauge(context, normWidth, goals.fiber, today.fiber), + ], + ), + ], + ); + }, + ); } } @@ -227,16 +231,19 @@ class FlNutritionalPlanPieChartState extends State Padding( - padding: const EdgeInsets.symmetric(vertical: 2), - child: Indicator(color: e.$2, text: e.$1, isSquare: true), - )) - .toList(), + children: + [ + (AppLocalizations.of(context).protein, LIST_OF_COLORS3[1]), + (AppLocalizations.of(context).carbohydrates, LIST_OF_COLORS3[0]), + (AppLocalizations.of(context).fat, LIST_OF_COLORS3[2]), + ] + .map( + (e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Indicator(color: e.$2, text: e.$1, isSquare: true), + ), + ) + .toList(), ), const SizedBox(width: 28), ], @@ -265,10 +272,8 @@ class FlNutritionalPlanPieChartState extends State value % 10 == 0, - getDrawingHorizontalLine: (value) => const FlLine( - color: Colors.black, - strokeWidth: 1, - ), + getDrawingHorizontalLine: (value) => + const FlLine(color: Colors.black, strokeWidth: 1), drawVerticalLine: false, ), borderData: FlBorderData(show: false), @@ -412,12 +406,7 @@ class NutritionalDiaryChartWidgetFlState extends State Indicator( - color: e.$2, - text: e.$1, - isSquare: true, - marginRight: 0, - ), - ) - .toList(), + children: + [ + (AppLocalizations.of(context).deficit, colorPlanned), + (AppLocalizations.of(context).surplus, COLOR_SURPLUS), + (AppLocalizations.of(context).today, colorLoggedToday), + (AppLocalizations.of(context).weekAverage, colorLogged7Day), + ] + .map( + (e) => Indicator(color: e.$2, text: e.$1, isSquare: true, marginRight: 0), + ) + .toList(), ), ), ], @@ -460,8 +445,8 @@ class MealDiaryBarChartWidget extends StatefulWidget { super.key, required NutritionalValues logged, required NutritionalValues planned, - }) : _logged = logged, - _planned = planned; + }) : _logged = logged, + _planned = planned; final NutritionalValues _logged; final NutritionalValues _planned; @@ -486,12 +471,12 @@ class MealDiaryBarChartWidgetState extends State { } Widget leftTitles(double value, TitleMeta meta) => SideTitleWidget( - axisSide: meta.axisSide, - child: Text( - AppLocalizations.of(context).percentValue(value.toStringAsFixed(0)), - style: const TextStyle(fontSize: 10), - ), - ); + axisSide: meta.axisSide, + child: Text( + AppLocalizations.of(context).percentValue(value.toStringAsFixed(0)), + style: const TextStyle(fontSize: 10), + ), + ); @override Widget build(BuildContext context) { @@ -523,19 +508,13 @@ class MealDiaryBarChartWidgetState extends State { getTitlesWidget: leftTitles, ), ), - topTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), - rightTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), ), gridData: FlGridData( show: true, - getDrawingHorizontalLine: (value) => const FlLine( - color: Colors.black, - strokeWidth: 1, - ), + getDrawingHorizontalLine: (value) => + const FlLine(color: Colors.black, strokeWidth: 1), drawVerticalLine: false, ), borderData: FlBorderData(show: false), diff --git a/lib/widgets/nutrition/forms.dart b/lib/widgets/nutrition/forms.dart index faad4a6f9..2c4d89b4a 100644 --- a/lib/widgets/nutrition/forms.dart +++ b/lib/widgets/nutrition/forms.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/helpers/ui.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/log.dart'; import 'package:wger/models/nutrition/meal.dart'; @@ -65,10 +65,7 @@ class MealForm extends StatelessWidget { FocusScope.of(context).requestFocus(FocusNode()); // Open time picker - final pickedTime = await showTimePicker( - context: context, - initialTime: _meal.time!, - ); + final pickedTime = await showTimePicker(context: context, initialTime: _meal.time!); if (pickedTime != null) { _timeController.text = timeToString(pickedTime)!; } @@ -101,10 +98,7 @@ class MealForm extends StatelessWidget { context, listen: false, ).addMeal(_meal, _planId) - : Provider.of( - context, - listen: false, - ).editMeal(_meal); + : Provider.of(context, listen: false).editMeal(_meal); } on WgerHttpException catch (error) { showHttpExceptionErrorDialog(error, context); } catch (error) { @@ -120,12 +114,7 @@ class MealForm extends StatelessWidget { } } -Widget MealItemForm( - Meal meal, - List recent, [ - String? barcode, - bool? test, -]) { +Widget MealItemForm(Meal meal, List recent, [String? barcode, bool? test]) { return IngredientForm( // TODO we use planId 0 here cause we don't have one and we don't need it I think? recent: recent.map((e) => Log.fromMealItem(e, "0", e.mealId)).toList(), @@ -143,14 +132,13 @@ Widget IngredientLogForm(NutritionalPlan plan) { return IngredientForm( recent: plan.dedupDiaryEntries, onSave: (BuildContext context, MealItem mealItem, DateTime? dt) { - Provider.of(context, listen: false) - .logIngredientToDiary(mealItem, plan.id!, dt); + Provider.of( + context, + listen: false, + ).logIngredientToDiary(mealItem, plan.id!, dt); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - AppLocalizations.of(context).ingredientLogged, - textAlign: TextAlign.center, - ), + content: Text(AppLocalizations.of(context).ingredientLogged, textAlign: TextAlign.center), ), ); }, @@ -241,8 +229,9 @@ class IngredientFormState extends State { Widget build(BuildContext context) { final String unit = AppLocalizations.of(context).g; final queryLower = _searchQuery.toLowerCase(); - final suggestions = - widget.recent.where((e) => e.ingredient.name.toLowerCase().contains(queryLower)).toList(); + final suggestions = widget.recent + .where((e) => e.ingredient.name.toLowerCase().contains(queryLower)) + .toList(); return Container( margin: const EdgeInsets.all(20), child: Form( @@ -263,9 +252,7 @@ class IngredientFormState extends State { Expanded( child: TextFormField( key: const Key('field-weight'), // needed ? - decoration: InputDecoration( - labelText: AppLocalizations.of(context).weight, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).weight), controller: _amountController, keyboardType: TextInputType.number, onChanged: (value) { @@ -361,10 +348,7 @@ class IngredientFormState extends State { context, listen: false, ).fetchIngredient(_mealItem.ingredientId), - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { _mealItem.ingredient = snapshot.data!; return MealItemValuesTile( @@ -403,13 +387,7 @@ class IngredientFormState extends State { try { var date = DateTime.parse(_dateController.text); final tod = stringToTime(_timeController.text); - date = DateTime( - date.year, - date.month, - date.day, - tod.hour, - tod.minute, - ); + date = DateTime(date.year, date.month, date.day, tod.hour, tod.minute); widget.onSave(context, _mealItem, date); } on WgerHttpException catch (error) { showHttpExceptionErrorDialog(error, context); @@ -431,11 +409,7 @@ class IngredientFormState extends State { itemBuilder: (context, index) { void select() { final ingredient = suggestions[index].ingredient; - selectIngredient( - ingredient.id, - ingredient.name, - suggestions[index].amount, - ); + selectIngredient(ingredient.id, ingredient.name, suggestions[index].amount); } return Card( @@ -444,10 +418,12 @@ class IngredientFormState extends State { title: Text( '${suggestions[index].ingredient.name} (${suggestions[index].amount.toStringAsFixed(0)}$unit)', ), - subtitle: Text(getShortNutritionValues( - suggestions[index].ingredient.nutritionalValues, - context, - )), + subtitle: Text( + getShortNutritionValues( + suggestions[index].ingredient.nutritionalValues, + context, + ), + ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -539,9 +515,7 @@ class _PlanFormState extends State { // Description TextFormField( key: const Key('field-description'), - decoration: InputDecoration( - labelText: AppLocalizations.of(context).description, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).description), controller: _descriptionController, onSaved: (newValue) { widget._plan.description = newValue!; @@ -569,12 +543,7 @@ class _PlanFormState extends State { child: DropdownButtonFormField( value: _goalType, items: GoalType.values - .map( - (e) => DropdownMenuItem( - value: e, - child: Text(e.label), - ), - ) + .map((e) => DropdownMenuItem(value: e, child: Text(e.label))) .toList(), onChanged: (GoalType? g) { setState(() { diff --git a/lib/widgets/nutrition/helpers.dart b/lib/widgets/nutrition/helpers.dart index b60661fb4..a588c7541 100644 --- a/lib/widgets/nutrition/helpers.dart +++ b/lib/widgets/nutrition/helpers.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; @@ -27,18 +27,18 @@ import 'package:wger/widgets/core/core.dart'; import 'package:wger/widgets/nutrition/ingredient_dialogs.dart'; List getNutritionColumnNames(BuildContext context) => [ - AppLocalizations.of(context).energy, - AppLocalizations.of(context).protein, - AppLocalizations.of(context).carbohydrates, - AppLocalizations.of(context).fat, - ]; + AppLocalizations.of(context).energy, + AppLocalizations.of(context).protein, + AppLocalizations.of(context).carbohydrates, + AppLocalizations.of(context).fat, +]; List getNutritionalValues(NutritionalValues values, BuildContext context) => [ - AppLocalizations.of(context).kcalValue(values.energy.toStringAsFixed(0)), - AppLocalizations.of(context).gValue(values.protein.toStringAsFixed(0)), - AppLocalizations.of(context).gValue(values.carbohydrates.toStringAsFixed(0)), - AppLocalizations.of(context).gValue(values.fat.toStringAsFixed(0)), - ]; + AppLocalizations.of(context).kcalValue(values.energy.toStringAsFixed(0)), + AppLocalizations.of(context).gValue(values.protein.toStringAsFixed(0)), + AppLocalizations.of(context).gValue(values.carbohydrates.toStringAsFixed(0)), + AppLocalizations.of(context).gValue(values.fat.toStringAsFixed(0)), +]; List getNutritionColumnFlexes(BuildContext context) { return getNutritionColumnNames(context).map((e) { @@ -51,11 +51,7 @@ List getNutritionColumnFlexes(BuildContext context) { } List muted(List children) => children - .map((e) => MutedText( - e, - textAlign: TextAlign.right, - overflow: TextOverflow.ellipsis, - )) + .map((e) => MutedText(e, textAlign: TextAlign.right, overflow: TextOverflow.ellipsis)) .toList(); // return a row of elements in the standard macros spacing @@ -85,15 +81,17 @@ String getShortNutritionValues(NutritionalValues values, BuildContext context) { } String getKcalConsumed(Meal meal, BuildContext context) { - final consumed = - meal.diaryEntriesToday.map((e) => e.nutritionalValues.energy).fold(0.0, (a, b) => a + b); + final consumed = meal.diaryEntriesToday + .map((e) => e.nutritionalValues.energy) + .fold(0.0, (a, b) => a + b); return AppLocalizations.of(context).kcalValue(consumed.toStringAsFixed(0)); } String getKcalConsumedVsPlanned(Meal meal, BuildContext context) { final planned = meal.plannedNutritionalValues.energy; - final consumed = - meal.diaryEntriesToday.map((e) => e.nutritionalValues.energy).fold(0.0, (a, b) => a + b); + final consumed = meal.diaryEntriesToday + .map((e) => e.nutritionalValues.energy) + .fold(0.0, (a, b) => a + b); final loc = AppLocalizations.of(context); return '${consumed.toStringAsFixed(0)} / ${planned.toStringAsFixed(0)} ${loc.kcal}'; diff --git a/lib/widgets/nutrition/ingredient_dialogs.dart b/lib/widgets/nutrition/ingredient_dialogs.dart index b5fcde098..933c6d989 100644 --- a/lib/widgets/nutrition/ingredient_dialogs.dart +++ b/lib/widgets/nutrition/ingredient_dialogs.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:wger/helpers/misc.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/nutritional_goals.dart'; import 'package:wger/widgets/nutrition/macro_nutrients_table.dart'; @@ -143,15 +143,14 @@ class IngredientScanResultDialog extends StatelessWidget { if (snapshot.connectionState == ConnectionState.done && ingredient == null) Padding( padding: const EdgeInsets.only(bottom: 8.0), - child: Text( - AppLocalizations.of(context).productNotFoundDescription(barcode), - ), + child: Text(AppLocalizations.of(context).productNotFoundDescription(barcode)), ), if (ingredient != null) Padding( padding: const EdgeInsets.only(bottom: 8.0), - child: - Text(AppLocalizations.of(context).productFoundDescription(ingredient.name)), + child: Text( + AppLocalizations.of(context).productFoundDescription(ingredient.name), + ), ), if (ingredient?.image?.image != null) ingredientImage(ingredient!.image!.image, context), diff --git a/lib/widgets/nutrition/macro_nutrients_table.dart b/lib/widgets/nutrition/macro_nutrients_table.dart index c5bc5c4b4..9226f7a40 100644 --- a/lib/widgets/nutrition/macro_nutrients_table.dart +++ b/lib/widgets/nutrition/macro_nutrients_table.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_goals.dart'; class MacronutrientsTable extends StatelessWidget { @@ -22,13 +22,13 @@ class MacronutrientsTable extends StatelessWidget { final loc = AppLocalizations.of(context); Widget columnHeader(bool left, String title) => Padding( - padding: const EdgeInsets.symmetric(vertical: tablePadding), - child: Text( - title, - style: const TextStyle(fontWeight: FontWeight.bold), - textAlign: left ? TextAlign.left : TextAlign.right, - ), - ); + padding: const EdgeInsets.symmetric(vertical: tablePadding), + child: Text( + title, + style: const TextStyle(fontWeight: FontWeight.bold), + textAlign: left ? TextAlign.left : TextAlign.right, + ), + ); TableRow macroRow(int indent, bool g, String title, double? Function(NutritionalGoals ng) get) { final goal = get(nutritionalGoals); @@ -53,10 +53,7 @@ class MacronutrientsTable extends StatelessWidget { return Table( defaultVerticalAlignment: TableCellVerticalAlignment.middle, border: TableBorder( - horizontalInside: BorderSide( - width: 1, - color: Theme.of(context).colorScheme.outline, - ), + horizontalInside: BorderSide(width: 1, color: Theme.of(context).colorScheme.outline), ), columnWidths: const {0: FractionColumnWidth(0.4)}, children: [ diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart index d79370616..7f829ff87 100644 --- a/lib/widgets/nutrition/meal.dart +++ b/lib/widgets/nutrition/meal.dart @@ -17,10 +17,10 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg_icons/flutter_svg_icons.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/providers/nutrition.dart'; @@ -36,7 +36,7 @@ import 'package:wger/widgets/nutrition/widgets.dart'; enum viewMode { base, // just highlevel meal info (name, time) withIngredients, // + ingredients - withAllDetails // + nutritional breakdown of ingredients, + logged today + withAllDetails, // + nutritional breakdown of ingredients, + logged today } class MealWidget extends StatefulWidget { @@ -45,12 +45,7 @@ class MealWidget extends StatefulWidget { final bool popTwice; final bool readOnly; - const MealWidget( - this._meal, - this._recentMealItems, - this.popTwice, - this.readOnly, - ); + const MealWidget(this._meal, this._recentMealItems, this.popTwice, this.readOnly); @override _MealWidgetState createState() => _MealWidgetState(); @@ -173,8 +168,9 @@ class _MealWidgetState extends State { ), ) else - ...widget._meal.mealItems - .map((item) => MealItemEditableFullTile(item, _viewMode, _editing)), + ...widget._meal.mealItems.map( + (item) => MealItemEditableFullTile(item, _viewMode, _editing), + ), if (_viewMode == viewMode.withIngredients || _viewMode == viewMode.withAllDetails) const Divider(), if (_viewMode == viewMode.withIngredients || _viewMode == viewMode.withAllDetails) @@ -201,12 +197,12 @@ class _MealWidgetState extends State { planned: widget._meal.plannedNutritionalValues, logged: widget._meal.loggedNutritionalValuesToday, ), - ...widget._meal.diaryEntriesToday.map((item) => Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [DiaryEntryTile(diaryEntry: item)], - ), - )), + ...widget._meal.diaryEntriesToday.map( + (item) => Padding( + padding: const EdgeInsets.all(8.0), + child: Column(children: [DiaryEntryTile(diaryEntry: item)]), + ), + ), ], ), ], @@ -293,11 +289,11 @@ class MealHeader extends StatelessWidget { required viewMode viewMode, required Function() toggleEditing, required Function() toggleViewMode, - }) : _meal = meal, - _editing = editing, - _viewMode = viewMode, - _toggleViewMode = toggleViewMode, - _toggleEditing = toggleEditing; + }) : _meal = meal, + _editing = editing, + _viewMode = viewMode, + _toggleViewMode = toggleViewMode, + _toggleEditing = toggleEditing; @override Widget build(BuildContext context) { @@ -306,35 +302,34 @@ class MealHeader extends StatelessWidget { children: [ ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - title: Row(children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _meal.name, - style: Theme.of(context).textTheme.titleLarge, - ), - Row( - children: [ - if (_meal.time != null) + title: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(_meal.name, style: Theme.of(context).textTheme.titleLarge), + Row( + children: [ + if (_meal.time != null) + Text( + _meal.time!.format(context), + style: Theme.of(context).textTheme.titleSmall, + ), + if (_meal.time != null) const SizedBox(width: 12), Text( - _meal.time!.format(context), + _meal.isRealMeal + ? getKcalConsumedVsPlanned(_meal, context) + : getKcalConsumed(_meal, context), style: Theme.of(context).textTheme.titleSmall, ), - if (_meal.time != null) const SizedBox(width: 12), - Text( - _meal.isRealMeal - ? getKcalConsumedVsPlanned(_meal, context) - : getKcalConsumed(_meal, context), - style: Theme.of(context).textTheme.titleSmall, - ), - ], - ), - ], + ], + ), + ], + ), ), - ), - ]), + ], + ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/widgets/nutrition/nutrition_tiles.dart b/lib/widgets/nutrition/nutrition_tiles.dart index 304ef9f38..2d72b1b95 100644 --- a/lib/widgets/nutrition/nutrition_tiles.dart +++ b/lib/widgets/nutrition/nutrition_tiles.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/log.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; @@ -18,11 +18,7 @@ class MealItemValuesTile extends StatelessWidget { final Ingredient ingredient; final NutritionalValues nutritionalValues; - const MealItemValuesTile({ - super.key, - required this.ingredient, - required this.nutritionalValues, - }); + const MealItemValuesTile({super.key, required this.ingredient, required this.nutritionalValues}); @override Widget build(BuildContext context) { @@ -53,11 +49,7 @@ class DiaryheaderTile extends StatelessWidget { /// a NutritionTitle showing diary entries class DiaryEntryTile extends StatelessWidget { - const DiaryEntryTile({ - super.key, - required this.diaryEntry, - this.nutritionalPlan, - }); + const DiaryEntryTile({super.key, required this.diaryEntry, this.nutritionalPlan}); final Log diaryEntry; final NutritionalPlan? nutritionalPlan; @@ -82,8 +74,10 @@ class DiaryEntryTile extends StatelessWidget { : IconButton( tooltip: AppLocalizations.of(context).delete, onPressed: () { - Provider.of(context, listen: false) - .deleteLog(diaryEntry.id!, nutritionalPlan!.id!); + Provider.of( + context, + listen: false, + ).deleteLog(diaryEntry.id!, nutritionalPlan!.id!); }, icon: const Icon(Icons.delete_outline), iconSize: ICON_SIZE_SMALL, diff --git a/lib/widgets/nutrition/nutritional_diary_detail.dart b/lib/widgets/nutrition/nutritional_diary_detail.dart index 93a9ae203..3b2ceb720 100644 --- a/lib/widgets/nutrition/nutritional_diary_detail.dart +++ b/lib/widgets/nutrition/nutritional_diary_detail.dart @@ -17,7 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; import 'package:wger/widgets/nutrition/charts.dart'; @@ -61,20 +61,14 @@ class NutritionalDiaryDetailWidget extends StatelessWidget { ), const SizedBox(height: 15), const DiaryheaderTile(), - ...logs.map( - (e) => DiaryEntryTile(diaryEntry: e, nutritionalPlan: _nutritionalPlan), - ), + ...logs.map((e) => DiaryEntryTile(diaryEntry: e, nutritionalPlan: _nutritionalPlan)), ], ); } } class NutritionDiaryTable extends StatelessWidget { - const NutritionDiaryTable({ - super.key, - required this.planned, - required this.logged, - }); + const NutritionDiaryTable({super.key, required this.planned, required this.logged}); static const double tablePadding = 7; final NutritionalValues planned; @@ -85,13 +79,13 @@ class NutritionDiaryTable extends StatelessWidget { final loc = AppLocalizations.of(context); Widget columnHeader(bool left, String title) => Padding( - padding: const EdgeInsets.symmetric(vertical: tablePadding), - child: Text( - title, - style: const TextStyle(fontWeight: FontWeight.bold), - textAlign: left ? TextAlign.left : TextAlign.right, - ), - ); + padding: const EdgeInsets.symmetric(vertical: tablePadding), + child: Text( + title, + style: const TextStyle(fontWeight: FontWeight.bold), + textAlign: left ? TextAlign.left : TextAlign.right, + ), + ); TableRow macroRow(int indent, bool g, String title, double Function(NutritionalValues nv) get) { final valFn = g ? loc.gValue : loc.kcalValue; @@ -115,12 +109,14 @@ class NutritionDiaryTable extends StatelessWidget { ), columnWidths: const {0: FractionColumnWidth(0.4)}, children: [ - TableRow(children: [ - columnHeader(true, loc.macronutrients), - columnHeader(false, loc.planned), - columnHeader(false, loc.logged), - columnHeader(false, loc.difference), - ]), + TableRow( + children: [ + columnHeader(true, loc.macronutrients), + columnHeader(false, loc.planned), + columnHeader(false, loc.logged), + columnHeader(false, loc.difference), + ], + ), macroRow(0, false, loc.energy, (NutritionalValues nv) => nv.energy), macroRow(0, true, loc.protein, (NutritionalValues nv) => nv.protein), macroRow(0, true, loc.carbohydrates, (NutritionalValues nv) => nv.carbohydrates), diff --git a/lib/widgets/nutrition/nutritional_diary_table.dart b/lib/widgets/nutrition/nutritional_diary_table.dart index f69bb814d..f27e719d6 100644 --- a/lib/widgets/nutrition/nutritional_diary_table.dart +++ b/lib/widgets/nutrition/nutritional_diary_table.dart @@ -1,17 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:wger/helpers/colors.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_goals.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/models/nutrition/nutritional_values.dart'; import 'package:wger/screens/nutritional_diary_screen.dart'; class NutritionalDiaryTable extends StatelessWidget { - const NutritionalDiaryTable({ - super.key, - required NutritionalPlan nutritionalPlan, - }) : plan = nutritionalPlan; + const NutritionalDiaryTable({super.key, required NutritionalPlan nutritionalPlan}) + : plan = nutritionalPlan; final NutritionalPlan plan; @@ -82,46 +80,49 @@ class NutritionalDiaryTable extends StatelessWidget { decoration: BoxDecoration( border: Border(top: BorderSide(color: Colors.grey[300]!)), ), - children: [ - Text( - style: Theme.of(context).textTheme.titleMedium?.copyWith(color: LIST_OF_COLORS3.first), - DateFormat.Md(Localizations.localeOf(context).languageCode).format(date), - ), - Text( - style: Theme.of(context).textTheme.titleMedium, - textAlign: TextAlign.end, - values.energy.toStringAsFixed(0), - ), - if (goals.energy != null) - Text( - style: Theme.of(context).textTheme.titleMedium, - textAlign: TextAlign.end, - (values.energy - goals.energy!).toStringAsFixed(0), - ), - Text( - style: Theme.of(context).textTheme.titleMedium, - textAlign: TextAlign.end, - values.protein.toStringAsFixed(0), - ), - Text( - style: Theme.of(context).textTheme.titleMedium, - textAlign: TextAlign.end, - values.carbohydrates.toStringAsFixed(0), - ), - Text( - style: Theme.of(context).textTheme.titleMedium, - textAlign: TextAlign.end, - values.fat.toStringAsFixed(0), - ), - ].map((element) { - return GestureDetector( - onTap: () => Navigator.of(context).pushNamed( - NutritionalDiaryScreen.routeName, - arguments: NutritionalDiaryArguments(plan.id!, date), - ), - child: element, - ); - }).toList(), + children: + [ + Text( + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(color: LIST_OF_COLORS3.first), + DateFormat.Md(Localizations.localeOf(context).languageCode).format(date), + ), + Text( + style: Theme.of(context).textTheme.titleMedium, + textAlign: TextAlign.end, + values.energy.toStringAsFixed(0), + ), + if (goals.energy != null) + Text( + style: Theme.of(context).textTheme.titleMedium, + textAlign: TextAlign.end, + (values.energy - goals.energy!).toStringAsFixed(0), + ), + Text( + style: Theme.of(context).textTheme.titleMedium, + textAlign: TextAlign.end, + values.protein.toStringAsFixed(0), + ), + Text( + style: Theme.of(context).textTheme.titleMedium, + textAlign: TextAlign.end, + values.carbohydrates.toStringAsFixed(0), + ), + Text( + style: Theme.of(context).textTheme.titleMedium, + textAlign: TextAlign.end, + values.fat.toStringAsFixed(0), + ), + ].map((element) { + return GestureDetector( + onTap: () => Navigator.of(context).pushNamed( + NutritionalDiaryScreen.routeName, + arguments: NutritionalDiaryArguments(plan.id!, date), + ), + child: element, + ); + }).toList(), ); } } diff --git a/lib/widgets/nutrition/nutritional_plan_detail.dart b/lib/widgets/nutrition/nutritional_plan_detail.dart index 63e69e534..9d594288c 100644 --- a/lib/widgets/nutrition/nutritional_plan_detail.dart +++ b/lib/widgets/nutrition/nutritional_plan_detail.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/widgets/nutrition/charts.dart'; @@ -34,86 +34,78 @@ class NutritionalPlanDetailWidget extends StatelessWidget { @override Widget build(BuildContext context) { final nutritionalGoals = _nutritionalPlan.nutritionalGoals; - final lastWeightEntry = - Provider.of(context, listen: false).getNewestEntry(); - final nutritionalGoalsGperKg = - lastWeightEntry != null ? nutritionalGoals / lastWeightEntry.weight.toDouble() : null; + final lastWeightEntry = Provider.of( + context, + listen: false, + ).getNewestEntry(); + final nutritionalGoalsGperKg = lastWeightEntry != null + ? nutritionalGoals / lastWeightEntry.weight.toDouble() + : null; return SliverList( - delegate: SliverChildListDelegate( - [ - SizedBox( - width: 300, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: FlNutritionalPlanGoalWidget( - nutritionalPlan: _nutritionalPlan, - ), - ), - ), - const SizedBox(height: 10), - ..._nutritionalPlan.meals.map((meal) => MealWidget( - meal, - _nutritionalPlan.dedupMealItems, - false, - false, - )), - MealWidget( - _nutritionalPlan.pseudoMealOthers('Other logs'), - _nutritionalPlan.dedupMealItems, - false, - true, - ), - if (nutritionalGoals.isComplete()) - Container( - padding: const EdgeInsets.all(15), - height: 220, - child: FlNutritionalPlanPieChartWidget(nutritionalGoals.toValues()), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: MacronutrientsTable( - nutritionalGoals: nutritionalGoals, - plannedValuesPercentage: nutritionalGoals.energyPercentage(), - nutritionalGoalsGperKg: nutritionalGoalsGperKg, - ), - ), - const Padding(padding: EdgeInsets.all(8.0)), - Text( - AppLocalizations.of(context).logged, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleLarge, + delegate: SliverChildListDelegate([ + SizedBox( + width: 300, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: FlNutritionalPlanGoalWidget(nutritionalPlan: _nutritionalPlan), ), + ), + const SizedBox(height: 10), + ..._nutritionalPlan.meals.map( + (meal) => MealWidget(meal, _nutritionalPlan.dedupMealItems, false, false), + ), + MealWidget( + _nutritionalPlan.pseudoMealOthers('Other logs'), + _nutritionalPlan.dedupMealItems, + false, + true, + ), + if (nutritionalGoals.isComplete()) Container( - padding: const EdgeInsets.only(top: 16, left: 8, right: 8), - height: 300, - child: NutritionalDiaryChartWidgetFl( - nutritionalPlan: _nutritionalPlan, - ), + padding: const EdgeInsets.all(15), + height: 220, + child: FlNutritionalPlanPieChartWidget(nutritionalGoals.toValues()), ), - if (_nutritionalPlan.logEntriesValues.isNotEmpty) - Padding( - padding: const EdgeInsets.only(bottom: 15, left: 15, right: 15), - child: Column( - children: [ - Text( - AppLocalizations.of(context).nutritionalDiary, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleLarge, - ), - SizedBox( - height: 200, - child: SingleChildScrollView( - child: NutritionalDiaryTable( - nutritionalPlan: _nutritionalPlan, - ), - ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: MacronutrientsTable( + nutritionalGoals: nutritionalGoals, + plannedValuesPercentage: nutritionalGoals.energyPercentage(), + nutritionalGoalsGperKg: nutritionalGoalsGperKg, + ), + ), + const Padding(padding: EdgeInsets.all(8.0)), + Text( + AppLocalizations.of(context).logged, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleLarge, + ), + Container( + padding: const EdgeInsets.only(top: 16, left: 8, right: 8), + height: 300, + child: NutritionalDiaryChartWidgetFl(nutritionalPlan: _nutritionalPlan), + ), + if (_nutritionalPlan.logEntriesValues.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 15, left: 15, right: 15), + child: Column( + children: [ + Text( + AppLocalizations.of(context).nutritionalDiary, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleLarge, + ), + SizedBox( + height: 200, + child: SingleChildScrollView( + child: NutritionalDiaryTable(nutritionalPlan: _nutritionalPlan), ), - ], - ), + ), + ], ), - ], - ), + ), + ]), ); } } diff --git a/lib/widgets/nutrition/nutritional_plans_list.dart b/lib/widgets/nutrition/nutritional_plans_list.dart index 16449e288..7e4fde613 100644 --- a/lib/widgets/nutrition/nutritional_plans_list.dart +++ b/lib/widgets/nutrition/nutritional_plans_list.dart @@ -19,9 +19,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/nutritional_plan_screen.dart'; @@ -39,8 +39,10 @@ class _NutritionalPlansListState extends State { @override void initState() { super.initState(); - final stream = - Provider.of(context, listen: false).watchNutritionPlans(); + final stream = Provider.of( + context, + listen: false, + ).watchNutritionPlans(); _subscription = stream.listen((plans) { if (!context.mounted) { return; @@ -71,10 +73,9 @@ class _NutritionalPlansListState extends State { return Card( child: ListTile( onTap: () { - Navigator.of(context).pushNamed( - NutritionalPlanScreen.routeName, - arguments: currentPlan.id, - ); + Navigator.of( + context, + ).pushNamed(NutritionalPlanScreen.routeName, arguments: currentPlan.id); }, title: Text(currentPlan.getLabel(context)), subtitle: Text( @@ -82,53 +83,56 @@ class _NutritionalPlansListState extends State { Localizations.localeOf(context).languageCode, ).format(currentPlan.creationDate), ), - trailing: Row(mainAxisSize: MainAxisSize.min, children: [ - const VerticalDivider(), - IconButton( - icon: const Icon(Icons.delete), - tooltip: AppLocalizations.of(context).delete, - onPressed: () async { - await showDialog( - context: context, - builder: (BuildContext contextDialog) { - return AlertDialog( - content: Text( - AppLocalizations.of(context).confirmDelete(currentPlan.description), - ), - actions: [ - TextButton( - child: Text( - MaterialLocalizations.of(context).cancelButtonLabel, - ), - onPressed: () => Navigator.of(contextDialog).pop(), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const VerticalDivider(), + IconButton( + icon: const Icon(Icons.delete), + tooltip: AppLocalizations.of(context).delete, + onPressed: () async { + await showDialog( + context: context, + builder: (BuildContext contextDialog) { + return AlertDialog( + content: Text( + AppLocalizations.of( + context, + ).confirmDelete(currentPlan.description), ), - TextButton( - child: Text( - AppLocalizations.of(context).delete, - style: TextStyle( - color: Theme.of(context).colorScheme.error, + actions: [ + TextButton( + child: Text( + MaterialLocalizations.of(context).cancelButtonLabel, ), + onPressed: () => Navigator.of(contextDialog).pop(), ), - onPressed: () { - provider.deletePlan(currentPlan.id!); - Navigator.of(contextDialog).pop(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, + TextButton( + child: Text( + AppLocalizations.of(context).delete, + style: TextStyle(color: Theme.of(context).colorScheme.error), + ), + onPressed: () { + provider.deletePlan(currentPlan.id!); + Navigator.of(contextDialog).pop(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, + ), ), - ), - ); - }, - ), - ], - ); - }, - ); - }, - ), - ]), + ); + }, + ), + ], + ); + }, + ); + }, + ), + ], + ), ), ); }, diff --git a/lib/widgets/nutrition/widgets.dart b/lib/widgets/nutrition/widgets.dart index 00afff5b4..2141ee880 100644 --- a/lib/widgets/nutrition/widgets.dart +++ b/lib/widgets/nutrition/widgets.dart @@ -18,7 +18,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; //import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -27,6 +26,7 @@ import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/misc.dart'; import 'package:wger/helpers/platform.dart'; import 'package:wger/helpers/ui.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/ingredient_api.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/providers/nutrition.dart'; @@ -38,19 +38,19 @@ class ScanReader extends StatelessWidget { const ScanReader(); @override Widget build(BuildContext context) => Scaffold( - body: ReaderWidget( - onScan: (result) { - // notes: - // 1. even if result.isValid, result.error is always non-null (and set to "") - // 2. i've never encountered scan errors to see when they occur, and - // i wouldn't know what to do about them anyway, so we simply return - // result.text in such case (which presumably will be null, or "") - // 3. when user cancels (swipe left / back button) this code is no longer - // run and the caller receives null - Navigator.pop(context, result.text); - }, - ), - ); + body: ReaderWidget( + onScan: (result) { + // notes: + // 1. even if result.isValid, result.error is always non-null (and set to "") + // 2. i've never encountered scan errors to see when they occur, and + // i wouldn't know what to do about them anyway, so we simply return + // result.text in such case (which presumably will be null, or "") + // 3. when user cancels (swipe left / back button) this code is no longer + // run and the caller receives null + Navigator.pop(context, result.text); + }, + ), + ); } class IngredientTypeahead extends StatefulWidget { @@ -92,8 +92,9 @@ class _IngredientTypeaheadState extends State { Future readerscan(BuildContext context) async { try { - final code = await Navigator.of(context) - .push(MaterialPageRoute(builder: (context) => const ScanReader())); + final code = await Navigator.of( + context, + ).push(MaterialPageRoute(builder: (context) => const ScanReader())); if (code == null) { return ''; } @@ -152,12 +153,8 @@ class _IngredientTypeaheadState extends State { final url = context.read().baseProvider.auth.serverUrl; return ListTile( leading: suggestion.data.image != null - ? CircleAvatar( - backgroundImage: NetworkImage(url! + suggestion.data.image!), - ) - : const CircleIconAvatar( - Icon(Icons.image, color: Colors.grey), - ), + ? CircleAvatar(backgroundImage: NetworkImage(url! + suggestion.data.image!)) + : const CircleIconAvatar(Icon(Icons.image, color: Colors.grey)), title: Text(suggestion.value), // subtitle: Text(suggestion.data.id.toString()), trailing: IconButton( @@ -217,8 +214,10 @@ class _IngredientTypeaheadState extends State { showDialog( context: context, builder: (context) => FutureBuilder( - future: Provider.of(context, listen: false) - .searchIngredientWithCode(barcode), + future: Provider.of( + context, + listen: false, + ).searchIngredientWithCode(barcode), builder: (BuildContext context, AsyncSnapshot snapshot) { return IngredientScanResultDialog(snapshot, barcode, widget.selectIngredient); }, @@ -238,9 +237,7 @@ class IngredientAvatar extends StatelessWidget { Widget build(BuildContext context) { return ingredient.image != null ? GestureDetector( - child: CircleAvatar( - backgroundImage: NetworkImage(ingredient.image!.image), - ), + child: CircleAvatar(backgroundImage: NetworkImage(ingredient.image!.image)), onTap: () async { if (ingredient.image!.objectUrl != '') { return launchURL(ingredient.image!.objectUrl, context); diff --git a/lib/widgets/user/forms.dart b/lib/widgets/user/forms.dart index b11ded4b7..66a3e3a93 100644 --- a/lib/widgets/user/forms.dart +++ b/lib/widgets/user/forms.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/user/profile.dart'; import 'package:wger/providers/user.dart'; import 'package:wger/theme/theme.dart'; @@ -132,9 +132,7 @@ class _UserProfileFormState extends State { context.read().saveProfile(); Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(AppLocalizations.of(context).successfullySaved), - ), + SnackBar(content: Text(AppLocalizations.of(context).successfullySaved)), ); }, child: Text(AppLocalizations.of(context).save), diff --git a/lib/widgets/weight/forms.dart b/lib/widgets/weight/forms.dart index 343a0cf87..502b3de6b 100644 --- a/lib/widgets/weight/forms.dart +++ b/lib/widgets/weight/forms.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/helpers/ui.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/body_weight/weight_entry.dart'; import 'package:wger/providers/body_weight.dart'; @@ -52,10 +52,7 @@ class WeightForm extends StatelessWidget { // Stop keyboard from appearing decoration: InputDecoration( labelText: AppLocalizations.of(context).date, - suffixIcon: const Icon( - Icons.calendar_today, - key: Key('calendarIcon'), - ), + suffixIcon: const Icon(Icons.calendar_today, key: Key('calendarIcon')), ), enableInteractiveSelection: false, controller: dateController, diff --git a/lib/widgets/weight/weight_overview.dart b/lib/widgets/weight/weight_overview.dart index c094d1b9f..facc0af22 100644 --- a/lib/widgets/weight/weight_overview.dart +++ b/lib/widgets/weight/weight_overview.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/user.dart'; @@ -38,8 +38,9 @@ class WeightOverview extends StatelessWidget { final weightProvider = Provider.of(context, listen: false); final plan = Provider.of(context, listen: false).currentPlan; - final entriesAll = - weightProvider.items.map((e) => MeasurementChartEntry(e.weight, e.date)).toList(); + final entriesAll = weightProvider.items + .map((e) => MeasurementChartEntry(e.weight, e.date)) + .toList(); final entries7dAvg = moving7dAverage(entriesAll); final unit = weightUnit(profile!.isMetric, context); @@ -55,10 +56,7 @@ class WeightOverview extends StatelessWidget { context, ), TextButton( - onPressed: () => Navigator.pushNamed( - context, - MeasurementCategoriesScreen.routeName, - ), + onPressed: () => Navigator.pushNamed(context, MeasurementCategoriesScreen.routeName), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ diff --git a/lib/widgets/workouts/app_bar.dart b/lib/widgets/workouts/app_bar.dart index eb1b7563f..312a2bd2f 100644 --- a/lib/widgets/workouts/app_bar.dart +++ b/lib/widgets/workouts/app_bar.dart @@ -17,14 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/screens/add_exercise_screen.dart'; import 'package:wger/screens/exercises_screen.dart'; -enum _WorkoutAppBarOptions { - list, - contribute, -} +enum _WorkoutAppBarOptions { list, contribute } class WorkoutOverviewAppBar extends StatelessWidget implements PreferredSizeWidget { const WorkoutOverviewAppBar(); diff --git a/lib/widgets/workouts/charts.dart b/lib/widgets/workouts/charts.dart index e41e249ab..60a83461f 100644 --- a/lib/widgets/workouts/charts.dart +++ b/lib/widgets/workouts/charts.dart @@ -18,10 +18,10 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:wger/helpers/charts.dart'; import 'package:wger/helpers/colors.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; class LogChartWidgetFl extends StatefulWidget { final Map _data; @@ -79,12 +79,8 @@ class _LogChartWidgetFlState extends State { ), titlesData: FlTitlesData( show: true, - rightTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), - topTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, @@ -116,10 +112,7 @@ class _LogChartWidgetFlState extends State { ), ), ), - borderData: FlBorderData( - show: true, - border: Border.all(color: const Color(0xff37434d)), - ), + borderData: FlBorderData(show: true, border: Border.all(color: const Color(0xff37434d))), lineBarsData: [ ...widget._data['chart_data'].map((e) { colors.moveNext(); @@ -138,11 +131,8 @@ class _LogChartWidgetFlState extends State { isStrokeCapRound: true, dotData: FlDotData( show: true, - getDotPainter: (p0, p1, p2, p3) => FlDotCirclePainter( - radius: 2, - color: Colors.black, - strokeWidth: 0, - ), + getDotPainter: (p0, p1, p2, p3) => + FlDotCirclePainter(radius: 2, color: Colors.black, strokeWidth: 0), ), ); }), diff --git a/lib/widgets/workouts/day.dart b/lib/widgets/workouts/day.dart index 82ab108a2..2c5ccb565 100644 --- a/lib/widgets/workouts/day.dart +++ b/lib/widgets/workouts/day.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/day.dart'; import 'package:wger/models/workouts/set.dart'; import 'package:wger/models/workouts/setting.dart'; @@ -57,15 +57,15 @@ class SettingWidget extends StatelessWidget { context: context, builder: (BuildContext context) { return AlertDialog( - title: Text(setting.exerciseObj - .getExercise(Localizations.localeOf(context).languageCode) - .name), + title: Text( + setting.exerciseObj + .getExercise(Localizations.localeOf(context).languageCode) + .name, + ), content: ExerciseDetail(setting.exerciseObj), actions: [ TextButton( - child: Text( - MaterialLocalizations.of(context).closeButtonLabel, - ), + child: Text(MaterialLocalizations.of(context).closeButtonLabel), onPressed: () { Navigator.of(context).pop(); }, @@ -81,9 +81,7 @@ class SettingWidget extends StatelessWidget { ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...set.getSmartRepr(setting.exerciseObj).map((e) => Text(e)), - ], + children: [...set.getSmartRepr(setting.exerciseObj).map((e) => Text(e))], ), ); } @@ -149,10 +147,7 @@ class _WorkoutDayWidgetState extends State { if (_editing) ReorderableDragStartListener( index: index, - child: const IconButton( - icon: Icon(Icons.drag_handle), - onPressed: null, - ), + child: const IconButton(icon: Icon(Icons.drag_handle), onPressed: null), ), ], ); @@ -167,11 +162,7 @@ class _WorkoutDayWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - DayHeader( - day: widget._day, - expanded: _editing, - toggle: _toggleExpanded, - ), + DayHeader(day: widget._day, expanded: _editing, toggle: _toggleExpanded), if (_editing) Padding( padding: const EdgeInsets.symmetric(vertical: 8), @@ -265,13 +256,10 @@ class DayHeader extends StatelessWidget { final bool _editing; final Function _toggle; - const DayHeader({ - required Day day, - required bool expanded, - required Function toggle, - }) : _day = day, - _editing = expanded, - _toggle = toggle; + const DayHeader({required Day day, required bool expanded, required Function toggle}) + : _day = day, + _editing = expanded, + _toggle = toggle; @override Widget build(BuildContext context) { @@ -285,22 +273,24 @@ class DayHeader extends StatelessWidget { subtitle: Text(_day.getDaysTextTranslated(Localizations.localeOf(context).languageCode)), leading: const Icon(Icons.play_arrow), minLeadingWidth: 8, - trailing: Row(mainAxisSize: MainAxisSize.min, children: [ - const SizedBox(height: 40, width: 1, child: VerticalDivider()), - const SizedBox(width: 10), - IconButton( - icon: _editing ? const Icon(Icons.done) : const Icon(Icons.edit), - tooltip: _editing ? AppLocalizations.of(context).done : AppLocalizations.of(context).edit, - onPressed: () { - _toggle(); - }, - ), - ]), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 40, width: 1, child: VerticalDivider()), + const SizedBox(width: 10), + IconButton( + icon: _editing ? const Icon(Icons.done) : const Icon(Icons.edit), + tooltip: _editing + ? AppLocalizations.of(context).done + : AppLocalizations.of(context).edit, + onPressed: () { + _toggle(); + }, + ), + ], + ), onTap: () { - Navigator.of(context).pushNamed( - GymModeScreen.routeName, - arguments: _day, - ); + Navigator.of(context).pushNamed(GymModeScreen.routeName, arguments: _day); }, ); } diff --git a/lib/widgets/workouts/forms.dart b/lib/widgets/workouts/forms.dart index 7609172dc..e23bb3c60 100644 --- a/lib/widgets/workouts/forms.dart +++ b/lib/widgets/workouts/forms.dart @@ -17,10 +17,10 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/day.dart'; import 'package:wger/models/workouts/repetition_unit.dart'; @@ -72,9 +72,7 @@ class WorkoutForm extends StatelessWidget { ), TextFormField( key: const Key('field-description'), - decoration: InputDecoration( - labelText: AppLocalizations.of(context).description, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).description), minLines: 3, maxLines: 10, controller: workoutDescriptionController, @@ -113,10 +111,9 @@ class WorkoutForm extends StatelessWidget { listen: false, ).addWorkout(_plan); if (context.mounted) { - Navigator.of(context).pushReplacementNamed( - WorkoutPlanScreen.routeName, - arguments: newPlan, - ); + Navigator.of( + context, + ).pushReplacementNamed(WorkoutPlanScreen.routeName, arguments: newPlan); } } }, @@ -142,10 +139,9 @@ class _DayCheckboxState extends State { Widget build(BuildContext context) { return CheckboxListTile( key: Key('field-checkbox-${widget._dayNr}'), - title: Text(widget._day.getDayTranslated( - widget._dayNr, - Localizations.localeOf(context).languageCode, - )), + title: Text( + widget._day.getDayTranslated(widget._dayNr, Localizations.localeOf(context).languageCode), + ), value: widget._day.daysOfWeek.contains(widget._dayNr), onChanged: (bool? newValue) { setState(() { @@ -223,14 +219,12 @@ class _DayFormWidgetState extends State { try { if (widget._day.id == null) { - Provider.of(context, listen: false).addDay( - widget._day, - widget.workout, - ); + Provider.of( + context, + listen: false, + ).addDay(widget._day, widget.workout); } else { - Provider.of(context, listen: false).editDay( - widget._day, - ); + Provider.of(context, listen: false).editDay(widget._day); } widget.dayController.clear(); @@ -442,22 +436,16 @@ class _SetFormWidgetState extends State { return null; } return context.read().searchExercise( - pattern, - languageCode: Localizations.localeOf(context).languageCode, - searchEnglish: _searchEnglish, - ); + pattern, + languageCode: Localizations.localeOf(context).languageCode, + searchEnglish: _searchEnglish, + ); }, - itemBuilder: ( - BuildContext context, - Exercise exerciseSuggestion, - ) => - ListTile( + itemBuilder: (BuildContext context, Exercise exerciseSuggestion) => ListTile( key: Key('exercise-${exerciseSuggestion.id}'), leading: SizedBox( width: 45, - child: ExerciseImageWidget( - image: exerciseSuggestion.getMainImage, - ), + child: ExerciseImageWidget(image: exerciseSuggestion.getMainImage), ), title: Text( exerciseSuggestion @@ -487,10 +475,7 @@ class _SetFormWidgetState extends State { ); }, transitionBuilder: (context, animation, child) => FadeTransition( - opacity: CurvedAnimation( - parent: animation, - curve: Curves.fastOutSlowIn, - ), + opacity: CurvedAnimation(parent: animation, curve: Curves.fastOutSlowIn), child: child, ), onSelected: (Exercise exerciseSuggestion) { @@ -538,8 +523,9 @@ class _SetFormWidgetState extends State { final index = entry.key; final exercise = entry.value; final showSupersetInfo = (index + 1) < widget._set.exerciseBasesObj.length; - final settings = - widget._set.settings.where((e) => e.exerciseObj.id == exercise.id).toList(); + final settings = widget._set.settings + .where((e) => e.exerciseObj.id == exercise.id) + .toList(); return Column( children: [ @@ -551,16 +537,10 @@ class _SetFormWidgetState extends State { removeExerciseBase, ), if (showSupersetInfo) - const Padding( - padding: EdgeInsets.all(3.0), - child: Text('+'), - ), + const Padding(padding: EdgeInsets.all(3.0), child: Text('+')), if (showSupersetInfo) Text(AppLocalizations.of(context).supersetWith), if (showSupersetInfo) - const Padding( - padding: EdgeInsets.all(3.0), - child: Text('+'), - ), + const Padding(padding: EdgeInsets.all(3.0), child: Text('+')), ], ); }), @@ -648,29 +628,17 @@ class ExerciseSetting extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Flexible( - flex: 2, - child: RepsInputWidget(setting, _detailed), - ), + Flexible(flex: 2, child: RepsInputWidget(setting, _detailed)), const SizedBox(width: 4), - Flexible( - flex: 3, - child: RepetitionUnitInputWidget(setting), - ), + Flexible(flex: 3, child: RepetitionUnitInputWidget(setting)), ], ), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Flexible( - flex: 2, - child: WeightInputWidget(setting, _detailed), - ), + Flexible(flex: 2, child: WeightInputWidget(setting, _detailed)), const SizedBox(width: 4), - Flexible( - flex: 3, - child: WeightUnitInputWidget(setting, key: Key(i.toString())), - ), + Flexible(flex: 3, child: WeightUnitInputWidget(setting, key: Key(i.toString()))), ], ), Flexible(flex: 2, child: RiRInputWidget(setting)), @@ -919,15 +887,15 @@ class _WeightUnitInputWidgetState extends State { widget._setting.weightUnit = newValue; }); }, - items: Provider.of(context, listen: false) - .weightUnits + items: Provider.of(context, listen: false).weightUnits .map>((WeightUnit value) { - return DropdownMenuItem( - key: Key(value.id.toString()), - value: value, - child: Text(value.name), - ); - }).toList(), + return DropdownMenuItem( + key: Key(value.id.toString()), + value: value, + child: Text(value.name), + ); + }) + .toList(), ); } } @@ -951,9 +919,7 @@ class _RepetitionUnitInputWidgetState extends State { return DropdownButtonFormField( value: selectedWeightUnit, - decoration: InputDecoration( - labelText: AppLocalizations.of(context).repetitionUnit, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).repetitionUnit), isDense: true, onChanged: (RepetitionUnit? newValue) { setState(() { @@ -961,15 +927,15 @@ class _RepetitionUnitInputWidgetState extends State { widget._setting.repetitionUnit = newValue; }); }, - items: Provider.of(context, listen: false) - .repetitionUnits + items: Provider.of(context, listen: false).repetitionUnits .map>((RepetitionUnit value) { - return DropdownMenuItem( - key: Key(value.id.toString()), - value: value, - child: Text(value.name), - ); - }).toList(), + return DropdownMenuItem( + key: Key(value.id.toString()), + value: value, + child: Text(value.name), + ); + }) + .toList(), ); } } diff --git a/lib/widgets/workouts/gym_mode.dart b/lib/widgets/workouts/gym_mode.dart index 27a878737..8283cb74e 100644 --- a/lib/widgets/workouts/gym_mode.dart +++ b/lib/widgets/workouts/gym_mode.dart @@ -18,7 +18,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -27,8 +26,8 @@ import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/gym_mode.dart'; import 'package:wger/helpers/i18n.dart'; import 'package:wger/helpers/json.dart'; -import 'package:wger/helpers/misc.dart'; import 'package:wger/helpers/ui.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/day.dart'; import 'package:wger/models/workouts/log.dart'; @@ -83,8 +82,10 @@ class _GymModeState extends State { for (final set in widget._workoutDay.sets) { var firstPage = true; for (final setting in set.settingsComputed) { - final exerciseBase = Provider.of(context, listen: false) - .findExerciseById(setting.exerciseId); + final exerciseBase = Provider.of( + context, + listen: false, + ).findExerciseById(setting.exerciseId); if (firstPage) { _exercisePages[exerciseBase] = currentPage; @@ -116,23 +117,20 @@ class _GymModeState extends State { currentElement++; if (firstPage) { - out.add(ExerciseOverview( + out.add(ExerciseOverview(_controller, exerciseBase, ratioCompleted, _exercisePages)); + } + + out.add( + LogPage( _controller, + setting, + set, exerciseBase, + workoutProvider.findById(widget._workoutDay.workoutId), ratioCompleted, _exercisePages, - )); - } - - out.add(LogPage( - _controller, - setting, - set, - exerciseBase, - workoutProvider.findById(widget._workoutDay.workoutId), - ratioCompleted, - _exercisePages, - )); + ), + ); out.add(TimerWidget(_controller, ratioCompleted, _exercisePages)); firstPage = false; } @@ -149,8 +147,10 @@ class _GymModeState extends State { StartPage(_controller, widget._workoutDay, _exercisePages), ...getContent(), SessionPage( - Provider.of(context, listen: false) - .findById(widget._workoutDay.workoutId), + Provider.of( + context, + listen: false, + ).findById(widget._workoutDay.workoutId), _controller, widget._start, _exercisePages, @@ -298,9 +298,7 @@ class _LogPageState extends State { ), Expanded( child: TextFormField( - decoration: InputDecoration( - labelText: AppLocalizations.of(context).repetitions, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).repetitions), enabled: true, controller: _repsController, keyboardType: TextInputType.number, @@ -353,9 +351,7 @@ class _LogPageState extends State { ), Expanded( child: TextFormField( - decoration: InputDecoration( - labelText: AppLocalizations.of(context).weight, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).weight), controller: _weightController, keyboardType: TextInputType.number, onFieldSubmitted: (_) {}, @@ -512,9 +508,9 @@ class _LogPageState extends State { style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), - ...widget._workoutPlan - .filterLogsByExerciseBase(widget._exerciseBase, unique: true) - .map((log) { + ...widget._workoutPlan.filterLogsByExerciseBase(widget._exerciseBase, unique: true).map(( + log, + ) { return ListTile( title: Text(log.singleLogRepTextNoNl), subtitle: Text( @@ -533,9 +529,9 @@ class _LogPageState extends State { widget._log.weightUnit = log.weightUnitObj; ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context).dataCopied), - )); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).dataCopied))); }); }, contentPadding: const EdgeInsets.symmetric(horizontal: 40), @@ -584,9 +580,7 @@ class _LogPageState extends State { alignment: Alignment.center, child: Text( key.toString(), - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontWeight: FontWeight.bold), ), ), ), @@ -598,9 +592,7 @@ class _LogPageState extends State { ), ], ) - : MutedText( - AppLocalizations.of(context).plateCalculatorNotDivisible, - ), + : MutedText(AppLocalizations.of(context).plateCalculatorNotDivisible), ), const SizedBox(height: 3), ], @@ -676,20 +668,20 @@ class ExerciseOverview extends StatelessWidget { style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), - ..._exerciseBase.equipment.map((e) => Text( - getTranslation(e.name, context), - style: Theme.of(context).textTheme.titleLarge, - textAlign: TextAlign.center, - )), + ..._exerciseBase.equipment.map( + (e) => Text( + getTranslation(e.name, context), + style: Theme.of(context).textTheme.titleLarge, + textAlign: TextAlign.center, + ), + ), if (_exerciseBase.images.isNotEmpty) SizedBox( width: double.infinity, height: 200, child: ListView( scrollDirection: Axis.horizontal, - children: [ - ..._exerciseBase.images.map((e) => ExerciseImageWidget(image: e)), - ], + children: [..._exerciseBase.images.map((e) => ExerciseImageWidget(image: e))], ), ), Html( @@ -712,12 +704,7 @@ class SessionPage extends StatefulWidget { final TimeOfDay _start; final Map _exercisePages; - const SessionPage( - this._workoutPlan, - this._controller, - this._start, - this._exercisePages, - ); + const SessionPage(this._workoutPlan, this._controller, this._start, this._exercisePages); @override _SessionPageState createState() => _SessionPageState(); @@ -777,9 +764,11 @@ class _SessionPageState extends State { renderBorder: false, onPressed: (int index) { setState(() { - for (int buttonIndex = 0; - buttonIndex < selectedImpression.length; - buttonIndex++) { + for ( + int buttonIndex = 0; + buttonIndex < selectedImpression.length; + buttonIndex++ + ) { _session.impression = index + 1; if (buttonIndex == index) { @@ -798,9 +787,7 @@ class _SessionPageState extends State { ], ), TextFormField( - decoration: InputDecoration( - labelText: AppLocalizations.of(context).notes, - ), + decoration: InputDecoration(labelText: AppLocalizations.of(context).notes), maxLines: 3, controller: notesController, keyboardType: TextInputType.multiline, @@ -922,11 +909,7 @@ class TimerWidget extends StatefulWidget { final double _ratioCompleted; final Map _exercisePages; - const TimerWidget( - this._controller, - this._ratioCompleted, - this._exercisePages, - ); + const TimerWidget(this._controller, this._ratioCompleted, this._exercisePages); @override _TimerWidgetState createState() => _TimerWidgetState(); @@ -1054,18 +1037,11 @@ class NavigationHeader extends StatelessWidget { final String _title; final Map exercisePages; - const NavigationHeader( - this._title, - this._controller, { - required this.exercisePages, - }); + const NavigationHeader(this._title, this._controller, {required this.exercisePages}); Widget getDialog(BuildContext context) { return AlertDialog( - title: Text( - AppLocalizations.of(context).jumpTo, - textAlign: TextAlign.center, - ), + title: Text(AppLocalizations.of(context).jumpTo, textAlign: TextAlign.center), contentPadding: EdgeInsets.zero, content: SingleChildScrollView( child: Column( @@ -1121,10 +1097,7 @@ class NavigationHeader extends StatelessWidget { IconButton( icon: const Icon(Icons.toc), onPressed: () { - showDialog( - context: context, - builder: (ctx) => getDialog(context), - ); + showDialog(context: context, builder: (ctx) => getDialog(context)); }, ), ], diff --git a/lib/widgets/workouts/workout_logs.dart b/lib/widgets/workouts/workout_logs.dart index fa1610ec2..9524c963d 100644 --- a/lib/widgets/workouts/workout_logs.dart +++ b/lib/widgets/workouts/workout_logs.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/log.dart'; import 'package:wger/models/workouts/session.dart'; @@ -70,10 +70,7 @@ class _WorkoutLogsState extends State { ), Padding( padding: const EdgeInsets.all(8.0), - child: Text( - AppLocalizations.of(context).logHelpEntries, - textAlign: TextAlign.justify, - ), + child: Text(AppLocalizations.of(context).logHelpEntries, textAlign: TextAlign.justify), ), Padding( padding: const EdgeInsets.all(8.0), @@ -82,10 +79,7 @@ class _WorkoutLogsState extends State { textAlign: TextAlign.justify, ), ), - SizedBox( - width: double.infinity, - child: WorkoutLogCalendar(widget._workoutPlan), - ), + SizedBox(width: double.infinity, child: WorkoutLogCalendar(widget._workoutPlan)), ], ); } diff --git a/lib/widgets/workouts/workout_plan_detail.dart b/lib/widgets/workouts/workout_plan_detail.dart index 5fcfd7c78..c167cb81c 100644 --- a/lib/widgets/workouts/workout_plan_detail.dart +++ b/lib/widgets/workouts/workout_plan_detail.dart @@ -17,7 +17,7 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/workout_plan_screen.dart'; @@ -50,17 +50,11 @@ class _WorkoutPlanDetailState extends State { } }, isSelected: const [true, false], - children: const [ - Icon(Icons.table_chart), - Icon(Icons.show_chart), - ], + children: const [Icon(Icons.table_chart), Icon(Icons.show_chart)], ), ), if (widget._workoutPlan.description != '') - Padding( - padding: const EdgeInsets.all(15), - child: Text(widget._workoutPlan.description), - ), + Padding(padding: const EdgeInsets.all(15), child: Text(widget._workoutPlan.description)), ...widget._workoutPlan.days.map((workoutDay) => WorkoutDayWidget(workoutDay)), Column( children: [ diff --git a/lib/widgets/workouts/workout_plans_list.dart b/lib/widgets/workouts/workout_plans_list.dart index 14112ab13..534fcc803 100644 --- a/lib/widgets/workouts/workout_plans_list.dart +++ b/lib/widgets/workouts/workout_plans_list.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/workout_plan_screen.dart'; import 'package:wger/widgets/core/text_prompt.dart'; @@ -46,10 +46,9 @@ class WorkoutPlansList extends StatelessWidget { onTap: () { _workoutProvider.setCurrentPlan(currentWorkout.id!); - Navigator.of(context).pushNamed( - WorkoutPlanScreen.routeName, - arguments: currentWorkout, - ); + Navigator.of( + context, + ).pushNamed(WorkoutPlanScreen.routeName, arguments: currentWorkout); }, title: Text(currentWorkout.name), subtitle: Text( @@ -57,62 +56,65 @@ class WorkoutPlansList extends StatelessWidget { Localizations.localeOf(context).languageCode, ).format(currentWorkout.creationDate), ), - trailing: Row(mainAxisSize: MainAxisSize.min, children: [ - const VerticalDivider(), - IconButton( - icon: const Icon(Icons.delete), - tooltip: AppLocalizations.of(context).delete, - onPressed: () async { - // Delete workout from DB - await showDialog( - context: context, - builder: (BuildContext contextDialog) { - return AlertDialog( - content: Text( - AppLocalizations.of(context).confirmDelete(currentWorkout.name), - ), - actions: [ - TextButton( - child: Text( - MaterialLocalizations.of(context).cancelButtonLabel, - ), - onPressed: () => Navigator.of(contextDialog).pop(), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const VerticalDivider(), + IconButton( + icon: const Icon(Icons.delete), + tooltip: AppLocalizations.of(context).delete, + onPressed: () async { + // Delete workout from DB + await showDialog( + context: context, + builder: (BuildContext contextDialog) { + return AlertDialog( + content: Text( + AppLocalizations.of(context).confirmDelete(currentWorkout.name), ), - TextButton( - child: Text( - AppLocalizations.of(context).delete, - style: TextStyle( - color: Theme.of(context).colorScheme.error, + actions: [ + TextButton( + child: Text( + MaterialLocalizations.of(context).cancelButtonLabel, ), + onPressed: () => Navigator.of(contextDialog).pop(), ), - onPressed: () { - // Confirmed, delete the workout - Provider.of( - context, - listen: false, - ).deleteWorkout(currentWorkout.id!); + TextButton( + child: Text( + AppLocalizations.of(context).delete, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + onPressed: () { + // Confirmed, delete the workout + Provider.of( + context, + listen: false, + ).deleteWorkout(currentWorkout.id!); - // Close the popup - Navigator.of(contextDialog).pop(); + // Close the popup + Navigator.of(contextDialog).pop(); - // and inform the user - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, + // and inform the user + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, + ), ), - ), - ); - }, - ), - ], - ); - }, - ); - }, - ), - ]), + ); + }, + ), + ], + ); + }, + ); + }, + ), + ], + ), ), ); }, diff --git a/test/auth/auth_screen_test.dart b/test/auth/auth_screen_test.dart index c9a89cf48..a6050a3e2 100644 --- a/test/auth/auth_screen_test.dart +++ b/test/auth/auth_screen_test.dart @@ -19,7 +19,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; @@ -28,6 +27,7 @@ import 'package:mockito/mockito.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/screens/auth_screen.dart'; @@ -38,17 +38,9 @@ void main() { late AuthProvider authProvider; late MockClient mockClient; - final Uri tRegistration = Uri( - scheme: 'https', - host: 'wger.de', - path: 'api/v2/register/', - ); + final Uri tRegistration = Uri(scheme: 'https', host: 'wger.de', path: 'api/v2/register/'); - final Uri tLogin = Uri( - scheme: 'https', - host: 'wger.de', - path: 'api/v2/login/', - ); + final Uri tLogin = Uri(scheme: 'https', host: 'wger.de', path: 'api/v2/login/'); final responseLoginOk = {'token': 'b01c44d3e3e016a615d2f82b16d31f8b924fb936'}; @@ -85,19 +77,15 @@ void main() { buildSignature: 'buildSignature', ); - when(mockClient.post( - tLogin, - headers: anyNamed('headers'), - body: anyNamed('body'), - )).thenAnswer((_) => Future(() => Response(json.encode(responseLoginOk), 200))); + when( + mockClient.post(tLogin, headers: anyNamed('headers'), body: anyNamed('body')), + ).thenAnswer((_) => Future(() => Response(json.encode(responseLoginOk), 200))); when(mockClient.get(any)).thenAnswer((_) => Future(() => Response('"1.2.3.4"', 200))); - when(mockClient.post( - tRegistration, - headers: anyNamed('headers'), - body: anyNamed('body'), - )).thenAnswer((_) => Future(() => Response(json.encode(responseRegistrationOk), 201))); + when( + mockClient.post(tRegistration, headers: anyNamed('headers'), body: anyNamed('body')), + ).thenAnswer((_) => Future(() => Response(json.encode(responseRegistrationOk), 201))); }); group('Login mode', () { @@ -134,11 +122,13 @@ void main() { // Assert expect(find.textContaining('An Error Occurred'), findsNothing); verify(mockClient.get(any)); - verify(mockClient.post( - tLogin, - headers: anyNamed('headers'), - body: json.encode({'username': 'testuser', 'password': '123456789'}), - )); + verify( + mockClient.post( + tLogin, + headers: anyNamed('headers'), + body: json.encode({'username': 'testuser', 'password': '123456789'}), + ), + ); }); testWidgets('Login - wront username & password', (WidgetTester tester) async { @@ -149,11 +139,9 @@ void main() { 'non_field_errors': ['Username or password unknown'], }; - when(mockClient.post( - tLogin, - headers: anyNamed('headers'), - body: anyNamed('body'), - )).thenAnswer((_) => Future(() => Response(json.encode(response), 400))); + when( + mockClient.post(tLogin, headers: anyNamed('headers'), body: anyNamed('body')), + ).thenAnswer((_) => Future(() => Response(json.encode(response), 400))); await tester.pumpWidget(getWidget()); // Act @@ -166,11 +154,13 @@ void main() { expect(find.textContaining('An Error Occurred'), findsOne); expect(find.textContaining('Non field errors'), findsOne); expect(find.textContaining('Username or password unknown'), findsOne); - verify(mockClient.post( - tLogin, - headers: anyNamed('headers'), - body: json.encode({'username': 'testuser', 'password': '123456789'}), - )); + verify( + mockClient.post( + tLogin, + headers: anyNamed('headers'), + body: json.encode({'username': 'testuser', 'password': '123456789'}), + ), + ); }); }); @@ -219,11 +209,13 @@ void main() { // Assert expect(find.textContaining('An Error Occurred'), findsNothing); - verify(mockClient.post( - tRegistration, - headers: anyNamed('headers'), - body: json.encode({'username': 'testuser', 'password': '123456789'}), - )); + verify( + mockClient.post( + tRegistration, + headers: anyNamed('headers'), + body: json.encode({'username': 'testuser', 'password': '123456789'}), + ), + ); }); testWidgets('Registration - password problems', (WidgetTester tester) async { @@ -232,17 +224,12 @@ void main() { tester.view.devicePixelRatio = 1.0; final response = { 'username': ['This field must be unique.'], - 'password': [ - 'This password is too common.', - 'This password is entirely numeric.', - ], + 'password': ['This password is too common.', 'This password is entirely numeric.'], }; - when(mockClient.post( - tRegistration, - headers: anyNamed('headers'), - body: anyNamed('body'), - )).thenAnswer((_) => Future(() => Response(json.encode(response), 400))); + when( + mockClient.post(tRegistration, headers: anyNamed('headers'), body: anyNamed('body')), + ).thenAnswer((_) => Future(() => Response(json.encode(response), 400))); await tester.pumpWidget(getWidget()); // Act @@ -259,11 +246,13 @@ void main() { expect(find.textContaining('This password is entirely numeric'), findsOne); expect(find.textContaining('This field must be unique'), findsOne); - verify(mockClient.post( - tRegistration, - headers: anyNamed('headers'), - body: json.encode({'username': 'testuser', 'password': '123456789'}), - )); + verify( + mockClient.post( + tRegistration, + headers: anyNamed('headers'), + body: json.encode({'username': 'testuser', 'password': '123456789'}), + ), + ); }); }); } diff --git a/test/core/settings_test.dart b/test/core/settings_test.dart index 72806b48c..8eb931e74 100644 --- a/test/core/settings_test.dart +++ b/test/core/settings_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/widgets/core/settings.dart'; diff --git a/test/exercises/contribute_exercise_test.dart b/test/exercises/contribute_exercise_test.dart index b1c595ab0..f252aa3d5 100644 --- a/test/exercises/contribute_exercise_test.dart +++ b/test/exercises/contribute_exercise_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/user.dart'; diff --git a/test/exercises/exercises_detail_widget_test.dart b/test/exercises/exercises_detail_widget_test.dart index d40dd8575..91be517d3 100644 --- a/test/exercises/exercises_detail_widget_test.dart +++ b/test/exercises/exercises_detail_widget_test.dart @@ -17,9 +17,9 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/widgets/exercises/exercises.dart'; diff --git a/test/gallery/gallery_form_test.dart b/test/gallery/gallery_form_test.dart index 6fdc69e01..fc84a3e54 100644 --- a/test/gallery/gallery_form_test.dart +++ b/test/gallery/gallery_form_test.dart @@ -27,6 +27,7 @@ import 'package:wger/helpers/consts.dart'; import 'package:wger/models/gallery/image.dart' as gallery; import 'package:wger/providers/gallery.dart'; import 'package:wger/widgets/gallery/forms.dart'; + import '../../test_data/gallery.dart'; import 'gallery_form_test.mocks.dart'; diff --git a/test/gallery/gallery_screen_test.dart b/test/gallery/gallery_screen_test.dart index 0f796ab19..08186601b 100644 --- a/test/gallery/gallery_screen_test.dart +++ b/test/gallery/gallery_screen_test.dart @@ -17,13 +17,13 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:network_image_mock/network_image_mock.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/gallery.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/widgets/gallery/overview.dart'; diff --git a/test/measurements/measurement_categories_screen_test.dart b/test/measurements/measurement_categories_screen_test.dart index 75d8ad181..a222f5dfb 100644 --- a/test/measurements/measurement_categories_screen_test.dart +++ b/test/measurements/measurement_categories_screen_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/models/measurements/measurement_entry.dart'; import 'package:wger/providers/measurement.dart'; @@ -37,14 +37,24 @@ void main() { setUp(() { mockMeasurementProvider = MockMeasurementProvider(); when(mockMeasurementProvider.categories).thenReturn([ - MeasurementCategory(id: 1, name: 'body fat', unit: '%', entries: [ - MeasurementEntry(id: 1, category: 1, date: DateTime(2021, 9, 1), value: 10, notes: ''), - MeasurementEntry(id: 2, category: 1, date: DateTime(2021, 9, 5), value: 11, notes: ''), - ]), - MeasurementCategory(id: 2, name: 'biceps', unit: 'cm', entries: [ - MeasurementEntry(id: 3, category: 2, date: DateTime(2021, 9, 1), value: 30, notes: ''), - MeasurementEntry(id: 4, category: 2, date: DateTime(2021, 9, 5), value: 40, notes: ''), - ]), + MeasurementCategory( + id: 1, + name: 'body fat', + unit: '%', + entries: [ + MeasurementEntry(id: 1, category: 1, date: DateTime(2021, 9, 1), value: 10, notes: ''), + MeasurementEntry(id: 2, category: 1, date: DateTime(2021, 9, 5), value: 11, notes: ''), + ], + ), + MeasurementCategory( + id: 2, + name: 'biceps', + unit: 'cm', + entries: [ + MeasurementEntry(id: 3, category: 2, date: DateTime(2021, 9, 1), value: 30, notes: ''), + MeasurementEntry(id: 4, category: 2, date: DateTime(2021, 9, 5), value: 40, notes: ''), + ], + ), ]); }); diff --git a/test/measurements/measurement_entries_screen_test.dart b/test/measurements/measurement_entries_screen_test.dart index fef0b2efe..f99a76947 100644 --- a/test/measurements/measurement_entries_screen_test.dart +++ b/test/measurements/measurement_entries_screen_test.dart @@ -17,10 +17,10 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/models/measurements/measurement_entry.dart'; import 'package:wger/providers/measurement.dart'; @@ -37,10 +37,21 @@ void main() { setUp(() { mockMeasurementProvider = MockMeasurementProvider(); when(mockMeasurementProvider.findCategoryById(any)).thenReturn( - MeasurementCategory(id: 1, name: 'body fat', unit: '%', entries: [ - MeasurementEntry(id: 1, category: 1, date: DateTime(2021, 8, 1), value: 10.2, notes: ''), - MeasurementEntry(id: 1, category: 1, date: DateTime(2021, 8, 10), value: 18.1, notes: 'a'), - ]), + MeasurementCategory( + id: 1, + name: 'body fat', + unit: '%', + entries: [ + MeasurementEntry(id: 1, category: 1, date: DateTime(2021, 8, 1), value: 10.2, notes: ''), + MeasurementEntry( + id: 1, + category: 1, + date: DateTime(2021, 8, 10), + value: 18.1, + notes: 'a', + ), + ], + ), ); mockNutritionPlansProvider = MockNutritionPlansProvider(); diff --git a/test/nutrition/nutritional_diary_test.dart b/test/nutrition/nutritional_diary_test.dart index 515b20d47..8280f1cb2 100644 --- a/test/nutrition/nutritional_diary_test.dart +++ b/test/nutrition/nutritional_diary_test.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/widgets/nutrition/charts.dart'; import 'package:wger/widgets/nutrition/nutritional_diary_detail.dart'; diff --git a/test/nutrition/nutritional_meal_form_test.dart b/test/nutrition/nutritional_meal_form_test.dart index a053b19fa..79d6c0f71 100644 --- a/test/nutrition/nutritional_meal_form_test.dart +++ b/test/nutrition/nutritional_meal_form_test.dart @@ -18,13 +18,13 @@ import 'package:clock/clock.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/nutrition.dart'; @@ -61,9 +61,7 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, navigatorKey: key, home: Scaffold(body: MealForm("1", meal)), - routes: { - NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(), - }, + routes: {NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen()}, ), ); } @@ -81,11 +79,7 @@ void main() { await tester.pumpWidget(createFormScreen(meal1)); await tester.pumpAndSettle(); - expect( - find.text('17:00'), - findsOneWidget, - reason: 'Time of existing meal is filled in', - ); + expect(find.text('17:00'), findsOneWidget, reason: 'Time of existing meal is filled in'); expect( find.text('Initial Name 1'), diff --git a/test/nutrition/nutritional_meal_item_form_test.dart b/test/nutrition/nutritional_meal_item_form_test.dart index e37435e98..e5ee091a6 100644 --- a/test/nutrition/nutritional_meal_item_form_test.dart +++ b/test/nutrition/nutritional_meal_item_form_test.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:http/http.dart' as http; @@ -10,6 +9,7 @@ import 'package:mockito/mockito.dart'; import 'package:network_image_mock/network_image_mock.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/ingredient_api.dart'; import 'package:wger/models/nutrition/ingredient.dart'; import 'package:wger/models/nutrition/meal.dart'; @@ -74,14 +74,17 @@ void main() { when(mockNutrition.searchIngredientWithCode('123')).thenAnswer((_) => Future.value(ingredient)); when(mockNutrition.searchIngredientWithCode('')).thenAnswer((_) => Future.value(null)); when(mockNutrition.searchIngredientWithCode('222')).thenAnswer((_) => Future.value(null)); - when(mockNutrition.searchIngredient( - any, - languageCode: anyNamed('languageCode'), - searchEnglish: anyNamed('searchEnglish'), - )).thenAnswer( + when( + mockNutrition.searchIngredient( + any, + languageCode: anyNamed('languageCode'), + searchEnglish: anyNamed('searchEnglish'), + ), + ).thenAnswer( (_) => Future.value( - IngredientApiSearch.fromJson(json.decode(fixture('nutrition/ingredient_suggestions'))) - .suggestions, + IngredientApiSearch.fromJson( + json.decode(fixture('nutrition/ingredient_suggestions')), + ).suggestions, ), ); @@ -104,9 +107,7 @@ void main() { MealItemForm(meal, const [], code, test), ), ), - routes: { - NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(), - }, + routes: {NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen()}, ), ); } @@ -294,40 +295,41 @@ void main() { expect(find.text('Please enter a valid number'), findsOneWidget); }); - testWidgets( - 'save complete ingredient with correct weight input type', - (WidgetTester tester) async { - await tester.pumpWidget(createMealItemFormScreen(meal1, '123', true)); + testWidgets('save complete ingredient with correct weight input type', ( + WidgetTester tester, + ) async { + await tester.pumpWidget(createMealItemFormScreen(meal1, '123', true)); - final IngredientFormState formState = tester.state(find.byType(IngredientForm)); + final IngredientFormState formState = tester.state(find.byType(IngredientForm)); - await tester.tap(find.byKey(const Key('scan-button'))); - await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('scan-button'))); + await tester.pumpAndSettle(); - expect(find.byKey(const Key('ingredient-scan-result-dialog')), findsOneWidget); + expect(find.byKey(const Key('ingredient-scan-result-dialog')), findsOneWidget); - await tester.tap(find.byKey(const Key('ingredient-scan-result-dialog-confirm-button'))); - await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('ingredient-scan-result-dialog-confirm-button'))); + await tester.pumpAndSettle(); - expect(formState.ingredientIdController.text, '1'); + expect(formState.ingredientIdController.text, '1'); - await tester.enterText(find.byKey(const Key('field-weight')), '2'); + await tester.enterText(find.byKey(const Key('field-weight')), '2'); - // once ID and weight are set, it'll fetchIngredient and show macros preview and ingredient image - when(mockNutrition.fetchIngredient(1)).thenAnswer((_) => Future.value( - Ingredient.fromJson(jsonDecode(fixture('nutrition/ingredientinfo_59887.json'))), - )); - await mockNetworkImagesFor(() => tester.pumpAndSettle()); + // once ID and weight are set, it'll fetchIngredient and show macros preview and ingredient image + when(mockNutrition.fetchIngredient(1)).thenAnswer( + (_) => Future.value( + Ingredient.fromJson(jsonDecode(fixture('nutrition/ingredientinfo_59887.json'))), + ), + ); + await mockNetworkImagesFor(() => tester.pumpAndSettle()); - expect(find.byKey(const Key('ingredient-scan-result-dialog')), findsNothing); + expect(find.byKey(const Key('ingredient-scan-result-dialog')), findsNothing); - await tester.tap(find.byKey(const Key(SUBMIT_BUTTON_KEY_NAME))); - await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key(SUBMIT_BUTTON_KEY_NAME))); + await tester.pumpAndSettle(); - expect(formState.mealItem.amount, 2); + expect(formState.mealItem.amount, 2); - verify(mockNutrition.addMealItem(any, meal1)); - }, - ); + verify(mockNutrition.addMealItem(any, meal1)); + }); }); } diff --git a/test/nutrition/nutritional_plan_form_test.dart b/test/nutrition/nutritional_plan_form_test.dart index ce89c0fef..309105945 100644 --- a/test/nutrition/nutritional_plan_form_test.dart +++ b/test/nutrition/nutritional_plan_form_test.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/nutritional_plan_screen.dart'; @@ -59,9 +59,7 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, navigatorKey: key, home: Scaffold(body: PlanForm(plan)), - routes: { - NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(), - }, + routes: {NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen()}, ), ); } diff --git a/test/nutrition/nutritional_plan_screen_test.dart b/test/nutrition/nutritional_plan_screen_test.dart index e22f93083..0c51c121a 100644 --- a/test/nutrition/nutritional_plan_screen_test.dart +++ b/test/nutrition/nutritional_plan_screen_test.dart @@ -18,13 +18,13 @@ import 'package:drift/native.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; import 'package:http/http.dart' as http; import 'package:mockito/annotations.dart'; import 'package:provider/provider.dart'; import 'package:wger/database/ingredients/ingredients_database.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/providers/base_provider.dart'; import 'package:wger/providers/body_weight.dart'; @@ -54,11 +54,7 @@ void main() { return MultiProvider( providers: [ ChangeNotifierProvider( - create: (context) => NutritionPlansProvider( - mockBaseProvider, - [], - database: database, - ), + create: (context) => NutritionPlansProvider(mockBaseProvider, [], database: database), ), ChangeNotifierProvider( create: (context) => BodyWeightProvider(mockBaseProvider), @@ -82,64 +78,59 @@ void main() { ); } - testGoldens( - 'Test the widgets on the nutritional plan screen', - (tester) async { - await loadAppFonts(); - final globalKey = GlobalKey(); - await tester.pumpWidgetBuilder( - Material(key: globalKey), - wrapper: materialAppWrapper( - localizations: [AppLocalizations.delegate], - ), - surfaceSize: const Size(500, 1000), - ); - await tester.pumpWidget(createNutritionalPlan()); - await tester.tap(find.byType(TextButton)); - await tester.pumpAndSettle(); - - await screenMatchesGolden(tester, 'nutritional_plan_1_default_view'); - - // Default view shows plan description, info button, and no ingredients - expect(find.text('Less fat, more protein'), findsOneWidget); - expect(find.byIcon(Icons.info_outline), findsNWidgets(3)); // 2 meals, 1 "other logs" - expect(find.byIcon(Icons.info), findsNothing); - expect(find.text('100g Water'), findsNothing); - expect(find.text('75g Burger soup'), findsNothing); - - // tap the first info button changes it and reveals ingredients for the first meal - var infoOutlineButtons = find.byIcon(Icons.info_outline); - await tester.tap(infoOutlineButtons.first); // 2nd button shows up also, but is off-screen - await tester.pumpAndSettle(); - await screenMatchesGolden(tester, 'nutritional_plan_2_one_meal_with_ingredients'); - - // Ingredients show up now - expect(find.text('100g Water'), findsOneWidget); - expect(find.text('75g Burger soup'), findsOneWidget); - - // .. and the button icon has changed - expect(find.byIcon(Icons.info_outline), findsNWidgets(2)); - expect(find.byIcon(Icons.info), findsOneWidget); - - // the goals widget pushes this content down a bit. - // let's first find our icon (note: the previous icon no longer matches) - infoOutlineButtons = find.byIcon(Icons.info_outline); - - await tester.scrollUntilVisible(infoOutlineButtons.first, 30); - expect(find.text('300g Broccoli cake'), findsNothing); - - await tester.tap(infoOutlineButtons.first); - await tester.pumpAndSettle(); - await screenMatchesGolden(tester, 'nutritional_plan_3_both_meals_with_ingredients'); - expect(find.byIcon(Icons.info_outline), findsOneWidget); - expect(find.byIcon(Icons.info), findsNWidgets(2)); - - await tester.scrollUntilVisible(find.text('300g Broccoli cake'), 30); - expect(find.text('300g Broccoli cake'), findsOneWidget); - - expect(find.byType(Card), findsNWidgets(3)); - }, - ); + testGoldens('Test the widgets on the nutritional plan screen', (tester) async { + await loadAppFonts(); + final globalKey = GlobalKey(); + await tester.pumpWidgetBuilder( + Material(key: globalKey), + wrapper: materialAppWrapper(localizations: [AppLocalizations.delegate]), + surfaceSize: const Size(500, 1000), + ); + await tester.pumpWidget(createNutritionalPlan()); + await tester.tap(find.byType(TextButton)); + await tester.pumpAndSettle(); + + await screenMatchesGolden(tester, 'nutritional_plan_1_default_view'); + + // Default view shows plan description, info button, and no ingredients + expect(find.text('Less fat, more protein'), findsOneWidget); + expect(find.byIcon(Icons.info_outline), findsNWidgets(3)); // 2 meals, 1 "other logs" + expect(find.byIcon(Icons.info), findsNothing); + expect(find.text('100g Water'), findsNothing); + expect(find.text('75g Burger soup'), findsNothing); + + // tap the first info button changes it and reveals ingredients for the first meal + var infoOutlineButtons = find.byIcon(Icons.info_outline); + await tester.tap(infoOutlineButtons.first); // 2nd button shows up also, but is off-screen + await tester.pumpAndSettle(); + await screenMatchesGolden(tester, 'nutritional_plan_2_one_meal_with_ingredients'); + + // Ingredients show up now + expect(find.text('100g Water'), findsOneWidget); + expect(find.text('75g Burger soup'), findsOneWidget); + + // .. and the button icon has changed + expect(find.byIcon(Icons.info_outline), findsNWidgets(2)); + expect(find.byIcon(Icons.info), findsOneWidget); + + // the goals widget pushes this content down a bit. + // let's first find our icon (note: the previous icon no longer matches) + infoOutlineButtons = find.byIcon(Icons.info_outline); + + await tester.scrollUntilVisible(infoOutlineButtons.first, 30); + expect(find.text('300g Broccoli cake'), findsNothing); + + await tester.tap(infoOutlineButtons.first); + await tester.pumpAndSettle(); + await screenMatchesGolden(tester, 'nutritional_plan_3_both_meals_with_ingredients'); + expect(find.byIcon(Icons.info_outline), findsOneWidget); + expect(find.byIcon(Icons.info), findsNWidgets(2)); + + await tester.scrollUntilVisible(find.text('300g Broccoli cake'), 30); + expect(find.text('300g Broccoli cake'), findsOneWidget); + + expect(find.byType(Card), findsNWidgets(3)); + }); testWidgets('Tests the localization of times - EN', (WidgetTester tester) async { await tester.pumpWidget(createNutritionalPlan()); diff --git a/test/nutrition/nutritional_plans_screen_test.dart b/test/nutrition/nutritional_plans_screen_test.dart index eea8de89a..8a3ee4e24 100644 --- a/test/nutrition/nutritional_plans_screen_test.dart +++ b/test/nutrition/nutritional_plans_screen_test.dart @@ -18,13 +18,13 @@ import 'package:drift/native.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/database/ingredients/ingredients_database.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/providers/base_provider.dart'; @@ -51,34 +51,29 @@ void main() { }); Widget createHomeScreen({locale = 'en'}) { - when(client.delete(any, headers: anyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 200)); + when( + client.delete(any, headers: anyNamed('headers')), + ).thenAnswer((_) async => http.Response('', 200)); - when(mockBaseProvider.deleteRequest(any, any)).thenAnswer( - (_) async => http.Response('', 200), - ); + when(mockBaseProvider.deleteRequest(any, any)).thenAnswer((_) async => http.Response('', 200)); when(mockAuthProvider.token).thenReturn('1234'); when(mockAuthProvider.serverUrl).thenReturn('http://localhost'); when(mockAuthProvider.getAppNameHeader()).thenReturn('wger app'); return ChangeNotifierProvider( - create: (context) => NutritionPlansProvider( - mockBaseProvider, - [ - NutritionalPlan( - id: 'deadbeefa', - description: 'test plan 1', - creationDate: DateTime(2021, 01, 01), - ), - NutritionalPlan( - id: 'deadbeefb', - description: 'test plan 2', - creationDate: DateTime(2021, 01, 10), - ), - ], - database: database, - ), + create: (context) => NutritionPlansProvider(mockBaseProvider, [ + NutritionalPlan( + id: 'deadbeefa', + description: 'test plan 1', + creationDate: DateTime(2021, 01, 01), + ), + NutritionalPlan( + id: 'deadbeefb', + description: 'test plan 2', + creationDate: DateTime(2021, 01, 10), + ), + ], database: database), child: MaterialApp( locale: Locale(locale), localizationsDelegates: AppLocalizations.localizationsDelegates, diff --git a/test/weight/weight_form_test.dart b/test/weight/weight_form_test.dart index 7c81ef7e7..443f07f56 100644 --- a/test/weight/weight_form_test.dart +++ b/test/weight/weight_form_test.dart @@ -17,8 +17,8 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/body_weight/weight_entry.dart'; import 'package:wger/widgets/weight/forms.dart'; diff --git a/test/weight/weight_screen_test.dart b/test/weight/weight_screen_test.dart index fcebe2608..af3d100af 100644 --- a/test/weight/weight_screen_test.dart +++ b/test/weight/weight_screen_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/user.dart'; diff --git a/test/workout/gym_mode_screen_test.dart b/test/workout/gym_mode_screen_test.dart index 5175ef8da..cfe51f0f6 100644 --- a/test/workout/gym_mode_screen_test.dart +++ b/test/workout/gym_mode_screen_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/base_provider.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/workout_plans.dart'; @@ -45,11 +45,8 @@ void main() { Widget createHomeScreen({locale = 'en'}) { return ChangeNotifierProvider( - create: (context) => WorkoutPlansProvider( - mockBaseProvider, - mockExerciseProvider, - [workoutPlan], - ), + create: (context) => + WorkoutPlansProvider(mockBaseProvider, mockExerciseProvider, [workoutPlan]), child: ChangeNotifierProvider( create: (context) => mockExerciseProvider, child: MaterialApp( @@ -66,9 +63,7 @@ void main() { ), child: const SizedBox(), ), - routes: { - WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen(), - }, + routes: {WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen()}, ), ), ); diff --git a/test/workout/repetition_unit_form_widget_test.dart b/test/workout/repetition_unit_form_widget_test.dart index 423860073..18a172d03 100644 --- a/test/workout/repetition_unit_form_widget_test.dart +++ b/test/workout/repetition_unit_form_widget_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/repetition_unit.dart'; import 'package:wger/models/workouts/setting.dart'; import 'package:wger/providers/workout_plans.dart'; @@ -65,9 +65,7 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, navigatorKey: key, home: Scaffold(body: RepetitionUnitInputWidget(setting1)), - routes: { - WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen(), - }, + routes: {WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen()}, ), ); } diff --git a/test/workout/weight_unit_form_widget_test.dart b/test/workout/weight_unit_form_widget_test.dart index ed68f0448..ef333d10e 100644 --- a/test/workout/weight_unit_form_widget_test.dart +++ b/test/workout/weight_unit_form_widget_test.dart @@ -17,11 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/setting.dart'; import 'package:wger/models/workouts/weight_unit.dart'; import 'package:wger/providers/body_weight.dart'; @@ -66,9 +66,7 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, navigatorKey: key, home: Scaffold(body: WeightUnitInputWidget(setting1)), - routes: { - WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen(), - }, + routes: {WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen()}, ), ); } diff --git a/test/workout/workout_day_form_test.dart b/test/workout/workout_day_form_test.dart index ef466062b..fe0331d64 100644 --- a/test/workout/workout_day_form_test.dart +++ b/test/workout/workout_day_form_test.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/day.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/providers/workout_plans.dart'; diff --git a/test/workout/workout_form_test.dart b/test/workout/workout_form_test.dart index 7528c09fa..4ae21ebb3 100644 --- a/test/workout/workout_form_test.dart +++ b/test/workout/workout_form_test.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/workout_plan_screen.dart'; @@ -45,8 +45,9 @@ void main() { setUp(() { mockWorkoutPlans = MockWorkoutPlansProvider(); when(mockWorkoutPlans.editWorkout(any)).thenAnswer((_) => Future.value(existingPlan)); - when(mockWorkoutPlans.fetchAndSetWorkoutPlanFull(any)) - .thenAnswer((_) => Future.value(existingPlan)); + when( + mockWorkoutPlans.fetchAndSetWorkoutPlanFull(any), + ).thenAnswer((_) => Future.value(existingPlan)); }); Widget createHomeScreen(WorkoutPlan workoutPlan, {locale = 'en'}) { @@ -60,9 +61,7 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, navigatorKey: key, home: Scaffold(body: WorkoutForm(workoutPlan)), - routes: { - WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen(), - }, + routes: {WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen()}, ), ); } @@ -79,11 +78,7 @@ void main() { await tester.pumpWidget(createHomeScreen(existingPlan)); await tester.pumpAndSettle(); - expect( - find.text('test 1'), - findsOneWidget, - reason: 'Name of existing workout plan', - ); + expect(find.text('test 1'), findsOneWidget, reason: 'Name of existing workout plan'); expect( find.text('description 1'), findsOneWidget, diff --git a/test/workout/workout_plan_screen_test.dart b/test/workout/workout_plan_screen_test.dart index b3bad988f..84658f08b 100644 --- a/test/workout/workout_plan_screen_test.dart +++ b/test/workout/workout_plan_screen_test.dart @@ -18,11 +18,11 @@ import 'package:drift/native.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:provider/provider.dart'; import 'package:wger/database/exercises/exercise_database.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/base_provider.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/workout_plans.dart'; @@ -57,9 +57,7 @@ void main() { ), child: const SizedBox(), ), - routes: { - WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen(), - }, + routes: {WorkoutPlanScreen.routeName: (ctx) => const WorkoutPlanScreen()}, ), ); } diff --git a/test/workout/workout_plans_screen_test.dart b/test/workout/workout_plans_screen_test.dart index ebc8095bf..315962924 100644 --- a/test/workout/workout_plans_screen_test.dart +++ b/test/workout/workout_plans_screen_test.dart @@ -18,13 +18,13 @@ import 'package:drift/native.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/database/exercises/exercise_database.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/workout_plan.dart'; import 'package:wger/providers/base_provider.dart'; import 'package:wger/providers/exercises.dart'; @@ -49,23 +49,15 @@ void main() { }); Widget createHomeScreen({locale = 'en'}) { - final uri = Uri( - scheme: 'https', - host: 'localhost', - path: 'api/v2/workout/', - ); + final uri = Uri(scheme: 'https', host: 'localhost', path: 'api/v2/workout/'); when(mockBaseProvider.makeUrl('workout', query: anyNamed('query'))).thenReturn(uri); when(mockBaseProvider.deleteRequest(any, any)).thenAnswer((_) async => http.Response('', 204)); return ChangeNotifierProvider( - create: (context) => WorkoutPlansProvider( - mockBaseProvider, - testExercisesProvider, - [ - WorkoutPlan(id: 1, creationDate: DateTime(2021, 01, 01), name: 'test 1'), - WorkoutPlan(id: 2, creationDate: DateTime(2021, 02, 12), name: 'test 2'), - ], - ), + create: (context) => WorkoutPlansProvider(mockBaseProvider, testExercisesProvider, [ + WorkoutPlan(id: 1, creationDate: DateTime(2021, 01, 01), name: 'test 1'), + WorkoutPlan(id: 2, creationDate: DateTime(2021, 02, 12), name: 'test 2'), + ]), child: MaterialApp( locale: Locale(locale), localizationsDelegates: AppLocalizations.localizationsDelegates, diff --git a/test/workout/workout_set_form_test.dart b/test/workout/workout_set_form_test.dart index 089618543..b808bc416 100644 --- a/test/workout/workout_set_form_test.dart +++ b/test/workout/workout_set_form_test.dart @@ -17,12 +17,12 @@ */ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/day.dart'; import 'package:wger/models/workouts/set.dart'; import 'package:wger/models/workouts/setting.dart'; @@ -51,11 +51,8 @@ void main() { Widget createHomeScreen({locale = 'en'}) { return ChangeNotifierProvider( - create: (context) => WorkoutPlansProvider( - mockBaseProvider, - mockExerciseProvider, - [workoutPlan], - ), + create: (context) => + WorkoutPlansProvider(mockBaseProvider, mockExerciseProvider, [workoutPlan]), child: ChangeNotifierProvider( create: (context) => mockExerciseProvider, child: MaterialApp( @@ -84,11 +81,13 @@ void main() { when(mockWorkoutPlans.addSet(any)).thenAnswer((_) => Future.value(Set.empty())); when(mockWorkoutPlans.addSetting(any)).thenAnswer((_) => Future.value(Setting.empty())); when(mockWorkoutPlans.fetchSmartText(any, any)).thenAnswer((_) => Future.value('2 x 10')); - when(mockExerciseProvider.searchExercise( - any, - languageCode: anyNamed('languageCode'), - searchEnglish: anyNamed('searchEnglish'), - )).thenAnswer((_) => Future.value([getTestExercises().first])); + when( + mockExerciseProvider.searchExercise( + any, + languageCode: anyNamed('languageCode'), + searchEnglish: anyNamed('searchEnglish'), + ), + ).thenAnswer((_) => Future.value([getTestExercises().first])); await tester.pumpWidget(createHomeScreen()); await tester.pumpAndSettle(); From 54b40696daf07780c30bb807642b19b8a5cdc185 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sun, 19 Oct 2025 14:50:19 +0200 Subject: [PATCH 19/19] Postmerge fixes --- .../exercises/exercise_database.g.dart | 449 ++-- .../ingredients/ingredients_database.g.dart | 120 +- lib/main.dart | 1 - lib/models/exercises/category.g.dart | 5 +- lib/models/exercises/exercise.g.dart | 4 +- .../exercises/exercise_api.freezed.dart | 2158 +++++++---------- lib/models/exercises/exercise_api.g.dart | 51 +- .../exercise_submission.freezed.dart | 1938 ++++++--------- .../exercises/exercise_submission.g.dart | 88 +- lib/models/exercises/translation.g.dart | 10 +- .../measurements/measurement_category.g.dart | 4 +- lib/models/nutrition/ingredient.g.dart | 4 +- .../ingredient_image_thumbnails.g.dart | 23 +- .../nutrition/ingredient_weight_unit.g.dart | 26 +- lib/models/nutrition/log.dart | 2 +- lib/models/nutrition/meal.g.dart | 4 +- lib/models/nutrition/nutritional_plan.dart | 6 +- lib/models/nutrition/nutritional_plan.g.dart | 1 + lib/models/workouts/routine.g.dart | 10 +- lib/models/workouts/session.g.dart | 10 +- lib/models/workouts/weight_unit.g.dart | 5 +- lib/powersync.dart | 2 +- lib/providers/nutrition.dart | 18 +- lib/screens/dashboard.dart | 2 +- lib/screens/home_tabs_screen.dart | 12 +- lib/screens/nutritional_diary_screen.dart | 10 +- lib/screens/nutritional_plan_screen.dart | 253 +- lib/widgets/nutrition/forms.dart | 2 +- .../nutrition/nutritional_diary_table.dart | 2 +- .../nutrition/nutritional_plan_detail.dart | 4 +- .../nutrition/nutritional_plans_list.dart | 178 +- lib/widgets/routines/gym_mode/navigation.dart | 8 +- linux/flutter/generated_plugin_registrant.cc | 6 +- pubspec.lock | 84 +- pubspec.yaml | 5 - test/auth/auth_screen_test.mocks.dart | 49 +- test/core/settings_test.mocks.dart | 387 ++- test/core/validators_test.mocks.dart | 1607 +++++++++--- .../contribute_exercise_image_test.mocks.dart | 135 +- .../exercises_detail_widget_test.mocks.dart | 130 +- test/gallery/gallery_form_test.mocks.dart | 70 +- test/gallery/gallery_screen_test.mocks.dart | 70 +- ...surement_categories_screen_test.mocks.dart | 36 +- .../measurement_provider_test.mocks.dart | 41 +- .../nutritional_meal_form_test.mocks.dart | 145 +- .../nutritional_meal_item_form_test.dart | 14 +- .../nutritional_plan_form_test.mocks.dart | 145 +- .../nutritional_plan_screen_test.mocks.dart | 179 +- .../nutritional_plans_screen_test.mocks.dart | 179 +- test/other/base_provider_test.mocks.dart | 49 +- .../plate_calculator_test.mocks.dart | 9 +- .../forms/session_form_test.mocks.dart | 151 +- test/user/provider_test.mocks.dart | 41 +- test/weight/weight_provider_test.mocks.dart | 41 +- test/weight/weight_screen_test.mocks.dart | 245 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 57 files changed, 5308 insertions(+), 3924 deletions(-) diff --git a/lib/database/exercises/exercise_database.g.dart b/lib/database/exercises/exercise_database.g.dart index 6da378e2d..a07b1092e 100644 --- a/lib/database/exercises/exercise_database.g.dart +++ b/lib/database/exercises/exercise_database.g.dart @@ -7,9 +7,7 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise @override final GeneratedDatabase attachedDatabase; final String? _alias; - $ExercisesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -28,7 +26,9 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _lastUpdateMeta = const VerificationMeta('lastUpdate'); + static const VerificationMeta _lastUpdateMeta = const VerificationMeta( + 'lastUpdate', + ); @override late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', @@ -37,7 +37,9 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise type: DriftSqlType.dateTime, requiredDuringInsert: true, ); - static const VerificationMeta _lastFetchedMeta = const VerificationMeta('lastFetched'); + static const VerificationMeta _lastFetchedMeta = const VerificationMeta( + 'lastFetched', + ); @override late final GeneratedColumn lastFetched = GeneratedColumn( 'last_fetched', @@ -46,17 +48,13 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise type: DriftSqlType.dateTime, requiredDuringInsert: true, ); - @override List get $columns => [id, data, lastUpdate, lastFetched]; - @override String get aliasedName => _alias ?? actualTableName; - @override String get actualTableName => $name; static const String $name = 'exercises'; - @override VerificationContext validateIntegrity( Insertable instance, { @@ -70,7 +68,10 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise context.missing(_idMeta); } if (data.containsKey('data')) { - context.handle(_dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + context.handle( + _dataMeta, + this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), + ); } else if (isInserting) { context.missing(_dataMeta); } @@ -85,7 +86,10 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise if (data.containsKey('last_fetched')) { context.handle( _lastFetchedMeta, - lastFetched.isAcceptableOrUnknown(data['last_fetched']!, _lastFetchedMeta), + lastFetched.isAcceptableOrUnknown( + data['last_fetched']!, + _lastFetchedMeta, + ), ); } else if (isInserting) { context.missing(_lastFetchedMeta); @@ -95,13 +99,18 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise @override Set get $primaryKey => const {}; - @override ExerciseTable map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return ExerciseTable( - id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, lastUpdate: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_update'], @@ -128,14 +137,12 @@ class ExerciseTable extends DataClass implements Insertable { /// when the exercise itself was last updated in `lastUpdate`, we can save /// ourselves a lot of requests if we don't check too often final DateTime lastFetched; - const ExerciseTable({ required this.id, required this.data, required this.lastUpdate, required this.lastFetched, }); - @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -155,7 +162,10 @@ class ExerciseTable extends DataClass implements Insertable { ); } - factory ExerciseTable.fromJson(Map json, {ValueSerializer? serializer}) { + factory ExerciseTable.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return ExerciseTable( id: serializer.fromJson(json['id']), @@ -164,7 +174,6 @@ class ExerciseTable extends DataClass implements Insertable { lastFetched: serializer.fromJson(json['lastFetched']), ); } - @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; @@ -176,14 +185,17 @@ class ExerciseTable extends DataClass implements Insertable { }; } - ExerciseTable copyWith({int? id, String? data, DateTime? lastUpdate, DateTime? lastFetched}) => - ExerciseTable( - id: id ?? this.id, - data: data ?? this.data, - lastUpdate: lastUpdate ?? this.lastUpdate, - lastFetched: lastFetched ?? this.lastFetched, - ); - + ExerciseTable copyWith({ + int? id, + String? data, + DateTime? lastUpdate, + DateTime? lastFetched, + }) => ExerciseTable( + id: id ?? this.id, + data: data ?? this.data, + lastUpdate: lastUpdate ?? this.lastUpdate, + lastFetched: lastFetched ?? this.lastFetched, + ); ExerciseTable copyWithCompanion(ExercisesCompanion data) { return ExerciseTable( id: data.id.present ? data.id.value : this.id, @@ -206,7 +218,6 @@ class ExerciseTable extends DataClass implements Insertable { @override int get hashCode => Object.hash(id, data, lastUpdate, lastFetched); - @override bool operator ==(Object other) => identical(this, other) || @@ -223,7 +234,6 @@ class ExercisesCompanion extends UpdateCompanion { final Value lastUpdate; final Value lastFetched; final Value rowid; - const ExercisesCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), @@ -231,7 +241,6 @@ class ExercisesCompanion extends UpdateCompanion { this.lastFetched = const Value.absent(), this.rowid = const Value.absent(), }); - ExercisesCompanion.insert({ required int id, required String data, @@ -242,7 +251,6 @@ class ExercisesCompanion extends UpdateCompanion { data = Value(data), lastUpdate = Value(lastUpdate), lastFetched = Value(lastFetched); - static Insertable custom({ Expression? id, Expression? data, @@ -313,9 +321,7 @@ class $MusclesTable extends Muscles with TableInfo<$MusclesTable, MuscleTable> { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $MusclesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -333,17 +339,13 @@ class $MusclesTable extends Muscles with TableInfo<$MusclesTable, MuscleTable> { type: DriftSqlType.string, requiredDuringInsert: true, ).withConverter($MusclesTable.$converterdata); - @override List get $columns => [id, data]; - @override String get aliasedName => _alias ?? actualTableName; - @override String get actualTableName => $name; static const String $name = 'muscles'; - @override VerificationContext validateIntegrity( Insertable instance, { @@ -361,14 +363,19 @@ class $MusclesTable extends Muscles with TableInfo<$MusclesTable, MuscleTable> { @override Set get $primaryKey => const {}; - @override MuscleTable map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MuscleTable( - id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, data: $MusclesTable.$converterdata.fromSql( - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, ), ); } @@ -384,9 +391,7 @@ class $MusclesTable extends Muscles with TableInfo<$MusclesTable, MuscleTable> { class MuscleTable extends DataClass implements Insertable { final int id; final Muscle data; - const MuscleTable({required this.id, required this.data}); - @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -401,14 +406,16 @@ class MuscleTable extends DataClass implements Insertable { return MusclesCompanion(id: Value(id), data: Value(data)); } - factory MuscleTable.fromJson(Map json, {ValueSerializer? serializer}) { + factory MuscleTable.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MuscleTable( id: serializer.fromJson(json['id']), data: serializer.fromJson(json['data']), ); } - @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; @@ -420,7 +427,6 @@ class MuscleTable extends DataClass implements Insertable { MuscleTable copyWith({int? id, Muscle? data}) => MuscleTable(id: id ?? this.id, data: data ?? this.data); - MuscleTable copyWithCompanion(MusclesCompanion data) { return MuscleTable( id: data.id.present ? data.id.value : this.id, @@ -439,7 +445,6 @@ class MuscleTable extends DataClass implements Insertable { @override int get hashCode => Object.hash(id, data); - @override bool operator ==(Object other) => identical(this, other) || @@ -450,20 +455,17 @@ class MusclesCompanion extends UpdateCompanion { final Value id; final Value data; final Value rowid; - const MusclesCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), this.rowid = const Value.absent(), }); - MusclesCompanion.insert({ required int id, required Muscle data, this.rowid = const Value.absent(), }) : id = Value(id), data = Value(data); - static Insertable custom({ Expression? id, Expression? data, @@ -476,8 +478,16 @@ class MusclesCompanion extends UpdateCompanion { }); } - MusclesCompanion copyWith({Value? id, Value? data, Value? rowid}) { - return MusclesCompanion(id: id ?? this.id, data: data ?? this.data, rowid: rowid ?? this.rowid); + MusclesCompanion copyWith({ + Value? id, + Value? data, + Value? rowid, + }) { + return MusclesCompanion( + id: id ?? this.id, + data: data ?? this.data, + rowid: rowid ?? this.rowid, + ); } @override @@ -487,7 +497,9 @@ class MusclesCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (data.present) { - map['data'] = Variable($MusclesTable.$converterdata.toSql(data.value)); + map['data'] = Variable( + $MusclesTable.$converterdata.toSql(data.value), + ); } if (rowid.present) { map['rowid'] = Variable(rowid.value); @@ -510,9 +522,7 @@ class $EquipmentsTable extends Equipments with TableInfo<$EquipmentsTable, Equip @override final GeneratedDatabase attachedDatabase; final String? _alias; - $EquipmentsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -530,17 +540,13 @@ class $EquipmentsTable extends Equipments with TableInfo<$EquipmentsTable, Equip type: DriftSqlType.string, requiredDuringInsert: true, ).withConverter($EquipmentsTable.$converterdata); - @override List get $columns => [id, data]; - @override String get aliasedName => _alias ?? actualTableName; - @override String get actualTableName => $name; static const String $name = 'equipments'; - @override VerificationContext validateIntegrity( Insertable instance, { @@ -558,14 +564,19 @@ class $EquipmentsTable extends Equipments with TableInfo<$EquipmentsTable, Equip @override Set get $primaryKey => const {}; - @override EquipmentTable map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return EquipmentTable( - id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, data: $EquipmentsTable.$converterdata.fromSql( - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, ), ); } @@ -581,15 +592,15 @@ class $EquipmentsTable extends Equipments with TableInfo<$EquipmentsTable, Equip class EquipmentTable extends DataClass implements Insertable { final int id; final Equipment data; - const EquipmentTable({required this.id, required this.data}); - @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); { - map['data'] = Variable($EquipmentsTable.$converterdata.toSql(data)); + map['data'] = Variable( + $EquipmentsTable.$converterdata.toSql(data), + ); } return map; } @@ -598,14 +609,16 @@ class EquipmentTable extends DataClass implements Insertable { return EquipmentsCompanion(id: Value(id), data: Value(data)); } - factory EquipmentTable.fromJson(Map json, {ValueSerializer? serializer}) { + factory EquipmentTable.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return EquipmentTable( id: serializer.fromJson(json['id']), data: serializer.fromJson(json['data']), ); } - @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; @@ -617,7 +630,6 @@ class EquipmentTable extends DataClass implements Insertable { EquipmentTable copyWith({int? id, Equipment? data}) => EquipmentTable(id: id ?? this.id, data: data ?? this.data); - EquipmentTable copyWithCompanion(EquipmentsCompanion data) { return EquipmentTable( id: data.id.present ? data.id.value : this.id, @@ -636,7 +648,6 @@ class EquipmentTable extends DataClass implements Insertable { @override int get hashCode => Object.hash(id, data); - @override bool operator ==(Object other) => identical(this, other) || @@ -647,20 +658,17 @@ class EquipmentsCompanion extends UpdateCompanion { final Value id; final Value data; final Value rowid; - const EquipmentsCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), this.rowid = const Value.absent(), }); - EquipmentsCompanion.insert({ required int id, required Equipment data, this.rowid = const Value.absent(), }) : id = Value(id), data = Value(data); - static Insertable custom({ Expression? id, Expression? data, @@ -673,7 +681,11 @@ class EquipmentsCompanion extends UpdateCompanion { }); } - EquipmentsCompanion copyWith({Value? id, Value? data, Value? rowid}) { + EquipmentsCompanion copyWith({ + Value? id, + Value? data, + Value? rowid, + }) { return EquipmentsCompanion( id: id ?? this.id, data: data ?? this.data, @@ -688,7 +700,9 @@ class EquipmentsCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (data.present) { - map['data'] = Variable($EquipmentsTable.$converterdata.toSql(data.value)); + map['data'] = Variable( + $EquipmentsTable.$converterdata.toSql(data.value), + ); } if (rowid.present) { map['rowid'] = Variable(rowid.value); @@ -711,9 +725,7 @@ class $CategoriesTable extends Categories with TableInfo<$CategoriesTable, Categ @override final GeneratedDatabase attachedDatabase; final String? _alias; - $CategoriesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -732,17 +744,13 @@ class $CategoriesTable extends Categories with TableInfo<$CategoriesTable, Categ type: DriftSqlType.string, requiredDuringInsert: true, ).withConverter($CategoriesTable.$converterdata); - @override List get $columns => [id, data]; - @override String get aliasedName => _alias ?? actualTableName; - @override String get actualTableName => $name; static const String $name = 'categories'; - @override VerificationContext validateIntegrity( Insertable instance, { @@ -760,14 +768,19 @@ class $CategoriesTable extends Categories with TableInfo<$CategoriesTable, Categ @override Set get $primaryKey => const {}; - @override CategoryTable map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return CategoryTable( - id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, data: $CategoriesTable.$converterdata.fromSql( - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, ), ); } @@ -783,15 +796,15 @@ class $CategoriesTable extends Categories with TableInfo<$CategoriesTable, Categ class CategoryTable extends DataClass implements Insertable { final int id; final ExerciseCategory data; - const CategoryTable({required this.id, required this.data}); - @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); { - map['data'] = Variable($CategoriesTable.$converterdata.toSql(data)); + map['data'] = Variable( + $CategoriesTable.$converterdata.toSql(data), + ); } return map; } @@ -800,14 +813,16 @@ class CategoryTable extends DataClass implements Insertable { return CategoriesCompanion(id: Value(id), data: Value(data)); } - factory CategoryTable.fromJson(Map json, {ValueSerializer? serializer}) { + factory CategoryTable.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return CategoryTable( id: serializer.fromJson(json['id']), data: serializer.fromJson(json['data']), ); } - @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; @@ -819,7 +834,6 @@ class CategoryTable extends DataClass implements Insertable { CategoryTable copyWith({int? id, ExerciseCategory? data}) => CategoryTable(id: id ?? this.id, data: data ?? this.data); - CategoryTable copyWithCompanion(CategoriesCompanion data) { return CategoryTable( id: data.id.present ? data.id.value : this.id, @@ -838,7 +852,6 @@ class CategoryTable extends DataClass implements Insertable { @override int get hashCode => Object.hash(id, data); - @override bool operator ==(Object other) => identical(this, other) || @@ -849,20 +862,17 @@ class CategoriesCompanion extends UpdateCompanion { final Value id; final Value data; final Value rowid; - const CategoriesCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), this.rowid = const Value.absent(), }); - CategoriesCompanion.insert({ required int id, required ExerciseCategory data, this.rowid = const Value.absent(), }) : id = Value(id), data = Value(data); - static Insertable custom({ Expression? id, Expression? data, @@ -875,7 +885,11 @@ class CategoriesCompanion extends UpdateCompanion { }); } - CategoriesCompanion copyWith({Value? id, Value? data, Value? rowid}) { + CategoriesCompanion copyWith({ + Value? id, + Value? data, + Value? rowid, + }) { return CategoriesCompanion( id: id ?? this.id, data: data ?? this.data, @@ -890,7 +904,9 @@ class CategoriesCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (data.present) { - map['data'] = Variable($CategoriesTable.$converterdata.toSql(data.value)); + map['data'] = Variable( + $CategoriesTable.$converterdata.toSql(data.value), + ); } if (rowid.present) { map['rowid'] = Variable(rowid.value); @@ -913,9 +929,7 @@ class $LanguagesTable extends Languages with TableInfo<$LanguagesTable, Language @override final GeneratedDatabase attachedDatabase; final String? _alias; - $LanguagesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -933,17 +947,13 @@ class $LanguagesTable extends Languages with TableInfo<$LanguagesTable, Language type: DriftSqlType.string, requiredDuringInsert: true, ).withConverter($LanguagesTable.$converterdata); - @override List get $columns => [id, data]; - @override String get aliasedName => _alias ?? actualTableName; - @override String get actualTableName => $name; static const String $name = 'languages'; - @override VerificationContext validateIntegrity( Insertable instance, { @@ -961,14 +971,19 @@ class $LanguagesTable extends Languages with TableInfo<$LanguagesTable, Language @override Set get $primaryKey => const {}; - @override LanguagesTable map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LanguagesTable( - id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, data: $LanguagesTable.$converterdata.fromSql( - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, ), ); } @@ -984,15 +999,15 @@ class $LanguagesTable extends Languages with TableInfo<$LanguagesTable, Language class LanguagesTable extends DataClass implements Insertable { final int id; final Language data; - const LanguagesTable({required this.id, required this.data}); - @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); { - map['data'] = Variable($LanguagesTable.$converterdata.toSql(data)); + map['data'] = Variable( + $LanguagesTable.$converterdata.toSql(data), + ); } return map; } @@ -1001,14 +1016,16 @@ class LanguagesTable extends DataClass implements Insertable { return LanguagesCompanion(id: Value(id), data: Value(data)); } - factory LanguagesTable.fromJson(Map json, {ValueSerializer? serializer}) { + factory LanguagesTable.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LanguagesTable( id: serializer.fromJson(json['id']), data: serializer.fromJson(json['data']), ); } - @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; @@ -1020,7 +1037,6 @@ class LanguagesTable extends DataClass implements Insertable { LanguagesTable copyWith({int? id, Language? data}) => LanguagesTable(id: id ?? this.id, data: data ?? this.data); - LanguagesTable copyWithCompanion(LanguagesCompanion data) { return LanguagesTable( id: data.id.present ? data.id.value : this.id, @@ -1039,7 +1055,6 @@ class LanguagesTable extends DataClass implements Insertable { @override int get hashCode => Object.hash(id, data); - @override bool operator ==(Object other) => identical(this, other) || @@ -1050,20 +1065,17 @@ class LanguagesCompanion extends UpdateCompanion { final Value id; final Value data; final Value rowid; - const LanguagesCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), this.rowid = const Value.absent(), }); - LanguagesCompanion.insert({ required int id, required Language data, this.rowid = const Value.absent(), }) : id = Value(id), data = Value(data); - static Insertable custom({ Expression? id, Expression? data, @@ -1076,7 +1088,11 @@ class LanguagesCompanion extends UpdateCompanion { }); } - LanguagesCompanion copyWith({Value? id, Value? data, Value? rowid}) { + LanguagesCompanion copyWith({ + Value? id, + Value? data, + Value? rowid, + }) { return LanguagesCompanion( id: id ?? this.id, data: data ?? this.data, @@ -1091,7 +1107,9 @@ class LanguagesCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (data.present) { - map['data'] = Variable($LanguagesTable.$converterdata.toSql(data.value)); + map['data'] = Variable( + $LanguagesTable.$converterdata.toSql(data.value), + ); } if (rowid.present) { map['rowid'] = Variable(rowid.value); @@ -1112,18 +1130,15 @@ class LanguagesCompanion extends UpdateCompanion { abstract class _$ExerciseDatabase extends GeneratedDatabase { _$ExerciseDatabase(QueryExecutor e) : super(e); - $ExerciseDatabaseManager get managers => $ExerciseDatabaseManager(this); late final $ExercisesTable exercises = $ExercisesTable(this); late final $MusclesTable muscles = $MusclesTable(this); late final $EquipmentsTable equipments = $EquipmentsTable(this); late final $CategoriesTable categories = $CategoriesTable(this); late final $LanguagesTable languages = $LanguagesTable(this); - @override Iterable> get allTables => allSchemaEntities.whereType>(); - @override List get allSchemaEntities => [ exercises, @@ -1159,18 +1174,25 @@ class $$ExercisesTableFilterComposer extends Composer<_$ExerciseDatabase, $Exerc super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); - ColumnFilters get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnFilters(column)); + ColumnFilters get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnFilters(column), + ); - ColumnFilters get lastUpdate => - $composableBuilder(column: $table.lastUpdate, builder: (column) => ColumnFilters(column)); + ColumnFilters get lastUpdate => $composableBuilder( + column: $table.lastUpdate, + builder: (column) => ColumnFilters(column), + ); - ColumnFilters get lastFetched => - $composableBuilder(column: $table.lastFetched, builder: (column) => ColumnFilters(column)); + ColumnFilters get lastFetched => $composableBuilder( + column: $table.lastFetched, + builder: (column) => ColumnFilters(column), + ); } class $$ExercisesTableOrderingComposer extends Composer<_$ExerciseDatabase, $ExercisesTable> { @@ -1181,18 +1203,25 @@ class $$ExercisesTableOrderingComposer extends Composer<_$ExerciseDatabase, $Exe super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get lastUpdate => - $composableBuilder(column: $table.lastUpdate, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get lastUpdate => $composableBuilder( + column: $table.lastUpdate, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get lastFetched => - $composableBuilder(column: $table.lastFetched, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get lastFetched => $composableBuilder( + column: $table.lastFetched, + builder: (column) => ColumnOrderings(column), + ); } class $$ExercisesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $ExercisesTable> { @@ -1203,17 +1232,20 @@ class $$ExercisesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $E super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumn get data => $composableBuilder(column: $table.data, builder: (column) => column); - GeneratedColumn get lastUpdate => - $composableBuilder(column: $table.lastUpdate, builder: (column) => column); + GeneratedColumn get lastUpdate => $composableBuilder( + column: $table.lastUpdate, + builder: (column) => column, + ); - GeneratedColumn get lastFetched => - $composableBuilder(column: $table.lastFetched, builder: (column) => column); + GeneratedColumn get lastFetched => $composableBuilder( + column: $table.lastFetched, + builder: (column) => column, + ); } class $$ExercisesTableTableManager @@ -1290,9 +1322,17 @@ typedef $$ExercisesTableProcessedTableManager = PrefetchHooks Function() >; typedef $$MusclesTableCreateCompanionBuilder = - MusclesCompanion Function({required int id, required Muscle data, Value rowid}); + MusclesCompanion Function({ + required int id, + required Muscle data, + Value rowid, + }); typedef $$MusclesTableUpdateCompanionBuilder = - MusclesCompanion Function({Value id, Value data, Value rowid}); + MusclesCompanion Function({ + Value id, + Value data, + Value rowid, + }); class $$MusclesTableFilterComposer extends Composer<_$ExerciseDatabase, $MusclesTable> { $$MusclesTableFilterComposer({ @@ -1302,9 +1342,10 @@ class $$MusclesTableFilterComposer extends Composer<_$ExerciseDatabase, $Muscles super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - - ColumnFilters get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); ColumnWithTypeConverterFilters get data => $composableBuilder( column: $table.data, @@ -1320,12 +1361,15 @@ class $$MusclesTableOrderingComposer extends Composer<_$ExerciseDatabase, $Muscl super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnOrderings(column), + ); } class $$MusclesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $MusclesTable> { @@ -1336,7 +1380,6 @@ class $$MusclesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $Mus super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumnWithTypeConverter get data => @@ -1374,8 +1417,11 @@ class $$MusclesTableTableManager Value rowid = const Value.absent(), }) => MusclesCompanion(id: id, data: data, rowid: rowid), createCompanionCallback: - ({required int id, required Muscle data, Value rowid = const Value.absent()}) => - MusclesCompanion.insert(id: id, data: data, rowid: rowid), + ({ + required int id, + required Muscle data, + Value rowid = const Value.absent(), + }) => MusclesCompanion.insert(id: id, data: data, rowid: rowid), withReferenceMapper: (p0) => p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(), prefetchHooksCallback: null, @@ -1398,9 +1444,17 @@ typedef $$MusclesTableProcessedTableManager = PrefetchHooks Function() >; typedef $$EquipmentsTableCreateCompanionBuilder = - EquipmentsCompanion Function({required int id, required Equipment data, Value rowid}); + EquipmentsCompanion Function({ + required int id, + required Equipment data, + Value rowid, + }); typedef $$EquipmentsTableUpdateCompanionBuilder = - EquipmentsCompanion Function({Value id, Value data, Value rowid}); + EquipmentsCompanion Function({ + Value id, + Value data, + Value rowid, + }); class $$EquipmentsTableFilterComposer extends Composer<_$ExerciseDatabase, $EquipmentsTable> { $$EquipmentsTableFilterComposer({ @@ -1410,9 +1464,10 @@ class $$EquipmentsTableFilterComposer extends Composer<_$ExerciseDatabase, $Equi super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - - ColumnFilters get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); ColumnWithTypeConverterFilters get data => $composableBuilder( column: $table.data, @@ -1428,12 +1483,15 @@ class $$EquipmentsTableOrderingComposer extends Composer<_$ExerciseDatabase, $Eq super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnOrderings(column), + ); } class $$EquipmentsTableAnnotationComposer extends Composer<_$ExerciseDatabase, $EquipmentsTable> { @@ -1444,7 +1502,6 @@ class $$EquipmentsTableAnnotationComposer extends Composer<_$ExerciseDatabase, $ super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumnWithTypeConverter get data => @@ -1515,7 +1572,11 @@ typedef $$CategoriesTableCreateCompanionBuilder = Value rowid, }); typedef $$CategoriesTableUpdateCompanionBuilder = - CategoriesCompanion Function({Value id, Value data, Value rowid}); + CategoriesCompanion Function({ + Value id, + Value data, + Value rowid, + }); class $$CategoriesTableFilterComposer extends Composer<_$ExerciseDatabase, $CategoriesTable> { $$CategoriesTableFilterComposer({ @@ -1525,9 +1586,10 @@ class $$CategoriesTableFilterComposer extends Composer<_$ExerciseDatabase, $Cate super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - - ColumnFilters get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); ColumnWithTypeConverterFilters get data => $composableBuilder( @@ -1544,12 +1606,15 @@ class $$CategoriesTableOrderingComposer extends Composer<_$ExerciseDatabase, $Ca super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnOrderings(column), + ); } class $$CategoriesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $CategoriesTable> { @@ -1560,7 +1625,6 @@ class $$CategoriesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $ super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumnWithTypeConverter get data => @@ -1625,9 +1689,17 @@ typedef $$CategoriesTableProcessedTableManager = PrefetchHooks Function() >; typedef $$LanguagesTableCreateCompanionBuilder = - LanguagesCompanion Function({required int id, required Language data, Value rowid}); + LanguagesCompanion Function({ + required int id, + required Language data, + Value rowid, + }); typedef $$LanguagesTableUpdateCompanionBuilder = - LanguagesCompanion Function({Value id, Value data, Value rowid}); + LanguagesCompanion Function({ + Value id, + Value data, + Value rowid, + }); class $$LanguagesTableFilterComposer extends Composer<_$ExerciseDatabase, $LanguagesTable> { $$LanguagesTableFilterComposer({ @@ -1637,9 +1709,10 @@ class $$LanguagesTableFilterComposer extends Composer<_$ExerciseDatabase, $Langu super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - - ColumnFilters get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); ColumnWithTypeConverterFilters get data => $composableBuilder( column: $table.data, @@ -1655,12 +1728,15 @@ class $$LanguagesTableOrderingComposer extends Composer<_$ExerciseDatabase, $Lan super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnOrderings(column), + ); } class $$LanguagesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $LanguagesTable> { @@ -1671,7 +1747,6 @@ class $$LanguagesTableAnnotationComposer extends Composer<_$ExerciseDatabase, $L super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumnWithTypeConverter get data => @@ -1738,18 +1813,12 @@ typedef $$LanguagesTableProcessedTableManager = class $ExerciseDatabaseManager { final _$ExerciseDatabase _db; - $ExerciseDatabaseManager(this._db); - $$ExercisesTableTableManager get exercises => $$ExercisesTableTableManager(_db, _db.exercises); - $$MusclesTableTableManager get muscles => $$MusclesTableTableManager(_db, _db.muscles); - $$EquipmentsTableTableManager get equipments => $$EquipmentsTableTableManager(_db, _db.equipments); - $$CategoriesTableTableManager get categories => $$CategoriesTableTableManager(_db, _db.categories); - $$LanguagesTableTableManager get languages => $$LanguagesTableTableManager(_db, _db.languages); } diff --git a/lib/database/ingredients/ingredients_database.g.dart b/lib/database/ingredients/ingredients_database.g.dart index a8bc80069..7dc02ac32 100644 --- a/lib/database/ingredients/ingredients_database.g.dart +++ b/lib/database/ingredients/ingredients_database.g.dart @@ -7,9 +7,7 @@ class $IngredientsTable extends Ingredients with TableInfo<$IngredientsTable, In @override final GeneratedDatabase attachedDatabase; final String? _alias; - $IngredientsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -28,7 +26,9 @@ class $IngredientsTable extends Ingredients with TableInfo<$IngredientsTable, In type: DriftSqlType.string, requiredDuringInsert: true, ); - static const VerificationMeta _lastFetchedMeta = const VerificationMeta('lastFetched'); + static const VerificationMeta _lastFetchedMeta = const VerificationMeta( + 'lastFetched', + ); @override late final GeneratedColumn lastFetched = GeneratedColumn( 'last_fetched', @@ -37,17 +37,13 @@ class $IngredientsTable extends Ingredients with TableInfo<$IngredientsTable, In type: DriftSqlType.dateTime, requiredDuringInsert: true, ); - @override List get $columns => [id, data, lastFetched]; - @override String get aliasedName => _alias ?? actualTableName; - @override String get actualTableName => $name; static const String $name = 'ingredients'; - @override VerificationContext validateIntegrity( Insertable instance, { @@ -61,14 +57,20 @@ class $IngredientsTable extends Ingredients with TableInfo<$IngredientsTable, In context.missing(_idMeta); } if (data.containsKey('data')) { - context.handle(_dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + context.handle( + _dataMeta, + this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), + ); } else if (isInserting) { context.missing(_dataMeta); } if (data.containsKey('last_fetched')) { context.handle( _lastFetchedMeta, - lastFetched.isAcceptableOrUnknown(data['last_fetched']!, _lastFetchedMeta), + lastFetched.isAcceptableOrUnknown( + data['last_fetched']!, + _lastFetchedMeta, + ), ); } else if (isInserting) { context.missing(_lastFetchedMeta); @@ -78,13 +80,18 @@ class $IngredientsTable extends Ingredients with TableInfo<$IngredientsTable, In @override Set get $primaryKey => const {}; - @override IngredientTable map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return IngredientTable( - id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, lastFetched: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}last_fetched'], @@ -104,9 +111,11 @@ class IngredientTable extends DataClass implements Insertable { /// The date when the ingredient was last fetched from the server final DateTime lastFetched; - - const IngredientTable({required this.id, required this.data, required this.lastFetched}); - + const IngredientTable({ + required this.id, + required this.data, + required this.lastFetched, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -117,10 +126,17 @@ class IngredientTable extends DataClass implements Insertable { } IngredientsCompanion toCompanion(bool nullToAbsent) { - return IngredientsCompanion(id: Value(id), data: Value(data), lastFetched: Value(lastFetched)); + return IngredientsCompanion( + id: Value(id), + data: Value(data), + lastFetched: Value(lastFetched), + ); } - factory IngredientTable.fromJson(Map json, {ValueSerializer? serializer}) { + factory IngredientTable.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return IngredientTable( id: serializer.fromJson(json['id']), @@ -128,7 +144,6 @@ class IngredientTable extends DataClass implements Insertable { lastFetched: serializer.fromJson(json['lastFetched']), ); } - @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; @@ -144,7 +159,6 @@ class IngredientTable extends DataClass implements Insertable { data: data ?? this.data, lastFetched: lastFetched ?? this.lastFetched, ); - IngredientTable copyWithCompanion(IngredientsCompanion data) { return IngredientTable( id: data.id.present ? data.id.value : this.id, @@ -165,7 +179,6 @@ class IngredientTable extends DataClass implements Insertable { @override int get hashCode => Object.hash(id, data, lastFetched); - @override bool operator ==(Object other) => identical(this, other) || @@ -180,14 +193,12 @@ class IngredientsCompanion extends UpdateCompanion { final Value data; final Value lastFetched; final Value rowid; - const IngredientsCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), this.lastFetched = const Value.absent(), this.rowid = const Value.absent(), }); - IngredientsCompanion.insert({ required int id, required String data, @@ -196,7 +207,6 @@ class IngredientsCompanion extends UpdateCompanion { }) : id = Value(id), data = Value(data), lastFetched = Value(lastFetched); - static Insertable custom({ Expression? id, Expression? data, @@ -257,14 +267,11 @@ class IngredientsCompanion extends UpdateCompanion { abstract class _$IngredientDatabase extends GeneratedDatabase { _$IngredientDatabase(QueryExecutor e) : super(e); - $IngredientDatabaseManager get managers => $IngredientDatabaseManager(this); late final $IngredientsTable ingredients = $IngredientsTable(this); - @override Iterable> get allTables => allSchemaEntities.whereType>(); - @override List get allSchemaEntities => [ingredients]; } @@ -292,15 +299,20 @@ class $$IngredientsTableFilterComposer extends Composer<_$IngredientDatabase, $I super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); - ColumnFilters get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnFilters(column)); + ColumnFilters get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnFilters(column), + ); - ColumnFilters get lastFetched => - $composableBuilder(column: $table.lastFetched, builder: (column) => ColumnFilters(column)); + ColumnFilters get lastFetched => $composableBuilder( + column: $table.lastFetched, + builder: (column) => ColumnFilters(column), + ); } class $$IngredientsTableOrderingComposer extends Composer<_$IngredientDatabase, $IngredientsTable> { @@ -311,15 +323,20 @@ class $$IngredientsTableOrderingComposer extends Composer<_$IngredientDatabase, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get id => - $composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get data => - $composableBuilder(column: $table.data, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get data => $composableBuilder( + column: $table.data, + builder: (column) => ColumnOrderings(column), + ); - ColumnOrderings get lastFetched => - $composableBuilder(column: $table.lastFetched, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get lastFetched => $composableBuilder( + column: $table.lastFetched, + builder: (column) => ColumnOrderings(column), + ); } class $$IngredientsTableAnnotationComposer @@ -331,14 +348,15 @@ class $$IngredientsTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumn get data => $composableBuilder(column: $table.data, builder: (column) => column); - GeneratedColumn get lastFetched => - $composableBuilder(column: $table.lastFetched, builder: (column) => column); + GeneratedColumn get lastFetched => $composableBuilder( + column: $table.lastFetched, + builder: (column) => column, + ); } class $$IngredientsTableTableManager @@ -359,8 +377,10 @@ class $$IngredientsTableTableManager IngredientTable, PrefetchHooks Function() > { - $$IngredientsTableTableManager(_$IngredientDatabase db, $IngredientsTable table) - : super( + $$IngredientsTableTableManager( + _$IngredientDatabase db, + $IngredientsTable table, + ) : super( TableManagerState( db: db, table: table, @@ -374,8 +394,12 @@ class $$IngredientsTableTableManager Value data = const Value.absent(), Value lastFetched = const Value.absent(), Value rowid = const Value.absent(), - }) => - IngredientsCompanion(id: id, data: data, lastFetched: lastFetched, rowid: rowid), + }) => IngredientsCompanion( + id: id, + data: data, + lastFetched: lastFetched, + rowid: rowid, + ), createCompanionCallback: ({ required int id, @@ -412,9 +436,7 @@ typedef $$IngredientsTableProcessedTableManager = class $IngredientDatabaseManager { final _$IngredientDatabase _db; - $IngredientDatabaseManager(this._db); - $$IngredientsTableTableManager get ingredients => $$IngredientsTableTableManager(_db, _db.ingredients); } diff --git a/lib/main.dart b/lib/main.dart index a6b385af6..967029334 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,7 +22,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart' as riverpod; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:wger/core/locator.dart'; -import 'package:wger/powersync.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/errors.dart'; import 'package:wger/helpers/shared_preferences.dart'; diff --git a/lib/models/exercises/category.g.dart b/lib/models/exercises/category.g.dart index c79663698..681e7dbea 100644 --- a/lib/models/exercises/category.g.dart +++ b/lib/models/exercises/category.g.dart @@ -8,7 +8,10 @@ part of 'category.dart'; ExerciseCategory _$ExerciseCategoryFromJson(Map json) { $checkKeys(json, requiredKeys: const ['id', 'name']); - return ExerciseCategory(id: (json['id'] as num).toInt(), name: json['name'] as String); + return ExerciseCategory( + id: (json['id'] as num).toInt(), + name: json['name'] as String, + ); } Map _$ExerciseCategoryToJson(ExerciseCategory instance) => { diff --git a/lib/models/exercises/exercise.g.dart b/lib/models/exercises/exercise.g.dart index 8d68fe3d7..83ae0b819 100644 --- a/lib/models/exercises/exercise.g.dart +++ b/lib/models/exercises/exercise.g.dart @@ -38,7 +38,9 @@ Exercise _$ExerciseFromJson(Map json) { .toList(), category: json['categories'] == null ? null - : ExerciseCategory.fromJson(json['categories'] as Map), + : ExerciseCategory.fromJson( + json['categories'] as Map, + ), ) ..categoryId = (json['category'] as num).toInt() ..musclesIds = (json['muscles'] as List).map((e) => (e as num).toInt()).toList() diff --git a/lib/models/exercises/exercise_api.freezed.dart b/lib/models/exercises/exercise_api.freezed.dart index e3959c069..cea2c699a 100644 --- a/lib/models/exercises/exercise_api.freezed.dart +++ b/lib/models/exercises/exercise_api.freezed.dart @@ -11,117 +11,61 @@ part of 'exercise_api.dart'; // dart format off T _$identity(T value) => value; - -ExerciseApiData _$ExerciseApiDataFromJson(Map json) { - return _ExerciseBaseData.fromJson( +ExerciseApiData _$ExerciseApiDataFromJson( + Map json +) { + return _ExerciseBaseData.fromJson( json - ); + ); } /// @nodoc mixin _$ExerciseApiData { - int get id; - - String get uuid; // ignore: invalid_annotation_target - @JsonKey(name: 'variations') int? get variationId; // ignore: invalid_annotation_target - @JsonKey(name: 'created') DateTime get created; // ignore: invalid_annotation_target - @JsonKey(name: 'last_update') DateTime get lastUpdate; // ignore: invalid_annotation_target - @JsonKey(name: 'last_update_global') DateTime get lastUpdateGlobal; - - ExerciseCategory get category; - - List get muscles; // ignore: invalid_annotation_target - @JsonKey(name: 'muscles_secondary') List< - Muscle> get musclesSecondary; // ignore: invalid_annotation_target - List get equipment; // ignore: invalid_annotation_target - @JsonKey(name: 'translations', defaultValue: []) List get translations; - - List get images; - - List