diff --git a/analysis_options.yaml b/analysis_options.yaml index a40338c..5dd5f4c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,44 +1,88 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. +# Static analysis configuration for the Flutter project. +# https://dart.dev/guides/language/analysis-options # -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. +# Base ruleset: flutter_lints (recommended Flutter defaults). +# Additional lints below catch real bugs and enforce consistent style. -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml analyzer: + language: + strict-casts: true + strict-raw-types: true errors: invalid_annotation_target: ignore + todo: ignore + exclude: + - "**/*.g.dart" + - "**/*.freezed.dart" + - "lib/firebase_options.dart" + - "build/**" linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - file_names: false + # === Project conventions === prefer_relative_imports: true - unnecessary_lambdas: true prefer_single_quotes: true - prefer_if_elements_to_conditional_expressions: true - prefer_expression_function_bodies: true - omit_local_variable_types: true eol_at_end_of_file: true - cast_nullable_to_non_nullable: true - avoid_void_async: true + omit_local_variable_types: true avoid_multiple_declarations_per_line: true -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + # === Bug catchers === + always_declare_return_types: true + avoid_empty_else: true + avoid_slow_async_io: true + avoid_type_to_string: true + avoid_void_async: true + await_only_futures: true + cancel_subscriptions: true + cast_nullable_to_non_nullable: true + close_sinks: true + empty_catches: true + hash_and_equals: true + no_adjacent_strings_in_list: true + no_duplicate_case_values: true + test_types_in_equals: true + throw_in_finally: true + unawaited_futures: true + unnecessary_statements: true + unrelated_type_equality_checks: true + use_build_context_synchronously: true + valid_regexps: true + + # === Flutter widget hygiene === + avoid_unnecessary_containers: true + sized_box_for_whitespace: true + sort_child_properties_last: true + use_colored_box: true + use_decorated_box: true + use_full_hex_values_for_flutter_colors: true + use_key_in_widget_constructors: true + + # === Code clarity === + directives_ordering: true + library_prefixes: true + no_leading_underscores_for_local_identifiers: true + prefer_conditional_assignment: true + prefer_if_elements_to_conditional_expressions: true + prefer_if_null_operators: true + prefer_initializing_formals: true + prefer_interpolation_to_compose_strings: true + prefer_is_empty: true + prefer_is_not_empty: true + prefer_is_not_operator: true + prefer_iterable_whereType: true + prefer_null_aware_operators: true + prefer_spread_collections: true + prefer_void_to_null: true + unnecessary_await_in_return: true + unnecessary_brace_in_string_interps: true + unnecessary_lambdas: true + unnecessary_null_aware_assignments: true + unnecessary_null_checks: true + unnecessary_parenthesis: true + unnecessary_string_interpolations: true + use_super_parameters: true + + # === File naming === + file_names: true diff --git a/android/app/build.gradle b/android/app/build.gradle index b62e5c5..befe6bb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -25,7 +25,7 @@ if (flutterVersionName == null) { android { namespace "eu.mhsl.marianum.mobile.client" compileSdk flutter.compileSdkVersion - ndkVersion "27.0.12077973" + ndkVersion "28.2.13676358" compileOptions { sourceCompatibility JavaVersion.VERSION_17 diff --git a/lib/api/apiError.dart b/lib/api/api_error.dart similarity index 100% rename from lib/api/apiError.dart rename to lib/api/api_error.dart diff --git a/lib/api/apiParams.dart b/lib/api/api_params.dart similarity index 100% rename from lib/api/apiParams.dart rename to lib/api/api_params.dart diff --git a/lib/api/apiRequest.dart b/lib/api/api_request.dart similarity index 100% rename from lib/api/apiRequest.dart rename to lib/api/api_request.dart diff --git a/lib/api/apiResponse.dart b/lib/api/api_response.dart similarity index 100% rename from lib/api/apiResponse.dart rename to lib/api/api_response.dart diff --git a/lib/api/errors/error_mapper.dart b/lib/api/errors/error_mapper.dart index 2619643..313fd50 100644 --- a/lib/api/errors/error_mapper.dart +++ b/lib/api/errors/error_mapper.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'package:http/http.dart' as http; -import '../apiError.dart'; -import '../marianumcloud/talk/talkError.dart'; -import '../webuntis/webuntisError.dart'; +import '../api_error.dart'; +import '../marianumcloud/talk/talk_error.dart'; +import '../webuntis/webuntis_error.dart'; import 'app_exception.dart'; import 'network_exception.dart'; import 'parse_exception.dart'; diff --git a/lib/api/errors/talk_exception.dart b/lib/api/errors/talk_exception.dart index f46c0c7..534d1b2 100644 --- a/lib/api/errors/talk_exception.dart +++ b/lib/api/errors/talk_exception.dart @@ -1,4 +1,4 @@ -import '../marianumcloud/talk/talkError.dart'; +import '../marianumcloud/talk/talk_error.dart'; import 'app_exception.dart'; class TalkException extends AppException { diff --git a/lib/api/errors/webuntis_exception.dart b/lib/api/errors/webuntis_exception.dart index fd35d35..211f5ae 100644 --- a/lib/api/errors/webuntis_exception.dart +++ b/lib/api/errors/webuntis_exception.dart @@ -1,4 +1,4 @@ -import '../webuntis/webuntisError.dart'; +import '../webuntis/webuntis_error.dart'; import 'app_exception.dart'; class WebuntisException extends AppException { diff --git a/lib/api/holidays/getHolidays.dart b/lib/api/holidays/get_holidays.dart similarity index 93% rename from lib/api/holidays/getHolidays.dart rename to lib/api/holidays/get_holidays.dart index 8ce3325..5014c48 100644 --- a/lib/api/holidays/getHolidays.dart +++ b/lib/api/holidays/get_holidays.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'getHolidaysResponse.dart'; +import 'get_holidays_response.dart'; class GetHolidays { Future query() async { diff --git a/lib/api/holidays/getHolidaysCache.dart b/lib/api/holidays/get_holidays_cache.dart similarity index 83% rename from lib/api/holidays/getHolidaysCache.dart rename to lib/api/holidays/get_holidays_cache.dart index 2707916..5781b59 100644 --- a/lib/api/holidays/getHolidaysCache.dart +++ b/lib/api/holidays/get_holidays_cache.dart @@ -1,6 +1,6 @@ -import '../requestCache.dart'; -import 'getHolidays.dart'; -import 'getHolidaysResponse.dart'; +import '../request_cache.dart'; +import 'get_holidays.dart'; +import 'get_holidays_response.dart'; class GetHolidaysCache extends SimpleCache { GetHolidaysCache({super.onUpdate, super.renew}) diff --git a/lib/api/holidays/getHolidaysResponse.dart b/lib/api/holidays/get_holidays_response.dart similarity index 93% rename from lib/api/holidays/getHolidaysResponse.dart rename to lib/api/holidays/get_holidays_response.dart index 6ba00bb..7039417 100644 --- a/lib/api/holidays/getHolidaysResponse.dart +++ b/lib/api/holidays/get_holidays_response.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../apiResponse.dart'; +import '../api_response.dart'; -part 'getHolidaysResponse.g.dart'; +part 'get_holidays_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetHolidaysResponse extends ApiResponse { diff --git a/lib/api/holidays/getHolidaysResponse.g.dart b/lib/api/holidays/get_holidays_response.g.dart similarity index 97% rename from lib/api/holidays/getHolidaysResponse.g.dart rename to lib/api/holidays/get_holidays_response.g.dart index 4642931..593ad0b 100644 --- a/lib/api/holidays/getHolidaysResponse.g.dart +++ b/lib/api/holidays/get_holidays_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getHolidaysResponse.dart'; +part of 'get_holidays_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/autocomplete/autocompleteApi.dart b/lib/api/marianumcloud/autocomplete/autocomplete_api.dart similarity index 77% rename from lib/api/marianumcloud/autocomplete/autocompleteApi.dart rename to lib/api/marianumcloud/autocomplete/autocomplete_api.dart index 1539bd7..e1bb9e3 100644 --- a/lib/api/marianumcloud/autocomplete/autocompleteApi.dart +++ b/lib/api/marianumcloud/autocomplete/autocomplete_api.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:http/http.dart' as http; import '../nextcloud_ocs.dart'; -import 'autocompleteResponse.dart'; +import 'autocomplete_response.dart'; class AutocompleteApi { Future find(String query) async { @@ -22,6 +22,7 @@ class AutocompleteApi { if (response.statusCode != HttpStatus.ok) { throw Exception('Api call failed with ${response.statusCode}: ${response.body}'); } - return AutocompleteResponse.fromJson(jsonDecode(response.body)['ocs']); + final decoded = jsonDecode(response.body) as Map; + return AutocompleteResponse.fromJson(decoded['ocs'] as Map); } } diff --git a/lib/api/marianumcloud/autocomplete/autocompleteResponse.dart b/lib/api/marianumcloud/autocomplete/autocomplete_response.dart similarity index 96% rename from lib/api/marianumcloud/autocomplete/autocompleteResponse.dart rename to lib/api/marianumcloud/autocomplete/autocomplete_response.dart index 8e72772..60b4e7b 100644 --- a/lib/api/marianumcloud/autocomplete/autocompleteResponse.dart +++ b/lib/api/marianumcloud/autocomplete/autocomplete_response.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'autocompleteResponse.g.dart'; +part 'autocomplete_response.g.dart'; @JsonSerializable(explicitToJson: true) class AutocompleteResponse { diff --git a/lib/api/marianumcloud/autocomplete/autocompleteResponse.g.dart b/lib/api/marianumcloud/autocomplete/autocomplete_response.g.dart similarity index 97% rename from lib/api/marianumcloud/autocomplete/autocompleteResponse.g.dart rename to lib/api/marianumcloud/autocomplete/autocomplete_response.g.dart index 41ecf2b..65b9863 100644 --- a/lib/api/marianumcloud/autocomplete/autocompleteResponse.g.dart +++ b/lib/api/marianumcloud/autocomplete/autocomplete_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'autocompleteResponse.dart'; +part of 'autocomplete_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/files-sharing/fileSharingApi.dart b/lib/api/marianumcloud/files_sharing/file_sharing_api.dart similarity index 93% rename from lib/api/marianumcloud/files-sharing/fileSharingApi.dart rename to lib/api/marianumcloud/files_sharing/file_sharing_api.dart index 5914915..1551ada 100644 --- a/lib/api/marianumcloud/files-sharing/fileSharingApi.dart +++ b/lib/api/marianumcloud/files_sharing/file_sharing_api.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:http/http.dart' as http; import '../nextcloud_ocs.dart'; -import 'fileSharingApiParams.dart'; +import 'file_sharing_api_params.dart'; class FileSharingApi { Future share(FileSharingApiParams query) async { diff --git a/lib/api/marianumcloud/files-sharing/fileSharingApiParams.dart b/lib/api/marianumcloud/files_sharing/file_sharing_api_params.dart similarity index 93% rename from lib/api/marianumcloud/files-sharing/fileSharingApiParams.dart rename to lib/api/marianumcloud/files_sharing/file_sharing_api_params.dart index edcc6a5..4078d29 100644 --- a/lib/api/marianumcloud/files-sharing/fileSharingApiParams.dart +++ b/lib/api/marianumcloud/files_sharing/file_sharing_api_params.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'fileSharingApiParams.g.dart'; +part 'file_sharing_api_params.g.dart'; @JsonSerializable() class FileSharingApiParams { diff --git a/lib/api/marianumcloud/files-sharing/fileSharingApiParams.g.dart b/lib/api/marianumcloud/files_sharing/file_sharing_api_params.g.dart similarity index 95% rename from lib/api/marianumcloud/files-sharing/fileSharingApiParams.g.dart rename to lib/api/marianumcloud/files_sharing/file_sharing_api_params.g.dart index 9fc1e8b..472d1bc 100644 --- a/lib/api/marianumcloud/files-sharing/fileSharingApiParams.g.dart +++ b/lib/api/marianumcloud/files_sharing/file_sharing_api_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'fileSharingApiParams.dart'; +part of 'file_sharing_api_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/actions/talk_actions.dart b/lib/api/marianumcloud/talk/actions/talk_actions.dart index 88a83f3..59272cb 100644 --- a/lib/api/marianumcloud/talk/actions/talk_actions.dart +++ b/lib/api/marianumcloud/talk/actions/talk_actions.dart @@ -1,8 +1,8 @@ import 'package:http/http.dart' as http; -import '../../../apiParams.dart'; -import '../../../apiResponse.dart'; -import '../talkApi.dart'; +import '../../../api_params.dart'; +import '../../../api_response.dart'; +import '../talk_api.dart'; /// Small POST/DELETE-only Talk endpoints that have no response payload. /// Each class extends [TalkApi] with `assemble` returning `null`. They share diff --git a/lib/api/marianumcloud/talk/chat/getChat.dart b/lib/api/marianumcloud/talk/chat/get_chat.dart similarity index 62% rename from lib/api/marianumcloud/talk/chat/getChat.dart rename to lib/api/marianumcloud/talk/chat/get_chat.dart index fb64466..9009744 100644 --- a/lib/api/marianumcloud/talk/chat/getChat.dart +++ b/lib/api/marianumcloud/talk/chat/get_chat.dart @@ -3,9 +3,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../talkApi.dart'; -import 'getChatParams.dart'; -import 'getChatResponse.dart'; +import '../talk_api.dart'; +import 'get_chat_params.dart'; +import 'get_chat_response.dart'; class GetChat extends TalkApi { String chatToken; @@ -14,7 +14,10 @@ class GetChat extends TalkApi { GetChat(this.chatToken, this.params) : super('v1/chat/$chatToken', null, getParameters: params.toJson()); @override - assemble(String raw) => GetChatResponse.fromJson(jsonDecode(raw)['ocs']); + GetChatResponse assemble(String raw) { + final decoded = jsonDecode(raw) as Map; + return GetChatResponse.fromJson(decoded['ocs'] as Map); + } @override Future request(Uri uri, Object? body, Map? headers) => http.get(uri, headers: headers); diff --git a/lib/api/marianumcloud/talk/chat/getChatCache.dart b/lib/api/marianumcloud/talk/chat/get_chat_cache.dart similarity index 83% rename from lib/api/marianumcloud/talk/chat/getChatCache.dart rename to lib/api/marianumcloud/talk/chat/get_chat_cache.dart index 92efd3b..608da9a 100644 --- a/lib/api/marianumcloud/talk/chat/getChatCache.dart +++ b/lib/api/marianumcloud/talk/chat/get_chat_cache.dart @@ -1,7 +1,7 @@ -import '../../../requestCache.dart'; -import 'getChat.dart'; -import 'getChatParams.dart'; -import 'getChatResponse.dart'; +import '../../../request_cache.dart'; +import 'get_chat.dart'; +import 'get_chat_params.dart'; +import 'get_chat_response.dart'; class GetChatCache extends SimpleCache { GetChatCache({ diff --git a/lib/api/marianumcloud/talk/chat/getChatParams.dart b/lib/api/marianumcloud/talk/chat/get_chat_params.dart similarity index 92% rename from lib/api/marianumcloud/talk/chat/getChatParams.dart rename to lib/api/marianumcloud/talk/chat/get_chat_params.dart index 08197b2..5287a3b 100644 --- a/lib/api/marianumcloud/talk/chat/getChatParams.dart +++ b/lib/api/marianumcloud/talk/chat/get_chat_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'getChatParams.g.dart'; +part 'get_chat_params.g.dart'; @JsonSerializable(explicitToJson: true, includeIfNull: false) class GetChatParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/chat/getChatParams.g.dart b/lib/api/marianumcloud/talk/chat/get_chat_params.g.dart similarity index 97% rename from lib/api/marianumcloud/talk/chat/getChatParams.g.dart rename to lib/api/marianumcloud/talk/chat/get_chat_params.g.dart index a2c9707..b318eb6 100644 --- a/lib/api/marianumcloud/talk/chat/getChatParams.g.dart +++ b/lib/api/marianumcloud/talk/chat/get_chat_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getChatParams.dart'; +part of 'get_chat_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/chat/getChatResponse.dart b/lib/api/marianumcloud/talk/chat/get_chat_response.dart similarity index 91% rename from lib/api/marianumcloud/talk/chat/getChatResponse.dart rename to lib/api/marianumcloud/talk/chat/get_chat_response.dart index 2c1db07..6470b19 100644 --- a/lib/api/marianumcloud/talk/chat/getChatResponse.dart +++ b/lib/api/marianumcloud/talk/chat/get_chat_response.dart @@ -1,10 +1,10 @@ import 'package:jiffy/jiffy.dart'; import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; -import '../room/getRoomResponse.dart'; +import '../../../api_response.dart'; +import '../room/get_room_response.dart'; -part 'getChatResponse.g.dart'; +part 'get_chat_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetChatResponse extends ApiResponse { @@ -87,10 +87,10 @@ class GetChatResponseObject { } Map? _fromJson(dynamic json) { - if(json is Map) { - var data = {}; - for (var element in json.keys) { - data.putIfAbsent(element, () => RichObjectString.fromJson(json[element])); + if (json is Map) { + final data = {}; + for (final element in json.keys) { + data.putIfAbsent(element, () => RichObjectString.fromJson(json[element] as Map)); } return data; } diff --git a/lib/api/marianumcloud/talk/chat/getChatResponse.g.dart b/lib/api/marianumcloud/talk/chat/get_chat_response.g.dart similarity index 99% rename from lib/api/marianumcloud/talk/chat/getChatResponse.g.dart rename to lib/api/marianumcloud/talk/chat/get_chat_response.g.dart index 1835359..c8f0276 100644 --- a/lib/api/marianumcloud/talk/chat/getChatResponse.g.dart +++ b/lib/api/marianumcloud/talk/chat/get_chat_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getChatResponse.dart'; +part of 'get_chat_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart b/lib/api/marianumcloud/talk/chat/rich_object_string_processor.dart similarity index 89% rename from lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart rename to lib/api/marianumcloud/talk/chat/rich_object_string_processor.dart index b61d064..af03502 100644 --- a/lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart +++ b/lib/api/marianumcloud/talk/chat/rich_object_string_processor.dart @@ -1,5 +1,5 @@ -import 'getChatResponse.dart'; +import 'get_chat_response.dart'; class RichObjectStringProcessor { static String parseToString(String message, Map? data) { diff --git a/lib/api/marianumcloud/talk/createRoom/createRoom.dart b/lib/api/marianumcloud/talk/create_room/create_room.dart similarity index 83% rename from lib/api/marianumcloud/talk/createRoom/createRoom.dart rename to lib/api/marianumcloud/talk/create_room/create_room.dart index 27d274d..e2183b6 100644 --- a/lib/api/marianumcloud/talk/createRoom/createRoom.dart +++ b/lib/api/marianumcloud/talk/create_room/create_room.dart @@ -2,15 +2,15 @@ import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../talkApi.dart'; -import 'createRoomParams.dart'; +import '../talk_api.dart'; +import 'create_room_params.dart'; class CreateRoom extends TalkApi { CreateRoomParams params; CreateRoom(this.params) : super('v4/room', params); @override - assemble(String raw) => null; + Null assemble(String raw) => null; @override Future? request(Uri uri, Object? body, Map? headers) { diff --git a/lib/api/marianumcloud/talk/createRoom/createRoomParams.dart b/lib/api/marianumcloud/talk/create_room/create_room_params.dart similarity index 89% rename from lib/api/marianumcloud/talk/createRoom/createRoomParams.dart rename to lib/api/marianumcloud/talk/create_room/create_room_params.dart index cb1d1b5..56ffe1d 100644 --- a/lib/api/marianumcloud/talk/createRoom/createRoomParams.dart +++ b/lib/api/marianumcloud/talk/create_room/create_room_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'createRoomParams.g.dart'; +part 'create_room_params.g.dart'; @JsonSerializable() class CreateRoomParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/createRoom/createRoomParams.g.dart b/lib/api/marianumcloud/talk/create_room/create_room_params.g.dart similarity index 96% rename from lib/api/marianumcloud/talk/createRoom/createRoomParams.g.dart rename to lib/api/marianumcloud/talk/create_room/create_room_params.g.dart index 8592ebb..b873df4 100644 --- a/lib/api/marianumcloud/talk/createRoom/createRoomParams.g.dart +++ b/lib/api/marianumcloud/talk/create_room/create_room_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'createRoomParams.dart'; +part of 'create_room_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessage.dart b/lib/api/marianumcloud/talk/delete_react_message/delete_react_message.dart similarity index 80% rename from lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessage.dart rename to lib/api/marianumcloud/talk/delete_react_message/delete_react_message.dart index d586d5b..9dc886c 100644 --- a/lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessage.dart +++ b/lib/api/marianumcloud/talk/delete_react_message/delete_react_message.dart @@ -1,9 +1,9 @@ import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../../../apiParams.dart'; -import '../talkApi.dart'; -import 'deleteReactMessageParams.dart'; +import '../../../api_params.dart'; +import '../talk_api.dart'; +import 'delete_react_message_params.dart'; class DeleteReactMessage extends TalkApi { String chatToken; @@ -11,7 +11,7 @@ class DeleteReactMessage extends TalkApi { DeleteReactMessage({required this.chatToken, required this.messageId, required DeleteReactMessageParams params}) : super('v1/reaction/$chatToken/$messageId', params); @override - assemble(String raw) => null; + Null assemble(String raw) => null; @override Future? request(Uri uri, ApiParams? body, Map? headers) { diff --git a/lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.dart b/lib/api/marianumcloud/talk/delete_react_message/delete_react_message_params.dart similarity index 83% rename from lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.dart rename to lib/api/marianumcloud/talk/delete_react_message/delete_react_message_params.dart index c40c317..d17bebc 100644 --- a/lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.dart +++ b/lib/api/marianumcloud/talk/delete_react_message/delete_react_message_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'deleteReactMessageParams.g.dart'; +part 'delete_react_message_params.g.dart'; @JsonSerializable() class DeleteReactMessageParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.g.dart b/lib/api/marianumcloud/talk/delete_react_message/delete_react_message_params.g.dart similarity index 92% rename from lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.g.dart rename to lib/api/marianumcloud/talk/delete_react_message/delete_react_message_params.g.dart index 7dc6c79..9f50520 100644 --- a/lib/api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.g.dart +++ b/lib/api/marianumcloud/talk/delete_react_message/delete_react_message_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'deleteReactMessageParams.dart'; +part of 'delete_react_message_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/getParticipants/getParticipants.dart b/lib/api/marianumcloud/talk/get_participants/get_participants.dart similarity index 58% rename from lib/api/marianumcloud/talk/getParticipants/getParticipants.dart rename to lib/api/marianumcloud/talk/get_participants/get_participants.dart index ec88234..03b302a 100644 --- a/lib/api/marianumcloud/talk/getParticipants/getParticipants.dart +++ b/lib/api/marianumcloud/talk/get_participants/get_participants.dart @@ -2,15 +2,18 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import '../talkApi.dart'; -import 'getParticipantsResponse.dart'; +import '../talk_api.dart'; +import 'get_participants_response.dart'; class GetParticipants extends TalkApi { String token; GetParticipants(this.token) : super('v4/room/$token/participants', null); @override - GetParticipantsResponse assemble(String raw) => GetParticipantsResponse.fromJson(jsonDecode(raw)['ocs']); + GetParticipantsResponse assemble(String raw) { + final decoded = jsonDecode(raw) as Map; + return GetParticipantsResponse.fromJson(decoded['ocs'] as Map); + } @override Future request(Uri uri, Object? body, Map? headers) => http.get(uri, headers: headers); diff --git a/lib/api/marianumcloud/talk/getParticipants/getParticipantsCache.dart b/lib/api/marianumcloud/talk/get_participants/get_participants_cache.dart similarity index 80% rename from lib/api/marianumcloud/talk/getParticipants/getParticipantsCache.dart rename to lib/api/marianumcloud/talk/get_participants/get_participants_cache.dart index c869b26..f40b017 100644 --- a/lib/api/marianumcloud/talk/getParticipants/getParticipantsCache.dart +++ b/lib/api/marianumcloud/talk/get_participants/get_participants_cache.dart @@ -1,6 +1,6 @@ -import '../../../requestCache.dart'; -import 'getParticipants.dart'; -import 'getParticipantsResponse.dart'; +import '../../../request_cache.dart'; +import 'get_participants.dart'; +import 'get_participants_response.dart'; class GetParticipantsCache extends SimpleCache { GetParticipantsCache({ diff --git a/lib/api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart b/lib/api/marianumcloud/talk/get_participants/get_participants_response.dart similarity index 96% rename from lib/api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart rename to lib/api/marianumcloud/talk/get_participants/get_participants_response.dart index 3d0e9ff..5f97086 100644 --- a/lib/api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart +++ b/lib/api/marianumcloud/talk/get_participants/get_participants_response.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getParticipantsResponse.g.dart'; +part 'get_participants_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetParticipantsResponse extends ApiResponse { diff --git a/lib/api/marianumcloud/talk/getParticipants/getParticipantsResponse.g.dart b/lib/api/marianumcloud/talk/get_participants/get_participants_response.g.dart similarity index 98% rename from lib/api/marianumcloud/talk/getParticipants/getParticipantsResponse.g.dart rename to lib/api/marianumcloud/talk/get_participants/get_participants_response.g.dart index 424b161..f3fd7cb 100644 --- a/lib/api/marianumcloud/talk/getParticipants/getParticipantsResponse.g.dart +++ b/lib/api/marianumcloud/talk/get_participants/get_participants_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getParticipantsResponse.dart'; +part of 'get_participants_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/getPoll/getPollState.dart b/lib/api/marianumcloud/talk/get_poll/get_poll_state.dart similarity index 61% rename from lib/api/marianumcloud/talk/getPoll/getPollState.dart rename to lib/api/marianumcloud/talk/get_poll/get_poll_state.dart index 503c1d0..c4c37b7 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollState.dart +++ b/lib/api/marianumcloud/talk/get_poll/get_poll_state.dart @@ -2,8 +2,8 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import '../talkApi.dart'; -import 'getPollStateResponse.dart'; +import '../talk_api.dart'; +import 'get_poll_state_response.dart'; class GetPollState extends TalkApi { String token; @@ -11,7 +11,10 @@ class GetPollState extends TalkApi { GetPollState({required this.token, required this.pollId}) : super('v1/poll/$token/$pollId', null); @override - GetPollStateResponse assemble(String raw) => GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); + GetPollStateResponse assemble(String raw) { + final decoded = jsonDecode(raw) as Map; + return GetPollStateResponse.fromJson(decoded['ocs'] as Map); + } @override Future request(Uri uri, Object? body, Map? headers) => http.get(uri, headers: headers); diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart b/lib/api/marianumcloud/talk/get_poll/get_poll_state_response.dart similarity index 94% rename from lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart rename to lib/api/marianumcloud/talk/get_poll/get_poll_state_response.dart index 75d20c0..5c43a38 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart +++ b/lib/api/marianumcloud/talk/get_poll/get_poll_state_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getPollStateResponse.g.dart'; +part 'get_poll_state_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetPollStateResponse extends ApiResponse { diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart b/lib/api/marianumcloud/talk/get_poll/get_poll_state_response.g.dart similarity index 97% rename from lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart rename to lib/api/marianumcloud/talk/get_poll/get_poll_state_response.g.dart index 3015979..c81d2f5 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart +++ b/lib/api/marianumcloud/talk/get_poll/get_poll_state_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getPollStateResponse.dart'; +part of 'get_poll_state_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/getReactions/getReactions.dart b/lib/api/marianumcloud/talk/get_reactions/get_reactions.dart similarity index 61% rename from lib/api/marianumcloud/talk/getReactions/getReactions.dart rename to lib/api/marianumcloud/talk/get_reactions/get_reactions.dart index 5b9a8e3..549c788 100644 --- a/lib/api/marianumcloud/talk/getReactions/getReactions.dart +++ b/lib/api/marianumcloud/talk/get_reactions/get_reactions.dart @@ -3,9 +3,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../../../apiParams.dart'; -import '../talkApi.dart'; -import 'getReactionsResponse.dart'; +import '../../../api_params.dart'; +import '../talk_api.dart'; +import 'get_reactions_response.dart'; class GetReactions extends TalkApi { String chatToken; @@ -13,7 +13,10 @@ class GetReactions extends TalkApi { GetReactions({required this.chatToken, required this.messageId}) : super('v1/reaction/$chatToken/$messageId', null); @override - assemble(String raw) => GetReactionsResponse.fromJson(jsonDecode(raw)['ocs']); + GetReactionsResponse assemble(String raw) { + final decoded = jsonDecode(raw) as Map; + return GetReactionsResponse.fromJson(decoded['ocs'] as Map); + } @override Future? request(Uri uri, ApiParams? body, Map? headers) => http.get(uri, headers: headers); diff --git a/lib/api/marianumcloud/talk/getReactions/getReactionsResponse.dart b/lib/api/marianumcloud/talk/get_reactions/get_reactions_response.dart similarity index 92% rename from lib/api/marianumcloud/talk/getReactions/getReactionsResponse.dart rename to lib/api/marianumcloud/talk/get_reactions/get_reactions_response.dart index 5a6c9f0..052b03a 100644 --- a/lib/api/marianumcloud/talk/getReactions/getReactionsResponse.dart +++ b/lib/api/marianumcloud/talk/get_reactions/get_reactions_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getReactionsResponse.g.dart'; +part 'get_reactions_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetReactionsResponse extends ApiResponse { diff --git a/lib/api/marianumcloud/talk/getReactions/getReactionsResponse.g.dart b/lib/api/marianumcloud/talk/get_reactions/get_reactions_response.g.dart similarity index 97% rename from lib/api/marianumcloud/talk/getReactions/getReactionsResponse.g.dart rename to lib/api/marianumcloud/talk/get_reactions/get_reactions_response.g.dart index 2fa0d24..a050c59 100644 --- a/lib/api/marianumcloud/talk/getReactions/getReactionsResponse.g.dart +++ b/lib/api/marianumcloud/talk/get_reactions/get_reactions_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getReactionsResponse.dart'; +part of 'get_reactions_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/reactMessage/reactMessage.dart b/lib/api/marianumcloud/talk/react_message/react_message.dart similarity index 80% rename from lib/api/marianumcloud/talk/reactMessage/reactMessage.dart rename to lib/api/marianumcloud/talk/react_message/react_message.dart index ac76bd2..c1e93b1 100644 --- a/lib/api/marianumcloud/talk/reactMessage/reactMessage.dart +++ b/lib/api/marianumcloud/talk/react_message/react_message.dart @@ -1,9 +1,9 @@ import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../../../apiParams.dart'; -import '../talkApi.dart'; -import 'reactMessageParams.dart'; +import '../../../api_params.dart'; +import '../talk_api.dart'; +import 'react_message_params.dart'; class ReactMessage extends TalkApi { String chatToken; @@ -11,7 +11,7 @@ class ReactMessage extends TalkApi { ReactMessage({required this.chatToken, required this.messageId, required ReactMessageParams params}) : super('v1/reaction/$chatToken/$messageId', params); @override - assemble(String raw) => null; + Null assemble(String raw) => null; @override Future? request(Uri uri, ApiParams? body, Map? headers) { diff --git a/lib/api/marianumcloud/talk/reactMessage/reactMessageParams.dart b/lib/api/marianumcloud/talk/react_message/react_message_params.dart similarity index 83% rename from lib/api/marianumcloud/talk/reactMessage/reactMessageParams.dart rename to lib/api/marianumcloud/talk/react_message/react_message_params.dart index 0fb6cc1..22b8845 100644 --- a/lib/api/marianumcloud/talk/reactMessage/reactMessageParams.dart +++ b/lib/api/marianumcloud/talk/react_message/react_message_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'reactMessageParams.g.dart'; +part 'react_message_params.g.dart'; @JsonSerializable() class ReactMessageParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/reactMessage/reactMessageParams.g.dart b/lib/api/marianumcloud/talk/react_message/react_message_params.g.dart similarity index 93% rename from lib/api/marianumcloud/talk/reactMessage/reactMessageParams.g.dart rename to lib/api/marianumcloud/talk/react_message/react_message_params.g.dart index 328c53a..054b0c6 100644 --- a/lib/api/marianumcloud/talk/reactMessage/reactMessageParams.g.dart +++ b/lib/api/marianumcloud/talk/react_message/react_message_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'reactMessageParams.dart'; +part of 'react_message_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/room/getRoom.dart b/lib/api/marianumcloud/talk/room/get_room.dart similarity index 57% rename from lib/api/marianumcloud/talk/room/getRoom.dart rename to lib/api/marianumcloud/talk/room/get_room.dart index dd7cc52..bb7d68e 100644 --- a/lib/api/marianumcloud/talk/room/getRoom.dart +++ b/lib/api/marianumcloud/talk/room/get_room.dart @@ -2,9 +2,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import '../talkApi.dart'; -import 'getRoomParams.dart'; -import 'getRoomResponse.dart'; +import '../talk_api.dart'; +import 'get_room_params.dart'; +import 'get_room_response.dart'; class GetRoom extends TalkApi { @@ -14,7 +14,10 @@ class GetRoom extends TalkApi { @override - GetRoomResponse assemble(String raw) => GetRoomResponse.fromJson(jsonDecode(raw)['ocs']); + GetRoomResponse assemble(String raw) { + final decoded = jsonDecode(raw) as Map; + return GetRoomResponse.fromJson(decoded['ocs'] as Map); + } @override Future request(Uri uri, Object? body, Map? headers) => http.get(uri, headers: headers); diff --git a/lib/api/marianumcloud/talk/room/getRoomCache.dart b/lib/api/marianumcloud/talk/room/get_room_cache.dart similarity index 73% rename from lib/api/marianumcloud/talk/room/getRoomCache.dart rename to lib/api/marianumcloud/talk/room/get_room_cache.dart index 03fd785..107a58b 100644 --- a/lib/api/marianumcloud/talk/room/getRoomCache.dart +++ b/lib/api/marianumcloud/talk/room/get_room_cache.dart @@ -1,7 +1,7 @@ -import '../../../requestCache.dart'; -import 'getRoom.dart'; -import 'getRoomParams.dart'; -import 'getRoomResponse.dart'; +import '../../../request_cache.dart'; +import 'get_room.dart'; +import 'get_room_params.dart'; +import 'get_room_response.dart'; class GetRoomCache extends SimpleCache { GetRoomCache({super.onUpdate, super.onError, super.renew}) diff --git a/lib/api/marianumcloud/talk/room/getRoomParams.dart b/lib/api/marianumcloud/talk/room/get_room_params.dart similarity index 90% rename from lib/api/marianumcloud/talk/room/getRoomParams.dart rename to lib/api/marianumcloud/talk/room/get_room_params.dart index 70d371d..09e397e 100644 --- a/lib/api/marianumcloud/talk/room/getRoomParams.dart +++ b/lib/api/marianumcloud/talk/room/get_room_params.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'getRoomParams.g.dart'; +part 'get_room_params.g.dart'; @JsonSerializable(explicitToJson: true) class GetRoomParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/room/getRoomParams.g.dart b/lib/api/marianumcloud/talk/room/get_room_params.g.dart similarity index 96% rename from lib/api/marianumcloud/talk/room/getRoomParams.g.dart rename to lib/api/marianumcloud/talk/room/get_room_params.g.dart index 0e20511..ceaa6b1 100644 --- a/lib/api/marianumcloud/talk/room/getRoomParams.g.dart +++ b/lib/api/marianumcloud/talk/room/get_room_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getRoomParams.dart'; +part of 'get_room_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/room/getRoomResponse.dart b/lib/api/marianumcloud/talk/room/get_room_response.dart similarity index 97% rename from lib/api/marianumcloud/talk/room/getRoomResponse.dart rename to lib/api/marianumcloud/talk/room/get_room_response.dart index 36e7b6d..c2ce467 100644 --- a/lib/api/marianumcloud/talk/room/getRoomResponse.dart +++ b/lib/api/marianumcloud/talk/room/get_room_response.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; -import '../chat/getChatResponse.dart'; +import '../../../api_response.dart'; +import '../chat/get_chat_response.dart'; -part 'getRoomResponse.g.dart'; +part 'get_room_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetRoomResponse extends ApiResponse { diff --git a/lib/api/marianumcloud/talk/room/getRoomResponse.g.dart b/lib/api/marianumcloud/talk/room/get_room_response.g.dart similarity index 99% rename from lib/api/marianumcloud/talk/room/getRoomResponse.g.dart rename to lib/api/marianumcloud/talk/room/get_room_response.g.dart index 92491fb..49362cc 100644 --- a/lib/api/marianumcloud/talk/room/getRoomResponse.g.dart +++ b/lib/api/marianumcloud/talk/room/get_room_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getRoomResponse.dart'; +part of 'get_room_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/sendMessage/sendMessage.dart b/lib/api/marianumcloud/talk/send_message/send_message.dart similarity index 77% rename from lib/api/marianumcloud/talk/sendMessage/sendMessage.dart rename to lib/api/marianumcloud/talk/send_message/send_message.dart index 61af457..af3a012 100644 --- a/lib/api/marianumcloud/talk/sendMessage/sendMessage.dart +++ b/lib/api/marianumcloud/talk/send_message/send_message.dart @@ -1,16 +1,16 @@ import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../../../apiParams.dart'; -import '../talkApi.dart'; -import 'sendMessageParams.dart'; +import '../../../api_params.dart'; +import '../talk_api.dart'; +import 'send_message_params.dart'; class SendMessage extends TalkApi { String chatToken; SendMessage(this.chatToken, SendMessageParams params) : super('v1/chat/$chatToken', params); @override - assemble(String raw) => null; + Null assemble(String raw) => null; @override Future? request(Uri uri, ApiParams? body, Map? headers) { diff --git a/lib/api/marianumcloud/talk/sendMessage/sendMessageParams.dart b/lib/api/marianumcloud/talk/send_message/send_message_params.dart similarity index 85% rename from lib/api/marianumcloud/talk/sendMessage/sendMessageParams.dart rename to lib/api/marianumcloud/talk/send_message/send_message_params.dart index d467246..8ded2e2 100644 --- a/lib/api/marianumcloud/talk/sendMessage/sendMessageParams.dart +++ b/lib/api/marianumcloud/talk/send_message/send_message_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'sendMessageParams.g.dart'; +part 'send_message_params.g.dart'; @JsonSerializable(explicitToJson: true, includeIfNull: false) class SendMessageParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/sendMessage/sendMessageParams.g.dart b/lib/api/marianumcloud/talk/send_message/send_message_params.g.dart similarity index 94% rename from lib/api/marianumcloud/talk/sendMessage/sendMessageParams.g.dart rename to lib/api/marianumcloud/talk/send_message/send_message_params.g.dart index 602decc..34e1d0c 100644 --- a/lib/api/marianumcloud/talk/sendMessage/sendMessageParams.g.dart +++ b/lib/api/marianumcloud/talk/send_message/send_message_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'sendMessageParams.dart'; +part of 'send_message_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/setReadMarker/setReadMarker.dart b/lib/api/marianumcloud/talk/set_read_marker/set_read_marker.dart similarity index 87% rename from lib/api/marianumcloud/talk/setReadMarker/setReadMarker.dart rename to lib/api/marianumcloud/talk/set_read_marker/set_read_marker.dart index c3ae029..24389ef 100644 --- a/lib/api/marianumcloud/talk/setReadMarker/setReadMarker.dart +++ b/lib/api/marianumcloud/talk/set_read_marker/set_read_marker.dart @@ -2,8 +2,8 @@ import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../talkApi.dart'; -import 'setReadMarkerParams.dart'; +import '../talk_api.dart'; +import 'set_read_marker_params.dart'; class SetReadMarker extends TalkApi { String chatToken; @@ -15,7 +15,7 @@ class SetReadMarker extends TalkApi { } @override - assemble(String raw) => null; + Null assemble(String raw) => null; @override Future request(Uri uri, Object? body, Map? headers) { diff --git a/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart b/lib/api/marianumcloud/talk/set_read_marker/set_read_marker_params.dart similarity index 83% rename from lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart rename to lib/api/marianumcloud/talk/set_read_marker/set_read_marker_params.dart index 5f037c2..50edee7 100644 --- a/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart +++ b/lib/api/marianumcloud/talk/set_read_marker/set_read_marker_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'setReadMarkerParams.g.dart'; +part 'set_read_marker_params.g.dart'; @JsonSerializable() class SetReadMarkerParams extends ApiParams { diff --git a/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.g.dart b/lib/api/marianumcloud/talk/set_read_marker/set_read_marker_params.g.dart similarity index 93% rename from lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.g.dart rename to lib/api/marianumcloud/talk/set_read_marker/set_read_marker_params.g.dart index 5c3c941..e6067b1 100644 --- a/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.g.dart +++ b/lib/api/marianumcloud/talk/set_read_marker/set_read_marker_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'setReadMarkerParams.dart'; +part of 'set_read_marker_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/talk/talkApi.dart b/lib/api/marianumcloud/talk/talk_api.dart similarity index 96% rename from lib/api/marianumcloud/talk/talkApi.dart rename to lib/api/marianumcloud/talk/talk_api.dart index 371d9f2..9e63d35 100644 --- a/lib/api/marianumcloud/talk/talkApi.dart +++ b/lib/api/marianumcloud/talk/talk_api.dart @@ -4,9 +4,9 @@ import 'dart:io'; import 'package:http/http.dart' as http; -import '../../apiParams.dart'; -import '../../apiRequest.dart'; -import '../../apiResponse.dart'; +import '../../api_params.dart'; +import '../../api_request.dart'; +import '../../api_response.dart'; import '../../errors/auth_exception.dart'; import '../../errors/network_exception.dart'; import '../../errors/not_found_exception.dart'; diff --git a/lib/api/marianumcloud/talk/talkError.dart b/lib/api/marianumcloud/talk/talk_error.dart similarity index 100% rename from lib/api/marianumcloud/talk/talkError.dart rename to lib/api/marianumcloud/talk/talk_error.dart diff --git a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFile.dart b/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFile.dart deleted file mode 100644 index 30269c6..0000000 --- a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFile.dart +++ /dev/null @@ -1,22 +0,0 @@ - - -import '../../../../apiResponse.dart'; -import '../../webdavApi.dart'; -import 'downloadFileParams.dart'; - -class DownloadFile extends WebdavApi { - DownloadFileParams params; - - DownloadFile(this.params) : super(params); - - @override - Future run() async { - - // final file = await File(localPath).create(); - // Uint8List downloadedFile = await (await WebdavApi.webdav).download(params.webdavPath); - // file.writeAsBytesSync(downloadedFile, flush: true, mode: FileMode.write); - // - // OpenFile.open(localPath); - throw UnimplementedError(); - } -} diff --git a/lib/api/marianumcloud/webdav/queries/download_file/download_file.dart b/lib/api/marianumcloud/webdav/queries/download_file/download_file.dart new file mode 100644 index 0000000..5a4f164 --- /dev/null +++ b/lib/api/marianumcloud/webdav/queries/download_file/download_file.dart @@ -0,0 +1,16 @@ + + +import '../../../../api_response.dart'; +import '../../webdav_api.dart'; +import 'download_file_params.dart'; + +class DownloadFile extends WebdavApi { + DownloadFileParams params; + + DownloadFile(this.params) : super(params); + + @override + Future run() async { + throw UnimplementedError(); + } +} diff --git a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileParams.dart b/lib/api/marianumcloud/webdav/queries/download_file/download_file_params.dart similarity index 85% rename from lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileParams.dart rename to lib/api/marianumcloud/webdav/queries/download_file/download_file_params.dart index 11b6231..ba8b075 100644 --- a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileParams.dart +++ b/lib/api/marianumcloud/webdav/queries/download_file/download_file_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../../apiParams.dart'; +import '../../../../api_params.dart'; -part 'downloadFileParams.g.dart'; +part 'download_file_params.g.dart'; @JsonSerializable() class DownloadFileParams extends ApiParams { diff --git a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileParams.g.dart b/lib/api/marianumcloud/webdav/queries/download_file/download_file_params.g.dart similarity index 95% rename from lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileParams.g.dart rename to lib/api/marianumcloud/webdav/queries/download_file/download_file_params.g.dart index 3b56332..aa72134 100644 --- a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileParams.g.dart +++ b/lib/api/marianumcloud/webdav/queries/download_file/download_file_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'downloadFileParams.dart'; +part of 'download_file_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileResponse.dart b/lib/api/marianumcloud/webdav/queries/download_file/download_file_response.dart similarity index 89% rename from lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileResponse.dart rename to lib/api/marianumcloud/webdav/queries/download_file/download_file_response.dart index 3368cdd..76ff712 100644 --- a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileResponse.dart +++ b/lib/api/marianumcloud/webdav/queries/download_file/download_file_response.dart @@ -1,7 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; -part 'downloadFileResponse.g.dart'; +part 'download_file_response.g.dart'; @JsonSerializable() class DownloadFileResponse { diff --git a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileResponse.g.dart b/lib/api/marianumcloud/webdav/queries/download_file/download_file_response.g.dart similarity index 92% rename from lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileResponse.g.dart rename to lib/api/marianumcloud/webdav/queries/download_file/download_file_response.g.dart index 745d832..bab583f 100644 --- a/lib/api/marianumcloud/webdav/queries/downloadFile/downloadFileResponse.g.dart +++ b/lib/api/marianumcloud/webdav/queries/download_file/download_file_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'downloadFileResponse.dart'; +part of 'download_file_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart b/lib/api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart deleted file mode 100644 index 3a61460..0000000 --- a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'dart:convert'; -import 'package:crypto/crypto.dart'; - -import '../../../../requestCache.dart'; -import 'listFiles.dart'; -import 'listFilesParams.dart'; -import 'listFilesResponse.dart'; - -class ListFilesCache extends SimpleCache { - ListFilesCache({ - required void Function(ListFilesResponse) onUpdate, - super.onCacheData, - super.onNetworkData, - super.onError, - required String path, - }) : super( - cacheTime: RequestCache.cacheNothing, - loader: () => ListFiles(ListFilesParams(path)).run(), - fromJson: ListFilesResponse.fromJson, - onUpdate: onUpdate, - ) { - final cacheName = md5.convert(utf8.encode('MarianumMobile-$path')).toString(); - start('wd-folder-$cacheName'); - } -} diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart b/lib/api/marianumcloud/webdav/queries/list_files/cacheable_file.dart similarity index 88% rename from lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart rename to lib/api/marianumcloud/webdav/queries/list_files/cacheable_file.dart index b8a9918..c716dfb 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/cacheable_file.dart @@ -1,7 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:nextcloud/nextcloud.dart'; -part 'cacheableFile.g.dart'; +part 'cacheable_file.g.dart'; @JsonSerializable(explicitToJson: true) class CacheableFile { @@ -15,10 +15,6 @@ class CacheableFile { DateTime? modifiedAt; String? sort; - @JsonKey(includeFromJson: false, includeToJson: false) - bool currentlyDownloading = false; - - CacheableFile({required this.path, required this.isDirectory, required this.name, this.mimeType, this.size, this.eTag, this.createdAt, this.modifiedAt}); CacheableFile.fromDavFile(WebDavFile file) { diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart b/lib/api/marianumcloud/webdav/queries/list_files/cacheable_file.g.dart similarity index 97% rename from lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart rename to lib/api/marianumcloud/webdav/queries/list_files/cacheable_file.g.dart index c79547b..4e3407d 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/cacheable_file.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'cacheableFile.dart'; +part of 'cacheable_file.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart b/lib/api/marianumcloud/webdav/queries/list_files/list_files.dart similarity index 67% rename from lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart rename to lib/api/marianumcloud/webdav/queries/list_files/list_files.dart index 8582989..6bebff9 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/list_files.dart @@ -1,10 +1,10 @@ import 'package:nextcloud/nextcloud.dart'; -import '../../webdavApi.dart'; -import 'cacheableFile.dart'; -import 'listFilesParams.dart'; -import 'listFilesResponse.dart'; +import '../../webdav_api.dart'; +import 'cacheable_file.dart'; +import 'list_files_params.dart'; +import 'list_files_response.dart'; class ListFiles extends WebdavApi { ListFilesParams params; @@ -27,16 +27,8 @@ class ListFiles extends WebdavApi { final webdav = await WebdavApi.webdav; final timeout = _isRoot ? _rootTimeout : _subfolderTimeout; final davFiles = (await webdav.propfind(PathUri.parse(params.path)).timeout(timeout)).toWebDavFiles(); - var files = davFiles.map(CacheableFile.fromDavFile).toSet(); + final files = davFiles.map(CacheableFile.fromDavFile).toSet(); - // webdav handles subdirectories wrong, this is a fix - // currently this fix is not needed anymore - // if(EndpointData().getEndpointMode() == EndpointMode.stage) { - // files = files.map((e) { // somehow - // e.path = e.path.split("mobile/cloud/remote.php/webdav")[1]; - // return e; - // }).toSet(); - // } // somehow the current working folder is also listed, it is filtered here. files.removeWhere((element) => element.path == '/${params.path}/' || element.path == '/'); diff --git a/lib/api/marianumcloud/webdav/queries/list_files/list_files_cache.dart b/lib/api/marianumcloud/webdav/queries/list_files/list_files_cache.dart new file mode 100644 index 0000000..4f17e6e --- /dev/null +++ b/lib/api/marianumcloud/webdav/queries/list_files/list_files_cache.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; +import 'package:localstore/localstore.dart'; + +import '../../../../../utils/cache_invalidation_bus.dart'; +import '../../../../request_cache.dart'; +import 'list_files.dart'; +import 'list_files_params.dart'; +import 'list_files_response.dart'; + +class ListFilesCache extends SimpleCache { + ListFilesCache({ + required void Function(ListFilesResponse) onUpdate, + super.onCacheData, + super.onNetworkData, + super.onError, + required String path, + }) : super( + cacheTime: RequestCache.cacheNothing, + loader: () => ListFiles(ListFilesParams(path)).run(), + fromJson: ListFilesResponse.fromJson, + onUpdate: onUpdate, + ) { + start(_documentId(path)); + } + + static String _documentId(String path) { + final cacheName = md5.convert(utf8.encode('MarianumMobile-$path')).toString(); + return 'wd-folder-$cacheName'; + } + + /// Drops the cached listing for [path] from local storage so the next + /// `listFiles` call cannot serve stale data, and notifies any live + /// `_FilesView` for that path via [CacheInvalidationBus] so it refetches + /// even while it is sitting in the background of the navigation stack. + static Future invalidate(String path) async { + await Localstore.instance.collection(RequestCache.collection).doc(_documentId(path)).delete(); + CacheInvalidationBus.notifyListFiles(path); + } +} diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesParams.dart b/lib/api/marianumcloud/webdav/queries/list_files/list_files_params.dart similarity index 83% rename from lib/api/marianumcloud/webdav/queries/listFiles/listFilesParams.dart rename to lib/api/marianumcloud/webdav/queries/list_files/list_files_params.dart index f0adf21..c18a539 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesParams.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/list_files_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../../apiParams.dart'; +import '../../../../api_params.dart'; -part 'listFilesParams.g.dart'; +part 'list_files_params.g.dart'; @JsonSerializable(explicitToJson: true) class ListFilesParams extends ApiParams { diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesParams.g.dart b/lib/api/marianumcloud/webdav/queries/list_files/list_files_params.g.dart similarity index 93% rename from lib/api/marianumcloud/webdav/queries/listFiles/listFilesParams.g.dart rename to lib/api/marianumcloud/webdav/queries/list_files/list_files_params.g.dart index 6a72efd..29f551f 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesParams.g.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/list_files_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'listFilesParams.dart'; +part of 'list_files_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart b/lib/api/marianumcloud/webdav/queries/list_files/list_files_response.dart similarity index 94% rename from lib/api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart rename to lib/api/marianumcloud/webdav/queries/list_files/list_files_response.dart index 59f8d0e..1983583 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/list_files_response.dart @@ -2,10 +2,10 @@ import 'package:jiffy/jiffy.dart'; import 'package:json_annotation/json_annotation.dart'; import '../../../../../view/pages/files/files.dart'; -import '../../../../apiResponse.dart'; -import 'cacheableFile.dart'; +import '../../../../api_response.dart'; +import 'cacheable_file.dart'; -part 'listFilesResponse.g.dart'; +part 'list_files_response.g.dart'; @JsonSerializable(explicitToJson: true) class ListFilesResponse extends ApiResponse { diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesResponse.g.dart b/lib/api/marianumcloud/webdav/queries/list_files/list_files_response.g.dart similarity index 95% rename from lib/api/marianumcloud/webdav/queries/listFiles/listFilesResponse.g.dart rename to lib/api/marianumcloud/webdav/queries/list_files/list_files_response.g.dart index 77522c2..09123cc 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/listFilesResponse.g.dart +++ b/lib/api/marianumcloud/webdav/queries/list_files/list_files_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'listFilesResponse.dart'; +part of 'list_files_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/marianumcloud/webdav/webdavApi.dart b/lib/api/marianumcloud/webdav/webdav_api.dart similarity index 93% rename from lib/api/marianumcloud/webdav/webdavApi.dart rename to lib/api/marianumcloud/webdav/webdav_api.dart index 5049153..3327b62 100644 --- a/lib/api/marianumcloud/webdav/webdavApi.dart +++ b/lib/api/marianumcloud/webdav/webdav_api.dart @@ -2,8 +2,8 @@ import 'package:nextcloud/nextcloud.dart'; import '../../../model/account_data.dart'; import '../../../model/endpoint_data.dart'; -import '../../apiRequest.dart'; -import '../../apiResponse.dart'; +import '../../api_request.dart'; +import '../../api_response.dart'; abstract class WebdavApi extends ApiRequest { T genericParams; diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakers.dart b/lib/api/mhsl/breaker/get_breakers/get_breakers.dart similarity index 73% rename from lib/api/mhsl/breaker/getBreakers/getBreakers.dart rename to lib/api/mhsl/breaker/get_breakers/get_breakers.dart index 63d2fe0..b8f5c93 100644 --- a/lib/api/mhsl/breaker/getBreakers/getBreakers.dart +++ b/lib/api/mhsl/breaker/get_breakers/get_breakers.dart @@ -2,14 +2,14 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; -import '../../mhslApi.dart'; -import 'getBreakersResponse.dart'; +import '../../mhsl_api.dart'; +import 'get_breakers_response.dart'; class GetBreakers extends MhslApi { GetBreakers() : super('breaker/'); @override - GetBreakersResponse assemble(String raw) => GetBreakersResponse.fromJson(jsonDecode(raw)); + GetBreakersResponse assemble(String raw) => GetBreakersResponse.fromJson(jsonDecode(raw) as Map); @override Future? request(Uri uri) => http.get(uri); diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakersCache.dart b/lib/api/mhsl/breaker/get_breakers/get_breakers_cache.dart similarity index 75% rename from lib/api/mhsl/breaker/getBreakers/getBreakersCache.dart rename to lib/api/mhsl/breaker/get_breakers/get_breakers_cache.dart index d7bc0f8..8f3c180 100644 --- a/lib/api/mhsl/breaker/getBreakers/getBreakersCache.dart +++ b/lib/api/mhsl/breaker/get_breakers/get_breakers_cache.dart @@ -1,6 +1,6 @@ -import '../../../requestCache.dart'; -import 'getBreakers.dart'; -import 'getBreakersResponse.dart'; +import '../../../request_cache.dart'; +import 'get_breakers.dart'; +import 'get_breakers_response.dart'; class GetBreakersCache extends SimpleCache { GetBreakersCache({super.onUpdate, super.renew}) diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.dart b/lib/api/mhsl/breaker/get_breakers/get_breakers_response.dart similarity index 93% rename from lib/api/mhsl/breaker/getBreakers/getBreakersResponse.dart rename to lib/api/mhsl/breaker/get_breakers/get_breakers_response.dart index 6e0cb73..aa0f3b1 100644 --- a/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.dart +++ b/lib/api/mhsl/breaker/get_breakers/get_breakers_response.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getBreakersResponse.g.dart'; +part 'get_breakers_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetBreakersResponse extends ApiResponse { diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.g.dart b/lib/api/mhsl/breaker/get_breakers/get_breakers_response.g.dart similarity index 97% rename from lib/api/mhsl/breaker/getBreakers/getBreakersResponse.g.dart rename to lib/api/mhsl/breaker/get_breakers/get_breakers_response.g.dart index 5a2418c..30d16e6 100644 --- a/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.g.dart +++ b/lib/api/mhsl/breaker/get_breakers/get_breakers_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getBreakersResponse.dart'; +part of 'get_breakers_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEvent.dart b/lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event.dart similarity index 85% rename from lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEvent.dart rename to lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event.dart index 15f237f..c7fe3fc 100644 --- a/lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEvent.dart +++ b/lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'package:http/http.dart'; import 'package:http/http.dart' as http; -import '../../mhslApi.dart'; -import 'addCustomTimetableEventParams.dart'; +import '../../mhsl_api.dart'; +import 'add_custom_timetable_event_params.dart'; class AddCustomTimetableEvent extends MhslApi { AddCustomTimetableEventParams params; diff --git a/lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.dart b/lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.dart similarity index 83% rename from lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.dart rename to lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.dart index 125d3ea..a1d3b74 100644 --- a/lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.dart +++ b/lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../customTimetableEvent.dart'; +import '../custom_timetable_event.dart'; -part 'addCustomTimetableEventParams.g.dart'; +part 'add_custom_timetable_event_params.g.dart'; @JsonSerializable(explicitToJson: true) class AddCustomTimetableEventParams { diff --git a/lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.g.dart b/lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.g.dart similarity index 92% rename from lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.g.dart rename to lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.g.dart index e8d2b80..eb39f91 100644 --- a/lib/api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.g.dart +++ b/lib/api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'addCustomTimetableEventParams.dart'; +part of 'add_custom_timetable_event_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/customTimetableEvent/customTimetableEvent.dart b/lib/api/mhsl/custom_timetable_event/custom_timetable_event.dart similarity index 93% rename from lib/api/mhsl/customTimetableEvent/customTimetableEvent.dart rename to lib/api/mhsl/custom_timetable_event/custom_timetable_event.dart index d34489b..be9e4a6 100644 --- a/lib/api/mhsl/customTimetableEvent/customTimetableEvent.dart +++ b/lib/api/mhsl/custom_timetable_event/custom_timetable_event.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../mhslApi.dart'; +import '../mhsl_api.dart'; -part 'customTimetableEvent.g.dart'; +part 'custom_timetable_event.g.dart'; @JsonSerializable() class CustomTimetableEvent { diff --git a/lib/api/mhsl/customTimetableEvent/customTimetableEvent.g.dart b/lib/api/mhsl/custom_timetable_event/custom_timetable_event.g.dart similarity index 97% rename from lib/api/mhsl/customTimetableEvent/customTimetableEvent.g.dart rename to lib/api/mhsl/custom_timetable_event/custom_timetable_event.g.dart index e15d6d8..b83138b 100644 --- a/lib/api/mhsl/customTimetableEvent/customTimetableEvent.g.dart +++ b/lib/api/mhsl/custom_timetable_event/custom_timetable_event.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'customTimetableEvent.dart'; +part of 'custom_timetable_event.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEvent.dart b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event.dart similarity index 80% rename from lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEvent.dart rename to lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event.dart index bca7fd0..dbdf476 100644 --- a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEvent.dart +++ b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event.dart @@ -3,9 +3,9 @@ import 'dart:convert'; import 'package:http/http.dart'; import 'package:http/http.dart' as http; -import '../../mhslApi.dart'; -import 'getCustomTimetableEventParams.dart'; -import 'getCustomTimetableEventResponse.dart'; +import '../../mhsl_api.dart'; +import 'get_custom_timetable_event_params.dart'; +import 'get_custom_timetable_event_response.dart'; class GetCustomTimetableEvent extends MhslApi { GetCustomTimetableEventParams params; diff --git a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventCache.dart b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_cache.dart similarity index 72% rename from lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventCache.dart rename to lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_cache.dart index 5adc186..ba49152 100644 --- a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventCache.dart +++ b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_cache.dart @@ -1,7 +1,7 @@ -import '../../../requestCache.dart'; -import 'getCustomTimetableEvent.dart'; -import 'getCustomTimetableEventParams.dart'; -import 'getCustomTimetableEventResponse.dart'; +import '../../../request_cache.dart'; +import 'get_custom_timetable_event.dart'; +import 'get_custom_timetable_event_params.dart'; +import 'get_custom_timetable_event_response.dart'; class GetCustomTimetableEventCache extends SimpleCache { GetCustomTimetableEventCache( diff --git a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.dart b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.dart similarity index 88% rename from lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.dart rename to lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.dart index c4d6c79..58a9103 100644 --- a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.dart +++ b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'getCustomTimetableEventParams.g.dart'; +part 'get_custom_timetable_event_params.g.dart'; @JsonSerializable() class GetCustomTimetableEventParams { diff --git a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.g.dart b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.g.dart similarity index 91% rename from lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.g.dart rename to lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.g.dart index 6fc4044..01fc5a5 100644 --- a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.g.dart +++ b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getCustomTimetableEventParams.dart'; +part of 'get_custom_timetable_event_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart similarity index 77% rename from lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart rename to lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart index c086b73..99684a2 100644 --- a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart +++ b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; -import '../customTimetableEvent.dart'; +import '../../../api_response.dart'; +import '../custom_timetable_event.dart'; -part 'getCustomTimetableEventResponse.g.dart'; +part 'get_custom_timetable_event_response.g.dart'; @JsonSerializable() class GetCustomTimetableEventResponse extends ApiResponse { diff --git a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.g.dart b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.g.dart similarity index 94% rename from lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.g.dart rename to lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.g.dart index 3e16db0..5bed445 100644 --- a/lib/api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.g.dart +++ b/lib/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getCustomTimetableEventResponse.dart'; +part of 'get_custom_timetable_event_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEvent.dart b/lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event.dart similarity index 84% rename from lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEvent.dart rename to lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event.dart index add1c55..436395e 100644 --- a/lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEvent.dart +++ b/lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'package:http/http.dart'; import 'package:http/http.dart' as http; -import '../../mhslApi.dart'; -import 'removeCustomTimetableEventParams.dart'; +import '../../mhsl_api.dart'; +import 'remove_custom_timetable_event_params.dart'; class RemoveCustomTimetableEvent extends MhslApi { RemoveCustomTimetableEventParams params; diff --git a/lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.dart b/lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.dart similarity index 88% rename from lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.dart rename to lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.dart index 3b1d989..a84ba07 100644 --- a/lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.dart +++ b/lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'removeCustomTimetableEventParams.g.dart'; +part 'remove_custom_timetable_event_params.g.dart'; @JsonSerializable() class RemoveCustomTimetableEventParams { diff --git a/lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.g.dart b/lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.g.dart similarity index 91% rename from lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.g.dart rename to lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.g.dart index aa30f6f..4e85a31 100644 --- a/lib/api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.g.dart +++ b/lib/api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'removeCustomTimetableEventParams.dart'; +part of 'remove_custom_timetable_event_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEvent.dart b/lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event.dart similarity index 84% rename from lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEvent.dart rename to lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event.dart index 9b1a754..4ae91d4 100644 --- a/lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEvent.dart +++ b/lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'package:http/http.dart'; import 'package:http/http.dart' as http; -import '../../mhslApi.dart'; -import 'updateCustomTimetableEventParams.dart'; +import '../../mhsl_api.dart'; +import 'update_custom_timetable_event_params.dart'; class UpdateCustomTimetableEvent extends MhslApi { UpdateCustomTimetableEventParams params; diff --git a/lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.dart b/lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.dart similarity index 83% rename from lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.dart rename to lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.dart index 4a09c83..75f4dae 100644 --- a/lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.dart +++ b/lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.dart @@ -1,9 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; -import '../customTimetableEvent.dart'; +import '../custom_timetable_event.dart'; -part 'updateCustomTimetableEventParams.g.dart'; +part 'update_custom_timetable_event_params.g.dart'; @JsonSerializable(explicitToJson: true) class UpdateCustomTimetableEventParams { diff --git a/lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.g.dart b/lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.g.dart similarity index 92% rename from lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.g.dart rename to lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.g.dart index 11ec8e9..37c5d71 100644 --- a/lib/api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.g.dart +++ b/lib/api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'updateCustomTimetableEventParams.dart'; +part of 'update_custom_timetable_event_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/mhslApi.dart b/lib/api/mhsl/mhsl_api.dart similarity index 98% rename from lib/api/mhsl/mhslApi.dart rename to lib/api/mhsl/mhsl_api.dart index 55f31c1..da380f3 100644 --- a/lib/api/mhsl/mhslApi.dart +++ b/lib/api/mhsl/mhsl_api.dart @@ -5,7 +5,7 @@ import 'dart:io'; import 'package:http/http.dart' as http; import 'package:jiffy/jiffy.dart'; -import '../apiRequest.dart'; +import '../api_request.dart'; import '../errors/network_exception.dart'; import '../errors/parse_exception.dart'; import '../errors/server_exception.dart'; diff --git a/lib/api/mhsl/notify/register/notifyRegister.dart b/lib/api/mhsl/notify/register/notify_register.dart similarity index 88% rename from lib/api/mhsl/notify/register/notifyRegister.dart rename to lib/api/mhsl/notify/register/notify_register.dart index a7053dc..b28c3dc 100644 --- a/lib/api/mhsl/notify/register/notifyRegister.dart +++ b/lib/api/mhsl/notify/register/notify_register.dart @@ -4,8 +4,8 @@ import 'dart:developer'; import 'package:http/http.dart' as http; -import '../../mhslApi.dart'; -import 'notifyRegisterParams.dart'; +import '../../mhsl_api.dart'; +import 'notify_register_params.dart'; class NotifyRegister extends MhslApi { NotifyRegisterParams params; diff --git a/lib/api/mhsl/notify/register/notifyRegisterParams.dart b/lib/api/mhsl/notify/register/notify_register_params.dart similarity index 92% rename from lib/api/mhsl/notify/register/notifyRegisterParams.dart rename to lib/api/mhsl/notify/register/notify_register_params.dart index 3f18319..1c92c46 100644 --- a/lib/api/mhsl/notify/register/notifyRegisterParams.dart +++ b/lib/api/mhsl/notify/register/notify_register_params.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'notifyRegisterParams.g.dart'; +part 'notify_register_params.g.dart'; @JsonSerializable() class NotifyRegisterParams { diff --git a/lib/api/mhsl/notify/register/notifyRegisterParams.g.dart b/lib/api/mhsl/notify/register/notify_register_params.g.dart similarity index 94% rename from lib/api/mhsl/notify/register/notifyRegisterParams.g.dart rename to lib/api/mhsl/notify/register/notify_register_params.g.dart index d862558..ad53ab5 100644 --- a/lib/api/mhsl/notify/register/notifyRegisterParams.g.dart +++ b/lib/api/mhsl/notify/register/notify_register_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'notifyRegisterParams.dart'; +part of 'notify_register_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/server/feedback/addFeedback.dart b/lib/api/mhsl/server/feedback/add_feedback.dart similarity index 85% rename from lib/api/mhsl/server/feedback/addFeedback.dart rename to lib/api/mhsl/server/feedback/add_feedback.dart index 54c3ce0..7f69978 100644 --- a/lib/api/mhsl/server/feedback/addFeedback.dart +++ b/lib/api/mhsl/server/feedback/add_feedback.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'package:http/http.dart'; import 'package:http/http.dart' as http; -import '../../mhslApi.dart'; -import 'addFeedbackParams.dart'; +import '../../mhsl_api.dart'; +import 'add_feedback_params.dart'; class AddFeedback extends MhslApi { diff --git a/lib/api/mhsl/server/feedback/addFeedbackParams.dart b/lib/api/mhsl/server/feedback/add_feedback_params.dart similarity index 93% rename from lib/api/mhsl/server/feedback/addFeedbackParams.dart rename to lib/api/mhsl/server/feedback/add_feedback_params.dart index 945b00c..ecf9adb 100644 --- a/lib/api/mhsl/server/feedback/addFeedbackParams.dart +++ b/lib/api/mhsl/server/feedback/add_feedback_params.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'addFeedbackParams.g.dart'; +part 'add_feedback_params.g.dart'; @JsonSerializable() class AddFeedbackParams { diff --git a/lib/api/mhsl/server/feedback/addFeedbackParams.g.dart b/lib/api/mhsl/server/feedback/add_feedback_params.g.dart similarity index 95% rename from lib/api/mhsl/server/feedback/addFeedbackParams.g.dart rename to lib/api/mhsl/server/feedback/add_feedback_params.g.dart index ec85d22..747d43b 100644 --- a/lib/api/mhsl/server/feedback/addFeedbackParams.g.dart +++ b/lib/api/mhsl/server/feedback/add_feedback_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'addFeedbackParams.dart'; +part of 'add_feedback_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/server/userIndex/update/updateUserIndexParams.dart b/lib/api/mhsl/server/user_index/update/update_user_index_params.dart similarity index 93% rename from lib/api/mhsl/server/userIndex/update/updateUserIndexParams.dart rename to lib/api/mhsl/server/user_index/update/update_user_index_params.dart index 680edda..7fd07f4 100644 --- a/lib/api/mhsl/server/userIndex/update/updateUserIndexParams.dart +++ b/lib/api/mhsl/server/user_index/update/update_user_index_params.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'updateUserIndexParams.g.dart'; +part 'update_user_index_params.g.dart'; @JsonSerializable() class UpdateUserIndexParams { diff --git a/lib/api/mhsl/server/userIndex/update/updateUserIndexParams.g.dart b/lib/api/mhsl/server/user_index/update/update_user_index_params.g.dart similarity index 95% rename from lib/api/mhsl/server/userIndex/update/updateUserIndexParams.g.dart rename to lib/api/mhsl/server/user_index/update/update_user_index_params.g.dart index 285302a..4d8775a 100644 --- a/lib/api/mhsl/server/userIndex/update/updateUserIndexParams.g.dart +++ b/lib/api/mhsl/server/user_index/update/update_user_index_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'updateUserIndexParams.dart'; +part of 'update_user_index_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/mhsl/server/userIndex/update/updateUserindex.dart b/lib/api/mhsl/server/user_index/update/update_userindex.dart similarity index 88% rename from lib/api/mhsl/server/userIndex/update/updateUserindex.dart rename to lib/api/mhsl/server/user_index/update/update_userindex.dart index c020fe3..9b728b6 100644 --- a/lib/api/mhsl/server/userIndex/update/updateUserindex.dart +++ b/lib/api/mhsl/server/user_index/update/update_userindex.dart @@ -1,4 +1,5 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:developer'; @@ -7,8 +8,8 @@ import 'package:http/http.dart' as http; import 'package:package_info_plus/package_info_plus.dart'; import '../../../../../model/account_data.dart'; -import '../../../mhslApi.dart'; -import 'updateUserIndexParams.dart'; +import '../../../mhsl_api.dart'; +import 'update_user_index_params.dart'; class UpdateUserIndex extends MhslApi { UpdateUserIndexParams params; @@ -25,7 +26,7 @@ class UpdateUserIndex extends MhslApi { } static Future index() async { - UpdateUserIndex( + unawaited(UpdateUserIndex( UpdateUserIndexParams( username: AccountData().getUsername(), user: AccountData().getUserSecret(), @@ -33,6 +34,6 @@ class UpdateUserIndex extends MhslApi { appVersion: int.parse((await PackageInfo.fromPlatform()).buildNumber), deviceInfo: jsonEncode((await DeviceInfoPlugin().deviceInfo).data).toString(), ), - ).run(); + ).run()); } } diff --git a/lib/api/requestCache.dart b/lib/api/request_cache.dart similarity index 89% rename from lib/api/requestCache.dart rename to lib/api/request_cache.dart index df88c25..8d6fdb6 100644 --- a/lib/api/requestCache.dart +++ b/lib/api/request_cache.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:localstore/localstore.dart'; -import 'apiResponse.dart'; +import 'api_response.dart'; abstract class RequestCache { static const int cacheNothing = 0; @@ -50,12 +50,13 @@ abstract class RequestCache { try { final tableData = await Localstore.instance.collection(collection).doc(document).get(); if (tableData != null) { - final cached = onLocalData(tableData['json']); + final cached = onLocalData(tableData['json'] as String); onUpdate?.call(cached); onCacheData?.call(cached); } - if (DateTime.now().millisecondsSinceEpoch - (maxCacheTime * 1000) < (tableData?['lastupdate'] ?? 0)) { + final lastUpdate = (tableData?['lastupdate'] as num?) ?? 0; + if (DateTime.now().millisecondsSinceEpoch - (maxCacheTime * 1000) < lastUpdate) { if (renew == null || !renew!) return; } @@ -63,10 +64,10 @@ abstract class RequestCache { final newValue = await onLoad(); onUpdate?.call(newValue); onNetworkData?.call(newValue); - Localstore.instance.collection(collection).doc(document).set({ + unawaited(Localstore.instance.collection(collection).doc(document).set({ 'json': jsonEncode(newValue), 'lastupdate': DateTime.now().millisecondsSinceEpoch, - }); + })); } on Exception catch (e) { onError(e); } @@ -112,5 +113,5 @@ class SimpleCache extends RequestCache { Future onLoad() => _loader(); @override - T onLocalData(String json) => _fromJson(jsonDecode(json)); + T onLocalData(String json) => _fromJson(jsonDecode(json) as Map); } diff --git a/lib/api/webuntis/queries/authenticate/authenticate.dart b/lib/api/webuntis/queries/authenticate/authenticate.dart index 5f49dc2..551d815 100644 --- a/lib/api/webuntis/queries/authenticate/authenticate.dart +++ b/lib/api/webuntis/queries/authenticate/authenticate.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'dart:convert'; import '../../../../model/account_data.dart'; -import '../../webuntisApi.dart'; -import 'authenticateParams.dart'; -import 'authenticateResponse.dart'; +import '../../webuntis_api.dart'; +import 'authenticate_params.dart'; +import 'authenticate_response.dart'; class Authenticate extends WebuntisApi { AuthenticateParams param; @@ -15,18 +15,19 @@ class Authenticate extends WebuntisApi { Future run() async { awaitingResponse = true; try { - var rawAnswer = await query(this); - AuthenticateResponse response = finalize(AuthenticateResponse.fromJson(jsonDecode(rawAnswer)['result'])); + final rawAnswer = await query(this); + final decoded = jsonDecode(rawAnswer) as Map; + final response = finalize(AuthenticateResponse.fromJson(decoded['result'] as Map)); _lastResponse = response; - if(!awaitedResponse.isCompleted) awaitedResponse.complete(); + if (!awaitedResponse.isCompleted) awaitedResponse.complete(); return response; } catch (e) { // Surface the error to anyone waiting on the current completer, then // install a fresh one so a future attempt can succeed. Without this, // any later call to getSession() would hang forever on a completer // that is already settled with no listeners (or never settles at all). - if(!awaitedResponse.isCompleted) awaitedResponse.completeError(e); - awaitedResponse = Completer(); + if (!awaitedResponse.isCompleted) awaitedResponse.completeError(e); + awaitedResponse = Completer(); rethrow; } finally { awaitingResponse = false; @@ -34,7 +35,7 @@ class Authenticate extends WebuntisApi { } static bool awaitingResponse = false; - static Completer awaitedResponse = Completer(); + static Completer awaitedResponse = Completer(); static AuthenticateResponse? _lastResponse; static Future createSession() async { diff --git a/lib/api/webuntis/queries/authenticate/authenticateParams.dart b/lib/api/webuntis/queries/authenticate/authenticate_params.dart similarity index 85% rename from lib/api/webuntis/queries/authenticate/authenticateParams.dart rename to lib/api/webuntis/queries/authenticate/authenticate_params.dart index bfa65e6..bf3b23e 100644 --- a/lib/api/webuntis/queries/authenticate/authenticateParams.dart +++ b/lib/api/webuntis/queries/authenticate/authenticate_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'authenticateParams.g.dart'; +part 'authenticate_params.g.dart'; @JsonSerializable() class AuthenticateParams extends ApiParams { diff --git a/lib/api/webuntis/queries/authenticate/authenticateParams.g.dart b/lib/api/webuntis/queries/authenticate/authenticate_params.g.dart similarity index 94% rename from lib/api/webuntis/queries/authenticate/authenticateParams.g.dart rename to lib/api/webuntis/queries/authenticate/authenticate_params.g.dart index ba8b65e..9765ad8 100644 --- a/lib/api/webuntis/queries/authenticate/authenticateParams.g.dart +++ b/lib/api/webuntis/queries/authenticate/authenticate_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'authenticateParams.dart'; +part of 'authenticate_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/authenticate/authenticateResponse.dart b/lib/api/webuntis/queries/authenticate/authenticate_response.dart similarity index 86% rename from lib/api/webuntis/queries/authenticate/authenticateResponse.dart rename to lib/api/webuntis/queries/authenticate/authenticate_response.dart index 509b1dc..0ca87db 100644 --- a/lib/api/webuntis/queries/authenticate/authenticateResponse.dart +++ b/lib/api/webuntis/queries/authenticate/authenticate_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'authenticateResponse.g.dart'; +part 'authenticate_response.g.dart'; @JsonSerializable() class AuthenticateResponse extends ApiResponse { diff --git a/lib/api/webuntis/queries/authenticate/authenticateResponse.g.dart b/lib/api/webuntis/queries/authenticate/authenticate_response.g.dart similarity index 96% rename from lib/api/webuntis/queries/authenticate/authenticateResponse.g.dart rename to lib/api/webuntis/queries/authenticate/authenticate_response.g.dart index c3ca62a..e7d7dd0 100644 --- a/lib/api/webuntis/queries/authenticate/authenticateResponse.g.dart +++ b/lib/api/webuntis/queries/authenticate/authenticate_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'authenticateResponse.dart'; +part of 'authenticate_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/getHolidays/getHolidays.dart b/lib/api/webuntis/queries/get_holidays/get_holidays.dart similarity index 82% rename from lib/api/webuntis/queries/getHolidays/getHolidays.dart rename to lib/api/webuntis/queries/get_holidays/get_holidays.dart index 145cb6e..68031ec 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidays.dart +++ b/lib/api/webuntis/queries/get_holidays/get_holidays.dart @@ -1,15 +1,15 @@ import 'dart:convert'; -import '../../webuntisApi.dart'; -import 'getHolidaysResponse.dart'; +import '../../webuntis_api.dart'; +import 'get_holidays_response.dart'; class GetHolidays extends WebuntisApi { GetHolidays() : super('getHolidays', null); @override Future run() async { - var rawAnswer = await query(this); - return finalize(GetHolidaysResponse.fromJson(jsonDecode(rawAnswer))); + final rawAnswer = await query(this); + return finalize(GetHolidaysResponse.fromJson(jsonDecode(rawAnswer) as Map)); } static GetHolidaysResponseObject? find(GetHolidaysResponse holidaysResponse, {DateTime? time}) { diff --git a/lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart b/lib/api/webuntis/queries/get_holidays/get_holidays_cache.dart similarity index 76% rename from lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart rename to lib/api/webuntis/queries/get_holidays/get_holidays_cache.dart index d6a2ff4..a974eb1 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart +++ b/lib/api/webuntis/queries/get_holidays/get_holidays_cache.dart @@ -1,6 +1,6 @@ -import '../../../requestCache.dart'; -import 'getHolidays.dart'; -import 'getHolidaysResponse.dart'; +import '../../../request_cache.dart'; +import 'get_holidays.dart'; +import 'get_holidays_response.dart'; class GetHolidaysCache extends SimpleCache { GetHolidaysCache({super.onUpdate, super.onError, super.renew}) diff --git a/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart b/lib/api/webuntis/queries/get_holidays/get_holidays_response.dart similarity index 91% rename from lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart rename to lib/api/webuntis/queries/get_holidays/get_holidays_response.dart index f087c4a..8fa2624 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart +++ b/lib/api/webuntis/queries/get_holidays/get_holidays_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getHolidaysResponse.g.dart'; +part 'get_holidays_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetHolidaysResponse extends ApiResponse { diff --git a/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.g.dart b/lib/api/webuntis/queries/get_holidays/get_holidays_response.g.dart similarity index 97% rename from lib/api/webuntis/queries/getHolidays/getHolidaysResponse.g.dart rename to lib/api/webuntis/queries/get_holidays/get_holidays_response.g.dart index 323b3fb..e373642 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.g.dart +++ b/lib/api/webuntis/queries/get_holidays/get_holidays_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getHolidaysResponse.dart'; +part of 'get_holidays_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/getRooms/getRooms.dart b/lib/api/webuntis/queries/get_rooms/get_rooms.dart similarity index 72% rename from lib/api/webuntis/queries/getRooms/getRooms.dart rename to lib/api/webuntis/queries/get_rooms/get_rooms.dart index 45c53c5..4b7bf86 100644 --- a/lib/api/webuntis/queries/getRooms/getRooms.dart +++ b/lib/api/webuntis/queries/get_rooms/get_rooms.dart @@ -1,18 +1,18 @@ import 'dart:convert'; import 'dart:developer'; -import '../../webuntisApi.dart'; -import 'getRoomsResponse.dart'; +import '../../webuntis_api.dart'; +import 'get_rooms_response.dart'; class GetRooms extends WebuntisApi { GetRooms() : super('getRooms', null); @override Future run() async { - var rawAnswer = await query(this); + final rawAnswer = await query(this); try { - return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer))); - } catch(e, trace) { + return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer) as Map)); + } catch (e, trace) { log(trace.toString()); log('Failed to parse getRoom data with server response: $rawAnswer'); } diff --git a/lib/api/webuntis/queries/getRooms/getRoomsCache.dart b/lib/api/webuntis/queries/get_rooms/get_rooms_cache.dart similarity index 76% rename from lib/api/webuntis/queries/getRooms/getRoomsCache.dart rename to lib/api/webuntis/queries/get_rooms/get_rooms_cache.dart index 4f8e064..a07a449 100644 --- a/lib/api/webuntis/queries/getRooms/getRoomsCache.dart +++ b/lib/api/webuntis/queries/get_rooms/get_rooms_cache.dart @@ -1,6 +1,6 @@ -import '../../../requestCache.dart'; -import 'getRooms.dart'; -import 'getRoomsResponse.dart'; +import '../../../request_cache.dart'; +import 'get_rooms.dart'; +import 'get_rooms_response.dart'; class GetRoomsCache extends SimpleCache { GetRoomsCache({super.onUpdate, super.onError, super.renew}) diff --git a/lib/api/webuntis/queries/getRooms/getRoomsResponse.dart b/lib/api/webuntis/queries/get_rooms/get_rooms_response.dart similarity index 91% rename from lib/api/webuntis/queries/getRooms/getRoomsResponse.dart rename to lib/api/webuntis/queries/get_rooms/get_rooms_response.dart index fe4dc84..614406d 100644 --- a/lib/api/webuntis/queries/getRooms/getRoomsResponse.dart +++ b/lib/api/webuntis/queries/get_rooms/get_rooms_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getRoomsResponse.g.dart'; +part 'get_rooms_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetRoomsResponse extends ApiResponse { diff --git a/lib/api/webuntis/queries/getRooms/getRoomsResponse.g.dart b/lib/api/webuntis/queries/get_rooms/get_rooms_response.g.dart similarity index 97% rename from lib/api/webuntis/queries/getRooms/getRoomsResponse.g.dart rename to lib/api/webuntis/queries/get_rooms/get_rooms_response.g.dart index a88ade3..2bf9998 100644 --- a/lib/api/webuntis/queries/getRooms/getRoomsResponse.g.dart +++ b/lib/api/webuntis/queries/get_rooms/get_rooms_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getRoomsResponse.dart'; +part of 'get_rooms_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/getSubjects/getSubjects.dart b/lib/api/webuntis/queries/get_subjects/get_subjects.dart similarity index 61% rename from lib/api/webuntis/queries/getSubjects/getSubjects.dart rename to lib/api/webuntis/queries/get_subjects/get_subjects.dart index 8505381..75a4f1b 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjects.dart +++ b/lib/api/webuntis/queries/get_subjects/get_subjects.dart @@ -1,14 +1,14 @@ import 'dart:convert'; -import '../../webuntisApi.dart'; -import 'getSubjectsResponse.dart'; +import '../../webuntis_api.dart'; +import 'get_subjects_response.dart'; class GetSubjects extends WebuntisApi { GetSubjects() : super('getSubjects', null); @override Future run() async { - var rawAnswer = await query(this); - return finalize(GetSubjectsResponse.fromJson(jsonDecode(rawAnswer))); + final rawAnswer = await query(this); + return finalize(GetSubjectsResponse.fromJson(jsonDecode(rawAnswer) as Map)); } } diff --git a/lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart b/lib/api/webuntis/queries/get_subjects/get_subjects_cache.dart similarity index 76% rename from lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart rename to lib/api/webuntis/queries/get_subjects/get_subjects_cache.dart index 5eeb8d3..c513054 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart +++ b/lib/api/webuntis/queries/get_subjects/get_subjects_cache.dart @@ -1,6 +1,6 @@ -import '../../../requestCache.dart'; -import 'getSubjects.dart'; -import 'getSubjectsResponse.dart'; +import '../../../request_cache.dart'; +import 'get_subjects.dart'; +import 'get_subjects_response.dart'; class GetSubjectsCache extends SimpleCache { GetSubjectsCache({super.onUpdate, super.onError, super.renew}) diff --git a/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart b/lib/api/webuntis/queries/get_subjects/get_subjects_response.dart similarity index 92% rename from lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart rename to lib/api/webuntis/queries/get_subjects/get_subjects_response.dart index cfd2cf1..255b5ad 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart +++ b/lib/api/webuntis/queries/get_subjects/get_subjects_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getSubjectsResponse.g.dart'; +part 'get_subjects_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetSubjectsResponse extends ApiResponse { diff --git a/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.g.dart b/lib/api/webuntis/queries/get_subjects/get_subjects_response.g.dart similarity index 97% rename from lib/api/webuntis/queries/getSubjects/getSubjectsResponse.g.dart rename to lib/api/webuntis/queries/get_subjects/get_subjects_response.g.dart index 35bbb8b..9ce84d5 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.g.dart +++ b/lib/api/webuntis/queries/get_subjects/get_subjects_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getSubjectsResponse.dart'; +part of 'get_subjects_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnits.dart b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units.dart similarity index 76% rename from lib/api/webuntis/queries/getTimegridUnits/getTimegridUnits.dart rename to lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units.dart index 0e9c38f..9f910e1 100644 --- a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnits.dart +++ b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units.dart @@ -1,17 +1,17 @@ import 'dart:convert'; import 'dart:developer'; -import '../../webuntisApi.dart'; -import 'getTimegridUnitsResponse.dart'; +import '../../webuntis_api.dart'; +import 'get_timegrid_units_response.dart'; class GetTimegridUnits extends WebuntisApi { GetTimegridUnits() : super('getTimegridUnits', null); @override Future run() async { - var rawAnswer = await query(this); + final rawAnswer = await query(this); try { - return finalize(GetTimegridUnitsResponse.fromJson(jsonDecode(rawAnswer))); + return finalize(GetTimegridUnitsResponse.fromJson(jsonDecode(rawAnswer) as Map)); } catch (e, trace) { log(trace.toString()); log('Failed to parse getTimegridUnits data with server response: $rawAnswer'); diff --git a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsCache.dart b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_cache.dart similarity index 74% rename from lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsCache.dart rename to lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_cache.dart index 200aa9c..811ed86 100644 --- a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsCache.dart +++ b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_cache.dart @@ -1,6 +1,6 @@ -import '../../../requestCache.dart'; -import 'getTimegridUnits.dart'; -import 'getTimegridUnitsResponse.dart'; +import '../../../request_cache.dart'; +import 'get_timegrid_units.dart'; +import 'get_timegrid_units_response.dart'; class GetTimegridUnitsCache extends SimpleCache { GetTimegridUnitsCache({super.onUpdate, super.renew}) diff --git a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.dart similarity index 93% rename from lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart rename to lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.dart index a730567..5b458aa 100644 --- a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart +++ b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getTimegridUnitsResponse.g.dart'; +part 'get_timegrid_units_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetTimegridUnitsResponse extends ApiResponse { diff --git a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.g.dart b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.g.dart similarity index 97% rename from lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.g.dart rename to lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.g.dart index b6fc909..250b0fd 100644 --- a/lib/api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.g.dart +++ b/lib/api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getTimegridUnitsResponse.dart'; +part of 'get_timegrid_units_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/getTimetable/getTimetable.dart b/lib/api/webuntis/queries/get_timetable/get_timetable.dart similarity index 60% rename from lib/api/webuntis/queries/getTimetable/getTimetable.dart rename to lib/api/webuntis/queries/get_timetable/get_timetable.dart index e9da26d..d451d3c 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetable.dart +++ b/lib/api/webuntis/queries/get_timetable/get_timetable.dart @@ -1,8 +1,8 @@ import 'dart:convert'; -import '../../webuntisApi.dart'; -import 'getTimetableParams.dart'; -import 'getTimetableResponse.dart'; +import '../../webuntis_api.dart'; +import 'get_timetable_params.dart'; +import 'get_timetable_response.dart'; class GetTimetable extends WebuntisApi { GetTimetableParams params; @@ -11,8 +11,8 @@ class GetTimetable extends WebuntisApi { @override Future run() async { - var rawAnswer = await query(this); - return finalize(GetTimetableResponse.fromJson(jsonDecode(rawAnswer))); + final rawAnswer = await query(this); + return finalize(GetTimetableResponse.fromJson(jsonDecode(rawAnswer) as Map)); } } diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart b/lib/api/webuntis/queries/get_timetable/get_timetable_cache.dart similarity index 90% rename from lib/api/webuntis/queries/getTimetable/getTimetableCache.dart rename to lib/api/webuntis/queries/get_timetable/get_timetable_cache.dart index 8a8cd7e..56a73c9 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart +++ b/lib/api/webuntis/queries/get_timetable/get_timetable_cache.dart @@ -1,8 +1,8 @@ -import '../../../requestCache.dart'; +import '../../../request_cache.dart'; import '../authenticate/authenticate.dart'; -import 'getTimetable.dart'; -import 'getTimetableParams.dart'; -import 'getTimetableResponse.dart'; +import 'get_timetable.dart'; +import 'get_timetable_params.dart'; +import 'get_timetable_response.dart'; class GetTimetableCache extends SimpleCache { GetTimetableCache({ diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableParams.dart b/lib/api/webuntis/queries/get_timetable/get_timetable_params.dart similarity index 97% rename from lib/api/webuntis/queries/getTimetable/getTimetableParams.dart rename to lib/api/webuntis/queries/get_timetable/get_timetable_params.dart index 48ba379..9286863 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableParams.dart +++ b/lib/api/webuntis/queries/get_timetable/get_timetable_params.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiParams.dart'; +import '../../../api_params.dart'; -part 'getTimetableParams.g.dart'; +part 'get_timetable_params.g.dart'; @JsonSerializable(explicitToJson: true) class GetTimetableParams extends ApiParams { diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart b/lib/api/webuntis/queries/get_timetable/get_timetable_params.g.dart similarity index 99% rename from lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart rename to lib/api/webuntis/queries/get_timetable/get_timetable_params.g.dart index 92e2a1d..2d7ff35 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart +++ b/lib/api/webuntis/queries/get_timetable/get_timetable_params.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getTimetableParams.dart'; +part of 'get_timetable_params.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart b/lib/api/webuntis/queries/get_timetable/get_timetable_response.dart similarity index 98% rename from lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart rename to lib/api/webuntis/queries/get_timetable/get_timetable_response.dart index fc6663c..05e1ea1 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart +++ b/lib/api/webuntis/queries/get_timetable/get_timetable_response.dart @@ -1,8 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; -import '../../../apiResponse.dart'; +import '../../../api_response.dart'; -part 'getTimetableResponse.g.dart'; +part 'get_timetable_response.g.dart'; @JsonSerializable(explicitToJson: true) class GetTimetableResponse extends ApiResponse { diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableResponse.g.dart b/lib/api/webuntis/queries/get_timetable/get_timetable_response.g.dart similarity index 99% rename from lib/api/webuntis/queries/getTimetable/getTimetableResponse.g.dart rename to lib/api/webuntis/queries/get_timetable/get_timetable_response.g.dart index effce44..9a3b20b 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableResponse.g.dart +++ b/lib/api/webuntis/queries/get_timetable/get_timetable_response.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'getTimetableResponse.dart'; +part of 'get_timetable_response.dart'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/api/webuntis/webuntisApi.dart b/lib/api/webuntis/webuntis_api.dart similarity index 68% rename from lib/api/webuntis/webuntisApi.dart rename to lib/api/webuntis/webuntis_api.dart index 846c1e6..690bf94 100644 --- a/lib/api/webuntis/webuntisApi.dart +++ b/lib/api/webuntis/webuntis_api.dart @@ -5,13 +5,13 @@ import 'dart:io'; import 'package:http/http.dart' as http; import '../../model/endpoint_data.dart'; -import '../apiParams.dart'; -import '../apiRequest.dart'; -import '../apiResponse.dart'; +import '../api_params.dart'; +import '../api_request.dart'; +import '../api_response.dart'; import '../errors/network_exception.dart'; import '../errors/parse_exception.dart'; import 'queries/authenticate/authenticate.dart'; -import 'webuntisError.dart'; +import 'webuntis_error.dart'; abstract class WebuntisApi extends ApiRequest { Uri endpoint = Uri.parse('https://${EndpointData().webuntis().full()}/WebUntis/jsonrpc.do?school=marianum-fulda'); @@ -25,34 +25,36 @@ abstract class WebuntisApi extends ApiRequest { Future query(WebuntisApi untis, {bool retry = false}) async { - var query = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}'; + final body = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}'; var sessionId = '0'; - if(authenticatedResponse) { + if (authenticatedResponse) { sessionId = (await Authenticate.getSession()).sessionId; } - var data = await post(query, {'Cookie': 'JSESSIONID=$sessionId'}); + final data = await post(body, {'Cookie': 'JSESSIONID=$sessionId'}); response = data; - final dynamic jsonData; + final Map jsonData; try { - jsonData = jsonDecode(data.body); + jsonData = jsonDecode(data.body) as Map; } on FormatException catch (e) { throw ParseException(technicalDetails: 'WebUntis JSON decode: ${e.message}'); } - if(jsonData['error'] != null) { - if(jsonData['error']['code'] == -8520) { - if(retry) throw WebuntisError('Authentication was tried (probably session timeout), but was not successful!', -8520); + final error = jsonData['error'] as Map?; + if (error != null) { + final code = error['code'] as int; + if (code == -8520) { + if (retry) throw WebuntisError('Authentication was tried (probably session timeout), but was not successful!', -8520); await Authenticate.createSession(); - return await this.query(untis, retry: true); + return query(untis, retry: true); } else { - throw WebuntisError(jsonData['error']['message'], jsonData['error']['code']); + throw WebuntisError(error['message'] as String, code); } } return data.body; } - dynamic finalize(dynamic response) { + T finalize(T response) { response.rawResponse = this.response!; return response; } diff --git a/lib/api/webuntis/webuntisError.dart b/lib/api/webuntis/webuntis_error.dart similarity index 100% rename from lib/api/webuntis/webuntisError.dart rename to lib/api/webuntis/webuntis_error.dart diff --git a/lib/app.dart b/lib/app.dart index e483fb6..5ac8709 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,25 +1,25 @@ import 'dart:async'; import 'dart:developer'; -import 'package:easy_debounce/easy_throttle.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; -import 'api/mhsl/server/userIndex/update/updateUserindex.dart'; +import 'api/mhsl/breaker/get_breakers/get_breakers_response.dart'; +import 'api/mhsl/server/user_index/update/update_userindex.dart'; import 'main.dart'; -import 'widget/breaker/breaker.dart'; import 'model/data_cleaner.dart'; import 'notification/notification_controller.dart'; import 'notification/notification_tasks.dart'; import 'notification/notify_updater.dart'; import 'state/app/modules/app_modules.dart'; import 'state/app/modules/breaker/bloc/breaker_bloc.dart'; -import 'state/app/modules/chatList/bloc/chat_list_bloc.dart'; +import 'state/app/modules/chat_list/bloc/chat_list_bloc.dart'; import 'state/app/modules/settings/bloc/settings_cubit.dart'; +import 'utils/debouncer.dart'; import 'view/pages/overhang.dart'; +import 'widget/breaker/breaker.dart'; class App extends StatefulWidget { const App({super.key}); @@ -36,7 +36,7 @@ class _AppState extends State with WidgetsBindingObserver { void didChangeAppLifecycleState(AppLifecycleState state) { log('AppLifecycle: $state'); if (state == AppLifecycleState.resumed) { - EasyThrottle.throttle('appLifecycleState', const Duration(seconds: 10), () { + Debouncer.throttle('appLifecycleState', const Duration(seconds: 10), () { if (!mounted) return; log('Refreshing due to LifecycleChange'); NotificationTasks.updateProviders(context); diff --git a/lib/main.dart b/lib/main.dart index 411a05c..a8f88c7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,16 +16,15 @@ import 'package:loader_overlay/loader_overlay.dart'; import 'package:path_provider/path_provider.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; +import 'api/mhsl/breaker/get_breakers/get_breakers_response.dart'; import 'app.dart'; import 'firebase_options.dart'; import 'model/account_data.dart'; -import 'widget/breaker/breaker.dart'; import 'state/app/modules/account/bloc/account_bloc.dart'; import 'state/app/modules/account/bloc/account_state.dart'; import 'state/app/modules/breaker/bloc/breaker_bloc.dart'; import 'state/app/modules/chat/bloc/chat_bloc.dart'; -import 'state/app/modules/chatList/bloc/chat_list_bloc.dart'; +import 'state/app/modules/chat_list/bloc/chat_list_bloc.dart'; import 'state/app/modules/settings/bloc/settings_cubit.dart'; import 'state/app/modules/timetable/bloc/timetable_bloc.dart'; import 'storage/settings.dart'; @@ -33,6 +32,7 @@ import 'theming/dark_app_theme.dart'; import 'theming/light_app_theme.dart'; import 'view/login/login.dart'; import 'widget/app_progress_indicator.dart'; +import 'widget/breaker/breaker.dart'; Future main() async { log('MarianumMobile started'); diff --git a/lib/model/account_data.dart b/lib/model/account_data.dart index 66db8c8..712301a 100644 --- a/lib/model/account_data.dart +++ b/lib/model/account_data.dart @@ -15,9 +15,7 @@ class AccountData { static const _usernameField = 'username'; static const _passwordField = 'password'; - static const FlutterSecureStorage _secureStorage = FlutterSecureStorage( - aOptions: AndroidOptions(encryptedSharedPreferences: true), - ); + static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(); static final AccountData _instance = AccountData._construct(); Completer _populated = Completer(); diff --git a/lib/model/data_cleaner.dart b/lib/model/data_cleaner.dart index f24fb04..4094023 100644 --- a/lib/model/data_cleaner.dart +++ b/lib/model/data_cleaner.dart @@ -1,13 +1,13 @@ import 'package:localstore/localstore.dart'; -import '../api/requestCache.dart'; +import '../api/request_cache.dart'; class DataCleaner { static Future cleanOldCache() async { - var cacheData = await Localstore.instance.collection(RequestCache.collection).get(); + final cacheData = await Localstore.instance.collection(RequestCache.collection).get(); cacheData?.forEach((key, value) async { - var lastUpdate = DateTime.fromMillisecondsSinceEpoch(value['lastupdate']); - if(DateTime.now().subtract(const Duration(days: 200)).isAfter(lastUpdate)) { + final lastUpdate = DateTime.fromMillisecondsSinceEpoch(value['lastupdate'] as int); + if (DateTime.now().subtract(const Duration(days: 200)).isAfter(lastUpdate)) { await Localstore.instance.collection(RequestCache.collection).doc(key.split('/').last).delete(); } }); diff --git a/lib/notification/notification_controller.dart b/lib/notification/notification_controller.dart index 0a1631f..a9de28b 100644 --- a/lib/notification/notification_controller.dart +++ b/lib/notification/notification_controller.dart @@ -6,36 +6,10 @@ import '../widget/debug/json_viewer.dart'; import 'notification_tasks.dart'; class NotificationController { + // Notification display is handled by the Firebase SDK using server-generated payloads. @pragma('vm:entry-point') static Future onBackgroundMessageHandler(RemoteMessage message) async { NotificationTasks.updateBadgeCount(message); - return; // Displaying the notification is curently done via the Firebase SDK itself. The Message is server-generated. - - // - // await Firebase.initializeApp(); - // AccountData().waitForPopulation().then((value) { - // log("User account status: $value"); - // if(value) { - // GetRoom( - // GetRoomParams( - // includeStatus: false, - // ), - // ).run().then((value) { - // var messageCount = value.data.map((e) => e.unreadMessages).reduce((a, b) => a + b); - // var chatCount = value.data.where((e) => e.unreadMessages > 0).length; - // var people = value.data.where((e) => e.unreadMessages > 0).map((e) => e.displayName.split(" ")[0]); - // - // final NotificationService service = NotificationService(); - // service.initializeNotifications().then((value) { - // service.showNotification( - // title: "Du hast $messageCount ungelesene Nachrichten!", - // body: "In $chatCount Chats, von ${people.join(", ")}", - // badgeCount: messageCount, - // ); - // }); - // }); - // } - // }); } static Future onForegroundMessageHandler(RemoteMessage message, BuildContext context) async { diff --git a/lib/notification/notification_tasks.dart b/lib/notification/notification_tasks.dart index e5e43ff..42c310e 100644 --- a/lib/notification/notification_tasks.dart +++ b/lib/notification/notification_tasks.dart @@ -5,11 +5,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../routing/app_routes.dart'; import '../state/app/modules/chat/bloc/chat_bloc.dart'; -import '../state/app/modules/chatList/bloc/chat_list_bloc.dart'; +import '../state/app/modules/chat_list/bloc/chat_list_bloc.dart'; class NotificationTasks { static void updateBadgeCount(RemoteMessage notification) { - FlutterAppBadge.count(int.parse(notification.data['unreadCount'] ?? '0')); + FlutterAppBadge.count(int.parse((notification.data['unreadCount'] as String?) ?? '0')); } static void updateProviders(BuildContext context) { diff --git a/lib/notification/notify_updater.dart b/lib/notification/notify_updater.dart index 584b9f7..dc3ae15 100644 --- a/lib/notification/notify_updater.dart +++ b/lib/notification/notify_updater.dart @@ -1,8 +1,10 @@ +import 'dart:async'; + import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; -import '../api/mhsl/notify/register/notifyRegister.dart'; -import '../api/mhsl/notify/register/notifyRegisterParams.dart'; +import '../api/mhsl/notify/register/notify_register.dart'; +import '../api/mhsl/notify/register/notify_register_params.dart'; import '../model/account_data.dart'; import '../state/app/modules/settings/bloc/settings_cubit.dart'; import '../widget/confirm_dialog.dart'; @@ -17,9 +19,9 @@ class NotifyUpdater { 'Für mehr Informationen drücke lange auf die Einstellungsoption!', confirmButton: 'Aktivieren', onConfirm: () { - FirebaseMessaging.instance.requestPermission(provisional: false); + unawaited(FirebaseMessaging.instance.requestPermission(provisional: false)); settings.val(write: true).notificationSettings.enabled = true; - NotifyUpdater.registerToServer(); + unawaited(NotifyUpdater.registerToServer()); }, ); @@ -29,12 +31,12 @@ class NotifyUpdater { throw Exception('Failed to register push notification because there is no FBC token!'); } - NotifyRegister( + unawaited(NotifyRegister( NotifyRegisterParams( username: AccountData().getUsername(), password: AccountData().getPassword(), fcmToken: fcmToken, ), - ).run(); + ).run()); } } diff --git a/lib/routing/app_routes.dart b/lib/routing/app_routes.dart index 94b85d9..082d5ac 100644 --- a/lib/routing/app_routes.dart +++ b/lib/routing/app_routes.dart @@ -3,23 +3,23 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import '../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../api/marianumcloud/talk/room/get_room_response.dart'; import '../main.dart'; import '../model/account_data.dart'; import '../state/app/modules/app_modules.dart'; -import '../state/app/modules/chatList/bloc/chat_list_bloc.dart'; import '../state/app/modules/chat/bloc/chat_bloc.dart'; -import '../state/app/modules/marianumMessage/bloc/marianum_message_state.dart'; +import '../state/app/modules/chat_list/bloc/chat_list_bloc.dart'; +import '../state/app/modules/marianum_message/bloc/marianum_message_state.dart'; import '../view/pages/files/files.dart'; import '../view/pages/marianum_message/marianum_message_view.dart'; import '../view/pages/more/feedback/feedback_dialog.dart'; import '../view/pages/more/roomplan/roomplan.dart'; import '../view/pages/more/share/qr_share_view.dart'; +import '../view/pages/settings/settings.dart'; import '../view/pages/talk/chat_view.dart'; import '../view/pages/talk/details/message_reactions.dart'; import '../view/pages/talk/talk_navigator.dart'; import '../view/pages/timetable/custom_events/custom_events_view.dart'; -import '../view/pages/settings/settings.dart'; import '../widget/debug/cache_view.dart'; import '../widget/file_viewer.dart'; import '../widget/user_avatar.dart'; diff --git a/lib/state/app/basis/dataloader/holiday_data_loader.dart b/lib/state/app/basis/dataloader/holiday_data_loader.dart index 19c345f..e384f00 100644 --- a/lib/state/app/basis/dataloader/holiday_data_loader.dart +++ b/lib/state/app/basis/dataloader/holiday_data_loader.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; -import '../../infrastructure/dataLoader/data_loader.dart'; +import '../../infrastructure/data_loader/data_loader.dart'; abstract class HolidayDataLoader extends DataLoader { HolidayDataLoader() : super(Dio(BaseOptions( diff --git a/lib/state/app/basis/dataloader/mhsl_data_loader.dart b/lib/state/app/basis/dataloader/mhsl_data_loader.dart index 6b4baab..fa29cf4 100644 --- a/lib/state/app/basis/dataloader/mhsl_data_loader.dart +++ b/lib/state/app/basis/dataloader/mhsl_data_loader.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; -import '../../infrastructure/dataLoader/data_loader.dart'; +import '../../infrastructure/data_loader/data_loader.dart'; abstract class MhslDataLoader extends DataLoader { MhslDataLoader() : super(Dio(BaseOptions( diff --git a/lib/state/app/infrastructure/dataLoader/data_loader.dart b/lib/state/app/infrastructure/data_loader/data_loader.dart similarity index 81% rename from lib/state/app/infrastructure/dataLoader/data_loader.dart rename to lib/state/app/infrastructure/data_loader/data_loader.dart index 64c1aa7..ceec932 100644 --- a/lib/state/app/infrastructure/dataLoader/data_loader.dart +++ b/lib/state/app/infrastructure/data_loader/data_loader.dart @@ -6,9 +6,9 @@ import 'package:dio/dio.dart'; abstract class DataLoader { final Dio dio; DataLoader(this.dio) { - dio.options.connectTimeout = const Duration(seconds: 10).inMilliseconds; - dio.options.sendTimeout = const Duration(seconds: 30).inMilliseconds; - dio.options.receiveTimeout = const Duration(seconds: 30).inMilliseconds; + dio.options.connectTimeout = const Duration(seconds: 10); + dio.options.sendTimeout = const Duration(seconds: 30); + dio.options.receiveTimeout = const Duration(seconds: 30); } Future run() async { @@ -26,7 +26,7 @@ abstract class DataLoader { )); } catch(trace, e) { log(trace.toString()); - throw(e); + throw e; } } diff --git a/lib/state/app/infrastructure/loadableState/bloc/loadable_state_bloc.dart b/lib/state/app/infrastructure/loadable_state/bloc/loadable_state_bloc.dart similarity index 91% rename from lib/state/app/infrastructure/loadableState/bloc/loadable_state_bloc.dart rename to lib/state/app/infrastructure/loadable_state/bloc/loadable_state_bloc.dart index ef0e167..625e1dd 100644 --- a/lib/state/app/infrastructure/loadableState/bloc/loadable_state_bloc.dart +++ b/lib/state/app/infrastructure/loadable_state/bloc/loadable_state_bloc.dart @@ -1,8 +1,8 @@ import 'dart:async'; -import 'package:bloc/bloc.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jiffy/jiffy.dart'; import 'loadable_state_event.dart'; @@ -21,7 +21,7 @@ class LoadableStateBloc extends Bloc { } }); - emitConnectivity(List result) => add(ConnectivityChanged(LoadableStateState(connections: result))); + void emitConnectivity(List result) => add(ConnectivityChanged(LoadableStateState(connections: result))); Connectivity().checkConnectivity().then(emitConnectivity); _updateStream = Connectivity().onConnectivityChanged.listen(emitConnectivity); diff --git a/lib/state/app/infrastructure/loadableState/bloc/loadable_state_event.dart b/lib/state/app/infrastructure/loadable_state/bloc/loadable_state_event.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/bloc/loadable_state_event.dart rename to lib/state/app/infrastructure/loadable_state/bloc/loadable_state_event.dart diff --git a/lib/state/app/infrastructure/loadableState/bloc/loadable_state_state.dart b/lib/state/app/infrastructure/loadable_state/bloc/loadable_state_state.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/bloc/loadable_state_state.dart rename to lib/state/app/infrastructure/loadable_state/bloc/loadable_state_state.dart diff --git a/lib/state/app/infrastructure/loadableState/bloc/loadable_state_state.freezed.dart b/lib/state/app/infrastructure/loadable_state/bloc/loadable_state_state.freezed.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/bloc/loadable_state_state.freezed.dart rename to lib/state/app/infrastructure/loadable_state/bloc/loadable_state_state.freezed.dart diff --git a/lib/state/app/infrastructure/loadableState/loadable_state.dart b/lib/state/app/infrastructure/loadable_state/loadable_state.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/loadable_state.dart rename to lib/state/app/infrastructure/loadable_state/loadable_state.dart diff --git a/lib/state/app/infrastructure/loadableState/loadable_state.freezed.dart b/lib/state/app/infrastructure/loadable_state/loadable_state.freezed.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/loadable_state.freezed.dart rename to lib/state/app/infrastructure/loadable_state/loadable_state.freezed.dart diff --git a/lib/state/app/infrastructure/loadableState/loading_error.dart b/lib/state/app/infrastructure/loadable_state/loading_error.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/loading_error.dart rename to lib/state/app/infrastructure/loadable_state/loading_error.dart diff --git a/lib/state/app/infrastructure/loadableState/loading_error.freezed.dart b/lib/state/app/infrastructure/loadable_state/loading_error.freezed.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/loading_error.freezed.dart rename to lib/state/app/infrastructure/loadable_state/loading_error.freezed.dart diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_background_loading.dart b/lib/state/app/infrastructure/loadable_state/view/loadable_state_background_loading.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/view/loadable_state_background_loading.dart rename to lib/state/app/infrastructure/loadable_state/view/loadable_state_background_loading.dart diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart b/lib/state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart similarity index 95% rename from lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart rename to lib/state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart index cce2287..8867571 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart +++ b/lib/state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart @@ -1,10 +1,9 @@ -import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../../widget/conditional_wrapper.dart'; -import '../../utilityWidgets/bloc_module.dart'; -import '../../utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../utility_widgets/bloc_module.dart'; +import '../../utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../bloc/loadable_state_bloc.dart'; import '../bloc/loadable_state_state.dart'; import '../loadable_state.dart'; diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart b/lib/state/app/infrastructure/loadable_state/view/loadable_state_error_bar.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart rename to lib/state/app/infrastructure/loadable_state/view/loadable_state_error_bar.dart diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_screen.dart b/lib/state/app/infrastructure/loadable_state/view/loadable_state_error_screen.dart similarity index 97% rename from lib/state/app/infrastructure/loadableState/view/loadable_state_error_screen.dart rename to lib/state/app/infrastructure/loadable_state/view/loadable_state_error_screen.dart index 4d118bc..e56eb29 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_screen.dart +++ b/lib/state/app/infrastructure/loadable_state/view/loadable_state_error_screen.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../../widget/info_dialog.dart'; import '../bloc/loadable_state_bloc.dart'; diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_primary_loading.dart b/lib/state/app/infrastructure/loadable_state/view/loadable_state_primary_loading.dart similarity index 100% rename from lib/state/app/infrastructure/loadableState/view/loadable_state_primary_loading.dart rename to lib/state/app/infrastructure/loadable_state/view/loadable_state_primary_loading.dart diff --git a/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart b/lib/state/app/infrastructure/utility_widgets/bloc_module.dart similarity index 100% rename from lib/state/app/infrastructure/utilityWidgets/bloc_module.dart rename to lib/state/app/infrastructure/utility_widgets/bloc_module.dart diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart similarity index 95% rename from lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart rename to lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart index b6807b5..f241431 100644 --- a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart +++ b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart @@ -3,10 +3,10 @@ import 'dart:developer'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import '../../../../../api/errors/error_mapper.dart'; -import '../../loadableState/loading_error.dart'; +import '../../loadable_state/loadable_state.dart'; +import '../../loadable_state/loading_error.dart'; import '../../repository/repository.dart'; import 'loadable_hydrated_bloc_event.dart'; -import '../../loadableState/loadable_state.dart'; import 'loadable_save_context.dart'; abstract class LoadableHydratedBloc< @@ -90,7 +90,7 @@ abstract class LoadableHydratedBloc< } @override - fromJson(Map json) { + LoadableState fromJson(Map json) { var rawData = LoadableSaveContext.unwrap(json); return LoadableState( isLoading: true, diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart similarity index 91% rename from lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart rename to lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart index 55b8e6a..1485c60 100644 --- a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart +++ b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart @@ -1,4 +1,4 @@ -import '../../loadableState/loading_error.dart'; +import '../../loadable_state/loading_error.dart'; class LoadableHydratedBlocEvent {} class Emit extends LoadableHydratedBlocEvent { diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.dart similarity index 93% rename from lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart rename to lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.dart index 095f2b6..8d7dc2d 100644 --- a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.dart +++ b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.dart @@ -19,5 +19,5 @@ abstract class LoadableSaveContext with _$LoadableSaveContext { {dataKey: data, metaKey: LoadableSaveContext(timestamp: lastFetch).toJson()}; static ({Map data, LoadableSaveContext meta}) unwrap(Map data) => - (data: data[dataKey] as Map, meta: LoadableSaveContext.fromJson(data[metaKey])); + (data: data[dataKey] as Map, meta: LoadableSaveContext.fromJson(data[metaKey] as Map)); } diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.freezed.dart b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.freezed.dart similarity index 100% rename from lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.freezed.dart rename to lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.freezed.dart diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.g.dart b/lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.g.dart similarity index 100% rename from lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.g.dart rename to lib/state/app/infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_save_context.g.dart diff --git a/lib/state/app/modules/app_modules.dart b/lib/state/app/modules/app_modules.dart index b93c698..1d960f0 100644 --- a/lib/state/app/modules/app_modules.dart +++ b/lib/state/app/modules/app_modules.dart @@ -1,10 +1,9 @@ -import 'package:flutter/material.dart'; -import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:badges/badges.dart' as badges; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import '../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; +import '../../../api/mhsl/breaker/get_breakers/get_breakers_response.dart'; import '../../../routing/app_routes.dart'; import '../../../view/pages/files/files.dart'; import '../../../view/pages/grade_averages/grade_averages_view.dart'; @@ -16,9 +15,9 @@ import '../../../view/pages/talk/chat_list.dart'; import '../../../view/pages/timetable/timetable.dart'; import '../../../widget/breaker/breaker.dart'; import '../../../widget/centered_leading.dart'; -import '../infrastructure/loadableState/loadable_state.dart'; -import 'chatList/bloc/chat_list_bloc.dart'; -import 'chatList/bloc/chat_list_state.dart'; +import '../infrastructure/loadable_state/loadable_state.dart'; +import 'chat_list/bloc/chat_list_bloc.dart'; +import 'chat_list/bloc/chat_list_state.dart'; import 'settings/bloc/settings_cubit.dart'; class AppModule { @@ -30,8 +29,8 @@ class AppModule { AppModule(this.module, {required this.name, required this.icon, this.breakerArea = BreakerArea.global, required this.create}); - static Map modules(BuildContext context, { showFiltered = false }) { - var settings = context.read(); + static Map modules(BuildContext context, {bool showFiltered = false}) { + final settings = context.read(); var available = { Modules.timetable: AppModule( Modules.timetable, @@ -109,7 +108,7 @@ class AppModule { ), }; - if(!showFiltered) available.removeWhere((key, value) => settings.val().modulesSettings.hiddenModules.contains(key)); + if (!showFiltered) available.removeWhere((key, value) => settings.val().modulesSettings.hiddenModules.contains(key)); return { for (var element in settings.val().modulesSettings.moduleOrder.where((element) => available.containsKey(element))) element : available[element]! }; } diff --git a/lib/state/app/modules/breaker/bloc/breaker_bloc.dart b/lib/state/app/modules/breaker/bloc/breaker_bloc.dart index 0381513..86bb6fc 100644 --- a/lib/state/app/modules/breaker/bloc/breaker_bloc.dart +++ b/lib/state/app/modules/breaker/bloc/breaker_bloc.dart @@ -1,9 +1,9 @@ import 'package:flutter/foundation.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import '../../../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../../../api/mhsl/breaker/get_breakers/get_breakers_response.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/breaker_repository.dart'; import 'breaker_event.dart'; import 'breaker_state.dart'; diff --git a/lib/state/app/modules/breaker/bloc/breaker_event.dart b/lib/state/app/modules/breaker/bloc/breaker_event.dart index 5c9ed7e..e5b6030 100644 --- a/lib/state/app/modules/breaker/bloc/breaker_event.dart +++ b/lib/state/app/modules/breaker/bloc/breaker_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'breaker_state.dart'; sealed class BreakerEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/breaker/bloc/breaker_state.dart b/lib/state/app/modules/breaker/bloc/breaker_state.dart index 140cc45..60c1685 100644 --- a/lib/state/app/modules/breaker/bloc/breaker_state.dart +++ b/lib/state/app/modules/breaker/bloc/breaker_state.dart @@ -1,6 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; +import '../../../../../api/mhsl/breaker/get_breakers/get_breakers_response.dart'; part 'breaker_state.freezed.dart'; part 'breaker_state.g.dart'; diff --git a/lib/state/app/modules/breaker/dataProvider/breaker_data_provider.dart b/lib/state/app/modules/breaker/data_provider/breaker_data_provider.dart similarity index 64% rename from lib/state/app/modules/breaker/dataProvider/breaker_data_provider.dart rename to lib/state/app/modules/breaker/data_provider/breaker_data_provider.dart index 5623e71..d07fa83 100644 --- a/lib/state/app/modules/breaker/dataProvider/breaker_data_provider.dart +++ b/lib/state/app/modules/breaker/data_provider/breaker_data_provider.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import '../../../../../api/mhsl/breaker/getBreakers/getBreakersCache.dart'; -import '../../../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; +import '../../../../../api/mhsl/breaker/get_breakers/get_breakers_cache.dart'; +import '../../../../../api/mhsl/breaker/get_breakers/get_breakers_response.dart'; class BreakerDataProvider { Future getBreakers() { diff --git a/lib/state/app/modules/breaker/repository/breaker_repository.dart b/lib/state/app/modules/breaker/repository/breaker_repository.dart index 42bb070..7bc37ac 100644 --- a/lib/state/app/modules/breaker/repository/breaker_repository.dart +++ b/lib/state/app/modules/breaker/repository/breaker_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/breaker_state.dart'; -import '../dataProvider/breaker_data_provider.dart'; +import '../data_provider/breaker_data_provider.dart'; class BreakerRepository extends Repository { final BreakerDataProvider _provider; diff --git a/lib/state/app/modules/chat/bloc/chat_bloc.dart b/lib/state/app/modules/chat/bloc/chat_bloc.dart index 5dfb29a..ee1823f 100644 --- a/lib/state/app/modules/chat/bloc/chat_bloc.dart +++ b/lib/state/app/modules/chat/bloc/chat_bloc.dart @@ -1,8 +1,8 @@ import '../../../../../api/errors/error_mapper.dart'; -import '../../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; -import '../../../infrastructure/loadableState/loading_error.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; +import '../../../infrastructure/loadable_state/loading_error.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/chat_repository.dart'; import 'chat_event.dart'; import 'chat_state.dart'; diff --git a/lib/state/app/modules/chat/bloc/chat_event.dart b/lib/state/app/modules/chat/bloc/chat_event.dart index 460817d..015577f 100644 --- a/lib/state/app/modules/chat/bloc/chat_event.dart +++ b/lib/state/app/modules/chat/bloc/chat_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'chat_state.dart'; sealed class ChatEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/chat/bloc/chat_state.dart b/lib/state/app/modules/chat/bloc/chat_state.dart index 221b84d..5145b5b 100644 --- a/lib/state/app/modules/chat/bloc/chat_state.dart +++ b/lib/state/app/modules/chat/bloc/chat_state.dart @@ -1,6 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; +import '../../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; part 'chat_state.freezed.dart'; part 'chat_state.g.dart'; diff --git a/lib/state/app/modules/chat/dataProvider/chat_data_provider.dart b/lib/state/app/modules/chat/data_provider/chat_data_provider.dart similarity index 80% rename from lib/state/app/modules/chat/dataProvider/chat_data_provider.dart rename to lib/state/app/modules/chat/data_provider/chat_data_provider.dart index 25bdccc..8271b61 100644 --- a/lib/state/app/modules/chat/dataProvider/chat_data_provider.dart +++ b/lib/state/app/modules/chat/data_provider/chat_data_provider.dart @@ -1,5 +1,5 @@ -import '../../../../../api/marianumcloud/talk/chat/getChatCache.dart'; -import '../../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; +import '../../../../../api/marianumcloud/talk/chat/get_chat_cache.dart'; +import '../../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; class ChatDataProvider { Future getChat({ diff --git a/lib/state/app/modules/chat/repository/chat_repository.dart b/lib/state/app/modules/chat/repository/chat_repository.dart index 54e4356..38c3833 100644 --- a/lib/state/app/modules/chat/repository/chat_repository.dart +++ b/lib/state/app/modules/chat/repository/chat_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/chat_state.dart'; -import '../dataProvider/chat_data_provider.dart'; +import '../data_provider/chat_data_provider.dart'; class ChatRepository extends Repository { final ChatDataProvider _provider; diff --git a/lib/state/app/modules/chatList/bloc/chat_list_bloc.dart b/lib/state/app/modules/chat_list/bloc/chat_list_bloc.dart similarity index 77% rename from lib/state/app/modules/chatList/bloc/chat_list_bloc.dart rename to lib/state/app/modules/chat_list/bloc/chat_list_bloc.dart index dd69801..b1687c5 100644 --- a/lib/state/app/modules/chatList/bloc/chat_list_bloc.dart +++ b/lib/state/app/modules/chat_list/bloc/chat_list_bloc.dart @@ -1,9 +1,12 @@ +import 'dart:developer'; + import 'package:flutter_app_badge/flutter_app_badge.dart'; import '../../../../../api/errors/error_mapper.dart'; -import '../../../infrastructure/loadableState/loading_error.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../../../api/marianumcloud/talk/room/get_room_response.dart'; +import '../../../infrastructure/loadable_state/loading_error.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/chat_list_repository.dart'; import 'chat_list_event.dart'; import 'chat_list_state.dart'; @@ -72,10 +75,12 @@ class ChatListBloc extends LoadableHydratedBloc e.unreadMessages).fold(0, (a, b) => a + b as int); + final unread = rooms.data.fold(0, (a, room) => a + room.unreadMessages); FlutterAppBadge.count(unread); - } catch (_) {} + } on Object catch (e) { + log('Failed to update app badge: $e'); + } } } diff --git a/lib/state/app/modules/chatList/bloc/chat_list_event.dart b/lib/state/app/modules/chat_list/bloc/chat_list_event.dart similarity index 50% rename from lib/state/app/modules/chatList/bloc/chat_list_event.dart rename to lib/state/app/modules/chat_list/bloc/chat_list_event.dart index 614898d..302bb02 100644 --- a/lib/state/app/modules/chatList/bloc/chat_list_event.dart +++ b/lib/state/app/modules/chat_list/bloc/chat_list_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'chat_list_state.dart'; sealed class ChatListEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/chatList/bloc/chat_list_state.dart b/lib/state/app/modules/chat_list/bloc/chat_list_state.dart similarity index 83% rename from lib/state/app/modules/chatList/bloc/chat_list_state.dart rename to lib/state/app/modules/chat_list/bloc/chat_list_state.dart index 12ad303..25210cf 100644 --- a/lib/state/app/modules/chatList/bloc/chat_list_state.dart +++ b/lib/state/app/modules/chat_list/bloc/chat_list_state.dart @@ -1,6 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../../../api/marianumcloud/talk/room/get_room_response.dart'; part 'chat_list_state.freezed.dart'; part 'chat_list_state.g.dart'; diff --git a/lib/state/app/modules/chatList/bloc/chat_list_state.freezed.dart b/lib/state/app/modules/chat_list/bloc/chat_list_state.freezed.dart similarity index 100% rename from lib/state/app/modules/chatList/bloc/chat_list_state.freezed.dart rename to lib/state/app/modules/chat_list/bloc/chat_list_state.freezed.dart diff --git a/lib/state/app/modules/chatList/bloc/chat_list_state.g.dart b/lib/state/app/modules/chat_list/bloc/chat_list_state.g.dart similarity index 100% rename from lib/state/app/modules/chatList/bloc/chat_list_state.g.dart rename to lib/state/app/modules/chat_list/bloc/chat_list_state.g.dart diff --git a/lib/state/app/modules/chatList/dataProvider/chat_list_data_provider.dart b/lib/state/app/modules/chat_list/data_provider/chat_list_data_provider.dart similarity index 67% rename from lib/state/app/modules/chatList/dataProvider/chat_list_data_provider.dart rename to lib/state/app/modules/chat_list/data_provider/chat_list_data_provider.dart index 3bf5c62..6549df4 100644 --- a/lib/state/app/modules/chatList/dataProvider/chat_list_data_provider.dart +++ b/lib/state/app/modules/chat_list/data_provider/chat_list_data_provider.dart @@ -1,7 +1,7 @@ -import '../../../../../api/marianumcloud/talk/room/getRoomCache.dart'; -import '../../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; -import '../../../../../api/marianumcloud/talk/createRoom/createRoom.dart'; -import '../../../../../api/marianumcloud/talk/createRoom/createRoomParams.dart'; +import '../../../../../api/marianumcloud/talk/create_room/create_room.dart'; +import '../../../../../api/marianumcloud/talk/create_room/create_room_params.dart'; +import '../../../../../api/marianumcloud/talk/room/get_room_cache.dart'; +import '../../../../../api/marianumcloud/talk/room/get_room_response.dart'; class ChatListDataProvider { Future getRooms({ diff --git a/lib/state/app/modules/chatList/repository/chat_list_repository.dart b/lib/state/app/modules/chat_list/repository/chat_list_repository.dart similarity index 86% rename from lib/state/app/modules/chatList/repository/chat_list_repository.dart rename to lib/state/app/modules/chat_list/repository/chat_list_repository.dart index 5a10ce6..9589cb3 100644 --- a/lib/state/app/modules/chatList/repository/chat_list_repository.dart +++ b/lib/state/app/modules/chat_list/repository/chat_list_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/chat_list_state.dart'; -import '../dataProvider/chat_list_data_provider.dart'; +import '../data_provider/chat_list_data_provider.dart'; class ChatListRepository extends Repository { final ChatListDataProvider _provider; diff --git a/lib/state/app/modules/files/bloc/files_bloc.dart b/lib/state/app/modules/files/bloc/files_bloc.dart index 3483819..fbe72e3 100644 --- a/lib/state/app/modules/files/bloc/files_bloc.dart +++ b/lib/state/app/modules/files/bloc/files_bloc.dart @@ -1,8 +1,8 @@ import '../../../../../api/errors/error_mapper.dart'; -import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart'; -import '../../../infrastructure/loadableState/loading_error.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../../../api/marianumcloud/webdav/queries/list_files/list_files_response.dart'; +import '../../../infrastructure/loadable_state/loading_error.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/files_repository.dart'; import 'files_event.dart'; import 'files_state.dart'; diff --git a/lib/state/app/modules/files/bloc/files_event.dart b/lib/state/app/modules/files/bloc/files_event.dart index 5b6a3a1..2757b8b 100644 --- a/lib/state/app/modules/files/bloc/files_event.dart +++ b/lib/state/app/modules/files/bloc/files_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'files_state.dart'; sealed class FilesEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/files/bloc/files_state.dart b/lib/state/app/modules/files/bloc/files_state.dart index d13b079..448241f 100644 --- a/lib/state/app/modules/files/bloc/files_state.dart +++ b/lib/state/app/modules/files/bloc/files_state.dart @@ -1,6 +1,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart'; +import '../../../../../api/marianumcloud/webdav/queries/list_files/list_files_response.dart'; part 'files_state.freezed.dart'; part 'files_state.g.dart'; diff --git a/lib/state/app/modules/files/dataProvider/files_data_provider.dart b/lib/state/app/modules/files/data_provider/files_data_provider.dart similarity index 82% rename from lib/state/app/modules/files/dataProvider/files_data_provider.dart rename to lib/state/app/modules/files/data_provider/files_data_provider.dart index 8b1fef4..708cb29 100644 --- a/lib/state/app/modules/files/dataProvider/files_data_provider.dart +++ b/lib/state/app/modules/files/data_provider/files_data_provider.dart @@ -1,8 +1,8 @@ import 'package:nextcloud/nextcloud.dart'; -import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart'; -import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart'; -import '../../../../../api/marianumcloud/webdav/webdavApi.dart'; +import '../../../../../api/marianumcloud/webdav/queries/list_files/list_files_cache.dart'; +import '../../../../../api/marianumcloud/webdav/queries/list_files/list_files_response.dart'; +import '../../../../../api/marianumcloud/webdav/webdav_api.dart'; class FilesDataProvider { /// Lists files at [path]. Cached payload is delivered via [onCacheData] as diff --git a/lib/state/app/modules/files/repository/files_repository.dart b/lib/state/app/modules/files/repository/files_repository.dart index 341734e..c7e129c 100644 --- a/lib/state/app/modules/files/repository/files_repository.dart +++ b/lib/state/app/modules/files/repository/files_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/files_state.dart'; -import '../dataProvider/files_data_provider.dart'; +import '../data_provider/files_data_provider.dart'; class FilesRepository extends Repository { final FilesDataProvider _provider; diff --git a/lib/state/app/modules/gradeAverages/bloc/grade_averages_bloc.dart b/lib/state/app/modules/grade_averages/bloc/grade_averages_bloc.dart similarity index 100% rename from lib/state/app/modules/gradeAverages/bloc/grade_averages_bloc.dart rename to lib/state/app/modules/grade_averages/bloc/grade_averages_bloc.dart diff --git a/lib/state/app/modules/gradeAverages/bloc/grade_averages_event.dart b/lib/state/app/modules/grade_averages/bloc/grade_averages_event.dart similarity index 100% rename from lib/state/app/modules/gradeAverages/bloc/grade_averages_event.dart rename to lib/state/app/modules/grade_averages/bloc/grade_averages_event.dart diff --git a/lib/state/app/modules/gradeAverages/bloc/grade_averages_state.dart b/lib/state/app/modules/grade_averages/bloc/grade_averages_state.dart similarity index 100% rename from lib/state/app/modules/gradeAverages/bloc/grade_averages_state.dart rename to lib/state/app/modules/grade_averages/bloc/grade_averages_state.dart diff --git a/lib/state/app/modules/gradeAverages/bloc/grade_averages_state.freezed.dart b/lib/state/app/modules/grade_averages/bloc/grade_averages_state.freezed.dart similarity index 100% rename from lib/state/app/modules/gradeAverages/bloc/grade_averages_state.freezed.dart rename to lib/state/app/modules/grade_averages/bloc/grade_averages_state.freezed.dart diff --git a/lib/state/app/modules/gradeAverages/bloc/grade_averages_state.g.dart b/lib/state/app/modules/grade_averages/bloc/grade_averages_state.g.dart similarity index 100% rename from lib/state/app/modules/gradeAverages/bloc/grade_averages_state.g.dart rename to lib/state/app/modules/grade_averages/bloc/grade_averages_state.g.dart diff --git a/lib/state/app/modules/holidays/bloc/holidays_bloc.dart b/lib/state/app/modules/holidays/bloc/holidays_bloc.dart index 3f82d04..2e3b96b 100644 --- a/lib/state/app/modules/holidays/bloc/holidays_bloc.dart +++ b/lib/state/app/modules/holidays/bloc/holidays_bloc.dart @@ -1,5 +1,5 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/holidays_repository.dart'; import 'holidays_event.dart'; import 'holidays_state.dart'; @@ -22,16 +22,16 @@ class HolidaysBloc extends LoadableHydratedBloc const HolidaysState(showPastHolidays: false, holidays: [], showDisclaimer: true); + HolidaysState fromNothing() => const HolidaysState(showPastHolidays: false, holidays: [], showDisclaimer: true); @override - fromStorage(Map json) => HolidaysState.fromJson(json); + HolidaysState fromStorage(Map json) => HolidaysState.fromJson(json); @override Future gatherData() async { var holidays = await repo.getHolidays(); add(DataGathered((state) => state.copyWith(holidays: holidays))); } @override - repository() => HolidaysRepository(); + HolidaysRepository repository() => HolidaysRepository(); @override Map? toStorage(state) => state.toJson(); } diff --git a/lib/state/app/modules/holidays/bloc/holidays_event.dart b/lib/state/app/modules/holidays/bloc/holidays_event.dart index 8565250..4be4a68 100644 --- a/lib/state/app/modules/holidays/bloc/holidays_event.dart +++ b/lib/state/app/modules/holidays/bloc/holidays_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'holidays_state.dart'; sealed class HolidaysEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/holidays/bloc/holidays_state.dart b/lib/state/app/modules/holidays/bloc/holidays_state.dart index 1a7eef0..eec02b2 100644 --- a/lib/state/app/modules/holidays/bloc/holidays_state.dart +++ b/lib/state/app/modules/holidays/bloc/holidays_state.dart @@ -1,5 +1,5 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter/foundation.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; part 'holidays_state.freezed.dart'; part 'holidays_state.g.dart'; diff --git a/lib/state/app/modules/holidays/dataProvider/holidays_get_holidays.dart b/lib/state/app/modules/holidays/data_provider/holidays_get_holidays.dart similarity index 86% rename from lib/state/app/modules/holidays/dataProvider/holidays_get_holidays.dart rename to lib/state/app/modules/holidays/data_provider/holidays_get_holidays.dart index cd52128..ad663da 100644 --- a/lib/state/app/modules/holidays/dataProvider/holidays_get_holidays.dart +++ b/lib/state/app/modules/holidays/data_provider/holidays_get_holidays.dart @@ -1,7 +1,7 @@ import 'package:dio/dio.dart'; import '../../../basis/dataloader/holiday_data_loader.dart'; -import '../../../infrastructure/dataLoader/data_loader.dart'; +import '../../../infrastructure/data_loader/data_loader.dart'; import '../bloc/holidays_state.dart'; class HolidaysGetHolidays extends HolidayDataLoader> { diff --git a/lib/state/app/modules/holidays/repository/holidays_repository.dart b/lib/state/app/modules/holidays/repository/holidays_repository.dart index 72ec949..7e964c6 100644 --- a/lib/state/app/modules/holidays/repository/holidays_repository.dart +++ b/lib/state/app/modules/holidays/repository/holidays_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/holidays_state.dart'; -import '../dataProvider/holidays_get_holidays.dart'; +import '../data_provider/holidays_get_holidays.dart'; class HolidaysRepository extends Repository { Future> getHolidays() => HolidaysGetHolidays().run(); diff --git a/lib/state/app/modules/marianumDates/bloc/marianum_dates_bloc.dart b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_bloc.dart similarity index 65% rename from lib/state/app/modules/marianumDates/bloc/marianum_dates_bloc.dart rename to lib/state/app/modules/marianum_dates/bloc/marianum_dates_bloc.dart index 3f7961b..241370d 100644 --- a/lib/state/app/modules/marianumDates/bloc/marianum_dates_bloc.dart +++ b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_bloc.dart @@ -1,5 +1,5 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/marianum_dates_repository.dart'; import 'marianum_dates_event.dart'; import 'marianum_dates_state.dart'; @@ -18,16 +18,16 @@ class MarianumDatesBloc extends LoadableHydratedBloc const MarianumDatesState(showPastEvents: false, events: []); + MarianumDatesState fromNothing() => const MarianumDatesState(showPastEvents: false, events: []); @override - fromStorage(Map json) => MarianumDatesState.fromJson(json); + MarianumDatesState fromStorage(Map json) => MarianumDatesState.fromJson(json); @override Future gatherData() async { final events = await repo.getEvents(); add(DataGathered((state) => state.copyWith(events: events))); } @override - repository() => MarianumDatesRepository(); + MarianumDatesRepository repository() => MarianumDatesRepository(); @override Map? toStorage(state) => state.toJson(); } diff --git a/lib/state/app/modules/marianumDates/bloc/marianum_dates_event.dart b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_event.dart similarity index 70% rename from lib/state/app/modules/marianumDates/bloc/marianum_dates_event.dart rename to lib/state/app/modules/marianum_dates/bloc/marianum_dates_event.dart index 34b5b8d..1bfcb88 100644 --- a/lib/state/app/modules/marianumDates/bloc/marianum_dates_event.dart +++ b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'marianum_dates_state.dart'; sealed class MarianumDatesEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/marianumDates/bloc/marianum_dates_state.dart b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.dart similarity index 100% rename from lib/state/app/modules/marianumDates/bloc/marianum_dates_state.dart rename to lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.dart index d3a7d14..26eb18f 100644 --- a/lib/state/app/modules/marianumDates/bloc/marianum_dates_state.dart +++ b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.dart @@ -1,5 +1,5 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:flutter/foundation.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; part 'marianum_dates_state.freezed.dart'; part 'marianum_dates_state.g.dart'; diff --git a/lib/state/app/modules/marianumDates/bloc/marianum_dates_state.freezed.dart b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.freezed.dart similarity index 100% rename from lib/state/app/modules/marianumDates/bloc/marianum_dates_state.freezed.dart rename to lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.freezed.dart diff --git a/lib/state/app/modules/marianumDates/bloc/marianum_dates_state.g.dart b/lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.g.dart similarity index 100% rename from lib/state/app/modules/marianumDates/bloc/marianum_dates_state.g.dart rename to lib/state/app/modules/marianum_dates/bloc/marianum_dates_state.g.dart diff --git a/lib/state/app/modules/marianumDates/dataProvider/marianum_dates_get_events.dart b/lib/state/app/modules/marianum_dates/data_provider/marianum_dates_get_events.dart similarity index 92% rename from lib/state/app/modules/marianumDates/dataProvider/marianum_dates_get_events.dart rename to lib/state/app/modules/marianum_dates/data_provider/marianum_dates_get_events.dart index 2ab5ecd..fc0c177 100644 --- a/lib/state/app/modules/marianumDates/dataProvider/marianum_dates_get_events.dart +++ b/lib/state/app/modules/marianum_dates/data_provider/marianum_dates_get_events.dart @@ -7,8 +7,8 @@ class MarianumDatesGetEvents { static const String url = 'https://public-cal.marianumlan.de/cal_public/ad4c5da8-7466-9c72-89cb-8b8d9a5cf26c'; final Dio _dio = Dio(BaseOptions( - connectTimeout: const Duration(seconds: 10).inMilliseconds, - receiveTimeout: const Duration(seconds: 30).inMilliseconds, + connectTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 30), )); Future> run() async { diff --git a/lib/state/app/modules/marianumDates/repository/marianum_dates_repository.dart b/lib/state/app/modules/marianum_dates/repository/marianum_dates_repository.dart similarity index 81% rename from lib/state/app/modules/marianumDates/repository/marianum_dates_repository.dart rename to lib/state/app/modules/marianum_dates/repository/marianum_dates_repository.dart index 416b4d5..ead14c9 100644 --- a/lib/state/app/modules/marianumDates/repository/marianum_dates_repository.dart +++ b/lib/state/app/modules/marianum_dates/repository/marianum_dates_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/marianum_dates_state.dart'; -import '../dataProvider/marianum_dates_get_events.dart'; +import '../data_provider/marianum_dates_get_events.dart'; class MarianumDatesRepository extends Repository { Future> getEvents() => MarianumDatesGetEvents().run(); diff --git a/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart b/lib/state/app/modules/marianum_message/bloc/marianum_message_bloc.dart similarity index 80% rename from lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart rename to lib/state/app/modules/marianum_message/bloc/marianum_message_bloc.dart index 97c3b95..daca62d 100644 --- a/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart +++ b/lib/state/app/modules/marianum_message/bloc/marianum_message_bloc.dart @@ -1,5 +1,5 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/marianum_message_repository.dart'; import 'marianum_message_event.dart'; import 'marianum_message_state.dart'; diff --git a/lib/state/app/modules/marianumMessage/bloc/marianum_message_event.dart b/lib/state/app/modules/marianum_message/bloc/marianum_message_event.dart similarity index 63% rename from lib/state/app/modules/marianumMessage/bloc/marianum_message_event.dart rename to lib/state/app/modules/marianum_message/bloc/marianum_message_event.dart index 43cbf2a..f71d5c7 100644 --- a/lib/state/app/modules/marianumMessage/bloc/marianum_message_event.dart +++ b/lib/state/app/modules/marianum_message/bloc/marianum_message_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'marianum_message_state.dart'; sealed class MarianumMessageEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/marianumMessage/bloc/marianum_message_state.dart b/lib/state/app/modules/marianum_message/bloc/marianum_message_state.dart similarity index 100% rename from lib/state/app/modules/marianumMessage/bloc/marianum_message_state.dart rename to lib/state/app/modules/marianum_message/bloc/marianum_message_state.dart diff --git a/lib/state/app/modules/marianumMessage/bloc/marianum_message_state.freezed.dart b/lib/state/app/modules/marianum_message/bloc/marianum_message_state.freezed.dart similarity index 100% rename from lib/state/app/modules/marianumMessage/bloc/marianum_message_state.freezed.dart rename to lib/state/app/modules/marianum_message/bloc/marianum_message_state.freezed.dart diff --git a/lib/state/app/modules/marianumMessage/bloc/marianum_message_state.g.dart b/lib/state/app/modules/marianum_message/bloc/marianum_message_state.g.dart similarity index 100% rename from lib/state/app/modules/marianumMessage/bloc/marianum_message_state.g.dart rename to lib/state/app/modules/marianum_message/bloc/marianum_message_state.g.dart diff --git a/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart b/lib/state/app/modules/marianum_message/data_provider/marianum_message_get_messages.dart similarity index 79% rename from lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart rename to lib/state/app/modules/marianum_message/data_provider/marianum_message_get_messages.dart index f8c4b24..a74dda4 100644 --- a/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart +++ b/lib/state/app/modules/marianum_message/data_provider/marianum_message_get_messages.dart @@ -1,12 +1,12 @@ import 'package:dio/dio.dart'; -import '../../../infrastructure/dataLoader/data_loader.dart'; import '../../../basis/dataloader/mhsl_data_loader.dart'; +import '../../../infrastructure/data_loader/data_loader.dart'; import '../bloc/marianum_message_state.dart'; class MarianumMessageGetMessages extends MhslDataLoader { @override Future> fetch() async => dio.get('/message/messages.json'); @override - MarianumMessageList assemble(DataLoaderResult data) => MarianumMessageList.fromJson(data.json); + MarianumMessageList assemble(DataLoaderResult data) => MarianumMessageList.fromJson(data.asMap()); } diff --git a/lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart b/lib/state/app/modules/marianum_message/repository/marianum_message_repository.dart similarity index 81% rename from lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart rename to lib/state/app/modules/marianum_message/repository/marianum_message_repository.dart index 3148b92..9a6d9bc 100644 --- a/lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart +++ b/lib/state/app/modules/marianum_message/repository/marianum_message_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/marianum_message_state.dart'; -import '../dataProvider/marianum_message_get_messages.dart'; +import '../data_provider/marianum_message_get_messages.dart'; class MarianumMessageRepository extends Repository { Future getMessages() => MarianumMessageGetMessages().run(); diff --git a/lib/state/app/modules/settings/bloc/settings_cubit.dart b/lib/state/app/modules/settings/bloc/settings_cubit.dart index efc372f..ad38792 100644 --- a/lib/state/app/modules/settings/bloc/settings_cubit.dart +++ b/lib/state/app/modules/settings/bloc/settings_cubit.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'dart:developer'; -import 'package:easy_debounce/easy_debounce.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import '../../../../../storage/settings.dart'; +import '../../../../../utils/debouncer.dart'; import '../../../../../view/pages/settings/data/default_settings.dart'; import '../../app_modules.dart'; @@ -27,7 +27,7 @@ class SettingsCubit extends HydratedCubit { _emitFreshInstance(); }); } - EasyDebounce.debounce(_debounceTag, const Duration(milliseconds: 500), _emitFreshInstance); + Debouncer.debounce(_debounceTag, const Duration(milliseconds: 500), _emitFreshInstance); } return state; } @@ -77,7 +77,7 @@ class SettingsCubit extends HydratedCubit { oldMap.forEach((key, value) { if (merged.containsKey(key)) { if (value is Map && merged[key] is Map) { - merged[key] = _mergeSettings(value, merged[key]); + merged[key] = _mergeSettings(value, merged[key] as Map); } else { merged[key] = value; } diff --git a/lib/state/app/modules/timetable/bloc/timetable_bloc.dart b/lib/state/app/modules/timetable/bloc/timetable_bloc.dart index 1b142e9..1c7dc5b 100644 --- a/lib/state/app/modules/timetable/bloc/timetable_bloc.dart +++ b/lib/state/app/modules/timetable/bloc/timetable_bloc.dart @@ -1,9 +1,9 @@ import 'package:intl/intl.dart'; -import '../../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; -import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; +import '../../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import '../repository/timetable_repository.dart'; import 'timetable_event.dart'; import 'timetable_state.dart'; diff --git a/lib/state/app/modules/timetable/bloc/timetable_event.dart b/lib/state/app/modules/timetable/bloc/timetable_event.dart index de90c8e..871f2bc 100644 --- a/lib/state/app/modules/timetable/bloc/timetable_event.dart +++ b/lib/state/app/modules/timetable/bloc/timetable_event.dart @@ -1,4 +1,4 @@ -import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; +import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart'; import 'timetable_state.dart'; sealed class TimetableEvent extends LoadableHydratedBlocEvent {} diff --git a/lib/state/app/modules/timetable/bloc/timetable_state.dart b/lib/state/app/modules/timetable/bloc/timetable_state.dart index 1d1b2ea..af26bee 100644 --- a/lib/state/app/modules/timetable/bloc/timetable_state.dart +++ b/lib/state/app/modules/timetable/bloc/timetable_state.dart @@ -1,11 +1,11 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart'; -import '../../../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; -import '../../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import '../../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import '../../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart'; -import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../../../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart'; +import '../../../../../api/webuntis/queries/get_holidays/get_holidays_response.dart'; +import '../../../../../api/webuntis/queries/get_rooms/get_rooms_response.dart'; +import '../../../../../api/webuntis/queries/get_subjects/get_subjects_response.dart'; +import '../../../../../api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.dart'; +import '../../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; part 'timetable_state.freezed.dart'; part 'timetable_state.g.dart'; diff --git a/lib/state/app/modules/timetable/dataProvider/timetable_data_provider.dart b/lib/state/app/modules/timetable/data_provider/timetable_data_provider.dart similarity index 67% rename from lib/state/app/modules/timetable/dataProvider/timetable_data_provider.dart rename to lib/state/app/modules/timetable/data_provider/timetable_data_provider.dart index 8e4766b..8859d1d 100644 --- a/lib/state/app/modules/timetable/dataProvider/timetable_data_provider.dart +++ b/lib/state/app/modules/timetable/data_provider/timetable_data_provider.dart @@ -1,25 +1,25 @@ import 'package:intl/intl.dart'; -import '../../../../../api/mhsl/customTimetableEvent/add/addCustomTimetableEvent.dart'; -import '../../../../../api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.dart'; -import '../../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; -import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventCache.dart'; -import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.dart'; -import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart'; -import '../../../../../api/mhsl/customTimetableEvent/remove/removeCustomTimetableEvent.dart'; -import '../../../../../api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.dart'; -import '../../../../../api/mhsl/customTimetableEvent/update/updateCustomTimetableEvent.dart'; -import '../../../../../api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.dart'; -import '../../../../../api/webuntis/queries/getHolidays/getHolidaysCache.dart'; -import '../../../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; -import '../../../../../api/webuntis/queries/getRooms/getRoomsCache.dart'; -import '../../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import '../../../../../api/webuntis/queries/getSubjects/getSubjectsCache.dart'; -import '../../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import '../../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsCache.dart'; -import '../../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart'; -import '../../../../../api/webuntis/queries/getTimetable/getTimetableCache.dart'; -import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../../../api/mhsl/custom_timetable_event/add/add_custom_timetable_event.dart'; +import '../../../../../api/mhsl/custom_timetable_event/add/add_custom_timetable_event_params.dart'; +import '../../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; +import '../../../../../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_cache.dart'; +import '../../../../../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.dart'; +import '../../../../../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart'; +import '../../../../../api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event.dart'; +import '../../../../../api/mhsl/custom_timetable_event/remove/remove_custom_timetable_event_params.dart'; +import '../../../../../api/mhsl/custom_timetable_event/update/update_custom_timetable_event.dart'; +import '../../../../../api/mhsl/custom_timetable_event/update/update_custom_timetable_event_params.dart'; +import '../../../../../api/webuntis/queries/get_holidays/get_holidays_cache.dart'; +import '../../../../../api/webuntis/queries/get_holidays/get_holidays_response.dart'; +import '../../../../../api/webuntis/queries/get_rooms/get_rooms_cache.dart'; +import '../../../../../api/webuntis/queries/get_rooms/get_rooms_response.dart'; +import '../../../../../api/webuntis/queries/get_subjects/get_subjects_cache.dart'; +import '../../../../../api/webuntis/queries/get_subjects/get_subjects_response.dart'; +import '../../../../../api/webuntis/queries/get_timegrid_units/get_timegrid_units_cache.dart'; +import '../../../../../api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.dart'; +import '../../../../../api/webuntis/queries/get_timetable/get_timetable_cache.dart'; +import '../../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; import '../../../../../model/account_data.dart'; class TimetableDataProvider { diff --git a/lib/state/app/modules/timetable/repository/timetable_repository.dart b/lib/state/app/modules/timetable/repository/timetable_repository.dart index 43ac3a7..36cb6e3 100644 --- a/lib/state/app/modules/timetable/repository/timetable_repository.dart +++ b/lib/state/app/modules/timetable/repository/timetable_repository.dart @@ -1,6 +1,6 @@ import '../../../infrastructure/repository/repository.dart'; import '../bloc/timetable_state.dart'; -import '../dataProvider/timetable_data_provider.dart'; +import '../data_provider/timetable_data_provider.dart'; class TimetableRepository extends Repository { final TimetableDataProvider _provider; diff --git a/lib/storage/settings.dart b/lib/storage/settings.dart index af3abbb..4e42830 100644 --- a/lib/storage/settings.dart +++ b/lib/storage/settings.dart @@ -4,8 +4,8 @@ import 'package:json_annotation/json_annotation.dart'; import 'dev_tools_settings.dart'; import 'file_settings.dart'; import 'file_view_settings.dart'; -import 'modules_settings.dart'; import 'holidays_settings.dart'; +import 'modules_settings.dart'; import 'notification_settings.dart'; import 'talk_settings.dart'; import 'timetable_settings.dart'; diff --git a/lib/utils/cache_invalidation_bus.dart b/lib/utils/cache_invalidation_bus.dart new file mode 100644 index 0000000..ee98c0d --- /dev/null +++ b/lib/utils/cache_invalidation_bus.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +/// App-wide pub/sub channel for cache invalidations. Producers (e.g. webdav +/// move/delete handlers) call [notifyListFiles] after they have dropped the +/// cached listing for a folder so that any [_FilesView] currently sitting on +/// that folder — possibly in the background, beneath a child route — can +/// refresh itself instead of showing the stale snapshot it loaded earlier. +class CacheInvalidationBus { + CacheInvalidationBus._(); + + static final StreamController _listFiles = StreamController.broadcast(); + + /// Emits the invalidated `pathString` (in `FilesBloc` format: relative, + /// no leading or trailing slash; root is '/'). + static Stream get listFilesStream => _listFiles.stream; + + static void notifyListFiles(String pathString) { + _listFiles.add(pathString); + } +} diff --git a/lib/utils/debouncer.dart b/lib/utils/debouncer.dart new file mode 100644 index 0000000..ce3fb5e --- /dev/null +++ b/lib/utils/debouncer.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +/// Static map-based debouncer/throttler. Replaces the `easy_debounce` package +/// with a minimal in-house implementation: each unique [tag] tracks its own +/// pending Timer and gating flag. +class Debouncer { + Debouncer._(); + + static final Map _debounceTimers = {}; + static final Map _throttleTimers = {}; + + /// Coalesces calls under [tag]: the [action] runs once [delay] has elapsed + /// without further calls for the same tag. + static void debounce(String tag, Duration delay, void Function() action) { + _debounceTimers[tag]?.cancel(); + _debounceTimers[tag] = Timer(delay, () { + _debounceTimers.remove(tag); + action(); + }); + } + + /// Runs [action] immediately and ignores subsequent calls under the same + /// [tag] until [duration] has elapsed. + static void throttle(String tag, Duration duration, void Function() action) { + if (_throttleTimers.containsKey(tag)) return; + _throttleTimers[tag] = Timer(duration, () => _throttleTimers.remove(tag)); + action(); + } + + static void cancel(String tag) { + _debounceTimers.remove(tag)?.cancel(); + _throttleTimers.remove(tag)?.cancel(); + } +} diff --git a/lib/utils/download_manager.dart b/lib/utils/download_manager.dart new file mode 100644 index 0000000..ba9c0dd --- /dev/null +++ b/lib/utils/download_manager.dart @@ -0,0 +1,146 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../api/marianumcloud/webdav/webdav_api.dart'; +import '../model/account_data.dart'; +import 'file_downloader.dart'; + +/// Snapshot of a single download's lifecycle. UI widgets rebuild whenever the +/// owning [DownloadJob.status] notifier emits a new instance. +sealed class DownloadStatus { + const DownloadStatus(); +} + +class DownloadInProgress extends DownloadStatus { + const DownloadInProgress(this.percent); + final double percent; +} + +class DownloadDone extends DownloadStatus { + const DownloadDone(this.localPath); + final String localPath; +} + +class DownloadCancelled extends DownloadStatus { + const DownloadCancelled(); +} + +class DownloadFailed extends DownloadStatus { + const DownloadFailed(this.message); + final String message; +} + +/// Tracks a single in-flight or finished download. Survives widget dispose so +/// that re-entering the screen reattaches to the same job. +class DownloadJob { + DownloadJob({ + required this.remotePath, + required this.name, + required this.localPath, + required FileDownloader downloader, + }) : _downloader = downloader; + + final String remotePath; + final String name; + final String localPath; + final FileDownloader _downloader; + + final ValueNotifier status = ValueNotifier(const DownloadInProgress(0)); + bool _disposed = false; + + bool get isFinished => + status.value is DownloadDone || + status.value is DownloadFailed || + status.value is DownloadCancelled; + + void cancel() { + if (isFinished) return; + _downloader.cancel(); + status.value = const DownloadCancelled(); + } + + void _dispose() { + if (_disposed) return; + _disposed = true; + status.dispose(); + } +} + +/// Central in-memory registry for downloads. Keyed by remote path so a file +/// triggered from multiple screens (Files + Chat) reuses one job. +/// +/// Not persistent across app restarts — restarting abandons in-flight +/// downloads and partial files are cleaned on next start attempt. +class DownloadManager { + DownloadManager._(); + static final DownloadManager instance = DownloadManager._(); + + final Map _jobs = {}; + + /// Active or recently finished job for [remotePath], or null if none. + DownloadJob? jobFor(String remotePath) => _jobs[remotePath]; + + /// Returns the existing job if a download is in progress for [remotePath], + /// otherwise starts a new one. Caller listens on [DownloadJob.status]. + Future start({required String remotePath, required String name}) async { + final existing = _jobs[remotePath]; + if (existing != null && !existing.isFinished) return existing; + if (existing != null) { + _jobs.remove(remotePath); + scheduleMicrotask(existing._dispose); + } + + final tempDir = await getTemporaryDirectory(); + final encodedPath = Uri.encodeComponent(remotePath).replaceAll('%2F', '/'); + final localPath = '${tempDir.path}${Platform.pathSeparator}$name'; + + final downloader = FileDownloader(); + final job = DownloadJob( + remotePath: remotePath, + name: name, + localPath: localPath, + downloader: downloader, + ); + _jobs[remotePath] = job; + + downloader.run( + client: Dio(BaseOptions(headers: AccountData().authHeaders())), + url: '${WebdavApi.buildWebdavUrl()}$encodedPath', + savePath: localPath, + onProgress: (percent) { + if (job.isFinished) return; + job.status.value = DownloadInProgress(percent); + }, + onDone: () { + if (job.isFinished) return; + job.status.value = DownloadDone(localPath); + }, + onError: (error) { + if (job.isFinished) return; + try { + File(localPath).deleteSync(); + } on FileSystemException { + // partial file may not exist — ignore + } + job.status.value = DownloadFailed(error.toString()); + }, + ); + + return job; + } + + /// Removes a finished job from the registry. Safe to call from a status + /// listener: actual disposal of the underlying notifier is deferred to the + /// next microtask so the in-flight `notifyListeners` cycle can finish before + /// the notifier is destroyed. Active (unfinished) jobs are left untouched. + void clear(String remotePath) { + final job = _jobs[remotePath]; + if (job == null || !job.isFinished) return; + _jobs.remove(remotePath); + scheduleMicrotask(job._dispose); + } +} diff --git a/lib/utils/file_clipboard.dart b/lib/utils/file_clipboard.dart new file mode 100644 index 0000000..a8abda3 --- /dev/null +++ b/lib/utils/file_clipboard.dart @@ -0,0 +1,44 @@ +import 'package:flutter/foundation.dart'; + +import '../api/marianumcloud/webdav/queries/list_files/cacheable_file.dart'; + +enum FileClipboardOperation { cut, copy } + +/// In-memory clipboard for file operations within the app. Mirrors the +/// cut/copy/paste pattern of native file managers (iOS Files, Android Files, +/// Finder). Contents are not persisted across app restarts. +/// +/// Listen via [ChangeNotifier] (e.g. `ListenableBuilder`) to render a paste +/// banner when [isEmpty] is false. +class FileClipboard extends ChangeNotifier { + FileClipboard._(); + static final FileClipboard instance = FileClipboard._(); + + FileClipboardOperation? _operation; + List _files = const []; + + FileClipboardOperation? get operation => _operation; + List get files => List.unmodifiable(_files); + bool get isEmpty => _files.isEmpty; + + void cut(List files) { + if (files.isEmpty) return; + _operation = FileClipboardOperation.cut; + _files = List.of(files); + notifyListeners(); + } + + void copy(List files) { + if (files.isEmpty) return; + _operation = FileClipboardOperation.copy; + _files = List.of(files); + notifyListeners(); + } + + void clear() { + if (_operation == null && _files.isEmpty) return; + _operation = null; + _files = const []; + notifyListeners(); + } +} diff --git a/lib/utils/file_downloader.dart b/lib/utils/file_downloader.dart new file mode 100644 index 0000000..d6b8152 --- /dev/null +++ b/lib/utils/file_downloader.dart @@ -0,0 +1,47 @@ +import 'package:dio/dio.dart'; + +/// Lightweight cancel handle around a single `Dio.download` call. The download +/// itself is started by [run]; the handle returns synchronously so callers can +/// install it into shared state before the first progress event can fire. +class FileDownloader { + FileDownloader(); + + final CancelToken _cancelToken = CancelToken(); + bool _cancelled = false; + + bool get isCancelled => _cancelled; + + void cancel() { + if (_cancelled) return; + _cancelled = true; + _cancelToken.cancel('user cancelled'); + } + + /// Kicks off the download. Returns immediately; the download progresses in + /// the background and events are delivered via callbacks. Callbacks are not + /// invoked once [cancel] has been called. + void run({ + required Dio client, + required String url, + required String savePath, + required void Function(double percent) onProgress, + required void Function() onDone, + required void Function(Object error) onError, + }) { + client.download( + url, + savePath, + cancelToken: _cancelToken, + onReceiveProgress: (received, total) { + if (_cancelled || total <= 0) return; + onProgress((received / total) * 100); + }, + ).then((_) { + if (_cancelled) return; + onDone(); + }).catchError((Object error) { + if (_cancelled) return; + onError(error); + }).ignore(); + } +} diff --git a/lib/utils/file_saver.dart b/lib/utils/file_saver.dart deleted file mode 100644 index 1a1a88c..0000000 --- a/lib/utils/file_saver.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'dart:io'; - -import 'package:permission_handler/permission_handler.dart'; - -// only tested on android! -class FileSaver { - static Future getExternalDocumentPath() async { - var permission = await Permission.storage.status; - if(!permission.isGranted) { - await Permission.storage.request(); - } - var directory = Directory('/storage/emulated/0/Download'); - final externalPath = directory.path; - await Directory(externalPath).create(recursive: true); - return externalPath; - } - - static Future writeBytes(List bytes, String name) async { - final path = await getExternalDocumentPath(); - var file = File('$path/$name'); - return file.writeAsBytes(bytes); - } -} diff --git a/lib/view/login/login.dart b/lib/view/login/login.dart index 73df505..a2d22cc 100644 --- a/lib/view/login/login.dart +++ b/lib/view/login/login.dart @@ -5,8 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_login/flutter_login.dart'; -import '../../api/marianumcloud/talk/room/getRoom.dart'; -import '../../api/marianumcloud/talk/room/getRoomParams.dart'; +import '../../api/marianumcloud/talk/room/get_room.dart'; +import '../../api/marianumcloud/talk/room/get_room_params.dart'; import '../../model/account_data.dart'; import '../../state/app/modules/account/bloc/account_bloc.dart'; import '../../state/app/modules/account/bloc/account_state.dart'; diff --git a/lib/view/pages/files/files.dart b/lib/view/pages/files/files.dart index 212c8fc..d673417 100644 --- a/lib/view/pages/files/files.dart +++ b/lib/view/pages/files/files.dart @@ -1,21 +1,26 @@ -import 'dart:io'; +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:nextcloud/nextcloud.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import '../../../api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart'; -import '../../../state/app/infrastructure/loadableState/loadable_state.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; -import '../../../state/app/infrastructure/utilityWidgets/bloc_module.dart'; +import '../../../api/marianumcloud/webdav/queries/list_files/cacheable_file.dart'; +import '../../../api/marianumcloud/webdav/queries/list_files/list_files_cache.dart'; +import '../../../api/marianumcloud/webdav/webdav_api.dart'; +import '../../../state/app/infrastructure/loadable_state/loadable_state.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart'; import '../../../state/app/modules/files/bloc/files_bloc.dart'; import '../../../state/app/modules/files/bloc/files_state.dart'; import '../../../state/app/modules/settings/bloc/settings_cubit.dart'; +import '../../../utils/cache_invalidation_bus.dart'; +import '../../../utils/file_clipboard.dart'; import '../../../widget/async_action_button.dart'; import '../../../widget/file_pick.dart'; import '../../../widget/placeholder_view.dart'; -import 'widgets/file_element.dart'; import 'files_upload_dialog.dart'; +import 'widgets/file_element.dart'; class BetterSortOption { String displayName; @@ -78,6 +83,11 @@ class _FilesViewState extends State<_FilesView> { late final SettingsCubit settings; late SortOption currentSort; late bool currentSortDirection; + late final StreamSubscription _invalidationSub; + + // Cache key in FilesBloc's pathString format: '/' for root, otherwise + // segments joined without leading/trailing slash. + String get _myPathString => widget.path.isEmpty ? '/' : widget.path.join('/'); @override void initState() { @@ -85,12 +95,25 @@ class _FilesViewState extends State<_FilesView> { settings = context.read(); currentSort = settings.val().fileSettings.sortBy; currentSortDirection = settings.val().fileSettings.ascending; + _invalidationSub = CacheInvalidationBus.listFilesStream.listen(_onInvalidation); + } + + void _onInvalidation(String invalidatedPath) { + if (!mounted) return; + if (invalidatedPath != _myPathString) return; + context.read().refresh(); + } + + @override + void dispose() { + _invalidationSub.cancel(); + super.dispose(); } Future mediaUpload(List? paths) async { if (paths == null) return; final bloc = context.read(); - pushScreen( + unawaited(pushScreen( context, withNavBar: false, screen: FilesUploadDialog( @@ -98,7 +121,7 @@ class _FilesViewState extends State<_FilesView> { remotePath: widget.path.join('/'), onUploadFinished: (_) => bloc.refresh(), ), - ); + )); } @override @@ -163,28 +186,39 @@ class _FilesViewState extends State<_FilesView> { onPressed: () => _showAddDialog(context, bloc), child: const Icon(Icons.add), ), - body: LoadableStateConsumer( - isReady: (state) => state.listing != null, - child: (state, _) { - final listing = state.listing!; - if (listing.files.isEmpty) { - return const PlaceholderView(icon: Icons.folder_off_rounded, text: 'Der Ordner ist leer'); - } - final files = listing.sortBy( - sortOption: currentSort, - foldersToTop: context.watch().val().fileSettings.sortFoldersToTop, - reversed: currentSortDirection, - ); - return ListView.builder( - padding: EdgeInsets.zero, - itemCount: files.length, - itemBuilder: (context, index) => FileElement(files[index], widget.path, bloc.refresh), - ); - }, + body: Column( + children: [ + _ClipboardBanner(currentFolder: _currentFolderPath, onPasteDone: bloc.refresh), + Expanded( + child: LoadableStateConsumer( + isReady: (state) => state.listing != null, + child: (state, _) { + final listing = state.listing!; + if (listing.files.isEmpty) { + return const PlaceholderView(icon: Icons.folder_off_rounded, text: 'Der Ordner ist leer'); + } + final files = listing.sortBy( + sortOption: currentSort, + foldersToTop: context.watch().val().fileSettings.sortFoldersToTop, + reversed: currentSortDirection, + ); + return ListView.builder( + padding: EdgeInsets.zero, + itemCount: files.length, + itemBuilder: (context, index) => FileElement(files[index], widget.path, bloc.refresh), + ); + }, + ), + ), + ], ), ); } + // Relative folder path matching the WebDAV format used by `CacheableFile.path` + // (no leading slash; trailing slash for non-root). Empty string means root. + String get _currentFolderPath => widget.path.isEmpty ? '' : '${widget.path.join('/')}/'; + void _showAddDialog(BuildContext context, FilesBloc bloc) { showDialog( context: context, @@ -205,18 +239,25 @@ class _FilesViewState extends State<_FilesView> { Navigator.of(dialogCtx).pop(); }, ), - Visibility( - visible: !Platform.isIOS, - child: ListTile( - leading: const Icon(Icons.add_a_photo_outlined), - title: const Text('Aus Gallerie hochladen'), - onTap: () { - FilePick.multipleGalleryPick().then((value) { - if (value != null) mediaUpload(value.map((e) => e.path).toList()); - }); - Navigator.of(dialogCtx).pop(); - }, - ), + ListTile( + leading: const Icon(Icons.add_a_photo_outlined), + title: const Text('Aus Galerie hochladen'), + onTap: () { + FilePick.multipleGalleryPick().then((value) { + if (value != null) mediaUpload(value.map((e) => e.path).toList()); + }); + Navigator.of(dialogCtx).pop(); + }, + ), + ListTile( + leading: const Icon(Icons.camera_alt_outlined), + title: const Text('Foto aufnehmen'), + onTap: () { + FilePick.cameraPick().then((image) { + if (image != null) mediaUpload([image.path]); + }); + Navigator.of(dialogCtx).pop(); + }, ), ]), ); @@ -248,3 +289,142 @@ class _FilesViewState extends State<_FilesView> { ); } } + +class _ClipboardBanner extends StatefulWidget { + const _ClipboardBanner({required this.currentFolder, required this.onPasteDone}); + final String currentFolder; + final void Function() onPasteDone; + + @override + State<_ClipboardBanner> createState() => _ClipboardBannerState(); +} + +class _ClipboardBannerState extends State<_ClipboardBanner> { + bool _busy = false; + + // All paths here are relative to the WebDAV root (matching `CacheableFile.path`). + // Root is the empty string ''. Folders end with '/'. + String _normalised(String path) { + final stripped = path.replaceAll(RegExp(r'^/+|/+$'), ''); + return stripped.isEmpty ? '' : '$stripped/'; + } + + String _joinPath(String folder, String name, {required bool isDirectory}) => + isDirectory ? '$folder$name/' : '$folder$name'; + + // Disabled when: + // - clipboard is empty + // - we'd be pasting a folder into itself or one of its descendants + // - every entry already lives in the current folder (paste would be a no-op) + bool get _canPaste { + final cb = FileClipboard.instance; + if (cb.isEmpty) return false; + final dst = _normalised(widget.currentFolder); + var atLeastOneActionable = false; + for (final f in cb.files) { + if (f.isDirectory) { + final src = _normalised(f.path); + if (dst == src || dst.startsWith(src)) return false; + } + final destination = _joinPath(widget.currentFolder, f.name, isDirectory: f.isDirectory); + if (destination != f.path) atLeastOneActionable = true; + } + return atLeastOneActionable; + } + + // Cache key format used by ListFilesCache (matches FilesBloc's pathString: + // relative, no leading or trailing slash; root is '/'). + String _parentCacheKey(String relativePath) { + final stripped = relativePath.replaceAll(RegExp(r'^/+|/+$'), ''); + if (!stripped.contains('/')) return '/'; + final parts = stripped.split('/')..removeLast(); + return parts.isEmpty ? '/' : parts.join('/'); + } + + Future _paste() async { + final cb = FileClipboard.instance; + if (_busy || !_canPaste) return; + setState(() => _busy = true); + final operation = cb.operation; + final errors = []; + final invalidatedSourceFolders = {}; + try { + final webdav = await WebdavApi.webdav; + for (final file in cb.files) { + final destination = _joinPath(widget.currentFolder, file.name, isDirectory: file.isDirectory); + if (destination == file.path) continue; + try { + if (operation == FileClipboardOperation.cut) { + await webdav.move(PathUri.parse(file.path), PathUri.parse(destination)); + invalidatedSourceFolders.add(_parentCacheKey(file.path)); + } else { + await webdav.copy(PathUri.parse(file.path), PathUri.parse(destination)); + } + } on Object catch (e) { + errors.add('${file.name}: $e'); + } + } + // After cut, the source folders no longer contain the moved files. Drop + // their cached listings so the next visit fetches fresh data instead of + // briefly showing the moved file as still present. + for (final folder in invalidatedSourceFolders) { + await ListFilesCache.invalidate(folder); + } + if (operation == FileClipboardOperation.cut) cb.clear(); + widget.onPasteDone(); + } finally { + if (mounted) setState(() => _busy = false); + } + if (errors.isNotEmpty && mounted) { + await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Einfügen teilweise fehlgeschlagen'), + content: SingleChildScrollView(child: Text(errors.join('\n\n'))), + actions: [TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('OK'))], + ), + ); + } + } + + @override + Widget build(BuildContext context) => ListenableBuilder( + listenable: FileClipboard.instance, + builder: (context, _) { + final cb = FileClipboard.instance; + if (cb.isEmpty) return const SizedBox.shrink(); + final cut = cb.operation == FileClipboardOperation.cut; + final count = cb.files.length; + final label = count == 1 ? '"${cb.files.first.name}"' : '$count Elemente'; + return Material( + color: Theme.of(context).colorScheme.secondaryContainer, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + children: [ + Icon(cut ? Icons.drive_file_move_outline : Icons.copy_outlined, size: 20), + const SizedBox(width: 12), + Expanded( + child: Text( + cut ? '$label verschieben' : '$label kopieren', + overflow: TextOverflow.ellipsis, + ), + ), + TextButton( + onPressed: _busy || !_canPaste ? null : _paste, + child: _busy + ? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2)) + : const Text('Hier einfügen'), + ), + IconButton( + tooltip: 'Verwerfen', + icon: const Icon(Icons.close, size: 20), + onPressed: _busy ? null : cb.clear, + ), + ], + ), + ), + ); + }, + ); +} diff --git a/lib/view/pages/files/files_upload_dialog.dart b/lib/view/pages/files/files_upload_dialog.dart index 4c9b1c6..44fe7cf 100644 --- a/lib/view/pages/files/files_upload_dialog.dart +++ b/lib/view/pages/files/files_upload_dialog.dart @@ -3,9 +3,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:loader_overlay/loader_overlay.dart'; import 'package:nextcloud/nextcloud.dart'; -import 'package:uuid/uuid.dart'; -import '../../../api/marianumcloud/webdav/webdavApi.dart'; +import '../../../api/marianumcloud/webdav/webdav_api.dart'; import '../../../widget/confirm_dialog.dart'; import '../../../widget/focus_behaviour.dart'; @@ -107,14 +106,14 @@ class _FilesUploadDialogState extends State { _showUploadError('Verbindung fehlgeschlagen: $e'); return; } - var conflictingFiles = _uploadableFiles.where((file) { - var fileName = file.fileName; - return result.any((element) => Uri.decodeComponent(element.href!).endsWith('/$fileName')); + final conflictingFiles = _uploadableFiles.where((file) { + final fileName = file.fileName; + return result.any((element) => Uri.decodeComponent((element as WebDavResponse).href!).endsWith('/$fileName')); }).toList(); - if(conflictingFiles.isNotEmpty) { + if (conflictingFiles.isNotEmpty) { if (!mounted) return; - bool replaceFiles = await showDialog( + final replaceFiles = await showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( @@ -160,7 +159,7 @@ class _FilesUploadDialogState extends State { ) ); - if(!replaceFiles) { + if (replaceFiles != true) { setState(() { _isUploading = false; _overallProgressValue = 0.0; @@ -179,7 +178,10 @@ class _FilesUploadDialogState extends State { var fileName = file.fileName; var filePath = file.filePath; - if(widget.uniqueNames) fileName = '${fileName.split('.').first}-${const Uuid().v4()}.${fileName.split('.').last}'; + if (widget.uniqueNames) { + final unique = DateTime.now().microsecondsSinceEpoch.toRadixString(36); + fileName = '${fileName.split('.').first}-$unique.${fileName.split('.').last}'; + } var fullRemotePath = '${widget.remotePath}/$fileName'; @@ -187,7 +189,7 @@ class _FilesUploadDialogState extends State { _infoText = '${_uploadableFiles.indexOf(file) + 1}/${_uploadableFiles.length}'; }); - final dynamic uploadTask; + final HttpClientResponse uploadTask; try { uploadTask = await webdavClient.putFile( File(filePath), diff --git a/lib/view/pages/files/widgets/file_details_sheet.dart b/lib/view/pages/files/widgets/file_details_sheet.dart new file mode 100644 index 0000000..418b50e --- /dev/null +++ b/lib/view/pages/files/widgets/file_details_sheet.dart @@ -0,0 +1,80 @@ +import 'package:filesize/filesize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:jiffy/jiffy.dart'; + +import '../../../../api/marianumcloud/webdav/queries/list_files/cacheable_file.dart'; + +/// Shows a modal bottom sheet with technical metadata about a single file or +/// folder: full path, MIME type, size, timestamps, ETag. +Future showFileDetailsSheet(BuildContext context, CacheableFile file) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + showDragHandle: true, + builder: (context) => SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: Icon(file.isDirectory ? Icons.folder : Icons.description_outlined, size: 32), + title: Text(file.name, style: const TextStyle(fontWeight: FontWeight.bold)), + subtitle: Text(file.isDirectory ? 'Ordner' : (file.mimeType ?? '–')), + ), + const Divider(), + _DetailRow(label: 'Pfad', value: file.path, copyable: true), + if (!file.isDirectory) _DetailRow(label: 'Größe', value: filesize(file.size)), + if (file.modifiedAt != null) + _DetailRow( + label: 'Geändert', + value: '${Jiffy.parseFromDateTime(file.modifiedAt!).format(pattern: 'dd.MM.yyyy HH:mm')} ' + '(${Jiffy.parseFromDateTime(file.modifiedAt!).fromNow()})', + ), + if (file.createdAt != null) + _DetailRow( + label: 'Erstellt', + value: Jiffy.parseFromDateTime(file.createdAt!).format(pattern: 'dd.MM.yyyy HH:mm'), + ), + if (file.eTag != null) _DetailRow(label: 'ETag', value: file.eTag!, copyable: true), + ], + ), + ), + ), + ); +} + +class _DetailRow extends StatelessWidget { + const _DetailRow({required this.label, required this.value, this.copyable = false}); + final String label; + final String value; + final bool copyable; + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 90, + child: Text(label, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)), + ), + Expanded(child: SelectableText(value)), + if (copyable) + IconButton( + tooltip: 'Kopieren', + icon: const Icon(Icons.copy, size: 18), + onPressed: () { + Clipboard.setData(ClipboardData(text: value)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('In Zwischenablage kopiert')), + ); + }, + ), + ], + ), + ); +} diff --git a/lib/view/pages/files/widgets/file_element.dart b/lib/view/pages/files/widgets/file_element.dart index 26439a0..3718c14 100644 --- a/lib/view/pages/files/widgets/file_element.dart +++ b/lib/view/pages/files/widgets/file_element.dart @@ -1,24 +1,18 @@ -import 'dart:io'; - -import 'package:dio/dio.dart'; import 'package:filesize/filesize.dart'; -import 'package:flowder/flowder.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import 'package:open_filex/open_filex.dart'; -import '../../../../widget/info_dialog.dart'; import 'package:nextcloud/nextcloud.dart'; -import 'package:path_provider/path_provider.dart'; -import '../../../../api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart'; -import '../../../../api/marianumcloud/webdav/webdavApi.dart'; -import '../../../../model/account_data.dart'; +import '../../../../api/marianumcloud/webdav/queries/list_files/cacheable_file.dart'; +import '../../../../api/marianumcloud/webdav/webdav_api.dart'; import '../../../../model/endpoint_data.dart'; import '../../../../routing/app_routes.dart'; +import '../../../../utils/download_manager.dart'; +import '../../../../utils/file_clipboard.dart'; import '../../../../widget/centered_leading.dart'; import '../../../../widget/confirm_dialog.dart'; -import '../../../../widget/unimplemented_dialog.dart'; +import '../../../../widget/info_dialog.dart'; +import 'file_details_sheet.dart'; class FileElement extends StatefulWidget { final CacheableFile file; @@ -26,57 +20,117 @@ class FileElement extends StatefulWidget { final void Function() refetch; const FileElement(this.file, this.path, this.refetch, {super.key}); - static Future download(BuildContext context, String remotePath, String name, Function(double) onProgress, Function(OpenResult) onDone) async { - var paths = await getTemporaryDirectory(); - - var encodedPath = Uri.encodeComponent(remotePath); - encodedPath = encodedPath.replaceAll('%2F', '/'); - - var local = paths.path + Platform.pathSeparator + name; - - var options = DownloaderUtils( - progressCallback: (current, total) { - final progress = (current / total) * 100; - onProgress(progress); - }, - file: File(local), - progress: ProgressImplementation(), - deleteOnCancel: true, - client: Dio(BaseOptions(headers: AccountData().authHeaders())), - onDone: () { - AppRoutes.openFileViewer(context, local); - onDone(OpenResult(message: 'File viewer opened', type: ResultType.done)); - }, - ); - - return await Flowder.download( - '${WebdavApi.buildWebdavUrl()}$encodedPath', - options, - ); - } - @override State createState() => _FileElementState(); } class _FileElementState extends State { - double percent = 0; - Future? downloadCore; + DownloadJob? _job; - Widget getSubtitle() { - if(widget.file.currentlyDownloading) { + @override + void initState() { + super.initState(); + _attachJob(DownloadManager.instance.jobFor(widget.file.path)); + } + + @override + void didUpdateWidget(covariant FileElement oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.file.path != widget.file.path) { + _detachJob(); + _attachJob(DownloadManager.instance.jobFor(widget.file.path)); + } + } + + @override + void dispose() { + _detachJob(); + super.dispose(); + } + + void _attachJob(DownloadJob? job) { + _job = job; + if (job == null) return; + job.status.addListener(_onStatusChange); + if (job.isFinished) { + WidgetsBinding.instance.addPostFrameCallback((_) => _onStatusChange()); + } + } + + void _detachJob() { + _job?.status.removeListener(_onStatusChange); + _job = null; + } + + void _onStatusChange() { + if (!mounted) return; + final job = _job; + if (job == null) return; + final status = job.status.value; + if (status is DownloadDone) { + DownloadManager.instance.clear(widget.file.path); + _detachJob(); + AppRoutes.openFileViewer(context, status.localPath); + setState(() {}); + } else if (status is DownloadFailed) { + final message = status.message; + DownloadManager.instance.clear(widget.file.path); + _detachJob(); + setState(() {}); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Download'), + content: Text(message), + ), + ); + } else if (status is DownloadCancelled) { + DownloadManager.instance.clear(widget.file.path); + _detachJob(); + setState(() {}); + } else { + setState(() {}); + } + } + + Future _startDownload() async { + final job = await DownloadManager.instance.start( + remotePath: widget.file.path, + name: widget.file.name, + ); + if (!mounted) return; + if (_job == job) return; + _detachJob(); + _attachJob(job); + setState(() {}); + } + + void _confirmCancel() { + showDialog( + context: context, + builder: (dialogContext) => ConfirmDialog( + title: 'Download abbrechen?', + content: 'Möchtest du den Download abbrechen?', + cancelButton: 'Nein', + confirmButton: 'Ja, Abbrechen', + onConfirm: () => _job?.cancel(), + ), + ); + } + + Widget _subtitle() { + final status = _job?.status.value; + if (status is DownloadInProgress) { return Row( children: [ Container( margin: const EdgeInsets.only(right: 10), child: const Text('Download:'), ), - Expanded( - child: LinearProgressIndicator(value: percent/100), - ), + Expanded(child: LinearProgressIndicator(value: status.percent / 100)), Container( margin: const EdgeInsets.only(left: 10), - child: Text('${percent.round()}%'), + child: Text('${status.percent.round()}%'), ), ], ); @@ -86,101 +140,173 @@ class _FileElementState extends State { : Text('${filesize(widget.file.size)}, ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}'); } + void _onTap() { + if (widget.file.isDirectory) { + AppRoutes.openFolder(context, widget.path.toList()..add(widget.file.name)); + return; + } + if (EndpointData().getEndpointMode() == EndpointMode.stage) { + InfoDialog.show(context, 'Virtuelle Dateien im Staging Prozess können nicht heruntergeladen werden!'); + return; + } + final status = _job?.status.value; + if (status is DownloadInProgress) { + _confirmCancel(); + return; + } + _startDownload(); + } + + // All paths here are relative to the WebDAV root (matching CacheableFile.path). + // Root parent is the empty string ''. Folders end with '/'. + String _parentPathOf(String path) { + final stripped = path.replaceAll(RegExp(r'^/+|/+$'), ''); + if (!stripped.contains('/')) return ''; + final parts = stripped.split('/')..removeLast(); + return parts.isEmpty ? '' : '${parts.join('/')}/'; + } + + String _joinPath(String folder, String name, {required bool isDirectory}) => + isDirectory ? '$folder$name/' : '$folder$name'; + + Future _rename() async { + final controller = TextEditingController(text: widget.file.name); + final newName = await showDialog( + context: context, + builder: (dialogCtx) => AlertDialog( + title: const Text('Umbenennen'), + content: TextField( + controller: controller, + decoration: const InputDecoration(labelText: 'Neuer Name'), + autofocus: true, + ), + actions: [ + TextButton(onPressed: () => Navigator.of(dialogCtx).pop(), child: const Text('Abbrechen')), + TextButton( + onPressed: () => Navigator.of(dialogCtx).pop(controller.text.trim()), + child: const Text('Umbenennen'), + ), + ], + ), + ); + if (newName == null || newName.isEmpty || newName == widget.file.name) return; + + final parent = _parentPathOf(widget.file.path); + final destination = _joinPath(parent, newName, isDirectory: widget.file.isDirectory); + await _runWebdavOp(() async { + final webdav = await WebdavApi.webdav; + await webdav.move(PathUri.parse(widget.file.path), PathUri.parse(destination)); + }, errorTitle: 'Umbenennen fehlgeschlagen'); + } + + void _putOnClipboard({required bool copy}) { + if (copy) { + FileClipboard.instance.copy([widget.file]); + } else { + FileClipboard.instance.cut([widget.file]); + } + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('"${widget.file.name}" zum ${copy ? "Kopieren" : "Verschieben"} bereitgelegt'), + duration: const Duration(seconds: 2), + )); + } + + Future _delete() async { + await showDialog( + context: context, + builder: (context) => ConfirmDialog( + title: 'Element löschen?', + content: 'Das Element wird unwiederruflich gelöscht.', + confirmButton: 'Löschen', + onConfirmAsync: () async { + final webdav = await WebdavApi.webdav; + await webdav.delete(PathUri.parse(widget.file.path)); + widget.refetch(); + }, + ), + ); + } + + Future _runWebdavOp(Future Function() action, {required String errorTitle}) async { + try { + await action(); + widget.refetch(); + } on Object catch (e) { + if (!mounted) return; + await showDialog( + context: context, + builder: (dialogCtx) => AlertDialog( + title: Text(errorTitle), + content: Text(e.toString()), + actions: [TextButton(onPressed: () => Navigator.of(dialogCtx).pop(), child: const Text('OK'))], + ), + ); + } + } + + void _showActionSheet() { + showModalBottomSheet( + context: context, + showDragHandle: true, + builder: (sheetCtx) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: const CenteredLeading(Icon(Icons.info_outline)), + title: const Text('Info'), + onTap: () { + Navigator.of(sheetCtx).pop(); + showFileDetailsSheet(context, widget.file); + }, + ), + ListTile( + leading: const CenteredLeading(Icon(Icons.drive_file_rename_outline)), + title: const Text('Umbenennen'), + onTap: () { + Navigator.of(sheetCtx).pop(); + _rename(); + }, + ), + ListTile( + leading: const CenteredLeading(Icon(Icons.drive_file_move_outline)), + title: const Text('Verschieben'), + onTap: () { + Navigator.of(sheetCtx).pop(); + _putOnClipboard(copy: false); + }, + ), + ListTile( + leading: const CenteredLeading(Icon(Icons.copy_outlined)), + title: const Text('Kopieren'), + onTap: () { + Navigator.of(sheetCtx).pop(); + _putOnClipboard(copy: true); + }, + ), + ListTile( + leading: const CenteredLeading(Icon(Icons.delete_outline)), + title: const Text('Löschen'), + onTap: () { + Navigator.of(sheetCtx).pop(); + _delete(); + }, + ), + ], + ), + ), + ); + } + @override Widget build(BuildContext context) => ListTile( - leading: CenteredLeading( - Icon(widget.file.isDirectory ? Icons.folder : Icons.description_outlined) - ), - title: Text(widget.file.name, maxLines: 2, overflow: TextOverflow.ellipsis), - subtitle: getSubtitle(), - trailing: Icon(widget.file.isDirectory ? Icons.arrow_right : null), - onTap: () { - if(widget.file.isDirectory) { - AppRoutes.openFolder(context, widget.path.toList()..add(widget.file.name)); - } else { - if(EndpointData().getEndpointMode() == EndpointMode.stage) { - InfoDialog.show(context, 'Virtuelle Dateien im Staging Prozess können nicht heruntergeladen werden!'); - return; - } - if(widget.file.currentlyDownloading) { - showDialog( - context: context, - builder: (context) => ConfirmDialog( - title: 'Download abbrechen?', - content: 'Möchtest du den Download abbrechen?', - cancelButton: 'Nein', - confirmButton: 'Ja, Abbrechen', - onConfirm: () { - downloadCore?.then((value) { - if(!value.isCancelled) value.cancel(); - }); - setState(() { - widget.file.currentlyDownloading = false; - percent = 0; - downloadCore = null; - }); - }, - ), - ); - - return; - } - - setState(() { - widget.file.currentlyDownloading = true; - }); - - downloadCore = FileElement.download(context, widget.file.path, widget.file.name, (progress) { - setState(() => percent = progress); - }, (result) { - if(result.type != ResultType.done) { - showDialog(context: context, builder: (context) => AlertDialog( - title: const Text('Download'), - content: Text(result.message), - )); - } - - setState(() { - widget.file.currentlyDownloading = false; - percent = 0; - }); - }); - - } - }, - onLongPress: () { - showDialog(context: context, builder: (context) => SimpleDialog( - children: [ - ListTile( - leading: const Icon(Icons.delete_outline), - title: const Text('Löschen'), - onTap: () { - Navigator.of(context).pop(); - showDialog(context: context, builder: (context) => ConfirmDialog( - title: 'Element löschen?', - content: 'Das Element wird unwiederruflich gelöscht.', - confirmButton: 'Löschen', - onConfirmAsync: () async { - final webdav = await WebdavApi.webdav; - await webdav.delete(PathUri.parse(widget.file.path)); - widget.refetch(); - }, - )); - }, - ), - Visibility( - visible: !kReleaseMode, - child: ListTile( - leading: const Icon(Icons.share_outlined), - title: const Text('Teilen'), - onTap: () { - Navigator.of(context).pop(); - UnimplementedDialog.show(context); - }, - ), - ), - ], - )); - }, - ); + leading: CenteredLeading( + Icon(widget.file.isDirectory ? Icons.folder : Icons.description_outlined), + ), + title: Text(widget.file.name, maxLines: 2, overflow: TextOverflow.ellipsis), + subtitle: _subtitle(), + trailing: Icon(widget.file.isDirectory ? Icons.arrow_right : null), + onTap: _onTap, + onLongPress: _showActionSheet, + ); } diff --git a/lib/view/pages/grade_averages/grade_averages_list_view.dart b/lib/view/pages/grade_averages/grade_averages_list_view.dart index d4152b2..48c0def 100644 --- a/lib/view/pages/grade_averages/grade_averages_list_view.dart +++ b/lib/view/pages/grade_averages/grade_averages_list_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../state/app/modules/gradeAverages/bloc/grade_averages_bloc.dart'; -import '../../../state/app/modules/gradeAverages/bloc/grade_averages_event.dart'; +import '../../../state/app/modules/grade_averages/bloc/grade_averages_bloc.dart'; +import '../../../state/app/modules/grade_averages/bloc/grade_averages_event.dart'; class GradeAveragesListView extends StatelessWidget { const GradeAveragesListView({super.key}); diff --git a/lib/view/pages/grade_averages/grade_averages_view.dart b/lib/view/pages/grade_averages/grade_averages_view.dart index 1a49e0f..828536a 100644 --- a/lib/view/pages/grade_averages/grade_averages_view.dart +++ b/lib/view/pages/grade_averages/grade_averages_view.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../state/app/modules/gradeAverages/bloc/grade_averages_bloc.dart'; -import '../../../state/app/modules/gradeAverages/bloc/grade_averages_event.dart'; -import '../../../state/app/modules/gradeAverages/bloc/grade_averages_state.dart'; +import '../../../state/app/modules/grade_averages/bloc/grade_averages_bloc.dart'; +import '../../../state/app/modules/grade_averages/bloc/grade_averages_event.dart'; +import '../../../state/app/modules/grade_averages/bloc/grade_averages_state.dart'; import '../../../widget/confirm_dialog.dart'; import 'grade_averages_list_view.dart'; diff --git a/lib/view/pages/holidays/holidays_view.dart b/lib/view/pages/holidays/holidays_view.dart index 6e9515c..4fa4fac 100644 --- a/lib/view/pages/holidays/holidays_view.dart +++ b/lib/view/pages/holidays/holidays_view.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import '../../../state/app/infrastructure/loadableState/loadable_state.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; -import '../../../state/app/infrastructure/utilityWidgets/bloc_module.dart'; +import '../../../state/app/infrastructure/loadable_state/loadable_state.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart'; import '../../../state/app/modules/holidays/bloc/holidays_bloc.dart'; import '../../../state/app/modules/holidays/bloc/holidays_event.dart'; import '../../../state/app/modules/holidays/bloc/holidays_state.dart'; diff --git a/lib/view/pages/marianum_dates/marianum_dates_view.dart b/lib/view/pages/marianum_dates/marianum_dates_view.dart index 081d9e8..74e1a84 100644 --- a/lib/view/pages/marianum_dates/marianum_dates_view.dart +++ b/lib/view/pages/marianum_dates/marianum_dates_view.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import '../../../state/app/infrastructure/loadableState/loadable_state.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; -import '../../../state/app/infrastructure/utilityWidgets/bloc_module.dart'; -import '../../../state/app/modules/marianumDates/bloc/marianum_dates_bloc.dart'; -import '../../../state/app/modules/marianumDates/bloc/marianum_dates_event.dart'; -import '../../../state/app/modules/marianumDates/bloc/marianum_dates_state.dart'; +import '../../../state/app/infrastructure/loadable_state/loadable_state.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart'; +import '../../../state/app/modules/marianum_dates/bloc/marianum_dates_bloc.dart'; +import '../../../state/app/modules/marianum_dates/bloc/marianum_dates_event.dart'; +import '../../../state/app/modules/marianum_dates/bloc/marianum_dates_state.dart'; import '../../../widget/animated_time.dart'; import '../../../widget/centered_leading.dart'; import '../../../widget/debug/debug_tile.dart'; diff --git a/lib/view/pages/marianum_message/marianum_message_list_view.dart b/lib/view/pages/marianum_message/marianum_message_list_view.dart index 7164219..637e160 100644 --- a/lib/view/pages/marianum_message/marianum_message_list_view.dart +++ b/lib/view/pages/marianum_message/marianum_message_list_view.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import '../../../routing/app_routes.dart'; -import '../../../state/app/infrastructure/loadableState/loadable_state.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; -import '../../../state/app/infrastructure/utilityWidgets/bloc_module.dart'; -import '../../../state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart'; -import '../../../state/app/modules/marianumMessage/bloc/marianum_message_state.dart'; +import '../../../state/app/infrastructure/loadable_state/loadable_state.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart'; +import '../../../state/app/modules/marianum_message/bloc/marianum_message_bloc.dart'; +import '../../../state/app/modules/marianum_message/bloc/marianum_message_state.dart'; class MarianumMessageListView extends StatelessWidget { const MarianumMessageListView({super.key}); diff --git a/lib/view/pages/marianum_message/marianum_message_view.dart b/lib/view/pages/marianum_message/marianum_message_view.dart index f95712a..cb518d8 100644 --- a/lib/view/pages/marianum_message/marianum_message_view.dart +++ b/lib/view/pages/marianum_message/marianum_message_view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../../../state/app/modules/marianumMessage/bloc/marianum_message_state.dart'; +import '../../../state/app/modules/marianum_message/bloc/marianum_message_state.dart'; import '../../../widget/confirm_dialog.dart'; class MessageView extends StatefulWidget { diff --git a/lib/view/pages/more/feedback/feedback_dialog.dart b/lib/view/pages/more/feedback/feedback_dialog.dart index 373883c..42d082d 100644 --- a/lib/view/pages/more/feedback/feedback_dialog.dart +++ b/lib/view/pages/more/feedback/feedback_dialog.dart @@ -1,16 +1,17 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; +import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; import 'package:loader_overlay/loader_overlay.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:badges/badges.dart' as badges; -import '../../../../api/mhsl/server/feedback/addFeedback.dart'; -import '../../../../api/mhsl/server/feedback/addFeedbackParams.dart'; +import '../../../../api/mhsl/server/feedback/add_feedback.dart'; +import '../../../../api/mhsl/server/feedback/add_feedback_params.dart'; import '../../../../model/account_data.dart'; import '../../../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../../../widget/file_pick.dart'; @@ -129,7 +130,8 @@ class _FeedbackDialogState extends State { child: IconButton( onPressed: () async { context.loaderOverlay.show(); - var imageData = await (await FilePick.galleryPick())?.readAsBytes(); + final picked = await FilePick.multipleGalleryPick(); + final imageData = await picked?.first.readAsBytes(); if(context.mounted) context.loaderOverlay.hide(); setState(() { _image = imageData; @@ -148,26 +150,26 @@ class _FeedbackDialogState extends State { return; } context.loaderOverlay.show(); - AddFeedback( + unawaited(AddFeedback( AddFeedbackParams( user: AccountData().getUserSecret(), feedback: _feedbackInput.text, screenshot: _image != null ? base64Encode(_image!) : null, appVersion: int.parse((await PackageInfo.fromPlatform()).buildNumber), - ) + ), ).run().then((value) { if (!context.mounted) return; Navigator.of(context).pop(); InfoDialog.show(context, 'Danke für dein Feedback!'); context.loaderOverlay.hide(); - }).catchError((error, trace) { + }).catchError((Object error, StackTrace trace) { if (!mounted) return; setState(() { _error = error.toString(); }); if (!context.mounted) return; context.loaderOverlay.hide(); - }); + })); }, child: const Text('Senden'), ) diff --git a/lib/view/pages/overhang.dart b/lib/view/pages/overhang.dart index f27445f..2489fa4 100644 --- a/lib/view/pages/overhang.dart +++ b/lib/view/pages/overhang.dart @@ -2,18 +2,18 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:in_app_review/in_app_review.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../extensions/render_not_null.dart'; +import 'package:in_app_review/in_app_review.dart'; +import '../../extensions/render_not_null.dart'; import '../../routing/app_routes.dart'; import '../../state/app/modules/app_modules.dart'; import '../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../storage/settings.dart' as model; import '../../widget/centered_leading.dart'; import '../../widget/info_dialog.dart'; -import 'settings/data/default_settings.dart'; import 'more/share/select_share_type_dialog.dart'; +import 'settings/data/default_settings.dart'; class Overhang extends StatefulWidget { const Overhang({super.key}); @@ -50,7 +50,11 @@ class _OverhangState extends State { final settings = context.read(); void changeVisibility(Modules module) { var hidden = settings.val(write: true).modulesSettings.hiddenModules; - hidden.contains(module) ? hidden.remove(module) : (hidden.length < 3 ? hidden.add(module) : null); + if (hidden.contains(module)) { + hidden.remove(module); + } else if (hidden.length < 3) { + hidden.add(module); + } } return ReorderableListView( diff --git a/lib/view/pages/settings/data/default_settings.dart b/lib/view/pages/settings/data/default_settings.dart index 49640a1..63e060a 100644 --- a/lib/view/pages/settings/data/default_settings.dart +++ b/lib/view/pages/settings/data/default_settings.dart @@ -3,16 +3,16 @@ import 'dart:io'; import 'package:flutter/material.dart'; import '../../../../state/app/modules/app_modules.dart'; -import '../../../../storage/settings.dart'; import '../../../../storage/dev_tools_settings.dart'; import '../../../../storage/file_settings.dart'; import '../../../../storage/file_view_settings.dart'; -import '../../../../storage/modules_settings.dart'; import '../../../../storage/holidays_settings.dart'; +import '../../../../storage/modules_settings.dart'; import '../../../../storage/notification_settings.dart'; +import '../../../../storage/settings.dart'; import '../../../../storage/talk_settings.dart'; -import '../../../../view/pages/timetable/data/timetable_name_mode.dart'; import '../../../../storage/timetable_settings.dart'; +import '../../../../view/pages/timetable/data/timetable_name_mode.dart'; import '../../files/files.dart'; class DefaultSettings { diff --git a/lib/view/pages/settings/sections/account_section.dart b/lib/view/pages/settings/sections/account_section.dart index 6cc6846..7067c22 100644 --- a/lib/view/pages/settings/sections/account_section.dart +++ b/lib/view/pages/settings/sections/account_section.dart @@ -31,9 +31,10 @@ class AccountSection extends StatelessWidget { await prefs.clear(); PaintingBinding.instance.imageCache.clear(); if (!context.mounted) return; - context.read().reset(); - const CacheView().clear(); - AccountData().removeData(context: context); + await context.read().reset(); + await const CacheView().clear(); + if (!context.mounted) return; + await AccountData().removeData(context: context); }, ), ); diff --git a/lib/view/pages/settings/sections/dev_tools_section.dart b/lib/view/pages/settings/sections/dev_tools_section.dart index 95d8efd..7cada5f 100644 --- a/lib/view/pages/settings/sections/dev_tools_section.dart +++ b/lib/view/pages/settings/sections/dev_tools_section.dart @@ -1,8 +1,8 @@ import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; -import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; import '../../../../routing/app_routes.dart'; import '../../../../state/app/modules/settings/bloc/settings_cubit.dart'; diff --git a/lib/view/pages/talk/chat_list.dart b/lib/view/pages/talk/chat_list.dart index 7037624..4a01136 100644 --- a/lib/view/pages/talk/chat_list.dart +++ b/lib/view/pages/talk/chat_list.dart @@ -3,20 +3,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_split_view/flutter_split_view.dart'; -import '../../../routing/app_routes.dart'; -import '../../../state/app/infrastructure/loadableState/loadable_state.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; -import '../../../state/app/infrastructure/utilityWidgets/bloc_module.dart'; -import '../../../state/app/modules/chatList/bloc/chat_list_bloc.dart'; -import '../../../state/app/modules/chatList/bloc/chat_list_state.dart'; import '../../../notification/notify_updater.dart'; +import '../../../routing/app_routes.dart'; +import '../../../state/app/infrastructure/loadable_state/loadable_state.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart'; +import '../../../state/app/modules/chat_list/bloc/chat_list_bloc.dart'; +import '../../../state/app/modules/chat_list/bloc/chat_list_state.dart'; import '../../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../../widget/confirm_dialog.dart'; import '../../../widget/placeholder_view.dart'; -import 'widgets/chat_tile.dart'; -import 'widgets/split_view_placeholder.dart'; import 'join_chat.dart'; import 'search_chat.dart'; +import 'widgets/chat_tile.dart'; +import 'widgets/split_view_placeholder.dart'; class ChatList extends StatelessWidget { const ChatList({super.key}); diff --git a/lib/view/pages/talk/chat_view.dart b/lib/view/pages/talk/chat_view.dart index e332150..0b8c78c 100644 --- a/lib/view/pages/talk/chat_view.dart +++ b/lib/view/pages/talk/chat_view.dart @@ -1,19 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../api/marianumcloud/talk/chat/getChatResponse.dart'; -import '../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../api/marianumcloud/talk/chat/get_chat_response.dart'; +import '../../../api/marianumcloud/talk/room/get_room_response.dart'; import '../../../extensions/date_time.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; import '../../../state/app/modules/chat/bloc/chat_bloc.dart'; import '../../../state/app/modules/chat/bloc/chat_state.dart'; import '../../../theming/app_theme.dart'; import '../../../widget/clickable_app_bar.dart'; import '../../../widget/user_avatar.dart'; import 'details/chat_info.dart'; +import 'talk_navigator.dart'; import 'widgets/chat_bubble.dart'; import 'widgets/chat_textfield.dart'; -import 'talk_navigator.dart'; class ChatView extends StatefulWidget { final GetRoomResponseObject room; @@ -99,7 +99,7 @@ class _ChatViewState extends State { ), ), ), - body: Container( + body: DecoratedBox( decoration: BoxDecoration( image: DecorationImage( image: const AssetImage('assets/background/chat.png'), @@ -122,7 +122,7 @@ class _ChatViewState extends State { ), ), ), - Container( + ColoredBox( color: Theme.of(context).colorScheme.surface, child: TalkNavigator.isSecondaryVisible(context) ? ChatTextfield(widget.room.token, selfId: widget.selfId) diff --git a/lib/view/pages/talk/data/chat_bubble_styles.dart b/lib/view/pages/talk/data/chat_bubble_styles.dart index bad0822..33db5e7 100644 --- a/lib/view/pages/talk/data/chat_bubble_styles.dart +++ b/lib/view/pages/talk/data/chat_bubble_styles.dart @@ -1,7 +1,7 @@ -import 'package:bubble/bubble.dart'; import 'package:flutter/material.dart'; import '../../../../theming/app_theme.dart'; +import '../widgets/bubble.dart'; extension ColorExtensions on Color { Color invert() { diff --git a/lib/view/pages/talk/data/chat_message.dart b/lib/view/pages/talk/data/chat_message.dart index 358e289..66b5aa1 100644 --- a/lib/view/pages/talk/data/chat_message.dart +++ b/lib/view/pages/talk/data/chat_message.dart @@ -3,8 +3,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; -import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; -import '../../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; +import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; +import '../../../../api/marianumcloud/talk/chat/rich_object_string_processor.dart'; import '../../../../model/account_data.dart'; import '../../../../model/endpoint_data.dart'; import '../../../../utils/url_opener.dart'; diff --git a/lib/view/pages/talk/details/chat_info.dart b/lib/view/pages/talk/details/chat_info.dart index 74afa79..820ee1a 100644 --- a/lib/view/pages/talk/details/chat_info.dart +++ b/lib/view/pages/talk/details/chat_info.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../../api/marianumcloud/talk/getParticipants/getParticipantsCache.dart'; -import '../../../../api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart'; -import '../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../../api/marianumcloud/talk/get_participants/get_participants_cache.dart'; +import '../../../../api/marianumcloud/talk/get_participants/get_participants_response.dart'; +import '../../../../api/marianumcloud/talk/room/get_room_response.dart'; import '../../../../widget/large_profile_picture_view.dart'; import '../../../../widget/loading_spinner.dart'; import '../../../../widget/user_avatar.dart'; diff --git a/lib/view/pages/talk/details/message_reactions.dart b/lib/view/pages/talk/details/message_reactions.dart index 29158f3..7f87faf 100644 --- a/lib/view/pages/talk/details/message_reactions.dart +++ b/lib/view/pages/talk/details/message_reactions.dart @@ -1,8 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import '../../../../api/marianumcloud/talk/getReactions/getReactions.dart'; -import '../../../../api/marianumcloud/talk/getReactions/getReactionsResponse.dart'; +import '../../../../api/marianumcloud/talk/get_reactions/get_reactions.dart'; +import '../../../../api/marianumcloud/talk/get_reactions/get_reactions_response.dart'; import '../../../../model/account_data.dart'; import '../../../../widget/centered_leading.dart'; import '../../../../widget/loading_spinner.dart'; diff --git a/lib/view/pages/talk/details/participants_list_view.dart b/lib/view/pages/talk/details/participants_list_view.dart index e58d1e5..e2ec3d7 100644 --- a/lib/view/pages/talk/details/participants_list_view.dart +++ b/lib/view/pages/talk/details/participants_list_view.dart @@ -1,7 +1,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import '../../../../api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart'; +import '../../../../api/marianumcloud/talk/get_participants/get_participants_response.dart'; import '../../../../widget/user_avatar.dart'; class ParticipantsListView extends StatelessWidget { @@ -10,7 +10,7 @@ class ParticipantsListView extends StatelessWidget { @override Widget build(BuildContext context) { - lastname(participant) => participant.displayName.toString().split(' ').last; + String lastname(participant) => participant.displayName.toString().split(' ').last; final participants = participantsResponse.data .sorted((a, b) { diff --git a/lib/view/pages/talk/join_chat.dart b/lib/view/pages/talk/join_chat.dart index f937bc3..56e99bb 100644 --- a/lib/view/pages/talk/join_chat.dart +++ b/lib/view/pages/talk/join_chat.dart @@ -3,8 +3,8 @@ import 'package:async/async.dart'; import 'package:flutter/material.dart'; import '../../../api/errors/error_mapper.dart'; -import '../../../api/marianumcloud/autocomplete/autocompleteApi.dart'; -import '../../../api/marianumcloud/autocomplete/autocompleteResponse.dart'; +import '../../../api/marianumcloud/autocomplete/autocomplete_api.dart'; +import '../../../api/marianumcloud/autocomplete/autocomplete_response.dart'; import '../../../model/endpoint_data.dart'; import '../../../widget/app_progress_indicator.dart'; import '../../../widget/placeholder_view.dart'; diff --git a/lib/view/pages/talk/search_chat.dart b/lib/view/pages/talk/search_chat.dart index 68976fc..0a58622 100644 --- a/lib/view/pages/talk/search_chat.dart +++ b/lib/view/pages/talk/search_chat.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import '../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../api/marianumcloud/talk/room/get_room_response.dart'; import 'widgets/chat_tile.dart'; -class SearchChat extends SearchDelegate { +class SearchChat extends SearchDelegate { List chats; SearchChat(this.chats); diff --git a/lib/view/pages/talk/widgets/answer_reference.dart b/lib/view/pages/talk/widgets/answer_reference.dart index 825ad18..8171d14 100644 --- a/lib/view/pages/talk/widgets/answer_reference.dart +++ b/lib/view/pages/talk/widgets/answer_reference.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; -import '../../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; +import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; +import '../../../../api/marianumcloud/talk/chat/rich_object_string_processor.dart'; import '../data/chat_bubble_styles.dart'; class AnswerReference extends StatelessWidget { diff --git a/lib/view/pages/talk/widgets/bubble.dart b/lib/view/pages/talk/widgets/bubble.dart new file mode 100644 index 0000000..408564d --- /dev/null +++ b/lib/view/pages/talk/widgets/bubble.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; + +enum BubbleNip { leftTop, rightBottom, none } + +class BubbleEdges { + const BubbleEdges.only({this.top = 0, this.bottom = 0, this.left = 0, this.right = 0}); + const BubbleEdges.all(double value) + : top = value, + bottom = value, + left = value, + right = value; + + final double top; + final double bottom; + final double left; + final double right; + + EdgeInsets toEdgeInsets() => EdgeInsets.fromLTRB(left, top, right, bottom); +} + +class BubbleStyle { + const BubbleStyle({ + this.color, + this.borderWidth = 0, + this.elevation = 0, + this.margin = const BubbleEdges.only(), + this.padding = const BubbleEdges.all(8), + this.alignment = Alignment.centerLeft, + this.nip = BubbleNip.none, + this.borderRadius = 12, + }); + + final Color? color; + final double borderWidth; + final double elevation; + final BubbleEdges margin; + final BubbleEdges padding; + final Alignment alignment; + final BubbleNip nip; + final double borderRadius; +} + +/// Lightweight chat bubble. Replaces the abandoned `bubble` package: renders a +/// rounded container with optional shadow / border. The nip is conveyed by +/// flattening one corner so the bubble visually anchors to the speaker side. +class Bubble extends StatelessWidget { + const Bubble({required this.child, required this.style, super.key}); + + final Widget child; + final BubbleStyle style; + + BorderRadius _radius() { + final r = Radius.circular(style.borderRadius); + final flat = Radius.zero; + switch (style.nip) { + case BubbleNip.leftTop: + return BorderRadius.only(topLeft: flat, topRight: r, bottomLeft: r, bottomRight: r); + case BubbleNip.rightBottom: + return BorderRadius.only(topLeft: r, topRight: r, bottomLeft: r, bottomRight: flat); + case BubbleNip.none: + return BorderRadius.all(r); + } + } + + @override + Widget build(BuildContext context) { + final radius = _radius(); + return Align( + alignment: style.alignment, + child: Container( + margin: style.margin.toEdgeInsets(), + decoration: BoxDecoration( + color: style.color, + borderRadius: radius, + border: style.borderWidth > 0 + ? Border.all(color: Theme.of(context).dividerColor, width: style.borderWidth) + : null, + boxShadow: style.elevation > 0 + ? [BoxShadow(color: Colors.black26, blurRadius: style.elevation * 2, offset: Offset(0, style.elevation))] + : null, + ), + padding: style.padding.toEdgeInsets(), + child: child, + ), + ); + } +} diff --git a/lib/view/pages/talk/widgets/chat_bubble.dart b/lib/view/pages/talk/widgets/chat_bubble.dart index ede8081..1ea1dab 100644 --- a/lib/view/pages/talk/widgets/chat_bubble.dart +++ b/lib/view/pages/talk/widgets/chat_bubble.dart @@ -1,25 +1,24 @@ -import 'package:bubble/bubble.dart'; -import 'package:flowder/flowder.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jiffy/jiffy.dart'; -import 'package:open_filex/open_filex.dart'; -import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; -import '../../../../api/marianumcloud/talk/deleteReactMessage/deleteReactMessage.dart'; -import '../../../../api/marianumcloud/talk/deleteReactMessage/deleteReactMessageParams.dart'; -import '../../../../api/marianumcloud/talk/getPoll/getPollState.dart'; -import '../../../../api/marianumcloud/talk/reactMessage/reactMessage.dart'; -import '../../../../api/marianumcloud/talk/reactMessage/reactMessageParams.dart'; -import '../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; +import '../../../../api/marianumcloud/talk/delete_react_message/delete_react_message.dart'; +import '../../../../api/marianumcloud/talk/delete_react_message/delete_react_message_params.dart'; +import '../../../../api/marianumcloud/talk/get_poll/get_poll_state.dart'; +import '../../../../api/marianumcloud/talk/react_message/react_message.dart'; +import '../../../../api/marianumcloud/talk/react_message/react_message_params.dart'; +import '../../../../api/marianumcloud/talk/room/get_room_response.dart'; import '../../../../extensions/text.dart'; +import '../../../../routing/app_routes.dart'; import '../../../../state/app/modules/chat/bloc/chat_bloc.dart'; +import '../../../../utils/download_manager.dart'; import '../../../../widget/async_action_button.dart'; import '../../../../widget/loading_spinner.dart'; -import '../../files/widgets/file_element.dart'; import '../data/chat_bubble_styles.dart'; import '../data/chat_message.dart'; import 'answer_reference.dart'; +import 'bubble.dart'; import 'chat_message_options_dialog.dart'; import 'poll_options_list.dart'; @@ -53,12 +52,95 @@ class ChatBubble extends StatefulWidget { class _ChatBubbleState extends State with SingleTickerProviderStateMixin { late ChatMessage message; - double downloadProgress = 0; - Future? downloadCore; + DownloadJob? _job; late Offset _position = const Offset(0, 0); late Offset _dragStartPosition = Offset.zero; + @override + void initState() { + super.initState(); + final filePath = widget.bubbleData.messageParameters?['file']?.path; + if (filePath != null) _attachJob(DownloadManager.instance.jobFor(filePath)); + } + + @override + void dispose() { + _detachJob(); + super.dispose(); + } + + void _attachJob(DownloadJob? job) { + _job = job; + if (job == null) return; + job.status.addListener(_onStatusChange); + if (job.isFinished) { + WidgetsBinding.instance.addPostFrameCallback((_) => _onStatusChange()); + } + } + + void _detachJob() { + _job?.status.removeListener(_onStatusChange); + _job = null; + } + + void _onStatusChange() { + if (!mounted) return; + final job = _job; + if (job == null) return; + final status = job.status.value; + if (status is DownloadDone) { + DownloadManager.instance.clear(job.remotePath); + _detachJob(); + AppRoutes.openFileViewer(context, status.localPath); + setState(() {}); + } else if (status is DownloadFailed) { + final message = status.message; + DownloadManager.instance.clear(job.remotePath); + _detachJob(); + setState(() {}); + showDialog(context: context, builder: (context) => AlertDialog(content: Text(message))); + } else if (status is DownloadCancelled) { + DownloadManager.instance.clear(job.remotePath); + _detachJob(); + setState(() {}); + } else { + setState(() {}); + } + } + + Future _startFileDownload() async { + final file = message.file; + final filePath = file?.path; + if (file == null || filePath == null) return; + final job = await DownloadManager.instance.start(remotePath: filePath, name: file.name); + if (!mounted) return; + if (_job == job) return; + _detachJob(); + _attachJob(job); + setState(() {}); + } + + void _confirmCancel() { + showDialog( + context: context, + builder: (dialogContext) => AlertDialog( + title: const Text('Download abbrechen?'), + content: const Text('Möchtest du den Download abbrechen?'), + actions: [ + TextButton(onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Nein')), + TextButton( + onPressed: () { + Navigator.of(dialogContext).pop(); + _job?.cancel(); + }, + child: const Text('Ja, Abbrechen'), + ), + ], + ), + ); + } + BubbleStyle getStyle() { var styles = ChatBubbleStyles(context); if(widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment) { @@ -162,53 +244,12 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM )); } - if(message.file == null) return; - - if(downloadProgress > 0) { - showDialog(context: context, builder: (context) => AlertDialog( - title: const Text('Download abbrechen?'), - content: const Text('Möchtest du den Download abbrechen?'), - actions: [ - TextButton(onPressed: () { - Navigator.of(context).pop(); - }, child: const Text('Nein')), - TextButton(onPressed: () { - downloadCore?.then((value) { - if(!value.isCancelled) value.cancel(); - if (!context.mounted) return; - Navigator.of(context).pop(); - }); - setState(() { - downloadProgress = 0; - downloadCore = null; - }); - }, child: const Text('Ja, Abbrechen')) - ], - )); - - return; + if (message.file == null) return; + if (_job?.status.value is DownloadInProgress) { + _confirmCancel(); + } else { + _startFileDownload(); } - - setState(() { - downloadProgress = 1; - }); - downloadCore = FileElement.download(context, message.file!.path!, message.file!.name, (progress) { - if(progress > 1) { - setState(() { - downloadProgress = progress; - }); - } - }, (result) { - setState(() { - downloadProgress = 0; - }); - - if(result.type != ResultType.done) { - showDialog(context: context, builder: (context) => AlertDialog( - content: Text(result.message), - )); - } - }); }, child: Transform.translate( offset: _position, @@ -270,15 +311,18 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM ) ), ), - Visibility( - visible: downloadProgress > 0, - child: Positioned( + if (_job?.status.value is DownloadInProgress) + Positioned( bottom: 0, right: 0, left: 0, - child: LinearProgressIndicator(value: downloadProgress == 1 ? null : downloadProgress/100), + child: LinearProgressIndicator( + value: () { + final s = _job!.status.value as DownloadInProgress; + return s.percent <= 0 ? null : s.percent / 100; + }(), + ), ), - ), ], ), ), diff --git a/lib/view/pages/talk/widgets/chat_message_options_dialog.dart b/lib/view/pages/talk/widgets/chat_message_options_dialog.dart index 7f7fa39..f5c5802 100644 --- a/lib/view/pages/talk/widgets/chat_message_options_dialog.dart +++ b/lib/view/pages/talk/widgets/chat_message_options_dialog.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; import '../../../../api/marianumcloud/talk/actions/talk_actions.dart'; -import '../../../../api/marianumcloud/talk/reactMessage/reactMessage.dart'; -import '../../../../api/marianumcloud/talk/reactMessage/reactMessageParams.dart'; -import '../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart'; +import '../../../../api/marianumcloud/talk/react_message/react_message.dart'; +import '../../../../api/marianumcloud/talk/react_message/react_message_params.dart'; +import '../../../../api/marianumcloud/talk/room/get_room_response.dart'; import '../../../../routing/app_routes.dart'; import '../../../../state/app/modules/chat/bloc/chat_bloc.dart'; import '../../../../widget/app_progress_indicator.dart'; diff --git a/lib/view/pages/talk/widgets/chat_textfield.dart b/lib/view/pages/talk/widgets/chat_textfield.dart index 266958d..a821cf1 100644 --- a/lib/view/pages/talk/widgets/chat_textfield.dart +++ b/lib/view/pages/talk/widgets/chat_textfield.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -5,11 +6,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import '../../../../api/marianumcloud/files-sharing/fileSharingApi.dart'; -import '../../../../api/marianumcloud/files-sharing/fileSharingApiParams.dart'; -import '../../../../api/marianumcloud/talk/sendMessage/sendMessage.dart'; -import '../../../../api/marianumcloud/talk/sendMessage/sendMessageParams.dart'; -import '../../../../api/marianumcloud/webdav/webdavApi.dart'; +import '../../../../api/marianumcloud/files_sharing/file_sharing_api.dart'; +import '../../../../api/marianumcloud/files_sharing/file_sharing_api_params.dart'; +import '../../../../api/marianumcloud/talk/send_message/send_message.dart'; +import '../../../../api/marianumcloud/talk/send_message/send_message_params.dart'; +import '../../../../api/marianumcloud/webdav/webdav_api.dart'; import '../../../../state/app/modules/chat/bloc/chat_bloc.dart'; import '../../../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../../../widget/async_action_button.dart'; @@ -51,10 +52,10 @@ class _ChatTextfieldState extends State { if (paths == null) return; const shareFolder = 'MarianumMobile'; - WebdavApi.webdav.then((webdav) => webdav.mkcol(PathUri.parse('/$shareFolder'))); + unawaited(WebdavApi.webdav.then((webdav) => webdav.mkcol(PathUri.parse('/$shareFolder')))); if (!mounted) return; - pushScreen( + unawaited(pushScreen( context, withNavBar: false, screen: FilesUploadDialog( @@ -63,7 +64,7 @@ class _ChatTextfieldState extends State { onUploadFinished: (uploaded) => share(shareFolder, uploaded), uniqueNames: true, ), - ); + )); } void _setDraft(String text) { diff --git a/lib/view/pages/talk/widgets/chat_tile.dart b/lib/view/pages/talk/widgets/chat_tile.dart index b4f9c8d..0f44672 100644 --- a/lib/view/pages/talk/widgets/chat_tile.dart +++ b/lib/view/pages/talk/widgets/chat_tile.dart @@ -5,13 +5,13 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jiffy/jiffy.dart'; import '../../../../api/marianumcloud/talk/actions/talk_actions.dart'; -import '../../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; -import '../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; -import '../../../../api/marianumcloud/talk/setReadMarker/setReadMarker.dart'; -import '../../../../api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart'; +import '../../../../api/marianumcloud/talk/chat/rich_object_string_processor.dart'; +import '../../../../api/marianumcloud/talk/room/get_room_response.dart'; +import '../../../../api/marianumcloud/talk/set_read_marker/set_read_marker.dart'; +import '../../../../api/marianumcloud/talk/set_read_marker/set_read_marker_params.dart'; import '../../../../model/account_data.dart'; import '../../../../state/app/modules/chat/bloc/chat_bloc.dart'; -import '../../../../state/app/modules/chatList/bloc/chat_list_bloc.dart'; +import '../../../../state/app/modules/chat_list/bloc/chat_list_bloc.dart'; import '../../../../widget/async_action_button.dart'; import '../../../../widget/confirm_dialog.dart'; import '../../../../widget/debug/debug_tile.dart'; diff --git a/lib/view/pages/talk/widgets/poll_options_list.dart b/lib/view/pages/talk/widgets/poll_options_list.dart index 4bade65..44f4162 100644 --- a/lib/view/pages/talk/widgets/poll_options_list.dart +++ b/lib/view/pages/talk/widgets/poll_options_list.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; -import '../../../../api/marianumcloud/talk/getPoll/getPollStateResponse.dart'; +import '../../../../api/marianumcloud/talk/get_poll/get_poll_state_response.dart'; import '../../../../utils/url_opener.dart'; class PollOptionsList extends StatefulWidget { diff --git a/lib/view/pages/timetable/custom_events/custom_event_edit_dialog.dart b/lib/view/pages/timetable/custom_events/custom_event_edit_dialog.dart index 34479ca..caa6e62 100644 --- a/lib/view/pages/timetable/custom_events/custom_event_edit_dialog.dart +++ b/lib/view/pages/timetable/custom_events/custom_event_edit_dialog.dart @@ -6,7 +6,7 @@ import 'package:jiffy/jiffy.dart'; import 'package:rrule_generator/rrule_generator.dart'; import 'package:time_range_picker/time_range_picker.dart'; -import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; +import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; import '../../../../extensions/date_time.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart'; import '../../../../widget/focus_behaviour.dart'; @@ -151,6 +151,7 @@ class _CustomEventEditDialogState extends State { selectedColor: Theme.of(context).primaryColor, ticks: 24, ); + if (range is! TimeRange) return; setState(() { _startTime = range.startTime; _endTime = range.endTime; diff --git a/lib/view/pages/timetable/custom_events/custom_events_view.dart b/lib/view/pages/timetable/custom_events/custom_events_view.dart index a1c6595..a7c4272 100644 --- a/lib/view/pages/timetable/custom_events/custom_events_view.dart +++ b/lib/view/pages/timetable/custom_events/custom_events_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import '../../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; +import '../../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_state.dart'; import '../../../../widget/centered_leading.dart'; diff --git a/lib/view/pages/timetable/data/arbitrary_appointment.dart b/lib/view/pages/timetable/data/arbitrary_appointment.dart index d9bb91a..6d2320a 100644 --- a/lib/view/pages/timetable/data/arbitrary_appointment.dart +++ b/lib/view/pages/timetable/data/arbitrary_appointment.dart @@ -1,5 +1,5 @@ -import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; -import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; +import '../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; sealed class ArbitraryAppointment { const ArbitraryAppointment(); diff --git a/lib/view/pages/timetable/data/lesson_period_schedule.dart b/lib/view/pages/timetable/data/lesson_period_schedule.dart index b163226..01b3f47 100644 --- a/lib/view/pages/timetable/data/lesson_period_schedule.dart +++ b/lib/view/pages/timetable/data/lesson_period_schedule.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart'; +import '../../../../api/webuntis/queries/get_timegrid_units/get_timegrid_units_response.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_state.dart'; class LessonPeriod { diff --git a/lib/view/pages/timetable/data/lesson_status.dart b/lib/view/pages/timetable/data/lesson_status.dart index 933f3cb..39eeb37 100644 --- a/lib/view/pages/timetable/data/lesson_status.dart +++ b/lib/view/pages/timetable/data/lesson_status.dart @@ -1,4 +1,4 @@ -import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; enum LessonStatus { cancelled, diff --git a/lib/view/pages/timetable/data/timetable_appointment_factory.dart b/lib/view/pages/timetable/data/timetable_appointment_factory.dart index 3d14aa8..29cebf2 100644 --- a/lib/view/pages/timetable/data/timetable_appointment_factory.dart +++ b/lib/view/pages/timetable/data/timetable_appointment_factory.dart @@ -1,16 +1,16 @@ import 'package:collection/collection.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; -import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; -import '../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import '../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; +import '../../../../api/webuntis/queries/get_rooms/get_rooms_response.dart'; +import '../../../../api/webuntis/queries/get_subjects/get_subjects_response.dart'; +import '../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; import '../../../../storage/timetable_settings.dart'; -import 'timetable_name_mode.dart'; import '../custom_events/custom_event_colors.dart'; import 'arbitrary_appointment.dart'; import 'lesson_color.dart'; import 'lesson_status.dart'; +import 'timetable_name_mode.dart'; import 'webuntis_time.dart'; class TimetableAppointmentFactory { diff --git a/lib/view/pages/timetable/details/_bottom_sheet.dart b/lib/view/pages/timetable/details/_bottom_sheet.dart deleted file mode 100644 index c50f3f0..0000000 --- a/lib/view/pages/timetable/details/_bottom_sheet.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:bottom_sheet/bottom_sheet.dart'; -import 'package:flutter/material.dart'; - -void showAppointmentBottomSheet( - BuildContext context, { - required Widget Function(BuildContext context) header, - required SliverChildListDelegate Function(BuildContext context) body, -}) { - showStickyFlexibleBottomSheet( - minHeight: 0, - initHeight: 0.4, - maxHeight: 0.7, - anchors: [0, 0.4, 0.7], - isSafeArea: true, - maxHeaderHeight: 100, - context: context, - headerBuilder: (context, _) => header(context), - bodyBuilder: (context, _) => body(context), - ); -} diff --git a/lib/view/pages/timetable/details/bottom_sheet.dart b/lib/view/pages/timetable/details/bottom_sheet.dart new file mode 100644 index 0000000..d834a09 --- /dev/null +++ b/lib/view/pages/timetable/details/bottom_sheet.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +void showAppointmentBottomSheet( + BuildContext context, { + required Widget Function(BuildContext context) header, + required SliverChildListDelegate Function(BuildContext context) body, +}) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + backgroundColor: Theme.of(context).colorScheme.surface, + builder: (sheetContext) => DraggableScrollableSheet( + expand: false, + initialChildSize: 0.4, + minChildSize: 0.2, + maxChildSize: 0.7, + snap: true, + snapSizes: const [0.4], + builder: (_, scrollController) => CustomScrollView( + controller: scrollController, + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: _StickyHeader(child: header(sheetContext)), + ), + SliverList(delegate: body(sheetContext)), + ], + ), + ), + ); +} + +class _StickyHeader extends SliverPersistentHeaderDelegate { + _StickyHeader({required this.child}); + final Widget child; + + @override + double get minExtent => 100; + @override + double get maxExtent => 100; + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) => Material( + color: Theme.of(context).colorScheme.surface, + child: SizedBox.expand(child: child), + ); + + @override + bool shouldRebuild(covariant _StickyHeader oldDelegate) => oldDelegate.child != child; +} diff --git a/lib/view/pages/timetable/details/custom_event_sheet.dart b/lib/view/pages/timetable/details/custom_event_sheet.dart index 3675abf..ce52d8c 100644 --- a/lib/view/pages/timetable/details/custom_event_sheet.dart +++ b/lib/view/pages/timetable/details/custom_event_sheet.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; import 'package:rrule/rrule.dart'; -import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; +import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; import '../../../../widget/centered_leading.dart'; import '../../../../widget/debug/debug_tile.dart'; import '../custom_events/custom_event_edit_dialog.dart'; -import '_bottom_sheet.dart'; +import 'bottom_sheet.dart'; import 'delete_custom_event.dart'; class CustomEventSheet { diff --git a/lib/view/pages/timetable/details/delete_custom_event.dart b/lib/view/pages/timetable/details/delete_custom_event.dart index 7d29e86..7361a70 100644 --- a/lib/view/pages/timetable/details/delete_custom_event.dart +++ b/lib/view/pages/timetable/details/delete_custom_event.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart'; +import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart'; import '../../../../widget/confirm_dialog.dart'; diff --git a/lib/view/pages/timetable/details/webuntis_lesson_sheet.dart b/lib/view/pages/timetable/details/webuntis_lesson_sheet.dart index 7adfee6..13885c9 100644 --- a/lib/view/pages/timetable/details/webuntis_lesson_sheet.dart +++ b/lib/view/pages/timetable/details/webuntis_lesson_sheet.dart @@ -3,15 +3,15 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; -import '../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import '../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../../api/webuntis/queries/get_rooms/get_rooms_response.dart'; +import '../../../../api/webuntis/queries/get_subjects/get_subjects_response.dart'; +import '../../../../api/webuntis/queries/get_timetable/get_timetable_response.dart'; import '../../../../routing/app_routes.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart'; import '../../../../state/app/modules/timetable/bloc/timetable_state.dart'; import '../../../../widget/debug/debug_tile.dart'; import '../../../../widget/unimplemented_dialog.dart'; -import '_bottom_sheet.dart'; +import 'bottom_sheet.dart'; class WebuntisLessonSheet { static void show(BuildContext context, TimetableBloc bloc, Appointment appointment, GetTimetableResponseObject lesson) { diff --git a/lib/view/pages/timetable/timetable.dart b/lib/view/pages/timetable/timetable.dart index ee4ed3a..cd119e6 100644 --- a/lib/view/pages/timetable/timetable.dart +++ b/lib/view/pages/timetable/timetable.dart @@ -4,10 +4,10 @@ import 'package:syncfusion_flutter_calendar/calendar.dart'; import '../../../extensions/date_time.dart'; import '../../../routing/app_routes.dart'; -import '../../../state/app/infrastructure/loadableState/view/loadable_state_consumer.dart'; +import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart'; +import '../../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../../state/app/modules/timetable/bloc/timetable_bloc.dart'; import '../../../state/app/modules/timetable/bloc/timetable_state.dart'; -import '../../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../../storage/timetable_settings.dart'; import 'custom_events/custom_event_edit_dialog.dart'; import 'data/arbitrary_appointment.dart'; diff --git a/lib/view/pages/timetable/widgets/appointment_tile.dart b/lib/view/pages/timetable/widgets/appointment_tile.dart index e34a5d5..d185f5c 100644 --- a/lib/view/pages/timetable/widgets/appointment_tile.dart +++ b/lib/view/pages/timetable/widgets/appointment_tile.dart @@ -56,7 +56,7 @@ class AppointmentTile extends StatelessWidget { ), if (crossedOut) Positioned.fill( - child: Container( + child: DecoratedBox( decoration: BoxDecoration( border: Border.all(width: 2, color: Colors.red.withAlpha(200)), borderRadius: const BorderRadius.all(Radius.circular(7)), diff --git a/lib/view/pages/timetable/widgets/custom_workweek_calendar.dart b/lib/view/pages/timetable/widgets/custom_workweek_calendar.dart index 18ed146..85ce55e 100644 --- a/lib/view/pages/timetable/widgets/custom_workweek_calendar.dart +++ b/lib/view/pages/timetable/widgets/custom_workweek_calendar.dart @@ -392,7 +392,7 @@ class _PeriodLabel extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { final showTimes = constraints.maxHeight >= 38; - return Container( + return DecoratedBox( decoration: BoxDecoration( border: Border(top: BorderSide(color: dividerColor, width: 0.5)), ), @@ -561,7 +561,7 @@ class _DayColumn extends StatelessWidget { return GestureDetector( behavior: HitTestBehavior.translucent, onLongPressStart: (details) => _handleLongPress(details, dayAppointments), - child: Container( + child: DecoratedBox( decoration: BoxDecoration( color: isToday ? theme.colorScheme.primary.withAlpha(14) : null, border: Border(left: BorderSide(color: theme.dividerColor.withAlpha(90), width: 0.5)), diff --git a/lib/view/pages/timetable/widgets/special_regions_builder.dart b/lib/view/pages/timetable/widgets/special_regions_builder.dart index 2007f46..bffdfb7 100644 --- a/lib/view/pages/timetable/widgets/special_regions_builder.dart +++ b/lib/view/pages/timetable/widgets/special_regions_builder.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; -import '../../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; +import '../../../../api/webuntis/queries/get_holidays/get_holidays_response.dart'; import '../../../../extensions/date_time.dart'; import '../data/calendar_layout.dart'; import '../data/lesson_period_schedule.dart'; diff --git a/lib/widget/animated_time.dart b/lib/widget/animated_time.dart index ea9991e..2c00b18 100644 --- a/lib/widget/animated_time.dart +++ b/lib/widget/animated_time.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:animated_digit/animated_digit.dart'; import 'package:flutter/material.dart'; class AnimatedTime extends StatefulWidget { @@ -42,14 +41,18 @@ class _AnimatedTimeState extends State { ], ); - AnimatedDigitWidget buildWidget(int value) => AnimatedDigitWidget( - value: value, - duration: const Duration(milliseconds: 100), - textStyle: TextStyle( - fontSize: 15, - color: Theme.of(context).colorScheme.onSurface, - ), - ); + Widget buildWidget(int value) => AnimatedSwitcher( + duration: const Duration(milliseconds: 100), + transitionBuilder: (child, animation) => FadeTransition(opacity: animation, child: child), + child: Text( + '$value', + key: ValueKey(value), + style: TextStyle( + fontSize: 15, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + ); @override void dispose() { diff --git a/lib/widget/breaker/breaker.dart b/lib/widget/breaker/breaker.dart index 9c9186e..006844e 100644 --- a/lib/widget/breaker/breaker.dart +++ b/lib/widget/breaker/breaker.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; +import '../../api/mhsl/breaker/get_breakers/get_breakers_response.dart'; import '../../state/app/modules/breaker/bloc/breaker_bloc.dart'; import '../../widget/placeholder_view.dart'; diff --git a/lib/widget/debug/cache_view.dart b/lib/widget/debug/cache_view.dart index c92132b..beb46ce 100644 --- a/lib/widget/debug/cache_view.dart +++ b/lib/widget/debug/cache_view.dart @@ -7,7 +7,7 @@ import 'package:jiffy/jiffy.dart'; import 'package:localstore/localstore.dart'; import '../../../widget/placeholder_view.dart'; -import '../../api/requestCache.dart'; +import '../../api/request_cache.dart'; import 'json_viewer.dart'; class CacheView extends StatefulWidget { @@ -21,9 +21,9 @@ class CacheView extends StatefulWidget { } Future totalSize() async { - var data = await Localstore.instance.collection(RequestCache.collection).get(); - if(data!.length <= 1) return jsonEncode(data.values.first).length * 8; - return data.values.reduce((a, b) => jsonEncode(a).length + jsonEncode(b).length) * 8; + final data = await Localstore.instance.collection(RequestCache.collection).get(); + if (data == null || data.isEmpty) return 0; + return data.values.fold(0, (sum, value) => sum + jsonEncode(value).length) * 8; } } @@ -49,15 +49,16 @@ class _CacheViewState extends State { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { - Map element = snapshot.data![snapshot.data!.keys.elementAt(index)]; - var filename = snapshot.data!.keys.elementAt(index).split('/').last; + final key = snapshot.data!.keys.elementAt(index); + final element = snapshot.data![key] as Map; + final filename = key.split('/').last; return ListTile( leading: const Icon(Icons.text_snippet_outlined), title: Text(filename), - subtitle: Text("${filesize(jsonEncode(element).length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(element['lastupdate']).fromNow()}"), + subtitle: Text('${filesize(jsonEncode(element).length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(element['lastupdate'] as int).fromNow()}'), trailing: const Icon(Icons.arrow_right), - onTap: () => JsonViewer.asDialog(context, jsonDecode(element['json'])), + onTap: () => JsonViewer.asDialog(context, jsonDecode(element['json'] as String) as Map), ); }, ); diff --git a/lib/widget/debug/json_viewer.dart b/lib/widget/debug/json_viewer.dart index 389ddc5..71f85ee 100644 --- a/lib/widget/debug/json_viewer.dart +++ b/lib/widget/debug/json_viewer.dart @@ -1,6 +1,7 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:pretty_json/pretty_json.dart'; class JsonViewer extends StatelessWidget { final String title; @@ -19,7 +20,9 @@ class JsonViewer extends StatelessWidget { ), ); - static String format(Map jsonInput) => prettyJson(jsonInput, indent: 2); + static final _encoder = const JsonEncoder.withIndent(' '); + + static String format(Map jsonInput) => _encoder.convert(jsonInput); static void asDialog(BuildContext context, Map dataMap) { showDialog(context: context, builder: (context) => AlertDialog( diff --git a/lib/widget/file_pick.dart b/lib/widget/file_pick.dart index b9d7dbb..0e678f3 100644 --- a/lib/widget/file_pick.dart +++ b/lib/widget/file_pick.dart @@ -1,29 +1,19 @@ - import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; class FilePick { static final _picker = ImagePicker(); - static Future galleryPick() async { - final pickedImage = await _picker.pickImage(source: ImageSource.gallery); - if (pickedImage != null) { - return pickedImage; - } - return null; - } - static Future?> multipleGalleryPick() async { final pickedImages = await _picker.pickMultiImage(); - if(pickedImages.isNotEmpty) { - return pickedImages; - } - return null; + return pickedImages.isNotEmpty ? pickedImages : null; } + static Future cameraPick() => _picker.pickImage(source: ImageSource.camera); + static Future?> documentPick() async { - var result = await FilePicker.platform.pickFiles(allowMultiple: true); - var paths = result?.files.nonNulls.map((e) => e.path).toList(); + final result = await FilePicker.pickFiles(allowMultiple: true); + final paths = result?.files.nonNulls.map((e) => e.path).toList(); return paths?.nonNulls.toList(); } } diff --git a/lib/widget/file_viewer.dart b/lib/widget/file_viewer.dart index 88bb8f8..06ce484 100644 --- a/lib/widget/file_viewer.dart +++ b/lib/widget/file_viewer.dart @@ -1,16 +1,17 @@ +import 'dart:async'; import 'dart:io'; import 'dart:math'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:open_filex/open_filex.dart'; import 'package:photo_view/photo_view.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:share_plus/share_plus.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; import '../routing/app_routes.dart'; import '../state/app/modules/settings/bloc/settings_cubit.dart'; -import '../utils/file_saver.dart'; import 'info_dialog.dart'; import 'placeholder_view.dart'; import 'share_position_origin.dart'; @@ -30,6 +31,55 @@ enum FileViewingActions { save } +/// Workaround for a Syncfusion PDF viewer race: SfPdfViewer's internal +/// LayoutBuilder calls `localToGlobal` during build, which asserts when an +/// ancestor RenderTransform (from the page-push animation) is still mid-layout. +/// We wait for the route's enter animation to complete before mounting it. +class _DeferredPdfViewer extends StatefulWidget { + const _DeferredPdfViewer({required this.path}); + final String path; + + @override + State<_DeferredPdfViewer> createState() => _DeferredPdfViewerState(); +} + +class _DeferredPdfViewerState extends State<_DeferredPdfViewer> { + bool _ready = false; + Animation? _routeAnimation; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (_ready || _routeAnimation != null) return; + final animation = ModalRoute.of(context)?.animation; + if (animation == null || animation.isCompleted) { + _ready = true; + return; + } + _routeAnimation = animation..addStatusListener(_onAnimationStatus); + } + + void _onAnimationStatus(AnimationStatus status) { + if (status == AnimationStatus.completed && mounted) { + setState(() => _ready = true); + } + } + + @override + void dispose() { + _routeAnimation?.removeStatusListener(_onAnimationStatus); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (!_ready) { + return const Center(child: CircularProgressIndicator()); + } + return SfPdfViewer.file(File(widget.path)); + } +} + class _FileViewerState extends State { PhotoViewController photoViewController = PhotoViewController(); @@ -44,7 +94,7 @@ class _FileViewerState extends State { @override Widget build(BuildContext context) { - AppBar appbar({List actions = const []}) => AppBar( + AppBar appbar({List actions = const []}) => AppBar( title: Text(widget.path.split('/').last), actions: [ ...actions, @@ -55,17 +105,26 @@ class _FileViewerState extends State { AppRoutes.openFileViewer(context, widget.path, openExternal: true); break; case FileViewingActions.share: - SharePlus.instance.share( - ShareParams( - files: [XFile(widget.path)], - sharePositionOrigin: SharePositionOrigin.get(context) - ) - ); + unawaited(SharePlus.instance.share( + ShareParams( + files: [XFile(widget.path)], + sharePositionOrigin: SharePositionOrigin.get(context), + ), + )); break; case FileViewingActions.save: - await FileSaver.writeBytes(await File(widget.path).readAsBytes(), widget.path.split('/').last); - if(!context.mounted) return; - InfoDialog.show(context, 'Die Datei wurde im Downloads Ordner gespeichert.'); + try { + final bytes = await File(widget.path).readAsBytes(); + final saved = await FilePicker.saveFile( + fileName: widget.path.split('/').last, + bytes: bytes, + ); + if (!context.mounted) return; + if (saved != null) InfoDialog.show(context, 'Datei gespeichert.'); + } on Object catch (e) { + if (!context.mounted) return; + InfoDialog.show(context, 'Speichern fehlgeschlagen: $e'); + } break; } }, @@ -86,7 +145,7 @@ class _FileViewerState extends State { dense: true, ), ), - if(Platform.isAndroid) const PopupMenuItem( + const PopupMenuItem( value: FileViewingActions.save, child: ListTile( leading: Icon(Icons.save_alt_outlined), @@ -129,9 +188,7 @@ class _FileViewerState extends State { case 'pdf': return Scaffold( appBar: appbar(), - body: SfPdfViewer.file( - File(widget.path), - ), + body: _DeferredPdfViewer(path: widget.path), ); default: diff --git a/pubspec.yaml b/pubspec.yaml index 776b411..1798def 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,6 +7,7 @@ version: 0.1.7+46 environment: sdk: ">=3.8.0 <4.0.0" +# nextcloud (custom Git fork) pins intl ^0.18.0; Flutter 3.41 needs ^0.20.2. dependency_overrides: intl: 0.20.2 @@ -17,33 +18,22 @@ dependencies: flutter_localizations: sdk: flutter - animated_digit: ^3.2.3 async: ^2.11.0 badges: ^3.1.2 - bloc: ^9.0.0 - bottom_sheet: ^4.0.4 - bubble: ^1.2.1 cached_network_image: ^3.4.1 collection: ^1.19.0 connectivity_plus: ^7.1.0 crypto: ^3.0.6 - cupertino_icons: ^1.0.8 device_info_plus: ^12.4.0 - dio: ^4.0.6 - easy_debounce: ^2.0.3 + dio: ^5.9.2 emoji_picker_flutter: ^4.3.0 - fast_rsa: ^3.7.1 - file_picker: ^10.3.2 + file_picker: ^11.0.2 filesize: ^2.0.1 firebase_core: ^4.1.0 - firebase_in_app_messaging: ^0.9.0+1 firebase_messaging: ^16.0.1 - flowder: - git: - url: https://github.com/Harsh223/flowder.git flutter_app_badge: ^2.0.2 flutter_bloc: ^9.0.0 - flutter_secure_storage: ^9.2.4 + flutter_secure_storage: ^10.0.0 intl: ^0.20.2 flutter_linkify: ^6.0.0 flutter_local_notifications: ^21.0.0 @@ -68,22 +58,18 @@ dependencies: open_filex: ^4.7.0 package_info_plus: ^9.0.1 path_provider: ^2.1.5 - permission_handler: ^12.0.1 persistent_bottom_nav_bar_v2: ^6.1.0 photo_view: ^0.15.0 - pretty_json: ^2.0.0 - provider: ^6.1.2 qr_flutter: ^4.1.0 rrule: ^0.2.17 rrule_generator: ^0.9.0 screen_brightness: ^2.1.7 share_plus: ^12.0.2 shared_preferences: ^2.3.5 - syncfusion_flutter_calendar: ^33.1.46 - syncfusion_flutter_pdfviewer: ^33.1.46 + syncfusion_flutter_calendar: ^33.2.5 + syncfusion_flutter_pdfviewer: ^33.2.5 time_range_picker: ^2.3.0 url_launcher: ^6.3.1 - uuid: ^4.5.1 enough_icalendar: ^0.17.0 dev_dependencies: