diff --git a/analysis_options.yaml b/analysis_options.yaml index 9080d1d5..db4e847e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,101 +1,125 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# 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`. - # 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-inference: true + # strict-raw-types: true + + errors: + close_sinks: ignore + missing_required_param: error + missing_return: error + record_literal_one_positional_no_trailing_comma: error + + exclude: + - lib/generated_plugin_registrant.dart + 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: + # TODO some are set to false, because right now they are not useful, but could be later always_declare_return_types: true - always_put_control_body_on_new_line: true always_put_required_named_parameters_first: true - always_require_non_null_named_parameters: true always_use_package_imports: true annotate_overrides: true - avoid_bool_literals_in_conditional_expressions: true + avoid_bool_literals_in_conditional_expressions: false + avoid_catching_errors: true + avoid_double_and_int_checks: true avoid_empty_else: true + avoid_equals_and_hash_code_on_mutable_classes: true avoid_escaping_inner_quotes: true avoid_field_initializers_in_const_classes: true + avoid_final_parameters: true + avoid_function_literals_in_foreach_calls: true avoid_init_to_null: true avoid_js_rounded_ints: true avoid_null_checks_in_equality_operators: true + avoid_print: false avoid_private_typedef_functions: true + avoid_relative_lib_imports: true + avoid_renaming_method_parameters: true avoid_return_types_on_setters: true + avoid_returning_null_for_void: true avoid_returning_this: true avoid_setters_without_getters: true avoid_shadowing_type_parameters: true avoid_single_cascade_in_expression_statements: true - avoid_slow_async_io: true + avoid_type_to_string: true avoid_types_as_parameter_names: true avoid_unnecessary_containers: true + avoid_unused_constructor_parameters: true avoid_void_async: true avoid_web_libraries_in_flutter: true await_only_futures: true camel_case_extensions: true camel_case_types: true cancel_subscriptions: true - close_sinks: true + cast_nullable_to_non_nullable: true + collection_methods_unrelated_type: true + combinators_ordering: true comment_references: true + conditional_uri_does_not_exist: true constant_identifier_names: true control_flow_in_finally: true curly_braces_in_flow_control_structures: true + dangling_library_doc_comments: true + depend_on_referenced_packages: true + deprecated_consistency: true empty_catches: true empty_constructor_bodies: true empty_statements: true + eol_at_end_of_file: true exhaustive_cases: true - file_names: true # .dart files should use 'lowercase_with_underscores' naming scheme + file_names: true + hash_and_equals: true + implicit_call_tearoffs: true implementation_imports: true + implicit_reopen: true + invalid_case_patterns: true iterable_contains_unrelated_type: true join_return_with_assignment: true leading_newlines_in_multiline_strings: true + library_annotations: true library_names: true library_prefixes: true + library_private_types_in_public_api: true list_remove_unrelated_type: true + literal_only_boolean_expressions: true missing_whitespace_between_adjacent_strings: true no_adjacent_strings_in_list: true no_duplicate_case_values: true + no_leading_underscores_for_library_prefixes: true + no_leading_underscores_for_local_identifiers: true no_logic_in_create_state: true no_runtimeType_toString: true non_constant_identifier_names: true + noop_primitive_operations: true + null_check_on_nullable_type_parameter: true null_closures: true + one_member_abstracts: true only_throw_errors: true overridden_fields: true package_api_docs: true package_names: true package_prefixed_library_names: true - parameter_assignments: true + parameter_assignments: false prefer_adjacent_string_concatenation: true prefer_asserts_in_initializer_lists: true prefer_asserts_with_message: true prefer_collection_literals: true prefer_conditional_assignment: true - prefer_const_constructors_in_immutables: true prefer_const_constructors: true + prefer_const_constructors_in_immutables: true prefer_const_declarations: true prefer_const_literals_to_create_immutables: true + prefer_constructors_over_static_methods: false prefer_contains: true - prefer_equal_for_default_values: true prefer_final_fields: true prefer_final_in_for_each: true prefer_final_locals: true prefer_for_elements_to_map_fromIterable: true - prefer_foreach: true prefer_function_declarations_over_variables: true prefer_generic_function_type_aliases: true prefer_if_elements_to_conditional_expressions: true @@ -108,7 +132,7 @@ linter: prefer_is_not_empty: true prefer_is_not_operator: true prefer_iterable_whereType: true - prefer_mixin: true + prefer_null_aware_method_calls: true prefer_null_aware_operators: true prefer_single_quotes: true prefer_spread_collections: true @@ -117,22 +141,32 @@ linter: provide_deprecation_message: true recursive_getters: true require_trailing_commas: true + sized_box_for_whitespace: true + sized_box_shrink_expand: true slash_for_doc_comments: true sort_child_properties_last: true sort_constructors_first: true sort_unnamed_constructors_first: true test_types_in_equals: true throw_in_finally: true + tighten_type_of_initializing_formals: true type_annotate_public_apis: true type_init_formals: true unawaited_futures: true unnecessary_await_in_return: true + unnecessary_breaks: true unnecessary_brace_in_string_interps: true unnecessary_const: true + unnecessary_constructor_name: true unnecessary_getters_setters: true + unnecessary_lambdas: true + unnecessary_late: true + unnecessary_library_directive: true unnecessary_new: true unnecessary_null_aware_assignments: true + unnecessary_null_checks: true unnecessary_null_in_if_null_operators: true + unnecessary_nullable_for_final_variable_declarations: true unnecessary_overrides: true unnecessary_parenthesis: true unnecessary_raw_strings: true @@ -140,25 +174,24 @@ linter: unnecessary_string_escapes: true unnecessary_string_interpolations: true unnecessary_this: true + unnecessary_to_list_in_spreads: true unrelated_type_equality_checks: true - unsafe_html: true use_build_context_synchronously: false + use_colored_box: true + use_enums: true use_full_hex_values_for_flutter_colors: true use_function_type_syntax_for_parameters: true use_is_even_rather_than_modulo: true use_key_in_widget_constructors: true + use_late_for_private_fields_and_variables: true + use_named_constants: true use_raw_strings: true use_rethrow_when_possible: true - use_string_buffers: true + use_setters_to_change_properties: true + use_string_buffers: false + use_string_in_part_of_directives: true + use_super_parameters: true + use_test_throws_matchers: true use_to_and_as_if_applicable: true valid_regexps: true void_checks: true - -# analyzer: -# language: -# strict-casts: true -# strict-inference: true -# strict-raw-types: true - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/lib/main.dart b/lib/main.dart index dcfe117d..100530d4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -11,7 +12,6 @@ import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:get/get.dart'; import 'package:logger_flutter_fork/logger_flutter_fork.dart'; -import 'package:sqflite/sqflite.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:statsfl/statsfl.dart'; @@ -90,7 +90,7 @@ void main() async { } class MainApp extends StatefulWidget { - const MainApp({Key? key}) : super(key: key); + const MainApp({super.key}); @override State createState() => _MainAppState(); @@ -119,12 +119,10 @@ class _MainAppState extends State { initHandlers(); if (Platform.isAndroid || Platform.isIOS) { - var window = WidgetsBinding.instance.window; - window.onPlatformBrightnessChanged = () { - // This callback is called every time the brightness changes and forces the app root to restate. - // This allows to not use darkTheme to avoid coloring bugs on AppBars - updateState(); - }; + final PlatformDispatcher window = WidgetsBinding.instance.platformDispatcher.views.first.platformDispatcher; + // This callback is called every time the brightness changes and forces the app root to restate. + // This allows to not use darkTheme to avoid coloring bugs on AppBars + window.onPlatformBrightnessChanged = updateState; } // TODO @@ -159,7 +157,7 @@ class _MainAppState extends State { settingsHandler.alice.setNavigatorKey(navigationHandler.navigatorKey); } - void setMaxFPS() async { + Future setMaxFPS() async { // enable higher refresh rate // TODO make this a setting? // TODO make it work on ios, desktop? @@ -169,7 +167,7 @@ class _MainAppState extends State { if (Platform.isAndroid) { await FlutterDisplayMode.setHighRefreshRate(); - DisplayMode currentMode = await FlutterDisplayMode.active; + final DisplayMode currentMode = await FlutterDisplayMode.active; if (currentMode.refreshRate > maxFps) { maxFps = currentMode.refreshRate.round(); @@ -208,7 +206,7 @@ class _MainAppState extends State { final bool useDynamicColor = settingsHandler.useDynamicColor.value; final bool isAmoled = settingsHandler.isAmoled.value; - ThemeHandler themeHandler = ThemeHandler( + final ThemeHandler themeHandler = ThemeHandler( theme: theme, themeMode: themeMode, useMaterial3: useMaterial3, @@ -246,24 +244,26 @@ class _MainAppState extends State { width: 110, height: 80, align: Alignment.centerLeft, - child: DynamicColorBuilder(builder: (lightDynamic, darkDynamic) { - themeHandler.setDynamicColors( - useDynamicColor ? lightDynamic : null, - useDynamicColor ? darkDynamic : null, - ); - - return MaterialApp( - title: 'LoliSnatcher', - debugShowCheckedModeBanner: false, // hide debug banner in the corner - showPerformanceOverlay: settingsHandler.showPerf.value, - scrollBehavior: const CustomScrollBehavior(), - theme: themeHandler.lightTheme(), - darkTheme: themeHandler.darkTheme(), - themeMode: themeMode, - navigatorKey: navigationHandler.navigatorKey, - home: const Home(), - ); - }), + child: DynamicColorBuilder( + builder: (lightDynamic, darkDynamic) { + themeHandler.setDynamicColors( + useDynamicColor ? lightDynamic : null, + useDynamicColor ? darkDynamic : null, + ); + + return MaterialApp( + title: 'LoliSnatcher', + debugShowCheckedModeBanner: false, // hide debug banner in the corner + showPerformanceOverlay: settingsHandler.showPerf.value, + scrollBehavior: const CustomScrollBehavior(), + theme: themeHandler.lightTheme(), + darkTheme: themeHandler.darkTheme(), + themeMode: themeMode, + navigatorKey: navigationHandler.navigatorKey, + home: const Home(), + ); + }, + ), ), ); }); @@ -271,7 +271,7 @@ class _MainAppState extends State { } class Home extends StatefulWidget { - const Home({Key? key}) : super(key: key); + const Home({super.key}); @override State createState() => _HomeState(); @@ -324,7 +324,7 @@ class _HomeState extends State with WidgetsBindingObserver { await Future.wait([ imageWriter.clearStaleCache(), imageWriter.clearCacheOverflow(), - ]); + ]); } Future initDeepLinks() async { @@ -334,7 +334,7 @@ class _HomeState extends State with WidgetsBindingObserver { // check if there is a deep link on app start final Uri? initialLink = await appLinks!.getInitialAppLink(); if (initialLink != null) { - openAppLink(initialLink.toString()); + unawaited(openAppLink(initialLink.toString())); } // listen for deep links @@ -349,7 +349,7 @@ class _HomeState extends State with WidgetsBindingObserver { // FlashElements.showSnackbar(title: Text('Deep Link: $url'), duration: null); if (url.contains('loli.snatcher')) { - Booru booru = Booru.fromLink(url); + final Booru booru = Booru.fromLink(url); if (booru.name != null && booru.name!.isNotEmpty) { if (settingsHandler.booruList.indexWhere((b) => b.name == booru.name) != -1) { // Rename config if its already in the list diff --git a/lib/src/boorus/agnph_handler.dart b/lib/src/boorus/agnph_handler.dart index 748fefbf..6dc673be 100644 --- a/lib/src/boorus/agnph_handler.dart +++ b/lib/src/boorus/agnph_handler.dart @@ -1,6 +1,5 @@ import 'package:xml/xml.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; @@ -9,50 +8,50 @@ import 'package:lolisnatcher/src/utils/logger.dart'; // TODO improve tag fecthing, add data from it to tag handler? class AGNPHHandler extends BooruHandler { - AGNPHHandler(Booru booru, int limit) : super(booru, limit); + AGNPHHandler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; /// Because the api doesn't return tags we will create fetched and have another function set tags at a later time. /// Seems to work for now but could cause a performance impact. /// Makes results show on screen faster than waiting on getDataByID @override - List parseListFromResponse(response) { - var parsedResponse = XmlDocument.parse(response.data); - totalCount.value = int.tryParse(parsedResponse.getElement("posts")?.getAttribute("count") ?? '0') ?? 0; - return parsedResponse.findAllElements("post").toList(); + List parseListFromResponse(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + totalCount.value = int.tryParse(parsedResponse.getElement('posts')?.getAttribute('count') ?? '0') ?? 0; + return parsedResponse.findAllElements('post').toList(); } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - String fileURL = responseItem.getElement("file_url")?.innerText ?? ""; - String sampleURL = responseItem.getElement("preview_url")?.innerText ?? ""; - String thumbnailURL = responseItem.getElement("thumbnail_url")?.innerText ?? ""; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final String fileURL = responseItem.getElement('file_url')?.innerText ?? ''; + String sampleURL = responseItem.getElement('preview_url')?.innerText ?? ''; + final String thumbnailURL = responseItem.getElement('thumbnail_url')?.innerText ?? ''; if (sampleURL.isEmpty) { sampleURL = fileURL; } - String postID = responseItem.getElement("id")?.innerText ?? ""; + final String postID = responseItem.getElement('id')?.innerText ?? ''; if (postID.isNotEmpty && fileURL.isNotEmpty) { - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: sampleURL, thumbnailURL: thumbnailURL, tagsList: [], - postURL: makePostURL(responseItem.getElement("id")?.innerText ?? ""), - fileWidth: double.tryParse(responseItem.getElement("width")?.innerText ?? ""), - fileHeight: double.tryParse(responseItem.getElement("height")?.innerText ?? ""), - serverId: responseItem.getElement("id")?.innerText ?? "", - rating: responseItem.getElement("rating")?.innerText, - score: responseItem.getElement("fav_count")?.innerText, - sources: [responseItem.getElement("source")?.innerText ?? ""], - md5String: responseItem.getElement("md5")?.innerText, - postDate: responseItem.getElement("created_at")?.innerText, // Fri Jun 18 02:13:45 -0500 2021 - postDateFormat: "unix", // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", + postURL: makePostURL(responseItem.getElement('id')?.innerText ?? ''), + fileWidth: double.tryParse(responseItem.getElement('width')?.innerText ?? ''), + fileHeight: double.tryParse(responseItem.getElement('height')?.innerText ?? ''), + serverId: responseItem.getElement('id')?.innerText ?? '', + rating: responseItem.getElement('rating')?.innerText, + score: responseItem.getElement('fav_count')?.innerText, + sources: [responseItem.getElement('source')?.innerText ?? ''], + md5String: responseItem.getElement('md5')?.innerText, + postDate: responseItem.getElement('created_at')?.innerText, // Fri Jun 18 02:13:45 -0500 2021 + postDateFormat: 'unix', // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", ); - int newIndex = fetched.length + index; + final int newIndex = fetched.length + index; getTagsLater(postID, newIndex); return item; @@ -61,56 +60,56 @@ class AGNPHHandler extends BooruHandler { } } - void getTagsLater(String postID, int fetchedIndex) async { + Future getTagsLater(String postID, int fetchedIndex) async { try { - var response = await DioNetwork.get("${booru.baseURL}/gallery/post/show/$postID/?api=xml", headers: getHeaders()); - Logger.Inst().log("Getting post data: $postID", className, "getTagsLater", LogTypes.booruHandlerRawFetched); + final response = await DioNetwork.get('${booru.baseURL}/gallery/post/show/$postID/?api=xml', headers: getHeaders()); + Logger.Inst().log('Getting post data: $postID', className, 'getTagsLater', LogTypes.booruHandlerRawFetched); if (response.statusCode == 200) { - Logger.Inst().log("Got data for: $postID", className, "getTagsLater", LogTypes.booruHandlerRawFetched); - var parsedResponse = XmlDocument.parse(response.data); - var post = parsedResponse.getElement('post'); - String tagStr = post!.getElement("tags")?.innerText ?? ""; - if (post.getElement("tags")!.innerText.isNotEmpty) { - String artist = post.getElement("artist")?.innerText ?? ""; + Logger.Inst().log('Got data for: $postID', className, 'getTagsLater', LogTypes.booruHandlerRawFetched); + final parsedResponse = XmlDocument.parse(response.data); + final post = parsedResponse.getElement('post'); + String tagStr = post!.getElement('tags')?.innerText ?? ''; + if (post.getElement('tags')!.innerText.isNotEmpty) { + final String artist = post.getElement('artist')?.innerText ?? ''; tagStr = "artist:$artist ${tagStr.replaceAll(artist, "")}"; } - fetched.elementAt(fetchedIndex).tagsList = tagStr.split(" "); + fetched.elementAt(fetchedIndex).tagsList = tagStr.split(' '); } else { - Logger.Inst().log("AGNPHHandler failed to get post", "AGNPHHandler", "getTagsLater", LogTypes.booruHandlerFetchFailed); + Logger.Inst().log('AGNPHHandler failed to get post', 'AGNPHHandler', 'getTagsLater', LogTypes.booruHandlerFetchFailed); } } catch (e) { - Logger.Inst().log(e.toString(), "AGNPHHandler", "getTagsLater", LogTypes.exception); + Logger.Inst().log(e.toString(), 'AGNPHHandler', 'getTagsLater', LogTypes.exception); } } @override String makePostURL(String id) { // EXAMPLE: https://agn.ph/gallery/post/show/352470/ - return "${booru.baseURL}/gallery/post/show/$id"; + return '${booru.baseURL}/gallery/post/show/$id'; } @override String makeURL(String tags) { - String tagStr = tags.replaceAll("artist:", "").replaceAll(" ", "+"); + final String tagStr = tags.replaceAll('artist:', '').replaceAll(' ', '+'); // EXAMPLE: https://agn.ph/gallery/post/?search=sylveon&page=1&api=xml - return "${booru.baseURL}/gallery/post/?search=$tagStr&page=$pageNum&api=xml"; + return '${booru.baseURL}/gallery/post/?search=$tagStr&page=$pageNum&api=xml'; } @override String makeTagURL(String input) { // EXAMPLE: https://agn.ph/gallery/tags/?sort=count&order=desc&search=gard&api=xml - return "${booru.baseURL}/gallery/tags/?sort=count&order=desc&search=$input&api=xml"; + return '${booru.baseURL}/gallery/tags/?sort=count&order=desc&search=$input&api=xml'; } @override - List parseTagSuggestionsList(response) { - var parsedResponse = XmlDocument.parse(response.data); - return parsedResponse.findAllElements("tag").toList(); + List parseTagSuggestionsList(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + return parsedResponse.findAllElements('tag').toList(); } @override - String? parseTagSuggestion(responseItem, int index) { + String? parseTagSuggestion(dynamic responseItem, int index) { // TODO parse tag type - return responseItem.getElement("name")?.innerText ?? ""; + return responseItem.getElement('name')?.innerText ?? ''; } } diff --git a/lib/src/boorus/booru_on_rails_handler.dart b/lib/src/boorus/booru_on_rails_handler.dart index 98922c06..8cf580ae 100644 --- a/lib/src/boorus/booru_on_rails_handler.dart +++ b/lib/src/boorus/booru_on_rails_handler.dart @@ -1,4 +1,3 @@ -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; @@ -6,50 +5,52 @@ import 'package:lolisnatcher/src/handlers/booru_handler.dart'; // TODO fix file names like we do with shimmie, probably should move file name encode/decode process to boorus themselves instead of image writer class BooruOnRailsHandler extends BooruHandler { - BooruOnRailsHandler(Booru booru, int limit) : super(booru,limit); + BooruOnRailsHandler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; @override String makePostURL(String id) { - return "${booru.baseURL}/$id"; + return '${booru.baseURL}/$id'; } @override String validateTags(String tags) { - if (tags == "" || tags == " "){ - return "*"; + if (tags == '' || tags == ' ') { + return '*'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - Map parsedResponse = response.data; + List parseListFromResponse(dynamic response) { + final Map parsedResponse = response.data; return (parsedResponse['posts'] as List?) ?? []; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - List currentTags = []; - for (int x = 0; x < responseItem['tags'].length; x++){ + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final List currentTags = []; + for (int x = 0; x < responseItem['tags'].length; x++) { // if (responseItem['tags'][x].contains(" ")){ // TODO why this? with this most tags are skipped - currentTags.add(responseItem['tags'][x].toString().replaceAll(" ", "+")); + currentTags.add(responseItem['tags'][x].toString().replaceAll(' ', '+')); // } } - if (responseItem['representations']['full'] != null && responseItem['representations']['medium'] != null && responseItem['representations']['large'] != null) { - String id = responseItem['id']?.toString() ?? ""; + if (responseItem['representations']['full'] != null && + responseItem['representations']['medium'] != null && + responseItem['representations']['large'] != null) { + final String id = responseItem['id']?.toString() ?? ''; String fileURL = responseItem['representations']['full'] ?? responseItem['representations']['large']; String sampleURL = responseItem['representations']['large']; String thumbURL = responseItem['representations']['medium']; - if(responseItem["mime_type"].toString().contains("video")) { - String tmpURL = "${sampleURL.substring(0, sampleURL.lastIndexOf("/") + 1)}thumb.gif"; + if (responseItem['mime_type'].toString().contains('video')) { + final String tmpURL = "${sampleURL.substring(0, sampleURL.lastIndexOf("/") + 1)}thumb.gif"; sampleURL = tmpURL; thumbURL = tmpURL; } - if (!fileURL.contains("http")) { + if (!fileURL.contains('http')) { fileURL = '${booru.baseURL!}$fileURL'; sampleURL = '${booru.baseURL!}$sampleURL'; thumbURL = '${booru.baseURL!}$thumbURL'; @@ -59,7 +60,7 @@ class BooruOnRailsHandler extends BooruHandler { sampleURL = '$sampleURL?$id'; thumbURL = '$thumbURL?$id'; - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, fileWidth: responseItem['width']?.toDouble(), fileHeight: responseItem['height']?.toDouble(), @@ -67,14 +68,14 @@ class BooruOnRailsHandler extends BooruHandler { sampleURL: sampleURL, thumbnailURL: thumbURL, tagsList: currentTags, - postURL: makePostURL(id.toString()), - serverId: id.toString(), + postURL: makePostURL(id), + serverId: id, score: responseItem['score']?.toString(), - sources: [responseItem['source_url']?.toString() ?? ""], + sources: [responseItem['source_url']?.toString() ?? ''], rating: currentTags[0][0], postDate: responseItem['created_at'], // 2021-06-13T02:09:45.138-04:00 postDateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", - fileNameExtras: "${booru.name}_${id.toString()}_"// when timezone support added: "yyyy-MM-dd'T'HH:mm:ss.SSSZ", + fileNameExtras: '${booru.name}_${id}_', // when timezone support added: "yyyy-MM-dd'T'HH:mm:ss.SSSZ", ); return item; } else { @@ -84,43 +85,42 @@ class BooruOnRailsHandler extends BooruHandler { @override String makeURL(String tags) { - final String tagsWithCommas = tags.replaceAll(" ", ","); + final String tagsWithCommas = tags.replaceAll(' ', ','); final String limitStr = limit.toString(); final String pageStr = pageNum.toString(); - final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? "key=${booru.apiKey}&" : ""; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? 'key=${booru.apiKey}&' : ''; // EXAMPLE: https://twibooru.org/api/v3/search/posts?q=*&perpage=10&page=1 - return "${booru.baseURL}/api/v3/search/posts?${apiKeyStr}q=$tagsWithCommas&perpage=$limitStr&page=$pageStr"; + return '${booru.baseURL}/api/v3/search/posts?${apiKeyStr}q=$tagsWithCommas&perpage=$limitStr&page=$pageStr'; } @override String makeTagURL(String input) { // EXAMPLE: https://twibooru.org/api/v3/search/tags?q=*rai* - return "${booru.baseURL}/api/v3/search/tags?q=*$input*"; + return '${booru.baseURL}/api/v3/search/tags?q=*$input*'; } @override - List parseTagSuggestionsList(response) { - List parsedResponse = response.data['tags']; + List parseTagSuggestionsList(dynamic response) { + final List parsedResponse = response.data['tags']; return parsedResponse; } static List> tagStringReplacements = [ - ["-colon-",":"], - ["-dash-","-"], - ["-fwslash-","/"], - ["-bwslash-","\\"], - ["-dot-","."], - ["-plus-","+"] + ['-colon-', ':'], + ['-dash-', '-'], + ['-fwslash-', '/'], + ['-bwslash-', r'\'], + ['-dot-', '.'], + ['-plus-', '+'] ]; @override - String? parseTagSuggestion(responseItem, int index) { + String? parseTagSuggestion(dynamic responseItem, int index) { String tag = responseItem['slug'].toString(); - for (int x = 0; x < tagStringReplacements.length; x++){ + for (int x = 0; x < tagStringReplacements.length; x++) { tag = tag.replaceAll(tagStringReplacements[x][0], tagStringReplacements[x][1]); } return tag; } } - diff --git a/lib/src/boorus/danbooru_handler.dart b/lib/src/boorus/danbooru_handler.dart index 066b8a40..a23eab53 100644 --- a/lib/src/boorus/danbooru_handler.dart +++ b/lib/src/boorus/danbooru_handler.dart @@ -1,5 +1,5 @@ import 'package:dio/dio.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; + import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/comment_item.dart'; import 'package:lolisnatcher/src/data/note_item.dart'; @@ -10,25 +10,25 @@ import 'package:lolisnatcher/src/utils/logger.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; class DanbooruHandler extends BooruHandler { - DanbooruHandler(Booru booru, int limit) : super(booru, limit); + DanbooruHandler(super.booru, super.limit); @override - Map tagTypeMap = { - '5': TagType.meta, - '3': TagType.copyright, - '4': TagType.character, - '1': TagType.artist, - '0': TagType.none, - }; + Map get tagTypeMap => { + '5': TagType.meta, + '3': TagType.copyright, + '4': TagType.character, + '1': TagType.artist, + '0': TagType.none, + }; @override - bool hasSizeData = true; + bool get hasSizeData => true; @override - bool hasCommentsSupport = true; + bool get hasCommentsSupport => true; @override - bool hasNotesSupport = true; + bool get hasNotesSupport => true; @override String validateTags(String tags) { @@ -65,13 +65,13 @@ class DanbooruHandler extends BooruHandler { } @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final current = responseItem; /** * This check is needed as danbooru will return items which have been banned or deleted and will not have any image urls @@ -140,7 +140,7 @@ class DanbooruHandler extends BooruHandler { // EXAMPLE: https://danbooru.donmai.us/posts.json?tags=rating:safe%20order:rank&limit=20&page=1 final String loginStr = booru.userID?.isNotEmpty == true ? '&login=${booru.userID}' : ''; final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - return "${booru.baseURL}/posts.json?tags=$tags&limit=${limit.toString()}&page=${pageNum.toString()}$loginStr$apiKeyStr"; + return '${booru.baseURL}/posts.json?tags=$tags&limit=$limit&page=$pageNum$loginStr$apiKeyStr'; } @override @@ -159,15 +159,17 @@ class DanbooruHandler extends BooruHandler { } @override - List parseTagSuggestionsList(response) { - List parsedResponse = response.data; + List parseTagSuggestionsList(dynamic response) { + final List parsedResponse = response.data; return parsedResponse; } @override - String? parseTagSuggestion(responseItem, int index) { + String? parseTagSuggestion(dynamic responseItem, int index) { final String tagStr = (responseItem['antecedent'] ?? responseItem['value'])?.toString() ?? ''; - if (tagStr.isEmpty) return null; + if (tagStr.isEmpty) { + return null; + } final String rawTagType = responseItem['category']?.toString() ?? ''; TagType tagType = TagType.none; @@ -179,13 +181,13 @@ class DanbooruHandler extends BooruHandler { } @override - List parseCommentsList(response) { - List parsedResponse = response.data; + List parseCommentsList(dynamic response) { + final List parsedResponse = response.data; return parsedResponse; } @override - CommentItem? parseComment(responseItem, int index) { + CommentItem? parseComment(dynamic responseItem, int index) { final String? dateStr = responseItem['created_at']?.toString().substring(0, responseItem['created_at']!.toString().length - 6); return CommentItem( id: responseItem['id'].toString(), @@ -208,13 +210,13 @@ class DanbooruHandler extends BooruHandler { } @override - List parseNotesList(response) { - List parsedResponse = response.data; + List parseNotesList(dynamic response) { + final List parsedResponse = response.data; return parsedResponse; } @override - NoteItem? parseNote(responseItem, int index) { + NoteItem? parseNote(dynamic responseItem, int index) { final current = responseItem; return NoteItem( id: current['id'].toString(), diff --git a/lib/src/boorus/e621_handler.dart b/lib/src/boorus/e621_handler.dart index 6ed0b790..9e590b6e 100644 --- a/lib/src/boorus/e621_handler.dart +++ b/lib/src/boorus/e621_handler.dart @@ -1,44 +1,44 @@ -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/tag_type.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; +// ignore: camel_case_types class e621Handler extends BooruHandler { - e621Handler(Booru booru, int limit) : super(booru, limit); + e621Handler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; @override - Map tagTypeMap = { - "7": TagType.meta, - "3": TagType.copyright, - "4": TagType.character, - "1": TagType.artist, - "5": TagType.species, - "0": TagType.none, - }; + Map get tagTypeMap => { + '7': TagType.meta, + '3': TagType.copyright, + '4': TagType.character, + '1': TagType.artist, + '5': TagType.species, + '0': TagType.none, + }; @override - List parseListFromResponse(response) { - Map parsedResponse = response.data; + List parseListFromResponse(dynamic response) { + final Map parsedResponse = response.data; return (parsedResponse['posts'] ?? []) as List; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final dynamic current = responseItem; if (current['file']['md5'] != null) { - String fileURL = ""; - String sampleURL = ""; - String thumbURL = ""; + String fileURL = ''; + String sampleURL = ''; + String thumbURL = ''; if (current['file']['url'] == null) { - String md5FirstSplit = current['file']['md5'].toString().substring(0, 2); - String md5SecondSplit = current['file']['md5'].toString().substring(2, 4); + final String md5FirstSplit = current['file']['md5'].toString().substring(0, 2); + final String md5SecondSplit = current['file']['md5'].toString().substring(2, 4); fileURL = "https://static1.e621.net/data/$md5FirstSplit/$md5SecondSplit/${current['file']['md5']}.${current['file']['ext']}"; - sampleURL = fileURL.replaceFirst("data", "data/sample").replaceFirst(current['file']['ext'], "jpg"); - thumbURL = sampleURL.replaceFirst("data/sample", "data/preview"); + sampleURL = fileURL.replaceFirst('data', 'data/sample').replaceFirst(current['file']['ext'], 'jpg'); + thumbURL = sampleURL.replaceFirst('data/sample', 'data/preview'); if (current['file']['size'] <= 2694254) { sampleURL = fileURL; } @@ -55,9 +55,9 @@ class e621Handler extends BooruHandler { addTagsWithType([...current['tags']['general']], TagType.none); addTagsWithType([...current['tags']['species']], TagType.species); - String? dateStr = current['created_at']?.toString().substring(0, current['created_at']!.toString().length - 6); + final String? dateStr = current['created_at']?.toString().substring(0, current['created_at']!.toString().length - 6); - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: sampleURL, thumbnailURL: thumbURL, @@ -96,38 +96,40 @@ class e621Handler extends BooruHandler { @override String makePostURL(String id) { - return "${booru.baseURL}/posts/$id?"; + return '${booru.baseURL}/posts/$id?'; } @override String makeURL(String tags) { - final String loginStr = booru.userID?.isNotEmpty == true ? "&login=${booru.userID}" : ""; - final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? "&api_key=${booru.apiKey}" : ''; + final String loginStr = booru.userID?.isNotEmpty == true ? '&login=${booru.userID}' : ''; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - return "${booru.baseURL}/posts.json?tags=$tags&limit=${limit.toString()}&page=${pageNum.toString()}$loginStr$apiKeyStr"; + return '${booru.baseURL}/posts.json?tags=$tags&limit=$limit&page=$pageNum$loginStr$apiKeyStr'; } @override String makeTagURL(String input) { - return "${booru.baseURL}/tags.json?search[name_matches]=$input*&limit=10&search[order]=count"; + return '${booru.baseURL}/tags.json?search[name_matches]=$input*&limit=10&search[order]=count'; } @override - List parseTagSuggestionsList(response) { - List parsedResponse = response.data; + List parseTagSuggestionsList(dynamic response) { + final List parsedResponse = response.data; return parsedResponse; } @override - String? parseTagSuggestion(responseItem, int index) { - final String tagStr = responseItem["name"] ?? ""; - if (tagStr.isEmpty) return null; + String? parseTagSuggestion(dynamic responseItem, int index) { + final String tagStr = responseItem['name'] ?? ''; + if (tagStr.isEmpty) { + return null; + } // record tag data for future use - final String rawTagType = responseItem["category"]?.toString() ?? ""; + final String rawTagType = responseItem['category']?.toString() ?? ''; TagType tagType = TagType.none; if (rawTagType.isNotEmpty && tagTypeMap.containsKey(rawTagType)) { - tagType = (tagTypeMap[rawTagType] ?? TagType.none); + tagType = tagTypeMap[rawTagType] ?? TagType.none; } addTagsWithType([tagStr], tagType); return tagStr; diff --git a/lib/src/boorus/empty_handler.dart b/lib/src/boorus/empty_handler.dart index 73995e34..449213f0 100644 --- a/lib/src/boorus/empty_handler.dart +++ b/lib/src/boorus/empty_handler.dart @@ -1,6 +1,5 @@ -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; class EmptyHandler extends BooruHandler { - EmptyHandler(Booru booru, int limit) : super(booru, limit); + EmptyHandler(super.booru, super.limit); } diff --git a/lib/src/boorus/favourites_handler.dart b/lib/src/boorus/favourites_handler.dart index 84f64851..2a358c74 100644 --- a/lib/src/boorus/favourites_handler.dart +++ b/lib/src/boorus/favourites_handler.dart @@ -1,10 +1,9 @@ -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/utils/logger.dart'; class FavouritesHandler extends BooruHandler { - FavouritesHandler(Booru booru, int limit) : super(booru, limit); + FavouritesHandler(super.booru, super.limit); @override String validateTags(String tags) { @@ -28,13 +27,21 @@ class FavouritesHandler extends BooruHandler { } // get amount of items before fetching - int length = fetched.length; - - fetched.addAll(await SettingsHandler.instance.dbHandler.searchDB(tags, (pageNum * limit).toString(), limit.toString(), "DESC", "Favourites")); + final int length = fetched.length; + + fetched.addAll( + await SettingsHandler.instance.dbHandler.searchDB( + tags, + (pageNum * limit).toString(), + limit.toString(), + 'DESC', + 'Favourites', + ), + ); prevTags = tags; if (fetched.isEmpty || fetched.length == length) { - Logger.Inst().log("dbhandler dbLocked", "FavouritesHandler", "search", LogTypes.booruHandlerInfo); + Logger.Inst().log('dbhandler dbLocked', 'FavouritesHandler', 'search', LogTypes.booruHandlerInfo); locked = true; } @@ -43,7 +50,7 @@ class FavouritesHandler extends BooruHandler { @override Future> tagSearch(String input) async { - List tags = await SettingsHandler.instance.dbHandler.getTags(input, limit); + final List tags = await SettingsHandler.instance.dbHandler.getTags(input, limit); return tags; } diff --git a/lib/src/boorus/furaffinity_handler.dart b/lib/src/boorus/furaffinity_handler.dart index 0db6a343..30b56738 100644 --- a/lib/src/boorus/furaffinity_handler.dart +++ b/lib/src/boorus/furaffinity_handler.dart @@ -2,7 +2,6 @@ import 'package:dio/dio.dart'; import 'package:html/dom.dart'; import 'package:html/parser.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; @@ -10,11 +9,12 @@ import 'package:lolisnatcher/src/utils/logger.dart'; /// Need to workout how login stuff works it will only display sfw atm, tried copying cookies from browser and that didn't seem to work class FurAffinityHandler extends BooruHandler { - FurAffinityHandler(Booru booru, int limit) : super(booru, limit); + FurAffinityHandler(super.booru, super.limit); + Map? body; @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final document = parse(response.data); final gallery = document.getElementById('gallery-browse') ?? document.getElementById('gallery-search-results'); final links = gallery?.querySelectorAll('figure > b > u > a') ?? []; @@ -45,13 +45,13 @@ class FurAffinityHandler extends BooruHandler { } @override - Future parseItemFromResponse(responseItem, int index) async { + Future parseItemFromResponse(dynamic responseItem, int index) async { final imgItem = responseItem.querySelector('img'); if (imgItem.attributes['src'] != null) { final String id = responseItem.attributes['href']!.replaceAll('/', '').replaceAll('view', ''); final String thumbURL = "https:${imgItem.attributes["src"]!}"; - Document? postPage = await getPostPage(id); + final Document? postPage = await getPostPage(id); if (postPage == null) { return null; diff --git a/lib/src/boorus/gelbooru_alikes_handler.dart b/lib/src/boorus/gelbooru_alikes_handler.dart index f25fb34a..e4c86920 100644 --- a/lib/src/boorus/gelbooru_alikes_handler.dart +++ b/lib/src/boorus/gelbooru_alikes_handler.dart @@ -5,7 +5,6 @@ import 'dart:math'; import 'package:html/parser.dart'; import 'package:xml/xml.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/comment_item.dart'; import 'package:lolisnatcher/src/data/note_item.dart'; @@ -19,104 +18,104 @@ import 'package:lolisnatcher/src/utils/tools.dart'; // rule34.xxx, safebooru.org, realbooru.com class GelbooruAlikesHandler extends BooruHandler { - GelbooruAlikesHandler(Booru booru, int limit) : super(booru, limit); + GelbooruAlikesHandler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; // TODO ? //Realbooru api returns 0 for models but on the website shows them //listed as model on the tagsearch so I dont think the api shows tag types properly @override - Map tagTypeMap = { - "5": TagType.meta, - "3": TagType.copyright, - "4": TagType.character, - "1": TagType.artist, - "0": TagType.none - }; + Map get tagTypeMap => { + '5': TagType.meta, + '3': TagType.copyright, + '4': TagType.character, + '1': TagType.artist, + '0': TagType.none, + }; @override - List parseListFromResponse(response) { - var parsedResponse = XmlDocument.parse(response.data); + List parseListFromResponse(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); // return parsedResponse.findAllElements('post').toList(); } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var current = responseItem; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final current = responseItem; - if (getAttrOrElem(current, "file_url") != null) { + if (getAttrOrElem(current, 'file_url') != null) { // Fix for bleachbooru - String fileURL = "", sampleURL = "", previewURL = ""; - fileURL += getAttrOrElem(current, "file_url")!.toString(); + String fileURL = '', sampleURL = '', previewURL = ''; + fileURL += getAttrOrElem(current, 'file_url')!.toString(); // sample url is optional, on gelbooru there is sample == 0/1 to tell if it exists - sampleURL += getAttrOrElem(current, "sample_url")?.toString() ?? getAttrOrElem(current, "file_url")!.toString(); - previewURL += getAttrOrElem(current, "preview_url")!.toString(); - if (!fileURL.contains("http")) { + sampleURL += getAttrOrElem(current, 'sample_url')?.toString() ?? getAttrOrElem(current, 'file_url')!.toString(); + previewURL += getAttrOrElem(current, 'preview_url')!.toString(); + if (!fileURL.contains('http')) { fileURL = booru.baseURL! + fileURL; sampleURL = booru.baseURL! + sampleURL; previewURL = booru.baseURL! + previewURL; } - - if(booru.baseURL!.contains('realbooru.com')) { + if (booru.baseURL!.contains('realbooru.com')) { // The api is shit and returns a bunch of broken urls so the urls need to be constructed, // We also cant trust the directory variable in the json because it is wrong on old posts - String hash = getAttrOrElem(current, "md5")!.toString(); - String directory = "${hash.substring(0,2)}/${hash.substring(2,4)}"; + final String hash = getAttrOrElem(current, 'md5')!.toString(); + final String directory = '${hash.substring(0, 2)}/${hash.substring(2, 4)}'; // Hash and file can be mismatched so we only use file for file ext - String fileExt = Tools.getFileExt(getAttrOrElem(current, "file_url")!.toString()); - fileURL = "${booru.baseURL}/images/$directory/$hash.$fileExt"; + final String fileExt = Tools.getFileExt(getAttrOrElem(current, 'file_url')!.toString()); + fileURL = '${booru.baseURL}/images/$directory/$hash.$fileExt'; - bool isSample = !fileURL.endsWith(".webm") && getAttrOrElem(current, "sample_url")!.toString().contains('/samples/'); + final bool isSample = !fileURL.endsWith('.webm') && getAttrOrElem(current, 'sample_url')!.toString().contains('/samples/'); // String sampleExt = Tools.getFileExt(getAttrOrElem(current, "sample_url")!.toString()); - sampleURL = isSample ? getAttrOrElem(current, "sample_url")!.toString() : fileURL; + sampleURL = isSample ? getAttrOrElem(current, 'sample_url')!.toString() : fileURL; // sampleURL = "${booru.baseURL}/${isSample ? "samples" : "images"}/$directory/${isSample ? "sample_" : ""}$hash.$sampleExt"; - previewURL = "${booru.baseURL}/thumbnails/$directory/thumbnail_$hash.jpg"; + previewURL = '${booru.baseURL}/thumbnails/$directory/thumbnail_$hash.jpg'; } - if(booru.baseURL!.contains('furry.booru.org')) { + if (booru.baseURL!.contains('furry.booru.org')) { previewURL = previewURL.replaceFirst('.png', '.jpg'); - if(sampleURL != fileURL && sampleURL.contains('samples')) sampleURL = sampleURL.replaceFirst('.png', '.jpg'); + if (sampleURL != fileURL && sampleURL.contains('samples')) { + sampleURL = sampleURL.replaceFirst('.png', '.jpg'); + } } - final List tags = parseFragment(getAttrOrElem(current, "tags")).text?.split(" ") ?? []; - + final List tags = parseFragment(getAttrOrElem(current, 'tags')).text?.split(' ') ?? []; - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: sampleURL, thumbnailURL: previewURL, tagsList: tags, - postURL: makePostURL(getAttrOrElem(current, "id")!.toString()), - fileWidth: double.tryParse(getAttrOrElem(current, "width")?.toString() ?? ''), - fileHeight: double.tryParse(getAttrOrElem(current, "height")?.toString() ?? ''), - sampleWidth: double.tryParse(getAttrOrElem(current, "sample_width")?.toString() ?? ''), - sampleHeight: double.tryParse(getAttrOrElem(current, "sample_height")?.toString() ?? ''), - previewWidth: double.tryParse(getAttrOrElem(current, "preview_width")?.toString() ?? ''), - previewHeight: double.tryParse(getAttrOrElem(current, "preview_height")?.toString() ?? ''), - hasNotes: getAttrOrElem(current, "has_notes")?.toString() == 'true', + postURL: makePostURL(getAttrOrElem(current, 'id')!.toString()), + fileWidth: double.tryParse(getAttrOrElem(current, 'width')?.toString() ?? ''), + fileHeight: double.tryParse(getAttrOrElem(current, 'height')?.toString() ?? ''), + sampleWidth: double.tryParse(getAttrOrElem(current, 'sample_width')?.toString() ?? ''), + sampleHeight: double.tryParse(getAttrOrElem(current, 'sample_height')?.toString() ?? ''), + previewWidth: double.tryParse(getAttrOrElem(current, 'preview_width')?.toString() ?? ''), + previewHeight: double.tryParse(getAttrOrElem(current, 'preview_height')?.toString() ?? ''), + hasNotes: getAttrOrElem(current, 'has_notes')?.toString() == 'true', // TODO rule34xxx api bug? sometimes (mostly when there is only one comment) api returns empty array - hasComments: getAttrOrElem(current, "has_comments")?.toString() == 'true', - serverId: getAttrOrElem(current, "id")?.toString(), - rating: getAttrOrElem(current, "rating")?.toString(), - score: getAttrOrElem(current, "score")?.toString(), - sources: getAttrOrElem(current, "source") != null ? [getAttrOrElem(current, "source")!] : null, - md5String: getAttrOrElem(current, "md5")?.toString(), - postDate: getAttrOrElem(current, "created_at")?.toString(), // Fri Jun 18 02:13:45 -0500 2021 - postDateFormat: "EEE MMM dd HH:mm:ss yyyy", // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", + hasComments: getAttrOrElem(current, 'has_comments')?.toString() == 'true', + serverId: getAttrOrElem(current, 'id')?.toString(), + rating: getAttrOrElem(current, 'rating')?.toString(), + score: getAttrOrElem(current, 'score')?.toString(), + sources: getAttrOrElem(current, 'source') != null ? [getAttrOrElem(current, 'source')!] : null, + md5String: getAttrOrElem(current, 'md5')?.toString(), + postDate: getAttrOrElem(current, 'created_at')?.toString(), // Fri Jun 18 02:13:45 -0500 2021 + postDateFormat: 'EEE MMM dd HH:mm:ss yyyy', // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", ); - if(booru.baseURL!.contains('realbooru.com')) { + if (booru.baseURL!.contains('realbooru.com')) { // the api is even shittier now and they don't even return correct file extensions // now we'll have to either rely on tags and make a bunch of requests for each item to get the real file ext item.possibleExt.value = (tags.contains('gif') || tags.contains('animated_gif')) - ? 'animation' - : (tags.contains('webm') || tags.contains('mp4') || tags.contains('sound')) - ? 'video' - : null; + ? 'animation' + : (tags.contains('webm') || tags.contains('mp4') || tags.contains('sound')) + ? 'video' + : null; item.mediaType.value = MediaType.needsExtraRequest; } @@ -133,23 +132,23 @@ class GelbooruAlikesHandler extends BooruHandler { @override String makePostURL(String id) { // EXAMPLE: https://safebooru.org/index.php?page=post&s=view&id=645243 - return "${booru.baseURL}/index.php?page=post&s=view&id=$id"; + return '${booru.baseURL}/index.php?page=post&s=view&id=$id'; } @override String makeURL(String tags) { // EXAMPLE: https://safebooru.org/index.php?page=dapi&s=post&q=index&tags=rating:safe+sort:score+translated&limit=50&pid=0 String baseUrl = booru.baseURL!; - if(baseUrl.contains('rule34.xxx')) { + if (baseUrl.contains('rule34.xxx')) { // because requests to default url are protected by a captcha baseUrl = 'https://api.rule34.xxx'; } final int cappedPage = max(0, pageNum); - final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? "&api_key=${booru.apiKey}" : ""; - final String userIdStr = booru.userID?.isNotEmpty == true ? "&user_id=${booru.userID}" : ""; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; + final String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; - return "$baseUrl/index.php?page=dapi&s=post&q=index&tags=${tags.replaceAll(" ", "+")}&limit=${limit.toString()}&pid=${cappedPage.toString()}$apiKeyStr$userIdStr"; + return "$baseUrl/index.php?page=dapi&s=post&q=index&tags=${tags.replaceAll(" ", "+")}&limit=$limit&pid=$cappedPage$apiKeyStr$userIdStr"; } // ----------------- Tag suggestions and tag handler stuff @@ -161,20 +160,20 @@ class GelbooruAlikesHandler extends BooruHandler { // EXAMPLE: https://safebooru.org/autocomplete.php?q=naga String baseUrl = booru.baseURL!; - if(baseUrl.contains('rule34.xxx')) { + if (baseUrl.contains('rule34.xxx')) { baseUrl = 'https://api.rule34.xxx'; } - return "$baseUrl/autocomplete.php?q=$input"; // doesn't allow limit, but sorts by popularity + return '$baseUrl/autocomplete.php?q=$input'; // doesn't allow limit, but sorts by popularity } @override - List parseTagSuggestionsList(response) { + List parseTagSuggestionsList(dynamic response) { return jsonDecode(response.data) ?? []; } @override - String? parseTagSuggestion(responseItem, int index) { - return responseItem["value"]; + String? parseTagSuggestion(dynamic responseItem, int index) { + return responseItem['value']; } // ----------------- Search count @@ -183,26 +182,26 @@ class GelbooruAlikesHandler extends BooruHandler { Future searchCount(String input) async { int result = 0; // gelbooru json has count in @attributes, but there is no count data on r34xxx json, so we switch back to xml - String url = makeURL(validateTags(input)); + final String url = makeURL(validateTags(input)); - final String cookies = await getCookies() ?? ""; + final String cookies = await getCookies() ?? ''; final Map headers = { ...getHeaders(), - if(cookies.isNotEmpty) 'Cookie': cookies, + if (cookies.isNotEmpty) 'Cookie': cookies, }; try { final response = await DioNetwork.get(url, headers: headers); // 200 is the success http response code if (response.statusCode == 200) { - var parsedResponse = XmlDocument.parse(response.data); - var root = parsedResponse.findAllElements('posts').toList(); + final parsedResponse = XmlDocument.parse(response.data); + final root = parsedResponse.findAllElements('posts').toList(); if (root.length == 1) { result = int.parse(root[0].getAttribute('count') ?? '0'); } } } catch (e) { - Logger.Inst().log(e.toString(), className, "searchCount", LogTypes.exception); + Logger.Inst().log(e.toString(), className, 'searchCount', LogTypes.exception); } totalCount.value = result; return; @@ -211,74 +210,74 @@ class GelbooruAlikesHandler extends BooruHandler { // ----------------- Comments @override - bool hasCommentsSupport = true; + bool get hasCommentsSupport => true; @override String makeCommentsURL(String postID, int pageNum) { // EXAMPLE: https://safebooru.org/index.php?page=dapi&s=comment&q=index&post_id=1 String baseUrl = booru.baseURL!; - if(baseUrl.contains('rule34.xxx')) { + if (baseUrl.contains('rule34.xxx')) { baseUrl = 'https://api.rule34.xxx'; } - return "$baseUrl/index.php?page=dapi&s=comment&q=index&post_id=$postID"; + return '$baseUrl/index.php?page=dapi&s=comment&q=index&post_id=$postID'; } @override - List parseCommentsList(response) { - var parsedResponse = XmlDocument.parse(response.data); - return parsedResponse.findAllElements("comment").toList(); + List parseCommentsList(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + return parsedResponse.findAllElements('comment').toList(); } @override - CommentItem? parseComment(responseItem, int index) { - var current = responseItem; + CommentItem? parseComment(dynamic responseItem, int index) { + final current = responseItem; return CommentItem( - id: current.getAttribute("id"), - title: current.getAttribute("id"), - content: current.getAttribute("body"), - authorID: current.getAttribute("creator_id"), - authorName: current.getAttribute("creator"), - postID: current.getAttribute("post_id"), + id: current.getAttribute('id'), + title: current.getAttribute('id'), + content: current.getAttribute('body'), + authorID: current.getAttribute('creator_id'), + authorName: current.getAttribute('creator'), + postID: current.getAttribute('post_id'), // TODO broken on rule34xxx? returns current time - createDate: current.getAttribute("created_at"), // 2021-11-15 12:09 - createDateFormat: "yyyy-MM-dd HH:mm", + createDate: current.getAttribute('created_at'), // 2021-11-15 12:09 + createDateFormat: 'yyyy-MM-dd HH:mm', ); } // ----------------- Notes @override - bool hasNotesSupport = true; + bool get hasNotesSupport => true; @override String makeNotesURL(String postID) { // EXAMPLE: https://safebooru.org/index.php?page=dapi&s=note&q=index&post_id=645243 String baseUrl = booru.baseURL!; - if(baseUrl.contains('rule34.xxx')) { + if (baseUrl.contains('rule34.xxx')) { baseUrl = 'https://api.rule34.xxx'; } - return "$baseUrl/index.php?page=dapi&s=note&q=index&post_id=$postID"; + return '$baseUrl/index.php?page=dapi&s=note&q=index&post_id=$postID'; } @override - List parseNotesList(response) { - var parsedResponse = XmlDocument.parse(response.data); - return parsedResponse.findAllElements("note").toList(); + List parseNotesList(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + return parsedResponse.findAllElements('note').toList(); } @override - NoteItem? parseNote(responseItem, int index) { - var current = responseItem; + NoteItem? parseNote(dynamic responseItem, int index) { + final current = responseItem; return NoteItem( - id: current.getAttribute("id"), - postID: current.getAttribute("post_id"), - content: current.getAttribute("body"), - posX: int.tryParse(current.getAttribute("x") ?? '0') ?? 0, - posY: int.tryParse(current.getAttribute("y") ?? '0') ?? 0, - width: int.tryParse(current.getAttribute("width") ?? '0') ?? 0, - height: int.tryParse(current.getAttribute("height") ?? '0') ?? 0, + id: current.getAttribute('id'), + postID: current.getAttribute('post_id'), + content: current.getAttribute('body'), + posX: int.tryParse(current.getAttribute('x') ?? '0') ?? 0, + posY: int.tryParse(current.getAttribute('y') ?? '0') ?? 0, + width: int.tryParse(current.getAttribute('width') ?? '0') ?? 0, + height: int.tryParse(current.getAttribute('height') ?? '0') ?? 0, ); } } diff --git a/lib/src/boorus/gelbooru_handler.dart b/lib/src/boorus/gelbooru_handler.dart index 6881ba2a..910cb8db 100644 --- a/lib/src/boorus/gelbooru_handler.dart +++ b/lib/src/boorus/gelbooru_handler.dart @@ -4,7 +4,6 @@ import 'dart:math'; import 'package:html/parser.dart'; import 'package:xml/xml.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/comment_item.dart'; import 'package:lolisnatcher/src/data/note_item.dart'; @@ -15,25 +14,25 @@ import 'package:lolisnatcher/src/utils/dio_network.dart'; import 'package:lolisnatcher/src/utils/logger.dart'; class GelbooruHandler extends BooruHandler { - GelbooruHandler(Booru booru, int limit) : super(booru, limit); + GelbooruHandler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; @override - Map tagTypeMap = { - "5": TagType.meta, - "3": TagType.copyright, - "4": TagType.character, - "1": TagType.artist, - "0": TagType.none, - }; + Map get tagTypeMap => { + '5': TagType.meta, + '3': TagType.copyright, + '4': TagType.character, + '1': TagType.artist, + '0': TagType.none, + }; @override Map getHeaders() { return { ...super.getHeaders(), - "Cookie": "fringeBenefits=yup;" // unlocks restricted content (but it's probably not necessary) + 'Cookie': 'fringeBenefits=yup;' // unlocks restricted content (but it's probably not necessary) }; } @@ -46,15 +45,15 @@ class GelbooruHandler extends BooruHandler { } @override - List parseListFromResponse(response) { - var parsedResponse; + List parseListFromResponse(dynamic response) { + dynamic parsedResponse; try { parsedResponse = response.data; - } catch(e) { + } catch (e) { // gelbooru returns xml response if request was denied for some reason // i.e. user hit a rate limit because he didn't include api key - parsedResponse = XmlDocument.parse(response.data) ; - String? errorMessage = (parsedResponse as XmlDocument).getElement('response')?.getAttribute('reason')?.toString(); + parsedResponse = XmlDocument.parse(response.data); + final String? errorMessage = (parsedResponse as XmlDocument).getElement('response')?.getAttribute('reason')?.toString(); if (errorMessage != null) { throw Exception(errorMessage); } @@ -63,51 +62,51 @@ class GelbooruHandler extends BooruHandler { try { parseSearchCount(parsedResponse); } catch (e) { - Logger.Inst().log("Error parsing search count: $e", className, 'parseListFromResponse::parseSearchCount', LogTypes.exception); + Logger.Inst().log('Error parsing search count: $e', className, 'parseListFromResponse::parseSearchCount', LogTypes.exception); } - return (parsedResponse["post"] ?? []) as List; + return (parsedResponse['post'] ?? []) as List; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var current = responseItem; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final current = responseItem; - if (current["file_url"] != null) { + if (current['file_url'] != null) { // Fix for bleachbooru - String fileURL = "", sampleURL = "", previewURL = ""; - fileURL += current["file_url"]!.toString(); + String fileURL = '', sampleURL = '', previewURL = ''; + fileURL += current['file_url']!.toString(); // sample url is optional, on gelbooru there is sample == 0/1 to tell if it exists - sampleURL += current["sample_url"]?.toString() ?? current["file_url"]!.toString(); - previewURL += current["preview_url"]!.toString(); - if (!fileURL.contains("http")) { + sampleURL += current['sample_url']?.toString() ?? current['file_url']!.toString(); + previewURL += current['preview_url']!.toString(); + if (!fileURL.contains('http')) { fileURL = booru.baseURL! + fileURL; sampleURL = booru.baseURL! + sampleURL; previewURL = booru.baseURL! + previewURL; } - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: sampleURL, thumbnailURL: previewURL, // parseFragment to parse html elements (i.e. & => &) - tagsList: parseFragment(current["tags"]).text?.split(" ") ?? [], - postURL: makePostURL(current["id"]!.toString()), - fileWidth: double.tryParse(current["width"]?.toString() ?? ''), - fileHeight: double.tryParse(current["height"]?.toString() ?? ''), - sampleWidth: double.tryParse(current["sample_width"]?.toString() ?? ''), - sampleHeight: double.tryParse(current["sample_height"]?.toString() ?? ''), - previewWidth: double.tryParse(current["preview_width"]?.toString() ?? ''), - previewHeight: double.tryParse(current["preview_height"]?.toString() ?? ''), - hasNotes: current["has_notes"]?.toString() == 'true', - hasComments: current["has_comments"]?.toString() == 'true', - serverId: current["id"]?.toString(), - rating: current["rating"]?.toString(), - score: current["score"]?.toString(), - sources: (current["source"].runtimeType == String) ? [current["source"]!] : null, - md5String: current["md5"]?.toString(), - postDate: current["created_at"]?.toString(), // Fri Jun 18 02:13:45 -0500 2021 - postDateFormat: "EEE MMM dd HH:mm:ss yyyy", // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", + tagsList: parseFragment(current['tags']).text?.split(' ') ?? [], + postURL: makePostURL(current['id']!.toString()), + fileWidth: double.tryParse(current['width']?.toString() ?? ''), + fileHeight: double.tryParse(current['height']?.toString() ?? ''), + sampleWidth: double.tryParse(current['sample_width']?.toString() ?? ''), + sampleHeight: double.tryParse(current['sample_height']?.toString() ?? ''), + previewWidth: double.tryParse(current['preview_width']?.toString() ?? ''), + previewHeight: double.tryParse(current['preview_height']?.toString() ?? ''), + hasNotes: current['has_notes']?.toString() == 'true', + hasComments: current['has_comments']?.toString() == 'true', + serverId: current['id']?.toString(), + rating: current['rating']?.toString(), + score: current['score']?.toString(), + sources: (current['source'].runtimeType == String) ? [current['source']!] : null, + md5String: current['md5']?.toString(), + postDate: current['created_at']?.toString(), // Fri Jun 18 02:13:45 -0500 2021 + postDateFormat: 'EEE MMM dd HH:mm:ss yyyy', // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", ); return item; @@ -117,26 +116,26 @@ class GelbooruHandler extends BooruHandler { @override Future afterParseResponse(List newItems) async { - int lengthBefore = fetched.length; + final int lengthBefore = fetched.length; fetched.addAll(newItems); - populateTagHandler(newItems); // difference from default afterParse + await populateTagHandler(newItems); // difference from default afterParse unawaited(setMultipleTrackedValues(lengthBefore, fetched.length)); } @override String makePostURL(String id) { // EXAMPLE: https://gelbooru.com/index.php?page=post&s=view&id=7296350 - return "${booru.baseURL}/index.php?page=post&s=view&id=$id"; + return '${booru.baseURL}/index.php?page=post&s=view&id=$id'; } @override String makeURL(String tags) { // EXAMPLE: https://gelbooru.com/index.php?page=dapi&s=post&q=index&tags=rating:general%20order:score&limit=20&pid=0&json=1 - int cappedPage = max(0, pageNum); - String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; + final int cappedPage = max(0, pageNum); + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; + final String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; - return "${booru.baseURL}/index.php?page=dapi&s=post&q=index&tags=${tags.replaceAll(" ", "+")}&limit=${limit.toString()}&pid=${cappedPage.toString()}&json=1$apiKeyStr$userIdStr"; + return "${booru.baseURL}/index.php?page=dapi&s=post&q=index&tags=${tags.replaceAll(" ", "+")}&limit=$limit&pid=$cappedPage&json=1$apiKeyStr$userIdStr"; } // ----------------- Tag suggestions and tag handler stuff @@ -144,27 +143,29 @@ class GelbooruHandler extends BooruHandler { @override String makeTagURL(String input) { // EXAMPLE https://gelbooru.com/index.php?page=dapi&s=tag&q=index&name_pattern=nagat%25&limit=10&json=1 - String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; - return "${booru.baseURL}/index.php?page=dapi&s=tag&q=index&name_pattern=$input%&limit=10&json=1$apiKeyStr$userIdStr"; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; + final String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; + return '${booru.baseURL}/index.php?page=dapi&s=tag&q=index&name_pattern=$input%&limit=10&json=1$apiKeyStr$userIdStr'; } @override - List parseTagSuggestionsList(response) { - var parsedResponse = response.data["tag"] ?? []; + List parseTagSuggestionsList(dynamic response) { + final parsedResponse = response.data['tag'] ?? []; return parsedResponse; } @override - String? parseTagSuggestion(responseItem, int index) { - final String tagStr = responseItem["name"] ?? ""; - if (tagStr.isEmpty) return null; + String? parseTagSuggestion(dynamic responseItem, int index) { + final String tagStr = responseItem['name'] ?? ''; + if (tagStr.isEmpty) { + return null; + } // record tag data for future use - final String rawTagType = responseItem["type"]?.toString() ?? ""; + final String rawTagType = responseItem['type']?.toString() ?? ''; TagType tagType = TagType.none; if (rawTagType.isNotEmpty && tagTypeMap.containsKey(rawTagType)) { - tagType = (tagTypeMap[rawTagType] ?? TagType.none); + tagType = tagTypeMap[rawTagType] ?? TagType.none; } addTagsWithType([tagStr], tagType); return tagStr; @@ -172,30 +173,36 @@ class GelbooruHandler extends BooruHandler { @override String makeDirectTagURL(List tags) { - String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; + final String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; return "${booru.baseURL}/index.php?page=dapi&s=tag&q=index&names=${tags.join(" ")}&limit=500&json=1$apiKeyStr$userIdStr"; } @override Future> genTagObjects(List tags) async { - List tagObjects = []; - Logger.Inst().log("Got tag list: $tags", className, "genTagObjects", LogTypes.booruHandlerTagInfo); - String url = makeDirectTagURL(tags); - Logger.Inst().log("DirectTagURL: $url", className, "genTagObjects", LogTypes.booruHandlerTagInfo); + final List tagObjects = []; + Logger.Inst().log('Got tag list: $tags', className, 'genTagObjects', LogTypes.booruHandlerTagInfo); + final String url = makeDirectTagURL(tags); + Logger.Inst().log('DirectTagURL: $url', className, 'genTagObjects', LogTypes.booruHandlerTagInfo); try { final response = await DioNetwork.get(url, headers: getHeaders()); // 200 is the success http response code if (response.statusCode == 200) { - var parsedResponse = (response.data["tag"]) ?? []; + final parsedResponse = (response.data['tag']) ?? []; if (parsedResponse?.isNotEmpty ?? false) { - Logger.Inst().log("Tag response length: ${parsedResponse.length},Tag list length: ${tags.length}", className, "genTagObjects", - LogTypes.booruHandlerTagInfo); + Logger.Inst().log( + 'Tag response length: ${parsedResponse.length},Tag list length: ${tags.length}', + className, + 'genTagObjects', + LogTypes.booruHandlerTagInfo, + ); for (int i = 0; i < parsedResponse.length; i++) { - String fullString = parseFragment(parsedResponse.elementAt(i)["name"]).text!; - String typeKey = parsedResponse.elementAt(i)["type"].toString(); + final String fullString = parseFragment(parsedResponse.elementAt(i)['name']).text!; + final String typeKey = parsedResponse.elementAt(i)['type'].toString(); TagType tagType = TagType.none; - if (tagTypeMap.containsKey(typeKey)) tagType = (tagTypeMap[typeKey] ?? TagType.none); + if (tagTypeMap.containsKey(typeKey)) { + tagType = tagTypeMap[typeKey] ?? TagType.none; + } if (fullString.isNotEmpty) { tagObjects.add(Tag(fullString, tagType: tagType)); } @@ -203,87 +210,87 @@ class GelbooruHandler extends BooruHandler { } } } catch (e) { - Logger.Inst().log(e.toString(), className, "tagSearch", LogTypes.exception); + Logger.Inst().log(e.toString(), className, 'tagSearch', LogTypes.exception); } return tagObjects; } // ----------------- Search count - void parseSearchCount(response) { - var parsedResponse = response["@attributes"]["count"] ?? 0; + void parseSearchCount(dynamic response) { + final parsedResponse = response['@attributes']['count'] ?? 0; totalCount.value = parsedResponse; } // ----------------- Comments @override - bool hasCommentsSupport = true; + bool get hasCommentsSupport => true; @override String makeCommentsURL(String postID, int pageNum) { // EXAMPLE: https://gelbooru.com/index.php?page=dapi&s=comment&q=index&post_id=7296350 - String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; - return "${booru.baseURL}/index.php?page=dapi&s=comment&q=index&post_id=$postID$apiKeyStr$userIdStr"; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; + final String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; + return '${booru.baseURL}/index.php?page=dapi&s=comment&q=index&post_id=$postID$apiKeyStr$userIdStr'; } @override - List parseCommentsList(response) { - var parsedResponse = XmlDocument.parse(response.data); - return parsedResponse.findAllElements("comment").toList(); + List parseCommentsList(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + return parsedResponse.findAllElements('comment').toList(); } @override - CommentItem? parseComment(responseItem, int index) { - var current = responseItem; - String? avatar = current.getAttribute("creator_id")!.isEmpty + CommentItem? parseComment(dynamic responseItem, int index) { + final current = responseItem; + final String avatar = current.getAttribute('creator_id')!.isEmpty ? "${booru.baseURL}/user_avatars/avatar_${current.getAttribute("creator")}.jpg" - : "${booru.baseURL}/user_avatars/honkonymous.png"; + : '${booru.baseURL}/user_avatars/honkonymous.png'; return CommentItem( - id: current.getAttribute("id"), - title: current.getAttribute("id"), - content: current.getAttribute("body"), - authorID: current.getAttribute("creator_id"), - authorName: current.getAttribute("creator"), - postID: current.getAttribute("post_id"), + id: current.getAttribute('id'), + title: current.getAttribute('id'), + content: current.getAttribute('body'), + authorID: current.getAttribute('creator_id'), + authorName: current.getAttribute('creator'), + postID: current.getAttribute('post_id'), avatarUrl: avatar, - createDate: current.getAttribute("created_at"), // 2021-11-15 12:09 - createDateFormat: "yyyy-MM-dd HH:mm", + createDate: current.getAttribute('created_at'), // 2021-11-15 12:09 + createDateFormat: 'yyyy-MM-dd HH:mm', ); } // ----------------- Notes @override - bool hasNotesSupport = true; + bool get hasNotesSupport => true; @override String makeNotesURL(String postID) { // EXAMPLE: https://gelbooru.com/index.php?page=dapi&s=note&q=index&post_id=6512262 - String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; - return "${booru.baseURL}/index.php?page=dapi&s=note&q=index&post_id=$postID$apiKeyStr$userIdStr"; + final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; + final String userIdStr = booru.userID?.isNotEmpty == true ? '&user_id=${booru.userID}' : ''; + return '${booru.baseURL}/index.php?page=dapi&s=note&q=index&post_id=$postID$apiKeyStr$userIdStr'; } @override - List parseNotesList(response) { - var parsedResponse = XmlDocument.parse(response.data); - return parsedResponse.findAllElements("note").toList(); + List parseNotesList(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + return parsedResponse.findAllElements('note').toList(); } @override - NoteItem? parseNote(responseItem, int index) { - var current = responseItem; + NoteItem? parseNote(dynamic responseItem, int index) { + final current = responseItem; return NoteItem( - id: current.getAttribute("id"), - postID: current.getAttribute("post_id"), - content: current.getAttribute("body"), - posX: int.tryParse(current.getAttribute("x") ?? '0') ?? 0, - posY: int.tryParse(current.getAttribute("y") ?? '0') ?? 0, - width: int.tryParse(current.getAttribute("width") ?? '0') ?? 0, - height: int.tryParse(current.getAttribute("height") ?? '0') ?? 0, + id: current.getAttribute('id'), + postID: current.getAttribute('post_id'), + content: current.getAttribute('body'), + posX: int.tryParse(current.getAttribute('x') ?? '0') ?? 0, + posY: int.tryParse(current.getAttribute('y') ?? '0') ?? 0, + width: int.tryParse(current.getAttribute('width') ?? '0') ?? 0, + height: int.tryParse(current.getAttribute('height') ?? '0') ?? 0, ); } } diff --git a/lib/src/boorus/gelbooruv1_handler.dart b/lib/src/boorus/gelbooruv1_handler.dart index 99c6dcf4..b2da7e17 100644 --- a/lib/src/boorus/gelbooruv1_handler.dart +++ b/lib/src/boorus/gelbooruv1_handler.dart @@ -1,39 +1,38 @@ import 'package:html/parser.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; class GelbooruV1Handler extends BooruHandler { - GelbooruV1Handler(Booru booru, int limit) : super(booru, limit); + GelbooruV1Handler(super.booru, super.limit); @override - String validateTags(tags) { - if (tags == " " || tags == "") { - return "all"; + String validateTags(String tags) { + if (tags == ' ' || tags == '') { + return 'all'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - var document = parse(response.data); - var spans = document.getElementsByClassName("thumb"); + List parseListFromResponse(dynamic response) { + final document = parse(response.data); + final spans = document.getElementsByClassName('thumb'); return spans; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var linkItem = responseItem.firstChild!; - var imgItem = linkItem.firstChild!; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final linkItem = responseItem.firstChild; + final imgItem = linkItem.firstChild; - if (imgItem.attributes["src"] != null) { - String id = linkItem.attributes["id"]!.substring(1); - String thumbURL = imgItem.attributes["src"]!; - String fileURL = thumbURL.replaceFirst("thumbs", "img").replaceFirst("thumbnails", "images").replaceFirst("thumbnail_", ""); - List tags = imgItem.attributes["title"]!.split(" "); - BooruItem item = BooruItem( + if (imgItem?.attributes['src'] != null && linkItem?.attributes['id'] != null) { + final String id = linkItem.attributes['id'].substring(1); + final String thumbURL = imgItem.attributes['src']; + final String fileURL = thumbURL.replaceFirst('thumbs', 'img').replaceFirst('thumbnails', 'images').replaceFirst('thumbnail_', ''); + final List tags = imgItem.attributes['title']!.split(' '); + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: fileURL, thumbnailURL: thumbURL, @@ -49,17 +48,17 @@ class GelbooruV1Handler extends BooruHandler { } String getHashFromURL(String url) { - String hash = url.substring(url.lastIndexOf("_") + 1, url.lastIndexOf(".")); + final String hash = url.substring(url.lastIndexOf('_') + 1, url.lastIndexOf('.')); return hash; } @override String makePostURL(String id) { - return "${booru.baseURL}/index.php?page=post&s=view&id=$id"; + return '${booru.baseURL}/index.php?page=post&s=view&id=$id'; } @override String makeURL(String tags) { - return "${booru.baseURL}/index.php?page=post&s=list&tags=${tags.replaceAll(" ", "+")}&pid=${(pageNum * 20).toString()}"; + return '${booru.baseURL}/index.php?page=post&s=list&tags=${tags.replaceAll(" ", "+")}&pid=${pageNum * 20}'; } } diff --git a/lib/src/boorus/hydrus_handler.dart b/lib/src/boorus/hydrus_handler.dart index 71f482bc..5913977d 100644 --- a/lib/src/boorus/hydrus_handler.dart +++ b/lib/src/boorus/hydrus_handler.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; @@ -14,7 +13,7 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; // TODO refactor class HydrusHandler extends BooruHandler { - HydrusHandler(Booru booru, int limit) : super(booru, limit); + HydrusHandler(super.booru, super.limit); var _fileIDs; @@ -22,13 +21,13 @@ class HydrusHandler extends BooruHandler { Map getHeaders() { return { ...super.getHeaders(), - "Hydrus-Client-API-Access-Key": booru.apiKey!, + 'Hydrus-Client-API-Access-Key': booru.apiKey!, }; } @override - Future parseListFromResponse(response) async { - Map parsedResponse = response.data is String ? jsonDecode(response.data) : response.data; + Future parseListFromResponse(dynamic response) async { + final Map parsedResponse = response.data is String ? jsonDecode(response.data) : response.data; if (parsedResponse['file_ids'] != null) { _fileIDs = parsedResponse['file_ids']; return await getResultsPage(pageNum); @@ -37,34 +36,34 @@ class HydrusHandler extends BooruHandler { } } - Future verifyApiAccess() async{ - Logger.Inst().log("Verifying access", "HydrusHandler", "verifyApiAccess", LogTypes.booruHandlerInfo); + Future verifyApiAccess() async { + Logger.Inst().log('Verifying access', 'HydrusHandler', 'verifyApiAccess', LogTypes.booruHandlerInfo); try { - final response = await DioNetwork.head("${booru.baseURL}/verify_access_key", headers: getHeaders()); - if(response.statusCode == 200){ + final response = await DioNetwork.head('${booru.baseURL}/verify_access_key', headers: getHeaders()); + if (response.statusCode == 200) { return true; } - } catch(e) { - Logger.Inst().log(e.toString(), "HydrusHandler", "verifyApiAccess", LogTypes.exception); + } catch (e) { + Logger.Inst().log(e.toString(), 'HydrusHandler', 'verifyApiAccess', LogTypes.exception); } return false; } @override Future search(String tags, int? pageNumCustom, {bool withCaptchaCheck = true}) async { - if (prevTags != tags){ + if (prevTags != tags) { fetched.value = []; prevTags = tags; } - String url = makeURL(tags); - Logger.Inst().log(url, "HydrusHandler", "Search", LogTypes.booruHandlerSearchURL); + final String url = makeURL(tags); + Logger.Inst().log(url, 'HydrusHandler', 'Search', LogTypes.booruHandlerSearchURL); if (_fileIDs == null) { try { final response = await DioNetwork.get(url, headers: getHeaders()); if (response.statusCode == 200) { - Map parsedResponse = response.data is String ? jsonDecode(response.data) : response.data; + final Map parsedResponse = response.data is String ? jsonDecode(response.data) : response.data; if (parsedResponse['file_ids'] != null) { _fileIDs = parsedResponse['file_ids']; return await getResultsPage(pageNum); @@ -72,12 +71,12 @@ class HydrusHandler extends BooruHandler { prevTags = tags; return fetched; } - } catch(e) { - Logger.Inst().log(e.toString(), "HydrusHandler", "Search", LogTypes.exception); + } catch (e) { + Logger.Inst().log(e.toString(), 'HydrusHandler', 'Search', LogTypes.exception); return fetched; } } else { - return await getResultsPage(pageNum); + return getResultsPage(pageNum); } } @@ -85,118 +84,125 @@ class HydrusHandler extends BooruHandler { limit = limit > 20 ? 20 : limit; try { - int pageMax = (_fileIDs.length > limit ? (_fileIDs.length / limit).ceil() : 1); + final int pageMax = (_fileIDs.length > limit ? (_fileIDs.length / limit).ceil() : 1); if (pageNum >= pageMax) { locked = true; } else { - int lowerBound = ((pageNum < 1) ? 0 : pageNum * limit); - int upperBound = (pageNum + 1< pageMax) ? (lowerBound + limit) : _fileIDs.length; + final int lowerBound = ((pageNum < 1) ? 0 : pageNum * limit); + final int upperBound = (pageNum + 1 < pageMax) ? (lowerBound + limit) : _fileIDs.length; String fileIDString = '['; - for (int i = lowerBound; i < upperBound ; i++){ + for (int i = lowerBound; i < upperBound; i++) { fileIDString += _fileIDs[i].toString(); - if(i != upperBound - 1) {fileIDString +=',';} + if (i != upperBound - 1) { + fileIDString += ','; + } } fileIDString += ']'; - String url = "${booru.baseURL}/get_files/file_metadata?file_ids=$fileIDString"; - final response = await DioNetwork.get(url, headers: { + final String url = '${booru.baseURL}/get_files/file_metadata?file_ids=$fileIDString'; + final response = await DioNetwork.get( + url, + headers: { ...super.getHeaders(), - "Hydrus-Client-API-Access-Key" : booru.apiKey!, + if (booru.apiKey?.isNotEmpty == true) 'Hydrus-Client-API-Access-Key': booru.apiKey, }, ); if (response.statusCode == 200) { - var parsedResponse = response.data; + final parsedResponse = response.data; //Logger.Inst().log(response.data, "HydrusHandler", "getResultsPage", LogTypes.booruHandlerRawFetched); - List newItems = []; + final List newItems = []; for (int i = 0; i < parsedResponse['metadata'].length; i++) { - List tagList = []; - List responseTags = []; + final List tagList = []; + List responseTags = []; - Map? tagsMap = parsedResponse['metadata'][i]['tags']; - if (tagsMap != null){ - for (MapEntry entry in tagsMap.entries){ - if(entry.value["name"] == "all known tags"){ - responseTags = entry.value["display_tags"]["0"] ?? []; - } - } + final Map? tagsMap = parsedResponse['metadata'][i]['tags']; + if (tagsMap != null) { + for (final MapEntry entry in tagsMap.entries) { + if (entry.value['name'] == 'all known tags') { + responseTags = entry.value['display_tags']['0'] ?? []; } - - for (int x = 0; x < responseTags.length; x++){ - tagList.add(responseTags[x].toString()); } - if (parsedResponse['metadata'][i]['file_id'] != null){ - List dynKnownUrls = parsedResponse['metadata'][i]['known_urls']; - List knownUrls = []; - if (dynKnownUrls.isNotEmpty){ - for (var element in dynKnownUrls) { - knownUrls.add(element.toString()); - } - } - BooruItem item = BooruItem( - fileURL: "${booru.baseURL}/get_files/file?file_id=${parsedResponse['metadata'][i]['file_id']}&Hydrus-Client-API-Access-Key=${booru.apiKey}", - sampleURL: "${booru.baseURL}/get_files/thumbnail?file_id=${parsedResponse['metadata'][i]['file_id']}&Hydrus-Client-API-Access-Key=${booru.apiKey}", - thumbnailURL: "${booru.baseURL}/get_files/thumbnail?file_id=${parsedResponse['metadata'][i]['file_id']}&Hydrus-Client-API-Access-Key=${booru.apiKey}", - tagsList: tagList, - postURL: '', - fileExt: parsedResponse['metadata'][i]['ext'].toString().substring(1), - fileWidth: parsedResponse['metadata'][i]['width'].toDouble(), - fileHeight: parsedResponse['metadata'][i]['height'].toDouble(), - md5String: parsedResponse['metadata'][i]['hash'], - sources: knownUrls, - fileNameExtras: "Hydrus_" - ); + } - newItems.add(item); + for (int x = 0; x < responseTags.length; x++) { + tagList.add(responseTags[x].toString()); + } + if (parsedResponse['metadata'][i]['file_id'] != null) { + final List dynKnownUrls = parsedResponse['metadata'][i]['known_urls']; + final List knownUrls = []; + if (dynKnownUrls.isNotEmpty) { + for (final element in dynKnownUrls) { + knownUrls.add(element.toString()); + } } + final BooruItem item = BooruItem( + fileURL: "${booru.baseURL}/get_files/file?file_id=${parsedResponse['metadata'][i]['file_id']}&Hydrus-Client-API-Access-Key=${booru.apiKey}", + sampleURL: + "${booru.baseURL}/get_files/thumbnail?file_id=${parsedResponse['metadata'][i]['file_id']}&Hydrus-Client-API-Access-Key=${booru.apiKey}", + thumbnailURL: + "${booru.baseURL}/get_files/thumbnail?file_id=${parsedResponse['metadata'][i]['file_id']}&Hydrus-Client-API-Access-Key=${booru.apiKey}", + tagsList: tagList, + postURL: '', + fileExt: parsedResponse['metadata'][i]['ext'].toString().substring(1), + fileWidth: parsedResponse['metadata'][i]['width'].toDouble(), + fileHeight: parsedResponse['metadata'][i]['height'].toDouble(), + md5String: parsedResponse['metadata'][i]['hash'], + sources: knownUrls, + fileNameExtras: 'Hydrus_', + ); + + newItems.add(item); + } } - int lengthBefore = fetched.length; + final int lengthBefore = fetched.length; fetched.addAll(newItems); unawaited(setMultipleTrackedValues(lengthBefore, fetched.length)); return fetched; } else { - Logger.Inst().log("Getting metadata failed", "HydrusHandler", "getResultsPage", LogTypes.booruHandlerInfo); + Logger.Inst().log('Getting metadata failed', 'HydrusHandler', 'getResultsPage', LogTypes.booruHandlerInfo); } } - }catch(e){ - Logger.Inst().log(e.toString(), "HydrusHandler", "getResultsPage", LogTypes.exception); + } catch (e) { + Logger.Inst().log(e.toString(), 'HydrusHandler', 'getResultsPage', LogTypes.exception); } return fetched; } - Future addURL(BooruItem item) async{ + Future addURL(BooruItem item) async { try { - String url = "${booru.baseURL}/add_urls/add_url"; - Logger.Inst().log(url, "HydrusHandler", "addURL", LogTypes.booruHandlerInfo); - Logger.Inst().log(booru.apiKey!, "HydrusHandler", "addURL", LogTypes.booruHandlerInfo); + final String url = '${booru.baseURL}/add_urls/add_url'; + Logger.Inst().log(url, 'HydrusHandler', 'addURL', LogTypes.booruHandlerInfo); + Logger.Inst().log(booru.apiKey, 'HydrusHandler', 'addURL', LogTypes.booruHandlerInfo); // Uses dio because darts http post doesn't send the content type header correctly and the post doesn't work - List tags = []; + final List tags = []; String tagString = ''; - for (var element in item.tagsList) { - tags.add(element.replaceAll("_", " ")); + for (final element in item.tagsList) { + tags.add(element.replaceAll('_', ' ')); tagString += '"$element",'; } - tagString = tagString.substring(0,tagString.length -1); - await DioNetwork.post(url, + tagString = tagString.substring(0, tagString.length - 1); + await DioNetwork.post( + url, headers: { - HttpHeaders.contentTypeHeader: "application/json", - "Hydrus-Client-API-Access-Key":booru.apiKey! - }, - data: {"url": item.fileURL, - "filterable_tags":item.tagsList + HttpHeaders.contentTypeHeader: 'application/json', + if (booru.apiKey?.isNotEmpty == true) 'Hydrus-Client-API-Access-Key': booru.apiKey, }, + data: {'url': item.fileURL, 'filterable_tags': item.tagsList}, ); - } catch(e) { + } catch (e) { FlashElements.showSnackbar( duration: null, title: const Text( - "Error!", - style: TextStyle(fontSize: 20) + 'Error!', + style: TextStyle(fontSize: 20), ), - content: Column( + content: const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text('Something went wrong importing to hydrus. You might not have given the correct api permissions, this can be edited in Review Services. Add tags to file and Add Urls'), + children: [ + Text( + 'Something went wrong importing to hydrus. You might not have given the correct api permissions, this can be edited in Review Services. Add tags to file and Add Urls', + ), Text('You might not have given the correct api permissions, this can be edited in Review Services.'), Text('Add tags to file and Add Urls.'), ], @@ -206,98 +212,100 @@ class HydrusHandler extends BooruHandler { sideColor: Colors.red, ); - Logger.Inst().log(e.toString(), "HydrusHandler", "addURL", LogTypes.exception); + Logger.Inst().log(e.toString(), 'HydrusHandler', 'addURL', LogTypes.exception); } return fetched; } Future getAccessKey() async { - String url = "${booru.baseURL}/request_new_permissions?name=LoliSnatcher&basic_permissions=[3,0,2]"; - Logger.Inst().log("Requesting key: $url", "HydrusHandler", "getAccessKey", LogTypes.booruHandlerInfo); + final String url = '${booru.baseURL}/request_new_permissions?name=LoliSnatcher&basic_permissions=[3,0,2]'; + Logger.Inst().log('Requesting key: $url', 'HydrusHandler', 'getAccessKey', LogTypes.booruHandlerInfo); try { - final response = await DioNetwork.get(url, headers: { + final response = await DioNetwork.get( + url, + headers: { ...super.getHeaders(), - "Hydrus-Client-API-Access-Key" : booru.apiKey!, + if (booru.apiKey?.isNotEmpty == true) 'Hydrus-Client-API-Access-Key': booru.apiKey, }, ); if (response.statusCode == 200) { - var parsedResponse = response.data; - Logger.Inst().log("Key Request Successful: ${parsedResponse['access_key']}", "HydrusHandler", "getAccessKey", LogTypes.booruHandlerInfo); + final parsedResponse = response.data; + Logger.Inst().log("Key Request Successful: ${parsedResponse['access_key']}", 'HydrusHandler', 'getAccessKey', LogTypes.booruHandlerInfo); return parsedResponse['access_key'].toString(); } else { - Logger.Inst().log("Key Request Failed: ${response.statusCode}", "HydrusHandler", "getAccessKey", LogTypes.booruHandlerInfo); - Logger.Inst().log(response.data, "HydrusHandler", "getAccessKey", LogTypes.booruHandlerInfo); + Logger.Inst().log('Key Request Failed: ${response.statusCode}', 'HydrusHandler', 'getAccessKey', LogTypes.booruHandlerInfo); + Logger.Inst().log(response.data, 'HydrusHandler', 'getAccessKey', LogTypes.booruHandlerInfo); } - } catch (e){ - Logger.Inst().log(e.toString(), "HydrusHandler", "getAccessKey", LogTypes.exception); + } catch (e) { + Logger.Inst().log(e.toString(), 'HydrusHandler', 'getAccessKey', LogTypes.exception); } - return ""; + return ''; } @override String makeURL(String tags) { - if(tags.trim().isEmpty){ - tags = "*"; + if (tags.trim().isEmpty) { + tags = '*'; } - List tagList = tags.split(","); + final List tagList = tags.split(','); int sortType = -1; bool ascending = false; - for (int i = tagList.length - 1; i >= 0; i--){ - if(tagList[i].contains("sort:")) { - sortType = getSortType(tagList[i].split(":")[1]); + for (int i = tagList.length - 1; i >= 0; i--) { + if (tagList[i].contains('sort:')) { + sortType = getSortType(tagList[i].split(':')[1]); tagList.remove(tagList[i].trim().toLowerCase()); - } else if(tagList[i].contains("order:asc")) { + } else if (tagList[i].contains('order:asc')) { ascending = true; tagList.remove(tagList[i].trim().toLowerCase()); - } else if(tagList[i].contains("order:desc")) { + } else if (tagList[i].contains('order:desc')) { ascending = false; tagList.remove(tagList[i].trim().toLowerCase()); } } - return "${booru.baseURL}/get_files/search_files?tags=${jsonEncode(tagList).replaceAll("%22", "'")}${sortType > -1 ? "&file_sort_type=$sortType":""}&file_sort_asc=$ascending"; + return "${booru.baseURL}/get_files/search_files?tags=${jsonEncode(tagList).replaceAll("%22", "'")}${sortType > -1 ? "&file_sort_type=$sortType" : ""}&file_sort_asc=$ascending"; } - int getSortType(String orderString){ - switch(orderString){ - case "filesize": + int getSortType(String orderString) { + switch (orderString) { + case 'filesize': return 0; - case "duration": + case 'duration': return 1; - case "importtime": + case 'importtime': return 2; - case "filetype": + case 'filetype': return 3; - case "random": + case 'random': return 4; - case "width": + case 'width': return 5; - case "height": + case 'height': return 6; - case "ratio": + case 'ratio': return 7; - case "numpixels": + case 'numpixels': return 8; - case "numtags": + case 'numtags': return 9; - case "numviews": + case 'numviews': return 10; - case "totalviewtime": + case 'totalviewtime': return 11; - case "bitrate": + case 'bitrate': return 12; - case "hasaudio": + case 'hasaudio': return 13; - case "modifiedtime": + case 'modifiedtime': return 14; - case "framerate": + case 'framerate': return 15; - case "framecount": + case 'framecount': return 16; - case "lastviewed": + case 'lastviewed': return 17; - case "archivetime": + case 'archivetime': return 18; - case "hashhex": + case 'hashhex': return 19; default: return -1; @@ -306,6 +314,6 @@ class HydrusHandler extends BooruHandler { @override String makeTagURL(String input) { - return "${booru.baseURL}/index.php?page=dapi&s=tag&q=index&name_pattern=$input%&limit=10"; + return '${booru.baseURL}/index.php?page=dapi&s=tag&q=index&name_pattern=$input%&limit=10'; } } diff --git a/lib/src/boorus/idol_sankaku_handler.dart b/lib/src/boorus/idol_sankaku_handler.dart index fd92c948..78ebe566 100644 --- a/lib/src/boorus/idol_sankaku_handler.dart +++ b/lib/src/boorus/idol_sankaku_handler.dart @@ -1,20 +1,19 @@ import 'package:lolisnatcher/src/boorus/sankaku_handler.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/comment_item.dart'; import 'package:lolisnatcher/src/data/tag_type.dart'; class IdolSankakuHandler extends SankakuHandler { - IdolSankakuHandler(Booru booru, int limit) : super(booru, limit); + IdolSankakuHandler(super.booru, super.limit); @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final current = responseItem; final List tags = []; final Map> tagMap = {}; @@ -70,7 +69,7 @@ class IdolSankakuHandler extends SankakuHandler { @override String makeURL(String tags) { - return '${booru.baseURL}/post/index.json?tags=$tags&limit=${limit.toString()}&page=${pageNum.toString()}'; + return '${booru.baseURL}/post/index.json?tags=$tags&limit=$limit&page=$pageNum'; } @override @@ -85,13 +84,13 @@ class IdolSankakuHandler extends SankakuHandler { } @override - List parseCommentsList(response) { + List parseCommentsList(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - CommentItem? parseComment(responseItem, int index) { + CommentItem? parseComment(dynamic responseItem, int index) { final current = responseItem; return CommentItem( id: current['id'].toString(), diff --git a/lib/src/boorus/ink_bunny_handler.dart b/lib/src/boorus/ink_bunny_handler.dart index 7ebfbeac..5078e14d 100644 --- a/lib/src/boorus/ink_bunny_handler.dart +++ b/lib/src/boorus/ink_bunny_handler.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:math'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/logger.dart'; @@ -10,19 +9,19 @@ import 'package:lolisnatcher/src/utils/logger.dart'; // We then need to get the is and do a search to get full info for all results from the search class InkBunnyHandler extends BooruHandler { - InkBunnyHandler(Booru booru, int limit) : super(booru, limit); + InkBunnyHandler(super.booru, super.limit); String sessionToken = ''; bool _gettingToken = false; @override - bool hasSizeData = true; + bool get hasSizeData => true; Future setSessionToken() async { //https://inkbunny.net/api_login.php?output_mode=xml&username=guest //https://inkbunny.net/api_login.php?output_mode=xml&username=myusername&password=mypassword _gettingToken = true; final String url = '${booru.baseURL}/api_login.php?output_mode=json'; - Map queryParams = {}; + final Map queryParams = {}; if ((booru.apiKey?.isEmpty ?? true) && (booru.userID?.isEmpty ?? true)) { queryParams['username'] = 'guest'; } else { @@ -31,7 +30,7 @@ class InkBunnyHandler extends BooruHandler { } try { final response = await fetchSearch(Uri.parse(url), queryParams: queryParams); - Map parsedResponse = response.data; + final Map parsedResponse = response.data; if (parsedResponse['sid'] != null) { sessionToken = parsedResponse['sid'].toString(); Logger.Inst().log('Inkbunny session token found: $sessionToken', 'InkBunnyHandler', 'getSessionToken', LogTypes.booruHandlerInfo); @@ -43,7 +42,7 @@ class InkBunnyHandler extends BooruHandler { Logger.Inst().log('Exception getting session token: $url $e', 'InkBunnyHandler', 'getSessionToken', LogTypes.booruHandlerInfo); } _gettingToken = false; - return sessionToken.isEmpty ? false : true; + return sessionToken.isNotEmpty; } // This sets ratings for the session so that all images are returned from the api @@ -51,7 +50,7 @@ class InkBunnyHandler extends BooruHandler { final String url = '${booru.baseURL}/api_userrating.php?output_mode=json&sid=$sessionToken&tag[2]=yes&tag[3]=yes&tag[4]=yes&tag[5]=yes'; try { final response = await fetchSearch(Uri.parse(url)); - Map parsedResponse = response.data; + final Map parsedResponse = response.data; if (parsedResponse['sid'] != null) { if (sessionToken == parsedResponse['sid']) { Logger.Inst().log('Inkbunny set ratings', className, 'setRatingOptions', LogTypes.booruHandlerInfo); @@ -66,9 +65,9 @@ class InkBunnyHandler extends BooruHandler { } // The api doesn't give much information about the posts so we need to get their ids and then do another query to get all the data - Future getSubmissionResponse(parsedResponse) async { + Future getSubmissionResponse(dynamic parsedResponse) async { totalCount.value = int.parse(parsedResponse['results_count_all']); - List ids = []; + final List ids = []; for (int i = 0; i < parsedResponse['submissions'].length; i++) { final current = parsedResponse['submissions'][i]; ids.add(current['submission_id'].toString()); @@ -77,7 +76,7 @@ class InkBunnyHandler extends BooruHandler { try { final Uri uri = Uri.parse("${booru.baseURL}/api_submissions.php?output_mode=json&sid=$sessionToken&submission_ids=${ids.join(",")}"); final response = await fetchSearch(uri); - Logger.Inst().log('Getting submission data: ${uri.toString()}', className, 'getSubmissionResponse', LogTypes.booruHandlerRawFetched); + Logger.Inst().log('Getting submission data: $uri', className, 'getSubmissionResponse', LogTypes.booruHandlerRawFetched); if (response.statusCode == 200) { Logger.Inst().log(response.data, className, 'getSubmissionResponse', LogTypes.booruHandlerRawFetched); return response.data; @@ -92,7 +91,7 @@ class InkBunnyHandler extends BooruHandler { } @override - Future parseListFromResponse(response) async { + Future parseListFromResponse(dynamic response) async { // The api will keep loading pages with the same results as the last page if pagenum is bigger than the max final parsedResponse = response.data; final int maxPageCount = parsedResponse['pages_count']; @@ -115,14 +114,14 @@ class InkBunnyHandler extends BooruHandler { } // Inkbunny can have multiple images per posts so the response cannot be parsed like the other boorus - List parseItemsFromResponse(parsedResponse) { + List parseItemsFromResponse(dynamic parsedResponse) { // Api orders the results the wrong way final List rawItems = (parsedResponse['submissions'] ?? []).reversed.toList(); - List items = []; + final List items = []; for (int i = 0; i < rawItems.length; i++) { final current = rawItems[i]; Logger.Inst().log(current.toString(), className, 'parseItemsFromResponse', LogTypes.booruHandlerRawFetched); - List currentTags = []; + final List currentTags = []; currentTags.add("artist:${current["username"]}"); final tags = current['keywords'] ?? []; @@ -171,7 +170,7 @@ class InkBunnyHandler extends BooruHandler { } @override - FutureOr parseItemFromResponse(responseItem, int index) { + FutureOr parseItemFromResponse(dynamic responseItem, int index) { return responseItem; } @@ -186,7 +185,7 @@ class InkBunnyHandler extends BooruHandler { String order = ''; String artist = ''; bool random = false; - List tagList = tags.split(' '); + final List tagList = tags.split(' '); String tagStr = ''; for (int i = 0; i < tagList.length; i++) { if (tagList[i].contains('artist:')) { @@ -221,7 +220,7 @@ class InkBunnyHandler extends BooruHandler { @override Future> tagSearch(String input) async { - List searchTags = []; + final List searchTags = []; final String url = makeTagURL(input); try { final Uri uri = Uri.parse(url); diff --git a/lib/src/boorus/mergebooru_handler.dart b/lib/src/boorus/mergebooru_handler.dart index 04f8de15..9a3dc01a 100644 --- a/lib/src/boorus/mergebooru_handler.dart +++ b/lib/src/boorus/mergebooru_handler.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:convert'; import 'package:crypto/crypto.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; @@ -11,7 +11,7 @@ import 'package:lolisnatcher/src/handlers/booru_handler_factory.dart'; import 'package:lolisnatcher/src/utils/logger.dart'; class MergebooruHandler extends BooruHandler { - MergebooruHandler(Booru booru, int limit) : super(booru, limit); + MergebooruHandler(super.booru, super.limit); List booruList = []; List booruHandlers = []; @@ -28,15 +28,15 @@ class MergebooruHandler extends BooruHandler { if (pageNumCustom != null) { pageNum = pageNumCustom; } - List> tmpFetchedList = []; - List isGelbooruV1List = []; + final List> tmpFetchedList = []; + final List isGelbooruV1List = []; int fetchedMax = 0; for (int i = 0; i < booruHandlers.length; i++) { - String currentTags = - tags.replaceAll(RegExp(r"(?!" "${i + 1}" r")\d+#[A-Za-z0-9\_\-~:]*"), "").replaceAll(" ", " ").replaceAll(RegExp(r"\d+#"), "").trim(); - Logger.Inst().log("TAGS FOR #$i are: $currentTags", "MergeBooruHandler", "Search", LogTypes.booruHandlerInfo); + final String currentTags = + tags.replaceAll(RegExp('(?!' '${i + 1}' r')\d+#[A-Za-z0-9\_\-~:]*'), '').replaceAll(' ', ' ').replaceAll(RegExp(r'\d+#'), '').trim(); + Logger.Inst().log('TAGS FOR #$i are: $currentTags', 'MergeBooruHandler', 'Search', LogTypes.booruHandlerInfo); booruHandlers[i].pageNum = pageNum + booruHandlerPageNums[i]; - List tmpFetched = (await booruHandlers[i].search(currentTags, null)) ?? []; + final List tmpFetched = (await booruHandlers[i].search(currentTags, null)) ?? []; tmpFetchedList.add(tmpFetched); if (booruHandlers[i].booru.type == BooruType.GelbooruV1) { isGelbooruV1List.add(true); @@ -59,18 +59,32 @@ class MergebooruHandler extends BooruHandler { if (!hashInFetched(fetched, tmpFetchedList[i][innerFetchedIndex].md5String, tmpFetchedList[i][innerFetchedIndex].fileURL)) { fetched.add(tmpFetchedList[i][innerFetchedIndex]); } else { - Logger.Inst() - .log("Skipped because hash match: ${tmpFetchedList[i][innerFetchedIndex].fileURL}", "MergeBooruHandler", "Search", LogTypes.booruHandlerInfo); + Logger.Inst().log( + 'Skipped because hash match: ${tmpFetchedList[i][innerFetchedIndex].fileURL}', + 'MergeBooruHandler', + 'Search', + LogTypes.booruHandlerInfo, + ); } } else { Logger.Inst().log( - "not adding item from ${booruHandlers[i].booru.name}, length: ${tmpFetchedList[i].length}, index: $innerFetchedIndex", - "MergeBooruHandler", - "Search", + 'not adding item from ${booruHandlers[i].booru.name}, length: ${tmpFetchedList[i].length}, index: $innerFetchedIndex', + 'MergeBooruHandler', + 'Search', + LogTypes.booruHandlerInfo, + ); + Logger.Inst().log( + 'innerLimit $innerLimit, pageNum: $pageNum', + 'MergeBooruHandler', + 'Search', + LogTypes.booruHandlerInfo, + ); + Logger.Inst().log( + 'fetched: ${fetched.length}, fetchedMax: $fetchedMax', + 'MergeBooruHandler', + 'Search', LogTypes.booruHandlerInfo, ); - Logger.Inst().log("innerLimit $innerLimit, pageNum: $pageNum", "MergeBooruHandler", "Search", LogTypes.booruHandlerInfo); - Logger.Inst().log("fetched: ${fetched.length}, fetchedMax: $fetchedMax", "MergeBooruHandler", "Search", LogTypes.booruHandlerInfo); } } innerFetchedOffset++; @@ -96,16 +110,21 @@ class MergebooruHandler extends BooruHandler { // GelbooruV1 uses a sha1 of an md5 String makeSha1Hash(String hash) { - List bytes = utf8.encode(hash); - Digest digest = sha1.convert(bytes); - Logger.Inst().log("converting hash to sha1", "MergeBooruHandler", "makeSha1Hash", LogTypes.booruHandlerInfo); + final List bytes = utf8.encode(hash); + final Digest digest = sha1.convert(bytes); + Logger.Inst().log('converting hash to sha1', 'MergeBooruHandler', 'makeSha1Hash', LogTypes.booruHandlerInfo); return digest.toString(); } - bool hashInFetched(List fetched, hash, fileURL) { + bool hashInFetched(List fetched, String? hash, String fileURL) { for (int i = 0; i < fetched.length; i++) { if (fetched[i].md5String == hash) { - Logger.Inst().log("hash match URL: $fileURL fetchedURL: ${fetched[i].fileURL}", "MergeBooruHandler", "hashInFetched", LogTypes.booruHandlerInfo); + Logger.Inst().log( + 'hash match URL: $fileURL fetchedURL: ${fetched[i].fileURL}', + 'MergeBooruHandler', + 'hashInFetched', + LogTypes.booruHandlerInfo, + ); return true; } } @@ -115,11 +134,11 @@ class MergebooruHandler extends BooruHandler { void setupMerge(List boorus) { innerLimit = (limit / boorus.length).ceil(); booruList.addAll(boorus); - for (var element in booruList) { - List factoryResults = BooruHandlerFactory().getBooruHandler([element], innerLimit); + for (final element in booruList) { + final List factoryResults = BooruHandlerFactory().getBooruHandler([element], innerLimit); booruHandlers.add(factoryResults[0]); booruHandlerPageNums.add(factoryResults[1]); - Logger.Inst().log("SETUP MERGE ADDING: ${element.name}", "MergeBooruHandler", "setupMerge", LogTypes.booruHandlerInfo); + Logger.Inst().log('SETUP MERGE ADDING: ${element.name}', 'MergeBooruHandler', 'setupMerge', LogTypes.booruHandlerInfo); if (element.type == BooruType.GelbooruV1) { hasGelbooruV1 = true; } diff --git a/lib/src/boorus/moebooru_handler.dart b/lib/src/boorus/moebooru_handler.dart index 7ae2be76..66cb75ee 100644 --- a/lib/src/boorus/moebooru_handler.dart +++ b/lib/src/boorus/moebooru_handler.dart @@ -2,35 +2,34 @@ import 'dart:math'; import 'package:xml/xml.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/tag_type.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/logger.dart'; class MoebooruHandler extends BooruHandler { - MoebooruHandler(Booru booru, int limit) : super(booru, limit); - + MoebooruHandler(super.booru, super.limit); + @override - bool hasSizeData = true; + bool get hasSizeData => true; @override - Map tagTypeMap = { - "5": TagType.meta, - "3": TagType.copyright, - "4": TagType.character, - "1": TagType.artist, - "0": TagType.none, - }; + Map get tagTypeMap => { + '5': TagType.meta, + '3': TagType.copyright, + '4': TagType.character, + '1': TagType.artist, + '0': TagType.none, + }; @override - List parseListFromResponse(response) { - var parsedResponse = XmlDocument.parse(response.data); + List parseListFromResponse(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); try { - int? count = int.tryParse(parsedResponse.findAllElements('posts').first.getAttribute('count') ?? '0'); + final int? count = int.tryParse(parsedResponse.findAllElements('posts').first.getAttribute('count') ?? '0'); totalCount.value = count ?? 0; } catch (e) { - Logger.Inst().log('$e', className, "searchCount", LogTypes.exception); + Logger.Inst().log('$e', className, 'searchCount', LogTypes.exception); } return parsedResponse.findAllElements('post').toList(); @@ -39,40 +38,42 @@ class MoebooruHandler extends BooruHandler { // TODO change to json // can probably use the same method as gelbooru when the individual BooruItem is moved to separate method @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var current = responseItem; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final current = responseItem; - if (current.getAttribute("file_url") != null) { + if (current.getAttribute('file_url') != null) { // Fix for bleachbooru - String fileURL = "", sampleURL = "", previewURL = ""; - fileURL += current.getAttribute("file_url")!.toString(); - sampleURL += current.getAttribute("sample_url")!.toString(); - previewURL += current.getAttribute("preview_url")!.toString(); - if (!fileURL.contains("http")) { + String fileURL = '', sampleURL = '', previewURL = ''; + fileURL += current.getAttribute('file_url')!.toString(); + sampleURL += current.getAttribute('sample_url')!.toString(); + previewURL += current.getAttribute('preview_url')!.toString(); + if (!fileURL.contains('http')) { fileURL = booru.baseURL! + fileURL; sampleURL = booru.baseURL! + sampleURL; previewURL = booru.baseURL! + previewURL; } - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: sampleURL, thumbnailURL: previewURL, - tagsList: current.getAttribute("tags")!.split(" "), - postURL: makePostURL(current.getAttribute("id")!), + tagsList: current.getAttribute('tags')!.split(' '), + postURL: makePostURL(current.getAttribute('id')!), fileWidth: double.tryParse(current.getAttribute('width') ?? ''), fileHeight: double.tryParse(current.getAttribute('height') ?? ''), sampleWidth: double.tryParse(current.getAttribute('sample_width') ?? ''), sampleHeight: double.tryParse(current.getAttribute('sample_height') ?? ''), previewWidth: double.tryParse(current.getAttribute('preview_width') ?? ''), previewHeight: double.tryParse(current.getAttribute('preview_height') ?? ''), - serverId: current.getAttribute("id"), - rating: current.getAttribute("rating"), - score: current.getAttribute("score"), - sources: [current.getAttribute("source") == null ? "" : current.getAttribute("source")!], - md5String: current.getAttribute("md5"), - postDate: current.getAttribute("created_at"), // Fri Jun 18 02:13:45 -0500 2021 - postDateFormat: "unix", // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", + serverId: current.getAttribute('id'), + rating: current.getAttribute('rating'), + score: current.getAttribute('score'), + sources: [ + if (current.getAttribute('source') == null) '' else current.getAttribute('source')!, + ], + md5String: current.getAttribute('md5'), + postDate: current.getAttribute('created_at'), // Fri Jun 18 02:13:45 -0500 2021 + postDateFormat: 'unix', // when timezone support added: "EEE MMM dd HH:mm:ss Z yyyy", ); return item; @@ -84,38 +85,40 @@ class MoebooruHandler extends BooruHandler { @override String makeURL(String tags, {bool forceXML = false}) { final int cappedPage = max(1, pageNum); - final String loginStr = booru.userID?.isNotEmpty == true ? '&login=${booru.userID}' : ""; + final String loginStr = booru.userID?.isNotEmpty == true ? '&login=${booru.userID}' : ''; final String apiKeyStr = booru.apiKey?.isNotEmpty == true ? '&api_key=${booru.apiKey}' : ''; - return "${booru.baseURL}/post.xml?tags=$tags&limit=${limit.toString()}&page=${cappedPage.toString()}$loginStr$apiKeyStr"; + return '${booru.baseURL}/post.xml?tags=$tags&limit=$limit&page=$cappedPage$loginStr$apiKeyStr'; } @override String makePostURL(String id) { - return "${booru.baseURL}/post/show/$id/"; + return '${booru.baseURL}/post/show/$id/'; } @override String makeTagURL(String input) { - return "${booru.baseURL}/tag.xml?limit=10&order=count&name=$input*"; + return '${booru.baseURL}/tag.xml?limit=10&order=count&name=$input*'; } @override - List parseTagSuggestionsList(response) { - var parsedResponse = XmlDocument.parse(response.data); - return parsedResponse.findAllElements("tag").toList(); + List parseTagSuggestionsList(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); + return parsedResponse.findAllElements('tag').toList(); } @override - String? parseTagSuggestion(responseItem, int index) { - final String tagStr = responseItem.getAttribute("name")?.trim() ?? ""; - if (tagStr.isEmpty) return null; + String? parseTagSuggestion(dynamic responseItem, int index) { + final String tagStr = responseItem.getAttribute('name')?.trim() ?? ''; + if (tagStr.isEmpty) { + return null; + } // record tag data for future use - final String rawTagType = responseItem.getAttribute("type")?.toString() ?? ""; + final String rawTagType = responseItem.getAttribute('type')?.toString() ?? ''; TagType tagType = TagType.none; if (rawTagType.isNotEmpty && tagTypeMap.containsKey(rawTagType)) { - tagType = (tagTypeMap[rawTagType] ?? TagType.none); + tagType = tagTypeMap[rawTagType] ?? TagType.none; } addTagsWithType([tagStr], tagType); return tagStr; @@ -123,5 +126,5 @@ class MoebooruHandler extends BooruHandler { // TODO parse comments from html @override - bool hasCommentsSupport = false; + bool get hasCommentsSupport => false; } diff --git a/lib/src/boorus/nyanpals_handler.dart b/lib/src/boorus/nyanpals_handler.dart index ba2fbbfd..40a0cae8 100644 --- a/lib/src/boorus/nyanpals_handler.dart +++ b/lib/src/boorus/nyanpals_handler.dart @@ -1,40 +1,46 @@ -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; class NyanPalsHandler extends BooruHandler { - NyanPalsHandler(Booru booru, int limit) : super(booru, limit); + NyanPalsHandler(super.booru, super.limit); @override - List parseListFromResponse(response) { - var parsedResponse = response.data; - totalCount.value = parsedResponse["total"]!; + List parseListFromResponse(dynamic response) { + final parsedResponse = response.data; + totalCount.value = parsedResponse['total']!; return (parsedResponse['rows'] ?? []) as List; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - String fileURL = responseItem["url"]!; - String md5 = fileURL.substring(fileURL.lastIndexOf("/") + 1, fileURL.lastIndexOf(".")); + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + String fileURL = responseItem['url']!; + final String md5 = fileURL.substring(fileURL.lastIndexOf('/') + 1, fileURL.lastIndexOf('.')); fileURL = booru.baseURL! + fileURL; - String thumbURL = ""; + String thumbURL = ''; - List currentTags = responseItem['tags'].split(","); + final List currentTags = responseItem['tags'].split(','); for (int x = 0; x < currentTags.length; x++) { - if (currentTags[x].contains(" ")) { - currentTags[x] = currentTags[x].replaceAll(" ", "_"); + if (currentTags[x].contains(' ')) { + currentTags[x] = currentTags[x].replaceAll(' ', '_'); } } - BooruItem item = BooruItem(fileURL: fileURL, sampleURL: fileURL, thumbnailURL: "", tagsList: currentTags, postURL: fileURL, md5String: md5); + final BooruItem item = BooruItem( + fileURL: fileURL, + sampleURL: fileURL, + thumbnailURL: '', + tagsList: currentTags, + postURL: fileURL, + md5String: md5, + ); - thumbURL = "${booru.baseURL!}/img/pettankontent/"; + thumbURL = '${booru.baseURL!}/img/pettankontent/'; if (item.mediaType.value.isVideo) { - thumbURL = "$thumbURL${item.md5String!}.mp4"; + thumbURL = '$thumbURL${item.md5String!}.mp4'; } else if (item.mediaType.value.isAnimation) { - thumbURL = "${thumbURL}_${item.md5String!}.gif"; + thumbURL = '${thumbURL}_${item.md5String!}.gif'; } else { - thumbURL = "${thumbURL}_${item.md5String!}.png"; + thumbURL = '${thumbURL}_${item.md5String!}.png'; } item.thumbnailURL = thumbURL; @@ -48,32 +54,32 @@ class NyanPalsHandler extends BooruHandler { @override String makePostURL(String id) { - return "${booru.baseURL}/index.php?page=post&s=view&id=$id"; + return '${booru.baseURL}/index.php?page=post&s=view&id=$id'; } @override String makeURL(String tags) { //https://nyanpals.com/kontent?include=nsfw&exclude=&allow=&limit=50&method=uploaded&offset=0&order=DESC&token=null - String includeTags = ""; - String excludeTags = ""; - tags.split(" ").forEach((element) { - String tag = element.replaceAll("_", " "); - if (tag.contains("-")) { + String includeTags = ''; + String excludeTags = ''; + tags.split(' ').forEach((element) { + final String tag = element.replaceAll('_', ' '); + if (tag.contains('-')) { excludeTags += "${tag.replaceAll("-", "")},"; } else { - includeTags += "$tag,"; + includeTags += '$tag,'; } }); - return "${booru.baseURL}/kontent?include=$includeTags&exclude=$excludeTags&allow=&limit=$limit&method=uploaded&offset=${pageNum * limit}&order=DESC&token=null"; + return '${booru.baseURL}/kontent?include=$includeTags&exclude=$excludeTags&allow=&limit=$limit&method=uploaded&offset=${pageNum * limit}&order=DESC&token=null'; } @override String makeTagURL(String input) { - if (booru.baseURL!.contains("rule34.xxx")) { - return "${booru.baseURL}/autocomplete.php?q=$input"; // doesn't allow limit, but sorts by popularity + if (booru.baseURL!.contains('rule34.xxx')) { + return '${booru.baseURL}/autocomplete.php?q=$input'; // doesn't allow limit, but sorts by popularity } else { - return "${booru.baseURL}/index.php?page=dapi&s=tag&q=index&name_pattern=$input%&limit=10"; + return '${booru.baseURL}/index.php?page=dapi&s=tag&q=index&name_pattern=$input%&limit=10'; } } } diff --git a/lib/src/boorus/philomena_handler.dart b/lib/src/boorus/philomena_handler.dart index 5240fa83..1a31001e 100644 --- a/lib/src/boorus/philomena_handler.dart +++ b/lib/src/boorus/philomena_handler.dart @@ -1,50 +1,49 @@ -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; class PhilomenaHandler extends BooruHandler { - PhilomenaHandler(Booru booru, int limit) : super(booru, limit); + PhilomenaHandler(super.booru, super.limit); @override - String validateTags(tags) { - if (tags == "" || tags == " ") { - return "*"; + String validateTags(String tags) { + if (tags == '' || tags == ' ') { + return '*'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - Map parsedResponse = response.data; + List parseListFromResponse(dynamic response) { + final Map parsedResponse = response.data; return (parsedResponse['images'] ?? []) as List; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var current = responseItem; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final current = responseItem; if (current['representations']['full'] != null) { String sampleURL = current['representations']['medium'], thumbURL = current['representations']['thumb_small']; - if (current["mime_type"].toString().contains("video")) { - String tmpURL = "${sampleURL.substring(0, sampleURL.lastIndexOf("/") + 1)}thumb.gif"; + if (current['mime_type'].toString().contains('video')) { + final String tmpURL = "${sampleURL.substring(0, sampleURL.lastIndexOf("/") + 1)}thumb.gif"; sampleURL = tmpURL; thumbURL = tmpURL; } String fileURL = current['representations']['full']; - if (!fileURL.contains("http")) { + if (!fileURL.contains('http')) { sampleURL = booru.baseURL! + sampleURL; thumbURL = booru.baseURL! + thumbURL; fileURL = booru.baseURL! + fileURL; } - List currentTags = current['tags'].toString().substring(1, current['tags'].toString().length - 1).split(", "); + final List currentTags = current['tags'].toString().substring(1, current['tags'].toString().length - 1).split(', '); for (int x = 0; x < currentTags.length; x++) { - if (currentTags[x].contains(" ")) { - currentTags[x] = currentTags[x].replaceAll(" ", "+"); + if (currentTags[x].contains(' ')) { + currentTags[x] = currentTags[x].replaceAll(' ', '+'); } } - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, fileWidth: current['width']?.toDouble(), fileHeight: current['height']?.toDouble(), @@ -58,7 +57,7 @@ class PhilomenaHandler extends BooruHandler { sources: [current['source_url'].toString()], postDate: current['created_at'], postDateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'", - fileNameExtras: "${booru.name}_${current['id']}_" + fileNameExtras: "${booru.name}_${current['id']}_", ); return item; @@ -69,20 +68,20 @@ class PhilomenaHandler extends BooruHandler { @override String makePostURL(String id) { - return "${booru.baseURL}/images/$id"; + return '${booru.baseURL}/images/$id'; } @override String makeURL(String tags) { // EXAMPLE: https://derpibooru.org/api/v1/json/search/images?q=solo&per_page=20&page=1 - String filter = "2"; - if (booru.baseURL!.contains("derpibooru")) { - filter = "56027"; + String filter = '2'; + if (booru.baseURL!.contains('derpibooru')) { + filter = '56027'; } if (booru.apiKey?.isEmpty ?? true) { - return "${booru.baseURL}/api/v1/json/search/images?filter_id=$filter&q=${tags.replaceAll(" ", ",")}&per_page=${limit.toString()}&page=${pageNum.toString()}"; + return "${booru.baseURL}/api/v1/json/search/images?filter_id=$filter&q=${tags.replaceAll(" ", ",")}&per_page=$limit&page=$pageNum"; } else { - return "${booru.baseURL}/api/v1/json/search/images?key=${booru.apiKey}&q=${tags.replaceAll(" ", ",")}&per_page=${limit.toString()}&page=${pageNum.toString()}"; + return "${booru.baseURL}/api/v1/json/search/images?key=${booru.apiKey}&q=${tags.replaceAll(" ", ",")}&per_page=$limit&page=$pageNum"; } } @@ -91,24 +90,24 @@ class PhilomenaHandler extends BooruHandler { if (input.isEmpty) { input = '*'; } - return "${booru.baseURL}/api/v1/json/search/tags?q=$input*&per_page=10"; + return '${booru.baseURL}/api/v1/json/search/tags?q=$input*&per_page=10'; } @override - List parseTagSuggestionsList(response) { - Map parsedResponse = response.data; + List parseTagSuggestionsList(dynamic response) { + final Map parsedResponse = response.data; return parsedResponse['tags']; } @override - String? parseTagSuggestion(responseItem, int index) { - List tagStringReplacements = [ - ["-colon-", ":"], - ["-dash-", "-"], - ["-fwslash-", "/"], - ["-bwslash-", "\\"], - ["-dot-", "."], - ["-plus-", "+"] + String? parseTagSuggestion(dynamic responseItem, int index) { + final List tagStringReplacements = [ + ['-colon-', ':'], + ['-dash-', '-'], + ['-fwslash-', '/'], + ['-bwslash-', r'\'], + ['-dot-', '.'], + ['-plus-', '+'] ]; String tag = responseItem['slug'].toString(); diff --git a/lib/src/boorus/r34hentai_handler.dart b/lib/src/boorus/r34hentai_handler.dart index c235a304..a0c6074b 100644 --- a/lib/src/boorus/r34hentai_handler.dart +++ b/lib/src/boorus/r34hentai_handler.dart @@ -12,10 +12,10 @@ import 'package:lolisnatcher/src/utils/dio_network.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; class R34HentaiHandler extends ShimmieHandler { - R34HentaiHandler(Booru booru, int limit) : super(booru, limit); + R34HentaiHandler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; @override String validateTags(String tags) { @@ -27,32 +27,32 @@ class R34HentaiHandler extends ShimmieHandler { final Map headers = { ...super.getHeaders(), if (booru.apiKey?.isNotEmpty == true && (booru.apiKey?.contains('shm_user') == true || booru.apiKey?.contains('shm_sesion') == true)) - 'Cookie': "${booru.apiKey};", + 'Cookie': '${booru.apiKey};', }; return headers; } @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final document = parse(response.data); - return document.getElementsByClassName("thumb"); + return document.getElementsByClassName('thumb'); } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final current = responseItem; - final String id = current.attributes["data-post-id"]!; - final String fileExt = current.attributes["data-mime"]?.split('/')[1] ?? "png"; - final String thumbURL = current.firstChild!.attributes["src"]!; - final double? thumbWidth = double.tryParse(current.firstChild!.attributes["width"] ?? ''); - final double? thumbHeight = double.tryParse(current.firstChild!.attributes["height"] ?? ''); - final double? fileWidth = double.tryParse(current.attributes["data-width"] ?? ''); - final double? fileHeight = double.tryParse(current.attributes["data-height"] ?? ''); + final String id = current.attributes['data-post-id']!; + final String fileExt = current.attributes['data-mime']?.split('/')[1] ?? 'png'; + final String thumbURL = current.firstChild!.attributes['src']!; + final double? thumbWidth = double.tryParse(current.firstChild!.attributes['width'] ?? ''); + final double? thumbHeight = double.tryParse(current.firstChild!.attributes['height'] ?? ''); + final double? fileWidth = double.tryParse(current.attributes['data-width'] ?? ''); + final double? fileHeight = double.tryParse(current.attributes['data-height'] ?? ''); final String fileURL = - thumbURL.replaceFirst("_thumbs", "_images").replaceFirst("thumbnails", "images").replaceFirst("thumbnail_", "").replaceFirst('.jpg', '.$fileExt'); - final List tags = current.attributes["data-tags"]?.split(" ") ?? []; + thumbURL.replaceFirst('_thumbs', '_images').replaceFirst('thumbnails', 'images').replaceFirst('thumbnail_', '').replaceFirst('.jpg', '.$fileExt'); + final List tags = current.attributes['data-tags']?.split(' ') ?? []; final BooruItem item = BooruItem( thumbnailURL: booru.baseURL! + thumbURL, @@ -72,7 +72,7 @@ class R34HentaiHandler extends ShimmieHandler { } String getHashFromURL(String url) { - final String hash = url.substring(url.lastIndexOf("_") + 1, url.lastIndexOf(".")); + final String hash = url.substring(url.lastIndexOf('_') + 1, url.lastIndexOf('.')); return hash; } @@ -80,7 +80,7 @@ class R34HentaiHandler extends ShimmieHandler { String makeURL(String tags) { String tagsText = tags.replaceAll(' ', '+'); tagsText = tagsText.isEmpty ? '' : '$tagsText/'; - return "${booru.baseURL}/post/list/$tagsText$pageNum"; + return '${booru.baseURL}/post/list/$tagsText$pageNum'; } @override @@ -99,7 +99,7 @@ class R34HentaiHandler extends ShimmieHandler { 'gobu': 'Log+In', }, options: Options( - contentType: "application/x-www-form-urlencoded", + contentType: 'application/x-www-form-urlencoded', ), headers: await Tools.getFileCustomHeaders( Booru( @@ -188,16 +188,16 @@ class R34HentaiHandler extends ShimmieHandler { /// Booru Handler for the r34hentai engine // TODO they removed their api, both shimmie and custom one, but maybe it is still hidden somewhere? class R34HentaiHandlerOld extends R34HentaiHandler { - R34HentaiHandlerOld(Booru booru, int limit) : super(booru, limit); + R34HentaiHandlerOld(super.booru, super.limit); @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final List parsedResponse = response.data; return parsedResponse; // Limit doesn't work with this api } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final current = responseItem; final String imageUrl = current['file_url']; final String sampleUrl = current['sample_url']; @@ -216,6 +216,6 @@ class R34HentaiHandlerOld extends R34HentaiHandler { @override String makeURL(String tags) { - return "${booru.baseURL}/post/index.json?limit=$limit&page=$pageNum&tags=$tags"; + return '${booru.baseURL}/post/index.json?limit=$limit&page=$pageNum&tags=$tags'; } } diff --git a/lib/src/boorus/r34us_handler.dart b/lib/src/boorus/r34us_handler.dart index 41bab48b..bb3cd7f5 100644 --- a/lib/src/boorus/r34us_handler.dart +++ b/lib/src/boorus/r34us_handler.dart @@ -1,7 +1,6 @@ import 'package:html/dom.dart' as dom; import 'package:html/parser.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; @@ -10,38 +9,38 @@ import 'package:lolisnatcher/src/utils/logger.dart'; // TODO finish if possible class R34USHandler extends BooruHandler { - R34USHandler(Booru booru, int limit) : super(booru, limit); + R34USHandler(super.booru, super.limit); @override - String validateTags(tags) { - if (tags == " " || tags == "") { - return "all"; + String validateTags(String tags) { + if (tags == ' ' || tags == '') { + return 'all'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - var document = parse(response.data); - return document.querySelectorAll("div.thumbail-container > div"); + List parseListFromResponse(dynamic response) { + final document = parse(response.data); + return document.querySelectorAll('div.thumbail-container > div'); } // user_id=24582; pass_hash=66f1d55e1e13efcf27bfe09736436af43a3b138f @override - Future parseItemFromResponse(responseItem, int index) async { - var current = responseItem.children[0]; - if (current.firstChild!.attributes["src"] != null) { - String id = current.attributes["id"]!; - String thumbURL = current.firstChild!.attributes["src"]!; - List tags = []; - current.firstChild!.attributes["title"]!.split(", ").forEach((tag) { - tags.add(tag.replaceAll(" ", "_")); + Future parseItemFromResponse(dynamic responseItem, int index) async { + final current = responseItem.children[0]; + if (current.firstChild!.attributes['src'] != null) { + final String id = current.attributes['id']!; + final String thumbURL = current.firstChild!.attributes['src']!; + final List tags = []; + current.firstChild!.attributes['title']!.split(', ').forEach((tag) { + tags.add(tag.replaceAll(' ', '_')); }); BooruItem item = BooruItem( - fileURL: "", - sampleURL: "", + fileURL: '', + sampleURL: '', thumbnailURL: thumbURL, tagsList: tags, md5String: getHashFromURL(thumbURL), @@ -64,37 +63,37 @@ class R34USHandler extends BooruHandler { try { final response = await DioNetwork.get(item.postURL, headers: getHeaders()); if (response.statusCode == 200) { - var document = parse(response.data); - dom.Element? div = document.querySelector("div.content_push > img"); - item.fileURL = div!.attributes["src"]!.toString(); - item.sampleURL = div.attributes["src"]!.toString(); - item.fileHeight = double.tryParse(div.attributes["height"]!.toString()); - item.fileWidth = double.tryParse(div.attributes["width"]!); + final document = parse(response.data); + final dom.Element? div = document.querySelector('div.content_push > img'); + item.fileURL = div!.attributes['src']!; + item.sampleURL = div.attributes['src']!; + item.fileHeight = double.tryParse(div.attributes['height']!); + item.fileWidth = double.tryParse(div.attributes['width']!); } else { - Logger.Inst().log("$className status is: ${response.statusCode}", className, "getPostData", LogTypes.booruHandlerFetchFailed); - Logger.Inst().log("$className url is: ${item.postURL}", className, "getPostData", LogTypes.booruHandlerFetchFailed); - Logger.Inst().log("$className url response is: ${response.data}", className, "getPostData", LogTypes.booruHandlerFetchFailed); + Logger.Inst().log('$className status is: ${response.statusCode}', className, 'getPostData', LogTypes.booruHandlerFetchFailed); + Logger.Inst().log('$className url is: ${item.postURL}', className, 'getPostData', LogTypes.booruHandlerFetchFailed); + Logger.Inst().log('$className url response is: ${response.data}', className, 'getPostData', LogTypes.booruHandlerFetchFailed); errorString = response.statusCode.toString(); } } catch (e) { - Logger.Inst().log(e.toString(), className, "getPostData", LogTypes.exception); + Logger.Inst().log(e.toString(), className, 'getPostData', LogTypes.exception); errorString = e.toString(); } return item; } String getHashFromURL(String url) { - String hash = url.substring(url.lastIndexOf("_") + 1, url.lastIndexOf(".")); + final String hash = url.substring(url.lastIndexOf('_') + 1, url.lastIndexOf('.')); return hash; } @override String makePostURL(String id) { - return "${booru.baseURL}/index.php?r=posts/view&id=$id"; + return '${booru.baseURL}/index.php?r=posts/view&id=$id'; } @override String makeURL(String tags) { - return "${booru.baseURL}/index.php?r=posts/index&q=${tags.replaceAll(" ", "+")}&pid=${(pageNum * 20).toString()}"; + return "${booru.baseURL}/index.php?r=posts/index&q=${tags.replaceAll(" ", "+")}&pid=${pageNum * 20}"; } } diff --git a/lib/src/boorus/rainbooru_handler.dart b/lib/src/boorus/rainbooru_handler.dart index 2e190cc8..9181762b 100644 --- a/lib/src/boorus/rainbooru_handler.dart +++ b/lib/src/boorus/rainbooru_handler.dart @@ -2,51 +2,50 @@ import 'dart:async'; import 'package:html/parser.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; //Slow piece of shit class RainbooruHandler extends BooruHandler { - RainbooruHandler(Booru booru, int limit) : super(booru, limit); + RainbooruHandler(super.booru, super.limit); @override String validateTags(String tags) { - if (tags == "" || tags == " ") { - return "*"; + if (tags == '' || tags == ' ') { + return '*'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - var document = parse(response.data); - return document.getElementsByClassName("thumbnail"); + List parseListFromResponse(dynamic response) { + final document = parse(response.data); + return document.getElementsByClassName('thumbnail'); } @override - Future parseItemFromResponse(responseItem, int index) async { - String thumbURL = ""; - var urlElem = responseItem.firstChild!; - thumbURL += urlElem.firstChild!.attributes["src"]!; - String url = makePostURL(urlElem.attributes["href"]!.split("img/")[1]); + Future parseItemFromResponse(dynamic responseItem, int index) async { + String thumbURL = ''; + final urlElem = responseItem.firstChild!; + thumbURL += urlElem.firstChild!.attributes['src']!; + final String url = makePostURL(urlElem.attributes['href']!.split('img/')[1]); final responseInner = await DioNetwork.get(url, headers: getHeaders()); if (responseInner.statusCode == 200) { - var document = parse(responseInner.data); - var post = document.getElementById("immainpage"); + final document = parse(responseInner.data); + final post = document.getElementById('immainpage'); if (post != null) { - var postsURLs = post.querySelector("div#immainpage > a"); - String fileURL = postsURLs!.attributes["href"]!; - String sampleURL = postsURLs.firstChild!.attributes["src"]!; - var tags = document.querySelectorAll("a.tag"); - List currentTags = []; + final postsURLs = post.querySelector('div#immainpage > a'); + final String fileURL = postsURLs!.attributes['href']!; + final String sampleURL = postsURLs.firstChild!.attributes['src']!; + final tags = document.querySelectorAll('a.tag'); + final List currentTags = []; for (int x = 0; x < tags.length; x++) { - currentTags.add(tags[x].innerHtml.replaceAll(" ", "+")); + currentTags.add(tags[x].innerHtml.replaceAll(' ', '+')); } - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: sampleURL, thumbnailURL: thumbURL, @@ -66,39 +65,39 @@ class RainbooruHandler extends BooruHandler { @override String makePostURL(String id) { - return "${booru.baseURL}/img/$id"; + return '${booru.baseURL}/img/$id'; } @override String makeURL(String tags) { // EXAMPLE: https://www.rainbooru.org/search?search=safe,solo&page=0 - if (tags != "*") { - return "${booru.baseURL}/search?filters=&search=${tags.replaceAll(" ", ",")}&page=${pageNum.toString()}"; + if (tags != '*') { + return "${booru.baseURL}/search?filters=&search=${tags.replaceAll(" ", ",")}&page=$pageNum"; } else { - return "${booru.baseURL}/?search=&page=${pageNum.toString()}"; + return '${booru.baseURL}/?search=&page=$pageNum'; } } @override String makeTagURL(String input) { - return "https://twibooru.org/tags.json?tq=*$input*"; + return 'https://twibooru.org/tags.json?tq=*$input*'; } @override - List parseTagSuggestionsList(response) { - List parsedResponse = response.data; + List parseTagSuggestionsList(dynamic response) { + final List parsedResponse = response.data; return parsedResponse; } @override - String? parseTagSuggestion(responseItem, int index) { - List tagStringReplacements = [ - ["-colon-", ":"], - ["-dash-", "-"], - ["-fwslash-", "/"], - ["-bwslash-", "\\"], - ["-dot-", "."], - ["-plus-", "+"] + String? parseTagSuggestion(dynamic responseItem, int index) { + final List tagStringReplacements = [ + ['-colon-', ':'], + ['-dash-', '-'], + ['-fwslash-', '/'], + ['-bwslash-', r'\'], + ['-dot-', '.'], + ['-plus-', '+'] ]; String tag = responseItem['slug'].toString(); diff --git a/lib/src/boorus/sankaku_handler.dart b/lib/src/boorus/sankaku_handler.dart index f604bb9d..02cf3aad 100644 --- a/lib/src/boorus/sankaku_handler.dart +++ b/lib/src/boorus/sankaku_handler.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:dio/dio.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/comment_item.dart'; import 'package:lolisnatcher/src/data/constants.dart'; @@ -14,29 +13,30 @@ import 'package:lolisnatcher/src/utils/logger.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; class SankakuHandler extends BooruHandler { - SankakuHandler(Booru booru, int limit) : super(booru, limit); + SankakuHandler(super.booru, super.limit); String authToken = ''; @override - Map tagTypeMap = { - '8': TagType.meta, - '3': TagType.copyright, - '4': TagType.character, - '1': TagType.artist, - '0': TagType.none, - }; + Map get tagTypeMap => { + '8': TagType.meta, + '3': TagType.copyright, + '4': TagType.character, + '1': TagType.artist, + '0': TagType.none, + }; @override - bool hasSizeData = true; + bool get hasSizeData => true; @override - bool hasCommentsSupport = true; + bool get hasCommentsSupport => true; @override - bool hasNotesSupport = true; + bool get hasNotesSupport => true; - bool hasItemUpdateSupport = true; + @override + bool get hasLoadItemSupport => true; @override Future> fetchSearch(Uri uri, {bool withCaptchaCheck = true, Map? queryParams}) async { @@ -58,13 +58,13 @@ class SankakuHandler extends BooruHandler { } @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final dynamic current = responseItem; final List tags = []; @@ -139,7 +139,7 @@ class SankakuHandler extends BooruHandler { } } } catch (e) { - if(e is DioException && e.type == DioExceptionType.cancel) { + if (e is DioException && e.type == DioExceptionType.cancel) { return [item, null, 'Cancelled']; } @@ -152,7 +152,7 @@ class SankakuHandler extends BooruHandler { Map getHeaders() { return { 'Accept': 'application/vnd.sankaku.api+json;v=2', - if(authToken.isNotEmpty) 'Authorization': authToken, + if (authToken.isNotEmpty) 'Authorization': authToken, // 'User-Agent': 'SCChannelApp/4.0', 'User-Agent': Constants.defaultBrowserUserAgent, 'Referer': 'https://sankaku.app/', @@ -167,7 +167,7 @@ class SankakuHandler extends BooruHandler { @override String makeURL(String tags) { - return '${booru.baseURL}/posts?tags=${tags.trim()}&limit=${limit.toString()}&page=${pageNum.toString()}'; + return '${booru.baseURL}/posts?tags=${tags.trim()}&limit=$limit&page=$pageNum'; } String makeApiPostURL(String id) { @@ -195,7 +195,7 @@ class SankakuHandler extends BooruHandler { } } if (token == '') { - Logger.Inst().log('Sankaku auth error ${response.statusCode.toString()}', className, 'getAuthToken', LogTypes.booruHandlerInfo); + Logger.Inst().log('Sankaku auth error ${response.statusCode}', className, 'getAuthToken', LogTypes.booruHandlerInfo); } return token; @@ -207,15 +207,17 @@ class SankakuHandler extends BooruHandler { } @override - List parseTagSuggestionsList(response) { + List parseTagSuggestionsList(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - String? parseTagSuggestion(responseItem, int index) { + String? parseTagSuggestion(dynamic responseItem, int index) { final String tagStr = responseItem['name'] ?? ''; - if (tagStr.isEmpty) return null; + if (tagStr.isEmpty) { + return null; + } // record tag data for future use final String rawTagType = responseItem['type']?.toString() ?? ''; @@ -235,13 +237,13 @@ class SankakuHandler extends BooruHandler { } @override - List parseCommentsList(response) { + List parseCommentsList(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - CommentItem? parseComment(responseItem, int index) { + CommentItem? parseComment(dynamic responseItem, int index) { final current = responseItem; return CommentItem( id: current['id'].toString(), @@ -263,13 +265,13 @@ class SankakuHandler extends BooruHandler { } @override - List parseNotesList(response) { + List parseNotesList(dynamic response) { final List parsedResponse = response.data; return parsedResponse; } @override - NoteItem? parseNote(responseItem, int index) { + NoteItem? parseNote(dynamic responseItem, int index) { final current = responseItem; return NoteItem( id: current['id'].toString(), diff --git a/lib/src/boorus/shimmie_handler.dart b/lib/src/boorus/shimmie_handler.dart index 0749f2ac..12ea018f 100644 --- a/lib/src/boorus/shimmie_handler.dart +++ b/lib/src/boorus/shimmie_handler.dart @@ -1,34 +1,33 @@ import 'package:html/parser.dart'; import 'package:xml/xml.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/comment_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/logger.dart'; class ShimmieHandler extends BooruHandler { - ShimmieHandler(Booru booru, int limit) : super(booru, limit); + ShimmieHandler(super.booru, super.limit); @override - bool hasSizeData = true; + bool get hasSizeData => true; @override - String validateTags(tags) { - if (tags == " " || tags == "") { - return "*"; + String validateTags(String tags) { + if (tags == ' ' || tags == '') { + return '*'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - var parsedResponse = XmlDocument.parse(response.data); + List parseListFromResponse(dynamic response) { + final parsedResponse = XmlDocument.parse(response.data); try { parseSearchCount(parsedResponse); } catch (e) { - Logger.Inst().log("Error parsing search count: $e", className, 'parseListFromResponse::parseSearchCount', LogTypes.exception); + Logger.Inst().log('Error parsing search count: $e', className, 'parseListFromResponse::parseSearchCount', LogTypes.exception); } /** * This creates a list of xml elements 'post' to extract only the post elements which contain @@ -42,30 +41,30 @@ class ShimmieHandler extends BooruHandler { } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var current = responseItem; - if (current.getAttribute("file_url") != null) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final current = responseItem; + if (current.getAttribute('file_url') != null) { String preURL = ''; - if (booru.baseURL!.contains("https://whyneko.com/booru")) { + if (booru.baseURL!.contains('https://whyneko.com/booru')) { // special case for whyneko - preURL = booru.baseURL!.split("/booru")[0]; + preURL = booru.baseURL!.split('/booru')[0]; } - String dateString = current.getAttribute("date").toString(); - BooruItem item = BooruItem( - fileURL: preURL + current.getAttribute("file_url")!, - sampleURL: preURL + current.getAttribute("file_url")!, - thumbnailURL: preURL + current.getAttribute("preview_url")!, - tagsList: current.getAttribute("tags")!.split(" "), - postURL: makePostURL(current.getAttribute("id")!), + final String dateString = current.getAttribute('date').toString(); + final BooruItem item = BooruItem( + fileURL: preURL + current.getAttribute('file_url')!, + sampleURL: preURL + current.getAttribute('file_url')!, + thumbnailURL: preURL + current.getAttribute('preview_url')!, + tagsList: current.getAttribute('tags')!.split(' '), + postURL: makePostURL(current.getAttribute('id')!), fileWidth: double.tryParse(current.getAttribute('width') ?? ''), fileHeight: double.tryParse(current.getAttribute('height') ?? ''), previewWidth: double.tryParse(current.getAttribute('preview_width') ?? ''), previewHeight: double.tryParse(current.getAttribute('preview_height') ?? ''), - serverId: current.getAttribute("id"), - score: current.getAttribute("score"), - sources: [current.getAttribute("source") ?? ''], - md5String: current.getAttribute("md5"), + serverId: current.getAttribute('id'), + score: current.getAttribute('score'), + sources: [current.getAttribute('source') ?? ''], + md5String: current.getAttribute('md5'), postDate: dateString.substring(0, dateString.length - 3), // 2021-06-18 04:37:31.471007 // microseconds? postDateFormat: 'yyyy-MM-dd HH:mm:ss.SSSSSS', ); @@ -77,46 +76,46 @@ class ShimmieHandler extends BooruHandler { } void parseSearchCount(XmlDocument response) { - var parsedResponse = response.findAllElements('posts').toList()[0]; - int? count = int.tryParse(parsedResponse.getAttribute('count') ?? '0') ?? 0; + final parsedResponse = response.findAllElements('posts').toList()[0]; + final int count = int.tryParse(parsedResponse.getAttribute('count') ?? '0') ?? 0; totalCount.value = count; } @override String makePostURL(String id) { - return "${booru.baseURL}/post/view/$id"; + return '${booru.baseURL}/post/view/$id'; } @override String makeURL(String tags) { - return "${booru.baseURL}/api/danbooru/find_posts/index.xml?tags=$tags&limit=${limit.toString()}&page=${pageNum.toString()}"; + return '${booru.baseURL}/api/danbooru/find_posts/index.xml?tags=$tags&limit=$limit&page=$pageNum'; } @override String makeTagURL(String input) { - if (booru.baseURL!.contains("rule34.paheal.net")) { - return "${booru.baseURL}/api/internal/autocomplete?s=$input"; // doesn't allow limit, but sorts by popularity + if (booru.baseURL!.contains('rule34.paheal.net')) { + return '${booru.baseURL}/api/internal/autocomplete?s=$input'; // doesn't allow limit, but sorts by popularity } else { // TODO others don't support / don't have the parser? return ''; - return "${booru.baseURL}/tags.json?search[name_matches]=$input*&limit=10"; + // return '${booru.baseURL}/tags.json?search[name_matches]=$input*&limit=10'; } } @override - List parseTagSuggestionsList(response) { + List parseTagSuggestionsList(dynamic response) { // TODO explain why this - return response.data.substring(1, (response.data.length - 1)).replaceAll(RegExp('(:.([0-9])+)'), "").replaceAll("\"", "").split(","); + return response.data.substring(1, response.data.length - 1).replaceAll(RegExp('(:.([0-9])+)'), '').replaceAll('"', '').split(','); } @override - String? parseTagSuggestion(responseItem, int index) { + String? parseTagSuggestion(dynamic responseItem, int index) { // all manipulations were already done in list parse return responseItem; } @override - bool hasCommentsSupport = true; + bool get hasCommentsSupport => true; @override String makeCommentsURL(String postID, int pageNum) { @@ -124,21 +123,21 @@ class ShimmieHandler extends BooruHandler { } @override - List parseCommentsList(response) { - var document = parse(response.data); - return document.querySelectorAll(".comment:not(.comment_add)"); + List parseCommentsList(dynamic response) { + final document = parse(response.data); + return document.querySelectorAll('.comment:not(.comment_add)'); } @override - CommentItem? parseComment(responseItem, int index) { - var current = responseItem; + CommentItem? parseComment(dynamic responseItem, int index) { + final current = responseItem; return CommentItem( - id: current.attributes["id"], + id: current.attributes['id'], // title: postID, content: current.nodes[current.nodes.length - 1].text.toString().replaceFirst(': ', '').replaceFirst('\n\t\t\t\t', ''), authorName: current.querySelector('.username')?.text.toString(), // postID: postID, - createDate: current.querySelector('time')?.attributes["datetime"]?.split('+')[0].toString(), // 2021-12-25t10:02:28+00:00 + createDate: current.querySelector('time')?.attributes['datetime']?.split('+')[0].toString(), // 2021-12-25t10:02:28+00:00 createDateFormat: "yyyy-MM-dd't'HH:mm:ss'Z'", ); } diff --git a/lib/src/boorus/szurubooru_handler.dart b/lib/src/boorus/szurubooru_handler.dart index 55af9560..1f7e89d1 100644 --- a/lib/src/boorus/szurubooru_handler.dart +++ b/lib/src/boorus/szurubooru_handler.dart @@ -1,44 +1,43 @@ import 'dart:convert'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; class SzurubooruHandler extends BooruHandler { - SzurubooruHandler(Booru booru, int limit) : super(booru, limit); + SzurubooruHandler(super.booru, super.limit); @override - String validateTags(tags) { - if (tags == "" || tags == " ") { - return "*"; + String validateTags(String tags) { + if (tags == '' || tags == ' ') { + return '*'; } else { return super.validateTags(tags); } } @override - List parseListFromResponse(response) { - Map parsedResponse = response.data; + List parseListFromResponse(dynamic response) { + final Map parsedResponse = response.data; return (parsedResponse['results'] ?? []) as List; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { final current = responseItem; - List tags = []; + final List tags = []; for (int x = 0; x < current['tags'].length; x++) { - String currentTags = current['tags'][x]['names'].toString().replaceAll(r":", r"\:"); + String currentTags = current['tags'][x]['names'].toString().replaceAll(':', r'\:'); currentTags = currentTags.substring(1, currentTags.length - 1); - if (currentTags.contains(",")) { - tags.addAll(currentTags.split(", ")); + if (currentTags.contains(',')) { + tags.addAll(currentTags.split(', ')); } else { tags.add(currentTags); } } if (current['contentUrl'] != null) { - BooruItem item = BooruItem( + final BooruItem item = BooruItem( fileURL: "${booru.baseURL}/${current['contentUrl']}", fileWidth: current['canvasWidth'].toDouble(), fileHeight: current['canvasHeight'].toDouble(), @@ -49,7 +48,7 @@ class SzurubooruHandler extends BooruHandler { score: current['score'].toString(), postURL: makePostURL(current['id'].toString()), rating: current['safety'], - postDate: (current['creationTime'].replaceAll("Z", "") + ".0000").substring(0, 22), + postDate: (current['creationTime'].replaceAll('Z', '') + '.0000').substring(0, 22), postDateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSS", ); @@ -62,37 +61,37 @@ class SzurubooruHandler extends BooruHandler { @override Map getHeaders() { return { - "Content-Type": "application/json", - "Accept": "application/json", - "User-Agent": Tools.browserUserAgent, - if (booru.apiKey?.isNotEmpty == true) "Authorization": "Token ${base64Encode(utf8.encode("${booru.userID}:${booru.apiKey}"))}" + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': Tools.browserUserAgent, + if (booru.apiKey?.isNotEmpty == true) 'Authorization': "Token ${base64Encode(utf8.encode("${booru.userID}:${booru.apiKey}"))}" }; } @override String makePostURL(String id) { - return "${booru.baseURL}/post/$id"; + return '${booru.baseURL}/post/$id'; } @override String makeURL(String tags) { - return "${booru.baseURL}/api/posts/?offset=${pageNum * limit}&limit=${limit.toString()}&query=$tags"; + return '${booru.baseURL}/api/posts/?offset=${pageNum * limit}&limit=$limit&query=$tags'; } @override String makeTagURL(String input) { - return "${booru.baseURL}/api/tags/?offset=0&limit=10&query=$input*"; + return '${booru.baseURL}/api/tags/?offset=0&limit=10&query=$input*'; } @override - List parseTagSuggestionsList(response) { - Map parsedResponse = response.data; - return parsedResponse["results"] ?? []; + List parseTagSuggestionsList(dynamic response) { + final Map parsedResponse = response.data; + return parsedResponse['results'] ?? []; } @override - String? parseTagSuggestion(responseItem, int index) { - String? tag = responseItem['names']?[0]?.toString().replaceAll(r":", r"\:"); + String? parseTagSuggestion(dynamic responseItem, int index) { + final String? tag = responseItem['names']?[0]?.toString().replaceAll(':', r'\:'); return tag; } } diff --git a/lib/src/boorus/wildcritters_handler.dart b/lib/src/boorus/wildcritters_handler.dart index e0fe51c4..98e3fff6 100644 --- a/lib/src/boorus/wildcritters_handler.dart +++ b/lib/src/boorus/wildcritters_handler.dart @@ -1,34 +1,33 @@ import 'package:html/parser.dart'; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; class WildCrittersHandler extends BooruHandler { - WildCrittersHandler(Booru booru, int limit) : super(booru, limit); + WildCrittersHandler(super.booru, super.limit); @override - List parseListFromResponse(response) { + List parseListFromResponse(dynamic response) { final document = parse(response.data); - final spans = document.getElementsByClassName("thumb"); + final spans = document.getElementsByClassName('thumb'); return spans; } @override - Future parseItemFromResponse(responseItem, int index) async { - final linkItem = responseItem.firstChild!; - final imgItem = linkItem.firstChild!; + Future parseItemFromResponse(dynamic responseItem, int index) async { + final linkItem = responseItem.firstChild; + final imgItem = linkItem.firstChild; - if (imgItem.attributes["src"] != null) { - String id = linkItem.attributes["id"]!.substring(1); - String thumbURL = imgItem.attributes["src"]!; - String fileURL = await getFileUrl(id); + if (imgItem?.attributes['src'] != null && linkItem.attributes['id'] != null) { + final String id = linkItem!.attributes['id']!.substring(1); + final String thumbURL = imgItem!.attributes['src']!; + final String fileURL = await getFileUrl(id); if (fileURL.isEmpty) { return null; } - List tags = imgItem.attributes["title"]!.split(" "); - BooruItem item = BooruItem( + final List tags = imgItem!.attributes['title']!.split(' '); + final BooruItem item = BooruItem( fileURL: fileURL, sampleURL: fileURL, thumbnailURL: thumbURL, @@ -44,31 +43,30 @@ class WildCrittersHandler extends BooruHandler { } Future getFileUrl(String id) async { - String url = makePostURL(id); - String fileUrl = ""; + final String url = makePostURL(id); + String fileUrl = ''; final response = await DioNetwork.get(url, headers: getHeaders()); if (response.statusCode == 200) { final document = parse(response.data); - fileUrl = document.getElementById("image")!.attributes["src"]!; + fileUrl = document.getElementById('image')!.attributes['src']!; } return fileUrl; } - @override String getHashFromURL(String url) { - String hash = url.substring(url.lastIndexOf("_") + 1, url.lastIndexOf(".")); + final String hash = url.substring(url.lastIndexOf('_') + 1, url.lastIndexOf('.')); return hash; } @override String makePostURL(String id) { - return "${booru.baseURL}/post/view/$id"; + return '${booru.baseURL}/post/view/$id'; } @override String makeURL(String tags) { if (tags.trim().isEmpty) { - return "${booru.baseURL}/post/all/$pageNum"; + return '${booru.baseURL}/post/all/$pageNum'; } return "${booru.baseURL}/post/list/${tags.replaceAll(" ", "+")}/$pageNum"; } diff --git a/lib/src/boorus/worldxyz_handler.dart b/lib/src/boorus/worldxyz_handler.dart index b8148a01..1815d524 100644 --- a/lib/src/boorus/worldxyz_handler.dart +++ b/lib/src/boorus/worldxyz_handler.dart @@ -1,34 +1,32 @@ -import 'package:get/get.dart'; +import 'package:dio/dio.dart'; +import 'package:get/get.dart' show FirstWhereExt; -import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/utils/dio_network.dart'; class WorldXyzHandler extends BooruHandler { - WorldXyzHandler(Booru booru, int limit) : super(booru, limit); + WorldXyzHandler(super.booru, super.limit); @override - List parseListFromResponse(response) { - Map parsedResponse = response.data; + List parseListFromResponse(dynamic response) { + final Map parsedResponse = response.data; return (parsedResponse['items'] ?? []) as List; } @override - BooruItem? parseItemFromResponse(responseItem, int index) { - var current = responseItem; + BooruItem? parseItemFromResponse(dynamic responseItem, int index) { + final current = responseItem; - List imageLinks = current['imageLinks']; + final List imageLinks = current['imageLinks']; imageLinks.sort((a, b) => a['type'].compareTo(b['type']) * -1); - bool isVideo = current['type'] == 1; //type 1 - video, type 0 - image - List possibleBestVideoFiles = [10, 40, 41, 11]; - String? bestFile = imageLinks.firstWhereOrNull((f) => isVideo - ? possibleBestVideoFiles.contains(f["type"]) - : f["type"] == 2)?["url"]; - String sampleImage = imageLinks.where((f) => f["type"] == 2).toList()[0] - ["url"]; // isVideo ? 2 : 5 ??? - String thumbImage = - imageLinks.where((f) => f["type"] == 4).toList()[0]["url"]; + final bool isVideo = current['type'] == 1; //type 1 - video, type 0 - image + final List possibleBestVideoFiles = [10, 40, 41, 11]; + String? bestFile = imageLinks.firstWhereOrNull( + (f) => isVideo ? possibleBestVideoFiles.contains(f['type']) : f['type'] == 2, + )?['url']; + String sampleImage = imageLinks.where((f) => f['type'] == 2).toList()[0]['url']; // isVideo ? 2 : 5 ??? + String thumbImage = imageLinks.where((f) => f['type'] == 4).toList()[0]['url']; // Site generates links to RAW images, but they often lead to 404, override them // if(bestImage.contains('.raw.')) { // bestIndex = 1; @@ -40,24 +38,16 @@ class WorldXyzHandler extends BooruHandler { } // They mostly use cdn, but sometimes they aren't and it leads to same domain, this fixes such links - bestFile = - bestFile.startsWith('https') ? bestFile : (booru.baseURL! + bestFile); - sampleImage = sampleImage.startsWith('https') - ? sampleImage - : (booru.baseURL! + sampleImage); - thumbImage = thumbImage.startsWith('https') - ? thumbImage - : (booru.baseURL! + thumbImage); + bestFile = bestFile.startsWith('https') ? bestFile : (booru.baseURL! + bestFile); + sampleImage = sampleImage.startsWith('https') ? sampleImage : (booru.baseURL! + sampleImage); + thumbImage = thumbImage.startsWith('https') ? thumbImage : (booru.baseURL! + thumbImage); // Uses retarded tag scheme: "tag with multiple words|next tag", instead of "tag_with_multiple_words next_tag", convert their scheme to ours here - List originalTags = - current['tags'] != null ? [...current['tags']] : []; - var fixedTags = - originalTags.map((tag) => tag.replaceAll(RegExp(r' '), '_')).toList(); - - String dateString = current['created'] - .split('.')[0]; // split off microseconds // use posted or created? - BooruItem item = BooruItem( + final List originalTags = current['tags'] != null ? [...current['tags']] : []; + final fixedTags = originalTags.map((tag) => tag.replaceAll(RegExp(' '), '_')).toList(); + + final String dateString = current['created'].split('.')[0]; // split off microseconds // use posted or created? + final BooruItem item = BooruItem( fileURL: bestFile, sampleURL: sampleImage, thumbnailURL: thumbImage, @@ -76,40 +66,35 @@ class WorldXyzHandler extends BooruHandler { @override String makePostURL(String id) { - return "${booru.baseURL}/post/$id"; + return '${booru.baseURL}/post/$id'; } @override String makeURL(String tags) { // convert "tag_name_1 tag_name_2" to "tag name 1|tag name 2" and filter excluded tags out - String includeTags = tags - .split(' ') - .where((f) => !f.startsWith('-')) - .toList() - .map((tag) => tag.replaceAll(RegExp(r'_'), '+')) - .toList() - .join('|'); - String excludeTags = tags + final String includeTags = tags.split(' ').where((f) => !f.startsWith('-')).toList().map((tag) => tag.replaceAll(RegExp('_'), '+')).toList().join('|'); + final String excludeTags = tags .split(' ') .where((f) => f.startsWith('-')) .toList() - .map((tag) => - tag.replaceAll(RegExp(r'_'), '+').replaceAll(RegExp(r'^-'), '')) + .map( + (tag) => tag.replaceAll(RegExp('_'), '+').replaceAll(RegExp('^-'), ''), + ) .toList() .join('|'); // ignores custom limit if search is empty, otherwise it works // - int skip = (pageNum * limit) < 0 ? 0 : (pageNum * limit); - return "${booru.baseURL}/api/post/Search?IncludeLinks=true&Tag=$includeTags&ExcludeTag=$excludeTags&OrderBy=0&Skip=${skip.toString()}&Take=${limit.toString()}&DisableTotal=false"; + final int skip = (pageNum * limit) < 0 ? 0 : (pageNum * limit); + return '${booru.baseURL}/api/post/Search?IncludeLinks=true&Tag=$includeTags&ExcludeTag=$excludeTags&OrderBy=0&Skip=$skip&Take=$limit&DisableTotal=false'; } @override String makeTagURL(String input) { - return "${booru.baseURL}/api/tag/Search"; + return '${booru.baseURL}/api/tag/Search'; } @override - fetchTagSuggestions(Uri uri, String input) { + Future fetchTagSuggestions(Uri uri, String input) { return DioNetwork.post( uri.toString(), headers: { @@ -117,22 +102,22 @@ class WorldXyzHandler extends BooruHandler { 'Content-Type': 'application/json', }, data: { - "searchText": input.replaceAll(RegExp(r'^-'), ''), - "skip": 0, - "take": 10, + 'searchText': input.replaceAll(RegExp('^-'), ''), + 'skip': 0, + 'take': 10, }, ); } @override - List parseTagSuggestionsList(response) { - var parsedResponse = response.data; - return parsedResponse["items"] ?? []; + List parseTagSuggestionsList(dynamic response) { + final parsedResponse = response.data; + return parsedResponse['items'] ?? []; } @override - String? parseTagSuggestion(responseItem, int index) { - return responseItem['value']?.replaceAll(RegExp(r' '), '_'); + String? parseTagSuggestion(dynamic responseItem, int index) { + return responseItem['value']?.replaceAll(RegExp(' '), '_'); } // TODO disabled because api has a limit of 600 items for any query diff --git a/lib/src/data/booru.dart b/lib/src/data/booru.dart index 155590e2..d3153818 100644 --- a/lib/src/data/booru.dart +++ b/lib/src/data/booru.dart @@ -1,28 +1,29 @@ import 'dart:convert'; import 'package:get/utils.dart'; + import 'package:lolisnatcher/src/boorus/booru_type.dart'; class Booru { - String? name = "", faviconURL = "", baseURL = "", apiKey = "", userID = "", defTags = ""; - BooruType? type; - Booru(this.name, this.type, this.faviconURL, this.baseURL, this.defTags); - Booru.withKey(this.name, this.type, this.faviconURL, this.baseURL, this.defTags, this.apiKey, this.userID); - - Map toJson() { - return { - "name": name, - "type": type?.name, - "faviconURL": faviconURL, - "baseURL": baseURL, - "defTags": defTags, - "apiKey": apiKey, - "userID": userID, - }; - } + Booru( + this.name, + this.type, + this.faviconURL, + this.baseURL, + this.defTags, + ); + Booru.withKey( + this.name, + this.type, + this.faviconURL, + this.baseURL, + this.defTags, + this.apiKey, + this.userID, + ); Booru.fromJSON(String jsonString) { - Map json = jsonDecode(jsonString); + final Map json = jsonDecode(jsonString); setFromMap(json); } @@ -30,35 +31,49 @@ class Booru { setFromMap(json); } + Booru.fromLink(String link) { + final String jsonString = String.fromCharCodes(base64Decode(link.split('?')[1])); + final Map json = jsonDecode(jsonString); + setFromMap(json); + } + + String? name = '', faviconURL = '', baseURL = '', apiKey = '', userID = '', defTags = ''; + BooruType? type; + + Map toJson() { + return { + 'name': name, + 'type': type?.name, + 'faviconURL': faviconURL, + 'baseURL': baseURL, + 'defTags': defTags, + 'apiKey': apiKey, + 'userID': userID, + }; + } + String toLink(bool withSensitiveData) { - Map json = toJson(); + final Map json = toJson(); if (withSensitiveData == false) { - json.remove("apiKey"); - json.remove("userID"); + json.remove('apiKey'); + json.remove('userID'); } - String jsonString = jsonEncode(json); - print(jsonString); + final String jsonString = jsonEncode(json); return 'https://www.loli.snatcher?${base64UrlEncode(jsonString.codeUnits)}'; } - Booru.fromLink(String link) { - String jsonString = String.fromCharCodes(base64Decode(link.split("?")[1])); - Map json = jsonDecode(jsonString); - setFromMap(json); - } - void setFromMap(Map json) { - name = json["name"]?.toString(); - type = BooruType.values.firstWhereOrNull((e) => e.name.toLowerCase() == json["type"]?.toString().toLowerCase()); - faviconURL = json["faviconURL"]?.toString(); - baseURL = json["baseURL"]?.toString(); - defTags = json["defTags"]?.toString(); - apiKey = json["apiKey"]?.toString(); - userID = json["userID"]?.toString(); + name = json['name']?.toString(); + type = BooruType.values.firstWhereOrNull((e) => e.name.toLowerCase() == json['type']?.toString().toLowerCase()); + faviconURL = json['faviconURL']?.toString(); + baseURL = json['baseURL']?.toString(); + defTags = json['defTags']?.toString(); + apiKey = json['apiKey']?.toString(); + userID = json['userID']?.toString(); } @override String toString() { - return ("Name: $name, Type: $type, BaseURL: $baseURL, FaviconURL: $faviconURL, APIKey: $apiKey, UserID: $userID"); + return 'Name: $name, Type: $type, BaseURL: $baseURL, FaviconURL: $faviconURL, APIKey: $apiKey, UserID: $userID'; } } diff --git a/lib/src/data/booru_cancel_tokens.dart b/lib/src/data/booru_cancel_tokens.dart index 636cd196..77e777cd 100644 --- a/lib/src/data/booru_cancel_tokens.dart +++ b/lib/src/data/booru_cancel_tokens.dart @@ -1,3 +1,5 @@ +// ignore_for_file: join_return_with_assignment + import 'package:dio/dio.dart'; class BooruCancelTokens { @@ -8,10 +10,11 @@ class BooruCancelTokens { CancelToken notes = CancelToken(); void cancelSearch() { - if(!search.isCancelled) { + if (!search.isCancelled) { search.cancel(); } } + CancelToken recreateSearch() { cancelSearch(); search = CancelToken(); @@ -19,10 +22,11 @@ class BooruCancelTokens { } void cancelCount() { - if(!count.isCancelled) { + if (!count.isCancelled) { count.cancel(); } } + CancelToken recreateCount() { cancelCount(); count = CancelToken(); @@ -30,10 +34,11 @@ class BooruCancelTokens { } void cancelSuggestions() { - if(!suggestions.isCancelled) { + if (!suggestions.isCancelled) { suggestions.cancel(); } } + CancelToken recreateSuggestions() { cancelSuggestions(); suggestions = CancelToken(); @@ -41,10 +46,11 @@ class BooruCancelTokens { } void cancelComments() { - if(!comments.isCancelled) { + if (!comments.isCancelled) { comments.cancel(); } } + CancelToken recreateComments() { cancelComments(); comments = CancelToken(); @@ -52,10 +58,11 @@ class BooruCancelTokens { } void cancelNotes() { - if(!notes.isCancelled) { + if (!notes.isCancelled) { notes.cancel(); } } + CancelToken recreateNotes() { cancelNotes(); notes = CancelToken(); @@ -69,4 +76,4 @@ class BooruCancelTokens { cancelComments(); cancelNotes(); } -} \ No newline at end of file +} diff --git a/lib/src/data/booru_item.dart b/lib/src/data/booru_item.dart index 0e6ba406..701bc676 100644 --- a/lib/src/data/booru_item.dart +++ b/lib/src/data/booru_item.dart @@ -8,26 +8,6 @@ import 'package:lolisnatcher/src/data/note_item.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; class BooruItem { - late Key key; - String fileURL, sampleURL, thumbnailURL, postURL; - List tagsList; - late Rx mediaType; - RxnString possibleExt = RxnString(null); - RxnBool isSnatched = RxnBool(null), isFavourite = RxnBool(null); - RxBool isHated = false.obs, isLoved = false.obs, isNoScale = false.obs; - - String? fileExt, serverId, rating, score, uploaderName, description, md5String, postDate, postDateFormat; - String fileNameExtras; - List? sources; - RxList notes = RxList([]); - bool? hasNotes, hasComments; - double? fileWidth, fileHeight, fileAspectRatio, sampleWidth, sampleHeight, sampleAspectRatio, previewWidth, previewHeight, previewAspectRatio; - int? fileSize; - - bool get isLong { - return fileAspectRatio != null && fileAspectRatio! < 0.3; - } - BooruItem({ required this.fileURL, required this.sampleURL, @@ -58,7 +38,7 @@ class BooruItem { // Create a unique key for every loaded item, to later use them to read the state of their viewer key = GlobalKey(); - if (sampleURL.isEmpty || sampleURL == "null") { + if (sampleURL.isEmpty || sampleURL == 'null') { sampleURL = thumbnailURL; } fileExt = (fileExt ?? Tools.getFileExt(fileURL)).toLowerCase(); @@ -76,23 +56,43 @@ class BooruItem { mediaType = Rx(MediaType.fromExtension(fileExt)); } + late Key key; + String fileURL, sampleURL, thumbnailURL, postURL; + List tagsList; + late Rx mediaType; + RxnString possibleExt = RxnString(null); + RxnBool isSnatched = RxnBool(null), isFavourite = RxnBool(null); + RxBool isHated = false.obs, isLoved = false.obs, isNoScale = false.obs; + + String? fileExt, serverId, rating, score, uploaderName, description, md5String, postDate, postDateFormat; + String fileNameExtras; + List? sources; + RxList notes = RxList([]); + bool? hasNotes, hasComments; + double? fileWidth, fileHeight, fileAspectRatio, sampleWidth, sampleHeight, sampleAspectRatio, previewWidth, previewHeight, previewAspectRatio; + int? fileSize; + + bool get isLong { + return fileAspectRatio != null && fileAspectRatio! < 0.3; + } + Map toJson() { return { - "postURL": postURL, - "fileURL": fileURL, - "sampleURL": sampleURL, - "thumbnailURL": thumbnailURL, - "tags": tagsList, - "fileExt": fileExt, - "isFavourite": isFavourite.value, - "isSnatched": isSnatched.value, - "serverId": serverId, - "rating": rating, - "score": score, - "sources": sources, - "md5String": md5String, - "postDate": postDate, - "postDateFormat": postDateFormat, + 'postURL': postURL, + 'fileURL': fileURL, + 'sampleURL': sampleURL, + 'thumbnailURL': thumbnailURL, + 'tags': tagsList, + 'fileExt': fileExt, + 'isFavourite': isFavourite.value, + 'isSnatched': isSnatched.value, + 'serverId': serverId, + 'rating': rating, + 'score': score, + 'sources': sources, + 'md5String': md5String, + 'postDate': postDate, + 'postDateFormat': postDateFormat, }; } @@ -102,41 +102,41 @@ class BooruItem { } static BooruItem fromJSON(String jsonString) { - Map json = jsonDecode(jsonString); + final Map json = jsonDecode(jsonString); return BooruItem.fromMap(json); } static BooruItem fromMap(Map json) { - List tags = []; - List tagz = json["tags"]; + final List tags = []; + final List tagz = json['tags']; for (int i = 0; i < tagz.length; i++) { tags.add(tagz[i].toString()); } //BooruItem(this.fileURL,this.sampleURL,this.thumbnailURL,this.tagsList,this.postURL,this.fileExt - BooruItem item = BooruItem( - fileURL: json["fileURL"].toString(), - sampleURL: json["sampleURL"].toString(), - thumbnailURL: json["thumbnailURL"].toString(), + final BooruItem item = BooruItem( + fileURL: json['fileURL'].toString(), + sampleURL: json['sampleURL'].toString(), + thumbnailURL: json['thumbnailURL'].toString(), tagsList: tags, - postURL: json["postURL"].toString(), + postURL: json['postURL'].toString(), // TODO stringify other options here ); - item.isFavourite.value = json["isFavourite"].toString() == "true" ? true : false; - item.isSnatched.value = json["isSnatched"].toString() == "true" ? true : false; + item.isFavourite.value = json['isFavourite'].toString() == 'true'; + item.isSnatched.value = json['isSnatched'].toString() == 'true'; return item; } static BooruItem fromDBRow(dynamic row, List tags) { - BooruItem item = BooruItem( - fileURL: row["fileURL"].toString(), - sampleURL: row["sampleURL"].toString(), - thumbnailURL: row["thumbnailURL"].toString(), + final BooruItem item = BooruItem( + fileURL: row['fileURL'].toString(), + sampleURL: row['sampleURL'].toString(), + thumbnailURL: row['thumbnailURL'].toString(), // use custom separator to avoid conflicts with tags containing commas tagsList: tags, - postURL: row["postURL"].toString(), + postURL: row['postURL'].toString(), ); - item.isFavourite.value = Tools.intToBool(row["isFavourite"]); - item.isSnatched.value = Tools.intToBool(row["isSnatched"]); + item.isFavourite.value = Tools.intToBool(row['isFavourite']); + item.isSnatched.value = Tools.intToBool(row['isSnatched']); return item; } } @@ -178,7 +178,7 @@ enum MediaType { } String toJson() { - return name.replaceAll(RegExp(r'(?<=[a-z])(?=[A-Z])'), '_').toLowerCase(); + return name.replaceAll(RegExp('(?<=[a-z])(?=[A-Z])'), '_').toLowerCase(); } static MediaType fromExtension(String? ext) { diff --git a/lib/src/data/comment_item.dart b/lib/src/data/comment_item.dart index c130e86a..9a926b7e 100644 --- a/lib/src/data/comment_item.dart +++ b/lib/src/data/comment_item.dart @@ -1,9 +1,6 @@ import 'dart:convert'; class CommentItem { - String? id, title, content, authorID, authorName, avatarUrl, postID, createDate, createDateFormat; - int? score; - CommentItem({ this.id, this.title, @@ -17,6 +14,9 @@ class CommentItem { this.createDateFormat, }); + String? id, title, content, authorID, authorName, avatarUrl, postID, createDate, createDateFormat; + int? score; + Map toJson() { return { 'id': id, @@ -33,7 +33,7 @@ class CommentItem { } static CommentItem fromJSON(String jsonString) { - Map json = jsonDecode(jsonString); + final Map json = jsonDecode(jsonString); return CommentItem.fromMap(json); } diff --git a/lib/src/data/constants.dart b/lib/src/data/constants.dart index 9778d132..48b175b5 100644 --- a/lib/src/data/constants.dart +++ b/lib/src/data/constants.dart @@ -1,10 +1,11 @@ // TODO add/move more stuff here class Constants { - static const String appName = "LoliSnatcher"; + static const String appName = 'LoliSnatcher'; + // TODO don't forget to update on every new release // TODO take these from smth like .env? - static const String appVersion = "2.3.3"; + static const String appVersion = '2.3.3'; static const int appBuildNumber = 183; // @@ -14,5 +15,5 @@ class Constants { static const String discordURL = 'https://discord.gg/r9E4HDx9dz'; - static const String defaultBrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0"; + static const String defaultBrowserUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0'; } diff --git a/lib/src/data/history_item.dart b/lib/src/data/history_item.dart index a40d278a..726680bf 100644 --- a/lib/src/data/history_item.dart +++ b/lib/src/data/history_item.dart @@ -1,15 +1,16 @@ import 'package:get/get.dart'; + import 'package:lolisnatcher/src/boorus/booru_type.dart'; class HistoryItem { - final int id; - final String searchText; - final BooruType? booruType; - final String booruName; - bool isFavourite; - final String timestamp; - - HistoryItem(this.id, this.searchText, this.booruType, this.booruName, this.isFavourite, this.timestamp); + HistoryItem( + this.id, + this.searchText, + this.booruType, + this.booruName, + this.isFavourite, + this.timestamp, + ); HistoryItem.fromMap(Map map) : id = map['id'] as int, @@ -18,4 +19,11 @@ class HistoryItem { booruName = map['booruName'] as String, isFavourite = map['isFavourite'] == '1', timestamp = map['timestamp'] as String; + + final int id; + final String searchText; + final BooruType? booruType; + final String booruName; + bool isFavourite; + final String timestamp; } diff --git a/lib/src/data/note_item.dart b/lib/src/data/note_item.dart index 2f8298d5..7872a302 100644 --- a/lib/src/data/note_item.dart +++ b/lib/src/data/note_item.dart @@ -1,19 +1,19 @@ import 'dart:convert'; class NoteItem { - String? id, postID, content; - int posX, posY, width, height; - NoteItem({ - this.id, required this.postID, required this.content, required this.posX, required this.posY, required this.width, required this.height, + this.id, }); + String? id, postID, content; + int posX, posY, width, height; + Map toJson() { return { 'id': id, @@ -27,7 +27,7 @@ class NoteItem { } static NoteItem fromJSON(String jsonString) { - Map json = jsonDecode(jsonString); + final Map json = jsonDecode(jsonString); return NoteItem.fromMap(json); } diff --git a/lib/src/data/settings/app_mode.dart b/lib/src/data/settings/app_mode.dart index 4950ebf8..cd779811 100644 --- a/lib/src/data/settings/app_mode.dart +++ b/lib/src/data/settings/app_mode.dart @@ -1,3 +1,5 @@ +// ignore_for_file: constant_identifier_names + import 'dart:io'; enum AppMode { diff --git a/lib/src/data/settings/hand_side.dart b/lib/src/data/settings/hand_side.dart index f082e2e4..339a9637 100644 --- a/lib/src/data/settings/hand_side.dart +++ b/lib/src/data/settings/hand_side.dart @@ -33,4 +33,4 @@ enum HandSide { static HandSide get defaultValue { return HandSide.right; } -} \ No newline at end of file +} diff --git a/lib/src/data/tag.dart b/lib/src/data/tag.dart index 7de86c96..a9c25fea 100644 --- a/lib/src/data/tag.dart +++ b/lib/src/data/tag.dart @@ -4,46 +4,51 @@ import 'package:lolisnatcher/src/data/constants.dart'; import 'package:lolisnatcher/src/data/tag_type.dart'; class Tag { - Tag(this.fullString, {this.tagType = TagType.none, this.updatedAt = 0}) { + Tag( + this.fullString, { + this.tagType = TagType.none, + this.updatedAt = 0, + }) { if (updatedAt == 0) { - updatedAt = DateTime.now().millisecondsSinceEpoch; + updatedAt = DateTime.now().millisecondsSinceEpoch; } } - String fullString = ""; + + Tag.fromJson(Map json) { + fullString = json['fullString']?.toString() ?? json['name']?.toString() ?? 'unknown'; + // if no updatedAt is stored, set it to the current time minus 3 days (will make it stale) + updatedAt = json['updatedAt'] ?? (DateTime.now().millisecondsSinceEpoch - Constants.tagStaleTime); + tagType = TagType.values.byName(json['tagType']?.toString() ?? 'none'); + } + + String fullString = ''; TagType tagType = TagType.none; int updatedAt = 0; Map toJson() { return { - "fullString": fullString, - "updatedAt": updatedAt, - "tagType": tagType.name, + 'fullString': fullString, + 'updatedAt': updatedAt, + 'tagType': tagType.name, }; } - Tag.fromJson(Map json) { - fullString = json["fullString"]?.toString() ?? json["name"]?.toString() ?? "unknown"; - // if no updatedAt is stored, set it to the current time minus 3 days (will make it stale) - updatedAt = json["updatedAt"] ?? (DateTime.now().millisecondsSinceEpoch - Constants.tagStaleTime); - tagType = TagType.values.byName(json["tagType"]?.toString() ?? "none"); - } - @override String toString() { - return ("fullString: $fullString, updatedAt: $updatedAt, tagType: ${tagType.name}"); + return 'fullString: $fullString, updatedAt: $updatedAt, tagType: ${tagType.name}'; } Color getColour() { switch (tagType) { - case (TagType.artist): + case TagType.artist: return Colors.red; - case (TagType.copyright): + case TagType.copyright: return Colors.purple; - case (TagType.character): + case TagType.character: return Colors.green; - case (TagType.species): + case TagType.species: return Colors.brown; - case (TagType.meta): + case TagType.meta: return Colors.orange; default: return Colors.transparent; diff --git a/lib/src/data/tag_type.dart b/lib/src/data/tag_type.dart index dd5f2a15..b176c758 100644 --- a/lib/src/data/tag_type.dart +++ b/lib/src/data/tag_type.dart @@ -45,18 +45,18 @@ enum TagType { Color getColour() { switch (this) { - case (artist): + case artist: return Colors.red; - case (copyright): + case copyright: return Colors.purple; - case (character): + case character: return Colors.green; - case (species): + case species: return Colors.brown; - case (meta): + case meta: return Colors.orange; default: return Colors.transparent; } } -} \ No newline at end of file +} diff --git a/lib/src/data/theme_item.dart b/lib/src/data/theme_item.dart index 24db3eda..b5c288cf 100644 --- a/lib/src/data/theme_item.dart +++ b/lib/src/data/theme_item.dart @@ -1,9 +1,14 @@ import 'package:flutter/material.dart'; -class ThemeItem{ +class ThemeItem { + ThemeItem({ + required this.name, + required this.primary, + required this.accent, + }); + String name; // Flutters Colors.color should be used instead of using Color(0xFFhexcolour) because it breaks the light/dark mode on the text and icons for some reason Color? primary; Color? accent; - ThemeItem({required this.name, required this.primary, required this.accent}); -} \ No newline at end of file +} diff --git a/lib/src/data/update_info.dart b/lib/src/data/update_info.dart index 2888d6f9..39eab75a 100644 --- a/lib/src/data/update_info.dart +++ b/lib/src/data/update_info.dart @@ -1,13 +1,4 @@ class UpdateInfo { - String versionName; - int buildNumber; - String title; - String changelog; - bool isInStore; - bool isImportant; - String storePackage; - String githubURL; - UpdateInfo({ required this.versionName, required this.buildNumber, @@ -18,4 +9,13 @@ class UpdateInfo { required this.storePackage, required this.githubURL, }); + + String versionName; + int buildNumber; + String title; + String changelog; + bool isInStore; + bool isImportant; + String storePackage; + String githubURL; } diff --git a/lib/src/handlers/booru_handler.dart b/lib/src/handlers/booru_handler.dart index c8027c0f..c8971212 100644 --- a/lib/src/handlers/booru_handler.dart +++ b/lib/src/handlers/booru_handler.dart @@ -4,7 +4,7 @@ import 'dart:math'; import 'package:dio/dio.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:get/get.dart' as Get; +import 'package:get/get.dart' as getx; import 'package:html/parser.dart'; import 'package:lolisnatcher/src/boorus/booru_type.dart'; @@ -38,15 +38,15 @@ abstract class BooruHandler { String errorString = ''; List failedItems = []; - Map tagTypeMap = {}; - Map tagModifierMap = { - 'rating:': 'R', - 'artist:': 'A', - 'order:': 'O', - 'sort:': 'S', - }; + Map get tagTypeMap => {}; + Map get tagModifierMap => { + 'rating:': 'R', + 'artist:': 'A', + 'order:': 'O', + 'sort:': 'S', + }; - Get.RxList fetched = Get.RxList([]); + getx.RxList fetched = getx.RxList([]); List get filteredFetched => fetched.where((el) { final SettingsHandler settingsHandler = SettingsHandler.instance; @@ -64,7 +64,7 @@ abstract class BooruHandler { String get className => runtimeType.toString(); - bool hasSizeData = false; + bool get hasSizeData => false; Future searchSetup() async { return true; @@ -98,7 +98,9 @@ abstract class BooruHandler { // create url final String url = makeURL(tags); - if (url.isEmpty) return fetched; + if (url.isEmpty) { + return fetched; + } Uri uri; try { @@ -108,14 +110,14 @@ abstract class BooruHandler { errorString = 'Invalid URL ($url)'; return fetched; } - Logger.Inst().log('$url ${uri.toString()}', className, 'Search', LogTypes.booruHandlerSearchURL); + Logger.Inst().log('$url $uri', className, 'Search', LogTypes.booruHandlerSearchURL); Response response; try { response = await fetchSearch(uri, withCaptchaCheck: withCaptchaCheck); if (response.statusCode == 200) { // parse response data - List newItems = await parseResponse(response); + final List newItems = await parseResponse(response); await afterParseResponse(newItems); // save tags for check on next search @@ -161,7 +163,7 @@ abstract class BooruHandler { ); } - FutureOr> parseResponse(response) async { + FutureOr> parseResponse(dynamic response) async { List posts = []; try { posts = await parseListFromResponse(response); @@ -173,12 +175,12 @@ abstract class BooruHandler { rethrow; } - List newItems = []; + final List newItems = []; if (posts.isNotEmpty) { for (int i = 0; i < posts.length; i++) { final post = posts.elementAt(i); try { - BooruItem? item = await parseItemFromResponse(post, i); + final BooruItem? item = await parseItemFromResponse(post, i); if (item != null) { final List> hatedAndLovedTags = SettingsHandler.instance.parseTagsList(item.tagsList); item.isHated.value = hatedAndLovedTags[0].isNotEmpty; @@ -199,12 +201,12 @@ abstract class BooruHandler { /// /// parse raw response into a list of posts, /// here you should also parse any other info included with the response (i.e. totalcount) - FutureOr parseListFromResponse(response) { + FutureOr parseListFromResponse(dynamic response) { return []; } /// [SHOULD BE OVERRIDDEN] - FutureOr parseItemFromResponse(responseItem, int index) { + FutureOr parseItemFromResponse(dynamic responseItem, int index) { return BooruItem(fileURL: '', sampleURL: '', thumbnailURL: '', tagsList: [], postURL: ''); } @@ -231,10 +233,12 @@ abstract class BooruHandler { // TODO rename to getTagSuggestions Future> tagSearch(String input) async { - List tags = []; + final List tags = []; final String url = makeTagURL(input); - if (url.isEmpty) return tags; + if (url.isEmpty) { + return tags; + } Uri uri; try { uri = Uri.parse(url); @@ -242,7 +246,7 @@ abstract class BooruHandler { Logger.Inst().log('invalid url: $url', className, 'tagSearch', LogTypes.booruHandlerFetchFailed); return tags; } - Logger.Inst().log('$url ${uri.toString()}', className, 'tagSearch', LogTypes.booruHandlerSearchURL); + Logger.Inst().log('$url $uri', className, 'tagSearch', LogTypes.booruHandlerSearchURL); Response response; const int limit = 10; @@ -263,7 +267,7 @@ abstract class BooruHandler { } } } catch (e) { - Logger.Inst().log('${e.toString()} $rawTag', className, 'parseTagSuggestion', LogTypes.booruHandlerRawFetched); + Logger.Inst().log('$e $rawTag', className, 'parseTagSuggestion', LogTypes.booruHandlerRawFetched); } } } else { @@ -288,12 +292,12 @@ abstract class BooruHandler { } /// [SHOULD BE OVERRIDDEN] - FutureOr parseTagSuggestionsList(response) { + FutureOr parseTagSuggestionsList(dynamic response) { return []; } /// [SHOULD BE OVERRIDDEN] - FutureOr parseTagSuggestion(responseItem, int index) { + FutureOr parseTagSuggestion(dynamic responseItem, int index) { return ''; } @@ -308,12 +312,14 @@ abstract class BooruHandler { //////////////////////////////////////////////////////////////////////// - bool hasCommentsSupport = false; + bool get hasCommentsSupport => false; Future> getComments(String postID, int pageNum) async { - List comments = []; + final List comments = []; final String url = makeCommentsURL(postID, pageNum); - if (url.isEmpty) return comments; + if (url.isEmpty) { + return comments; + } Uri uri; try { uri = Uri.parse(url); @@ -321,7 +327,7 @@ abstract class BooruHandler { Logger.Inst().log('invalid url: $url', className, 'getComments', LogTypes.booruHandlerFetchFailed); return comments; } - Logger.Inst().log('$url ${uri.toString()}', className, 'getComments', LogTypes.booruHandlerSearchURL); + Logger.Inst().log('$url $uri', className, 'getComments', LogTypes.booruHandlerSearchURL); Response response; try { @@ -331,10 +337,12 @@ abstract class BooruHandler { for (int i = 0; i < rawComments.length; i++) { final rawComment = rawComments[i]; try { - CommentItem? parsedComment = await parseComment(rawComment, i); - if (parsedComment != null) comments.add(parsedComment); + final CommentItem? parsedComment = await parseComment(rawComment, i); + if (parsedComment != null) { + comments.add(parsedComment); + } } catch (e) { - Logger.Inst().log('${e.toString()} $rawComment', className, 'parseCommentsList', LogTypes.booruHandlerRawFetched); + Logger.Inst().log('$e $rawComment', className, 'parseCommentsList', LogTypes.booruHandlerRawFetched); } } } else { @@ -359,12 +367,12 @@ abstract class BooruHandler { } /// [SHOULD BE OVERRIDDEN] - FutureOr parseCommentsList(response) { + FutureOr parseCommentsList(dynamic response) { return []; } /// [SHOULD BE OVERRIDDEN] - FutureOr parseComment(responseItem, int index) { + FutureOr parseComment(dynamic responseItem, int index) { return CommentItem(); } @@ -376,10 +384,10 @@ abstract class BooruHandler { //////////////////////////////////////////////////////////////////////// // TODO - bool hasLoadItemSupport = false; + bool get hasLoadItemSupport => false; // TODO fetch and overwrite current item data when entering tag view with a newer / more complete data - bool shouldUpdateIteminTagView = false; + bool get shouldUpdateIteminTagView => false; Future loadItem({required BooruItem item, CancelToken? cancelToken}) async { return null; @@ -387,12 +395,14 @@ abstract class BooruHandler { //////////////////////////////////////////////////////////////////////// - bool hasNotesSupport = false; + bool get hasNotesSupport => false; Future> getNotes(String postID) async { - List notes = []; + final List notes = []; final String url = makeNotesURL(postID); - if (url.isEmpty) return notes; + if (url.isEmpty) { + return notes; + } Uri uri; try { uri = Uri.parse(url); @@ -400,7 +410,7 @@ abstract class BooruHandler { Logger.Inst().log('invalid url: $url', className, 'getNotes', LogTypes.booruHandlerFetchFailed); return notes; } - Logger.Inst().log('$url ${uri.toString()}', className, 'getNotes', LogTypes.booruHandlerSearchURL); + Logger.Inst().log('$url $uri', className, 'getNotes', LogTypes.booruHandlerSearchURL); Response response; try { @@ -410,10 +420,12 @@ abstract class BooruHandler { for (int i = 0; i < rawNotes.length; i++) { final rawNote = rawNotes[i]; try { - NoteItem? parsedNote = await parseNote(rawNote, i); - if (parsedNote != null) notes.add(parsedNote); + final NoteItem? parsedNote = await parseNote(rawNote, i); + if (parsedNote != null) { + notes.add(parsedNote); + } } catch (e) { - Logger.Inst().log('${e.toString()} $rawNote', className, 'parseNotesList', LogTypes.booruHandlerRawFetched); + Logger.Inst().log('$e $rawNote', className, 'parseNotesList', LogTypes.booruHandlerRawFetched); } } } else { @@ -438,12 +450,12 @@ abstract class BooruHandler { } /// [SHOULD BE OVERRIDDEN] - FutureOr parseNotesList(response) { + FutureOr parseNotesList(dynamic response) { return []; } /// [SHOULD BE OVERRIDDEN] - FutureOr parseNote(responseItem, int index) { + FutureOr parseNote(dynamic responseItem, int index) { return NoteItem( id: '', postID: '', @@ -462,9 +474,9 @@ abstract class BooruHandler { //////////////////////////////////////////////////////////////////////// - Get.RxInt totalCount = 0.obs; + getx.RxInt totalCount = 0.obs; // TODO for boorus where api doesn't give amount outright and we have to calculate it based on smth (last page*items per page, for example) - show "~" symbol to indicate that - bool countIsQuestionable = false; + bool get countIsQuestionable => false; Future searchCount(String input) async { totalCount.value = 0; return; @@ -498,16 +510,14 @@ abstract class BooruHandler { } } - Map headers = getHeaders(); + final Map headers = getHeaders(); if (headers['Cookie']?.isNotEmpty ?? false) { cookieString += headers['Cookie']!; } Logger.Inst().log('${booru.baseURL}: $cookieString', className, 'getCookies', LogTypes.booruHandlerSearchURL); - cookieString = cookieString.trim(); - - return cookieString; + return cookieString.trim(); } void addTagsWithType(List tags, TagType type) { @@ -515,7 +525,7 @@ abstract class BooruHandler { } Future populateTagHandler(List items) async { - List unTyped = []; + final List unTyped = []; for (int x = 0; x < items.length; x++) { for (int i = 0; i < items[x].tagsList.length; i++) { final String tag = items[x].tagsList[i]; @@ -569,7 +579,7 @@ abstract class BooruHandler { if (settingsHandler.dbHandler.db != null) { // TODO make this work in batches, not calling it on every single item ??? - List values = await settingsHandler.dbHandler.getTrackedValues(fetched[fetchedIndex]); + final List values = await settingsHandler.dbHandler.getTrackedValues(fetched[fetchedIndex]); fetched[fetchedIndex].isSnatched.value = values[0]; fetched[fetchedIndex].isFavourite.value = values[1]; } @@ -598,7 +608,7 @@ abstract class BooruHandler { final SettingsHandler settingsHandler = SettingsHandler.instance; if (settingsHandler.dbHandler.db != null && diff > 0) { - List> valuesList = await settingsHandler.dbHandler + final List> valuesList = await settingsHandler.dbHandler .getMultipleTrackedValues(fetched.sublist(fetchedIndexes.first, fetchedIndexes.last)); //.map((e) => e.fileURL).toList() valuesList.asMap().forEach((index, values) { diff --git a/lib/src/handlers/booru_handler_factory.dart b/lib/src/handlers/booru_handler_factory.dart index 25a80fa2..49243ea8 100644 --- a/lib/src/handlers/booru_handler_factory.dart +++ b/lib/src/handlers/booru_handler_factory.dart @@ -27,7 +27,7 @@ import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; class BooruHandlerFactory { - BooruHandler? booruHandler; + late BooruHandler booruHandler; int pageNum = -1; List getBooruHandler(List boorus, int? customLimit) { @@ -37,17 +37,17 @@ class BooruHandlerFactory { final Booru booru = boorus.first; switch (booru.type) { - case (BooruType.Moebooru): + case BooruType.Moebooru: pageNum = 0; booruHandler = MoebooruHandler(booru, limit); break; - case (BooruType.Gelbooru): + case BooruType.Gelbooru: // current gelbooru is v.0.2.5, while safe and others are 0.2.0, but sice we had them under the same type from the start // we should keep them like that, but change sub-handler depending on the link // TODO are there only these 4 sites, or possible more? const List gelbooruAlikes = ['rule34.xxx', 'safebooru.org', 'realbooru.com', 'furry.booru.org']; - if (booru.baseURL!.contains("gelbooru.com")) { + if (booru.baseURL!.contains('gelbooru.com')) { booruHandler = GelbooruHandler(booru, limit); } else if (gelbooruAlikes.any((element) => booru.baseURL!.contains(element))) { booruHandler = GelbooruAlikesHandler(booru, limit); @@ -56,74 +56,74 @@ class BooruHandlerFactory { booruHandler = GelbooruAlikesHandler(booru, limit); } break; - case (BooruType.GelbooruAlike): + case BooruType.GelbooruAlike: // this type is not available in type selector booruHandler = GelbooruAlikesHandler(booru, limit); break; - case (BooruType.Danbooru): + case BooruType.Danbooru: pageNum = 0; booruHandler = DanbooruHandler(booru, limit); break; - case (BooruType.e621): + case BooruType.e621: pageNum = 0; booruHandler = e621Handler(booru, limit); break; - case (BooruType.Shimmie): + case BooruType.Shimmie: pageNum = 0; booruHandler = ShimmieHandler(booru, limit); break; - case (BooruType.Philomena): + case BooruType.Philomena: pageNum = 0; booruHandler = PhilomenaHandler(booru, limit); break; - case (BooruType.Szurubooru): + case BooruType.Szurubooru: booruHandler = SzurubooruHandler(booru, limit); break; - case (BooruType.Sankaku): + case BooruType.Sankaku: pageNum = 0; booruHandler = SankakuHandler(booru, limit); break; - case (BooruType.Hydrus): + case BooruType.Hydrus: booruHandler = HydrusHandler(booru, limit); break; - case (BooruType.GelbooruV1): + case BooruType.GelbooruV1: booruHandler = GelbooruV1Handler(booru, limit); break; - case (BooruType.BooruOnRails): + case BooruType.BooruOnRails: pageNum = 0; booruHandler = BooruOnRailsHandler(booru, limit); break; - case (BooruType.Favourites): + case BooruType.Favourites: booruHandler = FavouritesHandler(booru, limit); break; - case (BooruType.Rainbooru): + case BooruType.Rainbooru: pageNum = 0; booruHandler = RainbooruHandler(booru, limit); break; - case (BooruType.R34Hentai): + case BooruType.R34Hentai: pageNum = 0; booruHandler = R34HentaiHandler(booru, limit); break; - case (BooruType.World): + case BooruType.World: booruHandler = WorldXyzHandler(booru, limit); break; - case (BooruType.IdolSankaku): + case BooruType.IdolSankaku: pageNum = 0; booruHandler = IdolSankakuHandler(booru, limit); break; - case (BooruType.InkBunny): + case BooruType.InkBunny: pageNum = 0; booruHandler = InkBunnyHandler(booru, limit); break; - case (BooruType.AGNPH): + case BooruType.AGNPH: pageNum = 0; booruHandler = AGNPHHandler(booru, limit); break; - case (BooruType.NyanPals): + case BooruType.NyanPals: pageNum = 0; booruHandler = NyanPalsHandler(booru, limit); break; - case (BooruType.WildCritters): + case BooruType.WildCritters: pageNum = 0; booruHandler = WildCrittersHandler(booru, limit); break; @@ -136,7 +136,7 @@ class BooruHandlerFactory { break; } } else { - booruHandler = MergebooruHandler(Booru("Merge", BooruType.Merge, "", "", ""), limit); + booruHandler = MergebooruHandler(Booru('Merge', BooruType.Merge, '', '', ''), limit); (booruHandler as MergebooruHandler).setupMerge(boorus); } return [booruHandler, pageNum]; diff --git a/lib/src/handlers/database_handler.dart b/lib/src/handlers/database_handler.dart index 5f7cee08..97e0a721 100644 --- a/lib/src/handlers/database_handler.dart +++ b/lib/src/handlers/database_handler.dart @@ -41,49 +41,46 @@ class DBHandler { } Future updateTable() async { - await db?.execute('CREATE TABLE IF NOT EXISTS BooruItem' - '(id INTEGER PRIMARY KEY,' - 'thumbnailURL TEXT,' - 'sampleURL TEXT,' - 'fileURL TEXT,' - 'postURL TEXT,' - 'mediaType TEXT,' - 'isSnatched INTEGER,' - 'isFavourite INTEGER' + await db?.execute('CREATE TABLE IF NOT EXISTS BooruItem ' + '(id INTEGER PRIMARY KEY, ' + 'thumbnailURL TEXT, ' + 'sampleURL TEXT, ' + 'fileURL TEXT, ' + 'postURL TEXT, ' + 'mediaType TEXT, ' + 'isSnatched INTEGER, ' + 'isFavourite INTEGER ' ')'); - await db?.execute('CREATE TABLE IF NOT EXISTS Tag (' - 'id INTEGER PRIMARY KEY,' - 'name TEXT' - 'tagType TEXT' - 'updatedAt INTEGER' + await db?.execute('CREATE TABLE IF NOT EXISTS Tag ( ' + 'id INTEGER PRIMARY KEY, ' + 'name TEXT ' + 'tagType TEXT ' + 'updatedAt INTEGER ' ')'); - await db?.execute('CREATE TABLE IF NOT EXISTS ImageTag (' - 'tagID INTEGER,' - 'booruItemID INTEGER' + await db?.execute('CREATE TABLE IF NOT EXISTS ImageTag ( ' + 'tagID INTEGER, ' + 'booruItemID INTEGER ' ')'); - await db?.execute('CREATE TABLE IF NOT EXISTS SearchHistory (' - 'id INTEGER PRIMARY KEY,' - 'booruType TEXT,' - 'booruName TEXT,' - 'searchText TEXT,' - 'isFavourite INTEGER,' - 'timestamp TEXT DEFAULT CURRENT_TIMESTAMP' + await db?.execute('CREATE TABLE IF NOT EXISTS SearchHistory ( ' + 'id INTEGER PRIMARY KEY, ' + 'booruType TEXT, ' + 'booruName TEXT, ' + 'searchText TEXT, ' + 'isFavourite INTEGER, ' + 'timestamp TEXT DEFAULT CURRENT_TIMESTAMP ' ')'); - await db?.execute('CREATE TABLE IF NOT EXISTS TabRestore (' - 'id INTEGER PRIMARY KEY,' - 'restore TEXT' + await db?.execute('CREATE TABLE IF NOT EXISTS TabRestore ( ' + 'id INTEGER PRIMARY KEY, ' + 'restore TEXT ' ')'); try { if (!await columnExists('SearchHistory', 'isFavourite')) { - print('creating isFavourite'); await db?.execute('ALTER TABLE SearchHistory ADD COLUMN isFavourite INTEGER;'); } if (!await columnExists('Tag', 'tagType')) { - print('creating tagType'); await db?.execute('ALTER TABLE Tag ADD COLUMN tagType TEXT;'); } if (!await columnExists('Tag', 'updatedAt')) { - print('creating updatedAt'); await db?.execute('ALTER TABLE Tag ADD COLUMN updatedAt INTEGER;'); } } catch (e) { @@ -498,7 +495,9 @@ class DBHandler { /// Adds tags for a BooruItem to the database Future updateTags(List tags, String? itemID) async { - if (itemID == null) return; + if (itemID == null) { + return; + } String? id = ''; for (final tag in tags) { id = await getTagID(tag); diff --git a/lib/src/handlers/loli_sync_handler.dart b/lib/src/handlers/loli_sync_handler.dart index 6bf67fde..1afe035b 100644 --- a/lib/src/handlers/loli_sync_handler.dart +++ b/lib/src/handlers/loli_sync_handler.dart @@ -4,8 +4,8 @@ import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/data/tag.dart'; @@ -36,8 +36,8 @@ class LoliSync { yield 'Server active at $ip:$port'; await for (final req in server!) { - Logger.Inst().log(req.uri.path.toString(), 'LoliSync', 'startServer', LogTypes.loliSyncInfo); - switch (req.uri.path.toString()) { + Logger.Inst().log(req.uri.path, 'LoliSync', 'startServer', LogTypes.loliSyncInfo); + switch (req.uri.path) { case '/lolisync/boorubatch': yield 'Received booru items batch'; yield await storeBooruBatch(req, settingsHandler); @@ -68,7 +68,7 @@ class LoliSync { } } - Future storeSettings(var req, SettingsHandler settingsHandler) async { + Future storeSettings(dynamic req, SettingsHandler settingsHandler) async { if (req.method == 'POST') { try { Logger.Inst().log('request to update settings recieved', 'LoliSync', 'storeSettings', LogTypes.loliSyncInfo); @@ -81,7 +81,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'storeSettings', LogTypes.exception); req.response.statusCode = 404; req.response.write('Invalid Query'); - return 'Something went wrong ${e.toString()}'; + return 'Something went wrong $e'; } } else { req.response.statusCode = 404; @@ -90,7 +90,7 @@ class LoliSync { } } - Future storeBooruBatch(var req, SettingsHandler settingsHandler) async { + Future storeBooruBatch(dynamic req, SettingsHandler settingsHandler) async { if (req.method == 'POST') { try { final int amount = int.parse(req.uri.queryParameters['amount']!); @@ -126,7 +126,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'storeBooruItem', LogTypes.exception); req.response.statusCode = 404; req.response.write('Invalid Query'); - return 'Something went wrong ${e.toString()}'; + return 'Something went wrong $e'; } } else { req.response.statusCode = 404; @@ -135,7 +135,7 @@ class LoliSync { } } - Future storeBooruItem(var req, SettingsHandler settingsHandler) async { + Future storeBooruItem(dynamic req, SettingsHandler settingsHandler) async { if (req.method == 'POST') { try { final int amount = int.parse(req.uri.queryParameters['amount']!); @@ -161,7 +161,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'storeBooruItem', LogTypes.exception); req.response.statusCode = 404; req.response.write('Invalid Query'); - return 'Something went wrong ${e.toString()}'; + return 'Something went wrong $e'; } } else { req.response.statusCode = 404; @@ -170,7 +170,7 @@ class LoliSync { } } - Future storeBooru(var req, SettingsHandler settingsHandler) async { + Future storeBooru(dynamic req, SettingsHandler settingsHandler) async { if (req.method == 'POST') { try { Logger.Inst().log('request to add item recieved', 'LoliSync', 'storeBooru', LogTypes.loliSyncInfo); @@ -190,7 +190,9 @@ class LoliSync { // } // } final bool alreadyExists = settingsHandler.booruList.indexWhere((el) => el.baseURL == booru.baseURL && el.name == booru.name) != -1; - if (!alreadyExists) await settingsHandler.saveBooru(booru); + if (!alreadyExists) { + await settingsHandler.saveBooru(booru); + } } req.response.statusCode = 200; req.response.write('Success'); @@ -203,7 +205,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'storeBooru', LogTypes.exception); req.response.statusCode = 404; req.response.write('Invalid Query'); - return 'Something went wrong ${e.toString()}'; + return 'Something went wrong $e'; } } else { req.response.statusCode = 404; @@ -212,7 +214,7 @@ class LoliSync { } } - Future storeTabs(var req, SettingsHandler settingsHandler) async { + Future storeTabs(dynamic req, SettingsHandler settingsHandler) async { final SearchHandler searchHandler = SearchHandler.instance; if (req.method == 'POST') { @@ -238,7 +240,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'storeTabs', LogTypes.exception); req.response.statusCode = 404; req.response.write('Invalid Query'); - return 'Something went wrong ${e.toString()}'; + return 'Something went wrong $e'; } } else { req.response.statusCode = 404; @@ -247,7 +249,7 @@ class LoliSync { } } - Future storeTags(var req, SettingsHandler settingsHandler) async { + Future storeTags(dynamic req, SettingsHandler settingsHandler) async { final TagHandler tagHandler = TagHandler.instance; if (req.method == 'POST') { @@ -272,7 +274,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'storeTags', LogTypes.exception); req.response.statusCode = 404; req.response.write('Invalid Query'); - return 'Something went wrong ${e.toString()}'; + return 'Something went wrong $e'; } } else { req.response.statusCode = 404; @@ -402,7 +404,7 @@ class LoliSync { Logger.Inst().log(e.toString(), 'LoliSync', 'sendTest', LogTypes.exception); FlashElements.showSnackbar( title: Text( - 'Test error: ${e.toString()}', + 'Test error: $e', style: const TextStyle(fontSize: 20), ), leadingIcon: Icons.warning_amber, @@ -421,7 +423,7 @@ class LoliSync { return response.statusCode.toString(); } catch (e) { Logger.Inst().log(e.toString(), 'LoliSync', 'sendSyncComplete', LogTypes.exception); - return 'Sync Complete error ${e.toString()}'; + return 'Sync Complete error $e'; } } @@ -556,6 +558,6 @@ class LoliSync { } yield 'Sync Complete'; - final String resp = await sendSyncComplete(); + await sendSyncComplete(); } } diff --git a/lib/src/handlers/navigation_handler.dart b/lib/src/handlers/navigation_handler.dart index 47cd55b5..a44dacca 100644 --- a/lib/src/handlers/navigation_handler.dart +++ b/lib/src/handlers/navigation_handler.dart @@ -6,4 +6,4 @@ class NavigationHandler extends GetxController { static NavigationHandler get instance => Get.find(); final GlobalKey navigatorKey = GlobalKey(); -} \ No newline at end of file +} diff --git a/lib/src/handlers/search_handler.dart b/lib/src/handlers/search_handler.dart index 68c63939..4e5c99d2 100644 --- a/lib/src/handlers/search_handler.dart +++ b/lib/src/handlers/search_handler.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_inner_drawer/inner_drawer.dart'; import 'package:get/get.dart'; -import 'package:lolisnatcher/src/handlers/database_handler.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:uuid/uuid.dart'; @@ -15,6 +14,7 @@ import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; import 'package:lolisnatcher/src/handlers/booru_handler.dart'; import 'package:lolisnatcher/src/handlers/booru_handler_factory.dart'; +import 'package:lolisnatcher/src/handlers/database_handler.dart'; import 'package:lolisnatcher/src/handlers/service_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; @@ -44,7 +44,7 @@ class SearchHandler extends GetxController { // Add after the current tab // list.insert(currentIndex + 1, SearchTab(currentBooru.obs, null, searchText)); - Rx booru = (customBooru ?? currentBooru).obs; + final Rx booru = (customBooru ?? currentBooru).obs; // Add new tab to the end final SearchTab newTab = SearchTab(booru, null, searchText); @@ -214,7 +214,7 @@ class SearchHandler extends GetxController { bool ignoreSameIndexCheck = false, }) { // change only if new index != current index - final int oldIndex = currentIndex; + // final int oldIndex = currentIndex; int newIndex = i; // protection from early execution on start @@ -314,7 +314,7 @@ class SearchHandler extends GetxController { } List getTabsWithTag(String tag) { - List tabs = []; + final List tabs = []; for (final SearchTab tab in list) { if (tab.tags.toLowerCase().trim().split(' ').contains(tag.toLowerCase().trim())) { tabs.add(tab); @@ -341,7 +341,7 @@ class SearchHandler extends GetxController { BooruItem setViewedItem(int i) { final int newIndex = i; - final int oldIndex = viewedIndex.value; + // final int oldIndex = viewedIndex.value; final BooruItem newItem = newIndex != -1 ? currentFetched[newIndex] : BooruItem( @@ -411,7 +411,7 @@ class SearchHandler extends GetxController { // write to history if (text != '' && settingsHandler.searchHistoryEnabled) { - settingsHandler.dbHandler.updateSearchHistory(text, currentBooru.type?.name, currentBooru.name!); + settingsHandler.dbHandler.updateSearchHistory(text, currentBooru.type?.name, currentBooru.name); } } @@ -481,7 +481,7 @@ class SearchHandler extends GetxController { final SettingsHandler settingsHandler = SettingsHandler.instance; final bool canAddSecondary = secondaryBoorus != null && settingsHandler.booruList.length > 1; - RxList? secondary = canAddSecondary ? secondaryBoorus.obs : null; + final RxList? secondary = canAddSecondary ? secondaryBoorus.obs : null; final SearchTab newTab = SearchTab(currentBooru.obs, secondary, currentTab.tags); list[currentIndex] = newTab; @@ -502,8 +502,9 @@ class SearchHandler extends GetxController { // run search on current tab Future runSearch() async { // do nothing if reached the end or detected an error - if (isLastPage.value) return; - if (errorString.isNotEmpty) return; + if (isLastPage.value || errorString.isNotEmpty) { + return; + } // if not last page - set loading state and increment page if (!currentBooruHandler.locked) { @@ -606,10 +607,10 @@ class SearchHandler extends GetxController { // Restores tabs from a string saved in DB List> decodeBackupString(String input) { - List> result = []; - List splitInput = input.split(listDivider); + final List> result = []; + final List splitInput = input.split(listDivider); for (final String str in splitInput) { - List booruAndTags = str.split(tabDivider); + final List booruAndTags = str.split(tabDivider); result.add(booruAndTags); } return result; @@ -617,15 +618,15 @@ class SearchHandler extends GetxController { Future restoreTabs() async { final SettingsHandler settingsHandler = SettingsHandler.instance; - List result = await settingsHandler.dbHandler.getTabRestore(); - List restoredGlobals = []; + final List result = await settingsHandler.dbHandler.getTabRestore(); + final List restoredGlobals = []; bool foundBrokenItem = false; - List brokenItems = []; + final List brokenItems = []; int newIndex = 0; if (result.length == 2) { // split list into tabs - List> splitInput = decodeBackupString(result[1]); + final List> splitInput = decodeBackupString(result[1]); // print('restoreTabs: ${splitInput}'); for (final List booruAndTags in splitInput) { // check for parsing errors @@ -716,8 +717,8 @@ class SearchHandler extends GetxController { void mergeTabs(String tabStr) { final SettingsHandler settingsHandler = SettingsHandler.instance; - List> splitInput = decodeBackupString(tabStr); - List restoredGlobals = []; + final List> splitInput = decodeBackupString(tabStr); + final List restoredGlobals = []; for (final List booruAndTags in splitInput) { // check for parsing errors final bool isEntryValid = booruAndTags.length > 1 && booruAndTags[0].isNotEmpty; @@ -754,8 +755,8 @@ class SearchHandler extends GetxController { void replaceTabs(String tabStr) { final SettingsHandler settingsHandler = SettingsHandler.instance; - List> splitInput = decodeBackupString(tabStr); - List restoredGlobals = []; + final List> splitInput = decodeBackupString(tabStr); + final List restoredGlobals = []; int newIndex = 0; // reset current tab index to avoid exceptions when tab list length is different @@ -801,7 +802,7 @@ class SearchHandler extends GetxController { final SettingsHandler settingsHandler = SettingsHandler.instance; // if there are only one tab - check that its not with default booru and tags // if there are more than 1 tab or check return false - start backup - List tabList = list; + final List tabList = list; final int tabIndex = currentIndex; final bool onlyDefaultTab = tabList.length == 1 && tabList[0].booruHandler.booru.name == settingsHandler.prefBooru && tabList[0].tags == settingsHandler.defTags; @@ -827,7 +828,7 @@ class SearchHandler extends GetxController { return; } - String? backupString = getBackupString(); + final String? backupString = getBackupString(); final SettingsHandler settingsHandler = SettingsHandler.instance; // print('backupString: $backupString'); if (backupString != null) { @@ -842,7 +843,7 @@ class SearchHandler extends GetxController { class SearchTab { SearchTab(this.selectedBooru, this.secondaryBoorus, this.tags) { - List tempBooruList = []; + final List tempBooruList = []; tempBooruList.add(selectedBooru.value); if (secondaryBoorus != null) { tempBooruList.addAll(secondaryBoorus!); @@ -875,11 +876,11 @@ class SearchTab { @override String toString() { - return 'tags: $tags selectedBooru: ${selectedBooru.toString()} booruHandler: $booruHandler'; + return 'tags: $tags selectedBooru: $selectedBooru booruHandler: $booruHandler'; } List getSelected() { - List? selectedItems = []; + final List selectedItems = []; for (int i = 0; i < selected.length; i++) { selectedItems.add(booruHandler.filteredFetched.elementAt(selected[i])); } diff --git a/lib/src/handlers/service_handler.dart b/lib/src/handlers/service_handler.dart index 329ea7c1..4cef0e06 100644 --- a/lib/src/handlers/service_handler.dart +++ b/lib/src/handlers/service_handler.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; @@ -8,130 +9,137 @@ import 'package:vibration/vibration.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; - //The ServiceHandler class calls kotlin functions in MainActivity.kt -class ServiceHandler{ - static const platform = MethodChannel("com.noaisu.loliSnatcher/services"); +class ServiceHandler { + static void log(Object? e) { + if (kDebugMode) { + log(e); + } + } + + static const platform = MethodChannel('com.noaisu.loliSnatcher/services'); // Call androids native media scanner on a file path - static void callMediaScanner(String path) async { - try{ - await platform.invokeMethod("scanMedia",{"path": path}); - } catch(e){ - print(e); + static Future callMediaScanner(String path) async { + try { + await platform.invokeMethod('scanMedia', {'path': path}); + } catch (e) { + log(e); } } static Future restartApp() { - return platform.invokeMethod("restartApp"); + return platform.invokeMethod('restartApp'); } - static Future getAndroidSDKVersion() async{ + static Future getAndroidSDKVersion() async { int result = 0; - try{ - result = await platform.invokeMethod("getSdkVersion"); - } catch(e){ - print(e); + try { + result = await platform.invokeMethod('getSdkVersion'); + } catch (e) { + log(e); } return result; } // Gets main storage dir - static Future getExtDir() async{ - String result = ""; - try{ - if(Platform.isAndroid) { - result = await platform.invokeMethod("getExtPath"); - } else if(Platform.isLinux) { + static Future getExtDir() async { + String result = ''; + try { + if (Platform.isAndroid) { + result = await platform.invokeMethod('getExtPath'); + } else if (Platform.isLinux) { result = Platform.environment['HOME']!; - } else if(Platform.isWindows) { + } else if (Platform.isWindows) { result = Platform.environment['LOCALAPPDATA']!; - } else if(Platform.isIOS) { + } else if (Platform.isIOS) { result = (await getApplicationDocumentsDirectory()).path; } - } catch(e){ - print(e); + } catch (e) { + log(e); } return result; } static Future setExtDir() async { - String result = ""; + String result = ''; try { - result = await platform.invokeMethod("setExtPath"); - print("Service handler got uri back: $result"); + result = await platform.invokeMethod('setExtPath'); + log('Service handler got uri back: $result'); } catch (e) { - print(e); + log(e); } // File(result+"/test.txt").create(recursive: true); return result; } + static Future getImageSAFUri() async { - String result = ""; + String result = ''; try { - result = await platform.invokeMethod("selectImage"); - print("Service handler got uri back: $result"); + result = await platform.invokeMethod('selectImage'); + log('Service handler got uri back: $result'); } catch (e) { - print(e); + log(e); } // File(result+"/test.txt").create(recursive: true); return result; } + static Future getSAFFile(String contentUri) async { Uint8List? result; try { - result = await platform.invokeMethod("getFileBytes",{"uri":contentUri}); - print("Got file back"); + result = await platform.invokeMethod('getFileBytes', {'uri': contentUri}); + log('Got file back'); } catch (e) { - print(e); + log(e); } // File(result+"/test.txt").create(recursive: true); return result; } + static Future getSAFFileExtension(String contentUri) async { - String result = ""; + String result = ''; try { - result = await platform.invokeMethod("getFileExtension",{"uri":contentUri}); - print("Got file ext back"); - print(result); + result = await platform.invokeMethod('getFileExtension', {'uri': contentUri}); + log('Got file ext back'); + log(result); } catch (e) { - print(e); + log(e); } // File(result+"/test.txt").create(recursive: true); return result; } - // This function only grants access until device reboot static Future getSAFDirectoryAccess() async { - String result = ""; + String result = ''; try { - result = await platform.invokeMethod("getTempDirAccess"); - print("Got saf path back"); - print(result); + result = await platform.invokeMethod('getTempDirAccess'); + log('Got saf path back'); + log(result); } catch (e) { - print(e); + log(e); } return result; } - static Future getFileFromSAFDirectory(String SAFUri, String fileName) async { + static Future getFileFromSAFDirectory(String safUri, String fileName) async { Uint8List? result; try { - result = await platform.invokeMethod("getFileByName",{"uri":SAFUri,"fileName":fileName}); - print("found file $fileName"); + result = await platform.invokeMethod('getFileByName', {'uri': safUri, 'fileName': fileName}); + log('found file $fileName'); } catch (e) { - print(e); + log(e); } return result; } - static Future existsFileFromSAFDirectory(String SAFUri, String fileName) async { + static Future existsFileFromSAFDirectory(String safUri, String fileName) async { bool result = false; try { - result = await platform.invokeMethod("existsFileByName",{"uri":SAFUri,"fileName":fileName}); - print("found file $fileName $result"); + result = await platform.invokeMethod('existsFileByName', {'uri': safUri, 'fileName': fileName}); + log('found file $fileName $result'); } catch (e) { - print(e); + log(e); } return result; } @@ -139,224 +147,222 @@ class ServiceHandler{ static Future createFileStreamFromSAFDirectory(String fileName, String mediaType, String fileExt, String savePath) async { String? result; try { - result = await platform.invokeMethod("createFileStream", {"fileName":fileName,"mediaType":mediaType,"fileExt":fileExt,"savePath":savePath}); - print("created file $fileName $result"); + result = await platform.invokeMethod('createFileStream', {'fileName': fileName, 'mediaType': mediaType, 'fileExt': fileExt, 'savePath': savePath}); + log('created file $fileName $result'); } catch (e) { - print(e); + log(e); } return result; } - static Future writeStreamToFileFromSAFDirectory(String SAFUri, Uint8List data) async { + static Future writeStreamToFileFromSAFDirectory(String safUri, Uint8List data) async { bool result = false; try { - result = await platform.invokeMethod("writeFileStream", {"uri":SAFUri, "data":data}) ?? false; - // print("wrote file stream $SAFUri"); + result = await platform.invokeMethod('writeFileStream', {'uri': safUri, 'data': data}) ?? false; + // log("wrote file stream $SAFUri"); } catch (e) { - print(e); + log(e); } return result; } - static Future closeStreamToFileFromSAFDirectory(String SAFUri) async { + static Future closeStreamToFileFromSAFDirectory(String safUri) async { bool result = false; try { - result = await platform.invokeMethod("closeStreamToFile", {"uri":SAFUri}) ?? false; - print("closed file stream $SAFUri"); + result = await platform.invokeMethod('closeStreamToFile', {'uri': safUri}) ?? false; + log('closed file stream $safUri'); } catch (e) { - print(e); + log(e); } return result; } - static Future deleteStreamToFileFromSAFDirectory(String SAFUri) async { + static Future deleteStreamToFileFromSAFDirectory(String safUri) async { bool result = false; try { - result = await platform.invokeMethod("deleteStreamToFile", {"uri":SAFUri}) ?? false; - print("deleted file stream $SAFUri"); + result = await platform.invokeMethod('deleteStreamToFile', {'uri': safUri}) ?? false; + log('deleted file stream $safUri'); } catch (e) { - print(e); + log(e); } return result; } - static Future existsFileStreamFromSAFDirectory(String SAFUri) async { + static Future existsFileStreamFromSAFDirectory(String safUri) async { bool result = false; try { - result = await platform.invokeMethod("existsStreamToFile",{"uri":SAFUri}); - print("found file stream $SAFUri"); + result = await platform.invokeMethod('existsStreamToFile', {'uri': safUri}); + log('found file stream $safUri'); } catch (e) { - print(e); + log(e); } return result; } - static Future deleteFileFromSAFDirectory(String SAFUri, String fileName) async { + static Future deleteFileFromSAFDirectory(String safUri, String fileName) async { bool result = false; try { - result = await platform.invokeMethod("deleteFileByName",{"uri":SAFUri,"fileName":fileName}); - print("deleted file $fileName"); + result = await platform.invokeMethod('deleteFileByName', {'uri': safUri, 'fileName': fileName}); + log('deleted file $fileName'); } catch (e) { - print(e); + log(e); } return result; } - static Future getFilesByExt(String SAFUri, String fileExt) async { - String result = ""; + static Future getFilesByExt(String safUri, String fileExt) async { + String result = ''; try { - result = await platform.invokeMethod("getTempDirAccess"); - print("Got saf path back"); - print(result); + result = await platform.invokeMethod('getTempDirAccess'); + log('Got saf path back'); + log(result); } catch (e) { - print(e); + log(e); } return result; } - static Future copySafFileToDir(String SAFUri, String fileName, String targetPath) async { + static Future copySafFileToDir(String safUri, String fileName, String targetPath) async { bool result = false; try { - result = await platform.invokeMethod("copySafFileToDir",{"uri":SAFUri,"fileName":fileName,"targetPath":targetPath}); + result = await platform.invokeMethod('copySafFileToDir', {'uri': safUri, 'fileName': fileName, 'targetPath': targetPath}); } catch (e) { - print(e); + log(e); result = false; } return result; } - static Future getConfigDir() async { String result = ''; - if (Platform.isAndroid){ - result = "${await getExtDir()}/LoliSnatcher/config/"; // await platform.invokeMethod("getDocumentsPath"); ? - } else if (Platform.isLinux){ - result = "${await getExtDir()}/.loliSnatcher/config/"; + if (Platform.isAndroid) { + result = '${await getExtDir()}/LoliSnatcher/config/'; // await platform.invokeMethod("getDocumentsPath"); ? + } else if (Platform.isLinux) { + result = '${await getExtDir()}/.loliSnatcher/config/'; } else if (Platform.isWindows) { - result = "${await getExtDir()}/LoliSnatcher/config/"; - } else if(Platform.isIOS) { - result = "${await getExtDir()}/LoliSnatcher/config/"; + result = '${await getExtDir()}/LoliSnatcher/config/'; + } else if (Platform.isIOS) { + result = '${await getExtDir()}/LoliSnatcher/config/'; } return result; } static Future testSAFPersistence() async { - print("test saf persistence"); - String result = ""; - String safuri = "content://com.android.externalstorage.documents/tree/1206-2917%3ALolisnatcher"; + log('test saf persistence'); + String result = ''; + const String safuri = 'content://com.android.externalstorage.documents/tree/1206-2917%3ALolisnatcher'; try { - result = await platform.invokeMethod("testSAF",{"uri" : safuri}); - print("got saf result $result"); + result = await platform.invokeMethod('testSAF', {'uri': safuri}); + log('got saf result $result'); } catch (e) { - print(e); + log(e); } - Directory dir = Directory(result); + // final Directory dir = Directory(result); return result; } - static Future getDocumentsDir() async{ - String result = ""; - try{ - result = await platform.invokeMethod("getDocumentsPath"); - } catch(e){ - print(e); + static Future getDocumentsDir() async { + String result = ''; + try { + result = await platform.invokeMethod('getDocumentsPath'); + } catch (e) { + log(e); } return result; } - static Future getPicturesDir() async{ - String result = ""; - try{ - if (Platform.isAndroid){ + static Future getPicturesDir() async { + String result = ''; + try { + if (Platform.isAndroid) { result = "${await platform.invokeMethod("getPicturesPath")}/LoliSnatcher/"; // "${await getExtDir()}/Pictures/LoliSnatcher/"; - } else if (Platform.isLinux){ - result = "${await getExtDir()}/Pictures/LoliSnatcher/"; - } else if (Platform.isWindows){ - result = "${await getExtDir()}/LoliSnatcher/Pictures/"; - } else if(Platform.isIOS) { - result = "${await getExtDir()}/LoliSnatcher/Pictures/"; + } else if (Platform.isLinux) { + result = '${await getExtDir()}/Pictures/LoliSnatcher/'; + } else if (Platform.isWindows) { + result = '${await getExtDir()}/LoliSnatcher/Pictures/'; + } else if (Platform.isIOS) { + result = '${await getExtDir()}/LoliSnatcher/Pictures/'; } - } catch(e){ - print(e); + } catch (e) { + log(e); } return result; } - static Future getCacheDir() async{ - String result = ""; - try{ - if (Platform.isAndroid){ - result = await platform.invokeMethod("getCachePath") + "/"; - } else if (Platform.isLinux){ + static Future getCacheDir() async { + String result = ''; + try { + if (Platform.isAndroid) { + result = await platform.invokeMethod('getCachePath') + '/'; + } else if (Platform.isLinux) { result = '${await getExtDir()}/.loliSnatcher/cache/'; - } else if (Platform.isWindows){ + } else if (Platform.isWindows) { + result = '${await getExtDir()}/LoliSnatcher/cache/'; + } else if (Platform.isIOS) { result = '${await getExtDir()}/LoliSnatcher/cache/'; - } else if(Platform.isIOS) { - result = "${await getExtDir()}/LoliSnatcher/cache/"; } - } catch(e){ - print(e); + } catch (e) { + log(e); } return result; } - static Future loadShareTextIntent(String text) async{ - try{ - await platform.invokeMethod("shareText",{"text": text}); + static Future loadShareTextIntent(String text) async { + try { + await platform.invokeMethod('shareText', {'text': text}); return; - } catch(e){ - print(e); + } catch (e) { + log(e); return; } } - static Future loadShareFileIntent(String filePath, String mimeType) async{ - try{ - await platform.invokeMethod("shareFile", {"path": filePath, "mimeType": mimeType}); + static Future loadShareFileIntent(String filePath, String mimeType) async { + try { + await platform.invokeMethod('shareFile', {'path': filePath, 'mimeType': mimeType}); return; - // print('share closed'); - } catch(e){ - print(e); + // log('share closed'); + } catch (e) { + log(e); return; } } - static void disableSleep ({bool forceEnable = false}){ + static void disableSleep({bool forceEnable = false}) { final SettingsHandler settingsHandler = SettingsHandler.instance; - if (Platform.isAndroid && (settingsHandler.wakeLockEnabled || forceEnable)){ - platform.invokeMethod("disableSleep"); + if (Platform.isAndroid && (settingsHandler.wakeLockEnabled || forceEnable)) { + platform.invokeMethod('disableSleep'); } } - static void enableSleep (){ - if (Platform.isAndroid){ - platform.invokeMethod("enableSleep"); + static void enableSleep() { + if (Platform.isAndroid) { + platform.invokeMethod('enableSleep'); } } static Future emptyCache() async { try { - if (Platform.isAndroid){ - await platform.invokeMethod("emptyCache"); - } else if(Platform.isIOS) { + if (Platform.isAndroid) { + await platform.invokeMethod('emptyCache'); + } else if (Platform.isIOS) { // ??? - } else if (Platform.isLinux || Platform.isWindows){ - String cacheD = await getCacheDir(); - File cacheDir = File(cacheD); + } else if (Platform.isLinux || Platform.isWindows) { + final String cacheD = await getCacheDir(); + final File cacheDir = File(cacheD); // TODO parse through possible folder list and don't do recursive to exclude wrong path problems await cacheDir.delete(recursive: true); } - - } catch(e){ - print(e); + } catch (e) { + log(e); } return; } - static void deleteDB(SettingsHandler settingsHandler) async{ - if (Platform.isAndroid){ - File dbFile = File("${settingsHandler.path}store.db"); - print(settingsHandler.path); + static Future deleteDB(SettingsHandler settingsHandler) async { + if (Platform.isAndroid) { + final File dbFile = File('${settingsHandler.path}store.db'); + log(settingsHandler.path); await dbFile.delete(); } } @@ -369,25 +375,25 @@ class ServiceHandler{ } } - static void makeImmersive(){ - if (Platform.isAndroid){ - platform.invokeMethod("systemUIMode",{"mode":"immersive"}); + static void makeImmersive() { + if (Platform.isAndroid) { + platform.invokeMethod('systemUIMode', {'mode': 'immersive'}); } } - static void makeNormal(){ - if (Platform.isAndroid){ - platform.invokeMethod("systemUIMode",{"mode":"normal"}); + static void makeNormal() { + if (Platform.isAndroid) { + platform.invokeMethod('systemUIMode', {'mode': 'normal'}); } } static Future getIP() async { - String ip = ""; + String ip = ''; // TODO WIP - if (Platform.isAndroid){ - ip = await platform.invokeMethod("getIP"); + if (Platform.isAndroid) { + ip = await platform.invokeMethod('getIP'); } else { - var interfaces = await NetworkInterface.list(type: InternetAddressType.IPv4); + final interfaces = await NetworkInterface.list(type: InternetAddressType.IPv4); if (interfaces.isNotEmpty) { ip = interfaces[0].addresses[0].address; } @@ -396,19 +402,19 @@ class ServiceHandler{ } static Future> getIPList() async { - return await NetworkInterface.list(type: InternetAddressType.IPv4); + return NetworkInterface.list(type: InternetAddressType.IPv4); } static void setVolumeButtons(bool setActive) { - if (Platform.isAndroid){ - platform.invokeMethod("setVolumeButtons",{"setActive": setActive}); + if (Platform.isAndroid) { + platform.invokeMethod('setVolumeButtons', {'setActive': setActive}); } } - static void launchURL(String url){ - if(Platform.isAndroid){ - platform.invokeMethod("launchURL", {"url": url}); - } else if(Platform.isIOS) { + static void launchURL(String url) { + if (Platform.isAndroid) { + platform.invokeMethod('launchURL', {'url': url}); + } else if (Platform.isIOS) { // ??? } else if (Platform.isLinux) { Process.run('xdg-open', [url]); @@ -419,29 +425,42 @@ class ServiceHandler{ static Future makeVidThumb(String videoURL) async { Uint8List? thumbnail; - if (Platform.isAndroid){ - thumbnail = await platform.invokeMethod("makeVidThumb",{"videoURL" : videoURL}); + if (Platform.isAndroid) { + thumbnail = await platform.invokeMethod('makeVidThumb', {'videoURL': videoURL}); } return thumbnail; } - static Future writeImage(var imageData, String fileName, String mediaType, String fileExt, String? extPathOverride) async { + static Future writeImage( + dynamic imageData, + String fileName, + String mediaType, + String fileExt, + String? extPathOverride, + ) async { String? result; - try{ - result = await platform.invokeMethod("writeImage",{"imageData": imageData, "fileName": fileName, "mediaType": mediaType, "fileExt": fileExt, "extPathOverride":extPathOverride,}); - } catch(e){ - print(e); + try { + result = await platform.invokeMethod('writeImage', { + 'imageData': imageData, + 'fileName': fileName, + 'mediaType': mediaType, + 'fileExt': fileExt, + 'extPathOverride': extPathOverride, + }); + } catch (e) { + log(e); } return result; } + // ignore: avoid_void_async static void vibrate({ bool flutterWay = false, int duration = 10, int amplitude = -1, }) async { if (Platform.isAndroid || Platform.isIOS) { - if(flutterWay) { + if (flutterWay) { unawaited(HapticFeedback.vibrate()); } else { if (await Vibration.hasVibrator() ?? false) { @@ -450,5 +469,4 @@ class ServiceHandler{ } } } - } diff --git a/lib/src/handlers/settings_handler.dart b/lib/src/handlers/settings_handler.dart index bf9f0db4..25fc571d 100644 --- a/lib/src/handlers/settings_handler.dart +++ b/lib/src/handlers/settings_handler.dart @@ -37,9 +37,9 @@ class SettingsHandler extends GetxController { // service vars RxBool isInit = false.obs; - String cachePath = ""; - String path = ""; - String boorusPath = ""; + String cachePath = ''; + String path = ''; + String boorusPath = ''; Rx updateInfo = Rxn(null); @@ -63,21 +63,21 @@ class SettingsHandler extends GetxController { //////////////////////////////////////////////////// // saveable settings vars - String defTags = "rating:safe"; - String previewMode = "Sample"; - String videoCacheMode = "Stream"; - String prefBooru = ""; - String previewDisplay = "Square"; - String galleryMode = "Full Res"; - String shareAction = "Ask"; + String defTags = 'rating:safe'; + String previewMode = 'Sample'; + String videoCacheMode = 'Stream'; + String prefBooru = ''; + String previewDisplay = 'Square'; + String galleryMode = 'Full Res'; + String shareAction = 'Ask'; Rx appMode = AppMode.defaultValue.obs; Rx handSide = HandSide.defaultValue.obs; String galleryBarPosition = 'Top'; String galleryScrollDirection = 'Horizontal'; - String extPathOverride = ""; - String drawerMascotPathOverride = ""; - String zoomButtonPosition = "Right"; - String changePageButtonsPosition = (Platform.isWindows || Platform.isLinux) ? "Right" : "Disabled"; + String extPathOverride = ''; + String drawerMascotPathOverride = ''; + String zoomButtonPosition = 'Right'; + String changePageButtonsPosition = (Platform.isWindows || Platform.isLinux) ? 'Right' : 'Disabled'; String lastSyncIp = ''; String lastSyncPort = ''; // TODO move it to boorus themselves to have different user agents for different boorus? @@ -95,7 +95,7 @@ class SettingsHandler extends GetxController { int galleryAutoScrollTime = 4000; int cacheSize = 3; - double mousewheelScrollSpeed = 10.0; + double mousewheelScrollSpeed = 10; int currentColumnCount(BuildContext context) { return MediaQuery.of(context).orientation == Orientation.portrait ? portraitColumns : landscapeColumns; @@ -104,22 +104,22 @@ class SettingsHandler extends GetxController { Duration cacheDuration = Duration.zero; List> buttonList = [ - ["autoscroll", "AutoScroll"], - ["snatch", "Save"], - ["favourite", "Favourite"], - ["info", "Display Info"], - ["share", "Share"], - ["open", "Open in Browser"], - ["reloadnoscale", "Reload w/out scaling"] + ['autoscroll', 'AutoScroll'], + ['snatch', 'Save'], + ['favourite', 'Favourite'], + ['info', 'Display Info'], + ['share', 'Share'], + ['open', 'Open in Browser'], + ['reloadnoscale', 'Reload w/out scaling'] ]; List> buttonOrder = [ - ["autoscroll", "AutoScroll"], - ["snatch", "Save"], - ["favourite", "Favourite"], - ["info", "Display Info"], - ["share", "Share"], - ["open", "Open in Browser"], - ["reloadnoscale", "Reload w/out scaling"] + ['autoscroll', 'AutoScroll'], + ['snatch', 'Save'], + ['favourite', 'Favourite'], + ['info', 'Display Info'], + ['share', 'Share'], + ['open', 'Open in Browser'], + ['reloadnoscale', 'Reload w/out scaling'] ]; bool jsonWrite = false; @@ -146,12 +146,12 @@ class SettingsHandler extends GetxController { // themes wip Rx theme = ThemeItem( - name: "Pink", + name: 'Pink', primary: Colors.pink[200], accent: Colors.pink[600], ).obs ..listen((ThemeItem theme) { - print('newTheme ${theme.name} ${theme.primary}'); + // print('newTheme ${theme.name} ${theme.primary}'); }); Rx customPrimaryColor = Colors.pink[200].obs; @@ -209,261 +209,261 @@ class SettingsHandler extends GetxController { // TODO move it in another file? Map> map = { // stringFromList - "previewMode": { - "type": "stringFromList", - "default": "Sample", - "options": ["Sample", "Thumbnail"], + 'previewMode': { + 'type': 'stringFromList', + 'default': 'Sample', + 'options': ['Sample', 'Thumbnail'], }, - "previewDisplay": { - "type": "stringFromList", - "default": "Square", - "options": ["Square", "Rectangle", "Staggered"], + 'previewDisplay': { + 'type': 'stringFromList', + 'default': 'Square', + 'options': ['Square', 'Rectangle', 'Staggered'], }, - "shareAction": { - "type": "stringFromList", - "default": "Ask", - "options": ["Ask", "Post URL", "File URL", "File", "Hydrus"], + 'shareAction': { + 'type': 'stringFromList', + 'default': 'Ask', + 'options': ['Ask', 'Post URL', 'File URL', 'File', 'Hydrus'], }, - "videoCacheMode": { - "type": "stringFromList", - "default": "Stream", - "options": ["Stream", "Cache", "Stream+Cache"], + 'videoCacheMode': { + 'type': 'stringFromList', + 'default': 'Stream', + 'options': ['Stream', 'Cache', 'Stream+Cache'], }, - "galleryMode": { - "type": "stringFromList", - "default": "Full Res", - "options": ["Sample", "Full Res"], + 'galleryMode': { + 'type': 'stringFromList', + 'default': 'Full Res', + 'options': ['Sample', 'Full Res'], }, - "galleryScrollDirection": { - "type": "stringFromList", - "default": "Horizontal", - "options": ["Horizontal", "Vertical"], + 'galleryScrollDirection': { + 'type': 'stringFromList', + 'default': 'Horizontal', + 'options': ['Horizontal', 'Vertical'], }, - "galleryBarPosition": { - "type": "stringFromList", - "default": "Top", - "options": ["Top", "Bottom"], + 'galleryBarPosition': { + 'type': 'stringFromList', + 'default': 'Top', + 'options': ['Top', 'Bottom'], }, - "zoomButtonPosition": { - "type": "stringFromList", - "default": "Right", - "options": ["Disabled", "Left", "Right"], + 'zoomButtonPosition': { + 'type': 'stringFromList', + 'default': 'Right', + 'options': ['Disabled', 'Left', 'Right'], }, - "changePageButtonsPosition": { - "type": "stringFromList", - "default": (Platform.isWindows || Platform.isLinux) ? "Right" : "Disabled", - "options": ["Disabled", "Left", "Right"], + 'changePageButtonsPosition': { + 'type': 'stringFromList', + 'default': (Platform.isWindows || Platform.isLinux) ? 'Right' : 'Disabled', + 'options': ['Disabled', 'Left', 'Right'], }, // string - "defTags": { - "type": "string", - "default": "rating:safe", + 'defTags': { + 'type': 'string', + 'default': 'rating:safe', }, - "prefBooru": { - "type": "string", - "default": "", + 'prefBooru': { + 'type': 'string', + 'default': '', }, - "extPathOverride": { - "type": "string", - "default": "", + 'extPathOverride': { + 'type': 'string', + 'default': '', }, - "drawerMascotPathOverride": { - "type": "string", - "default": "", + 'drawerMascotPathOverride': { + 'type': 'string', + 'default': '', }, - "lastSyncIp": { - "type": "string", - "default": "", + 'lastSyncIp': { + 'type': 'string', + 'default': '', }, - "lastSyncPort": { - "type": "string", - "default": "", + 'lastSyncPort': { + 'type': 'string', + 'default': '', }, - "customUserAgent": { - "type": "string", - "default": "", + 'customUserAgent': { + 'type': 'string', + 'default': '', }, // stringList - "hatedTags": { - "type": "stringList", - "default": [], + 'hatedTags': { + 'type': 'stringList', + 'default': [], }, - "lovedTags": { - "type": "stringList", - "default": [], + 'lovedTags': { + 'type': 'stringList', + 'default': [], }, - "enabledLogTypes": { - "type": "logTypesList", - "default": [], + 'enabledLogTypes': { + 'type': 'logTypesList', + 'default': [], }, // int - "limit": { - "type": "int", - "default": Constants.defaultItemLimit, - "upperLimit": 100, - "lowerLimit": 10, + 'limit': { + 'type': 'int', + 'default': Constants.defaultItemLimit, + 'upperLimit': 100, + 'lowerLimit': 10, }, - "portraitColumns": { - "type": "int", - "default": 2, - "upperLimit": 100, - "lowerLimit": 1, + 'portraitColumns': { + 'type': 'int', + 'default': 2, + 'upperLimit': 100, + 'lowerLimit': 1, }, - "landscapeColumns": { - "type": "int", - "default": 4, - "upperLimit": 100, - "lowerLimit": 1, + 'landscapeColumns': { + 'type': 'int', + 'default': 4, + 'upperLimit': 100, + 'lowerLimit': 1, }, - "preloadCount": { - "type": "int", - "default": 1, - "upperLimit": 3, - "lowerLimit": 0, + 'preloadCount': { + 'type': 'int', + 'default': 1, + 'upperLimit': 3, + 'lowerLimit': 0, }, - "snatchCooldown": { - "type": "int", - "default": 250, - "upperLimit": 10000, - "lowerLimit": 0, + 'snatchCooldown': { + 'type': 'int', + 'default': 250, + 'upperLimit': 10000, + 'lowerLimit': 0, }, - "volumeButtonsScrollSpeed": { - "type": "int", - "default": 200, - "upperLimit": 1000000, - "lowerLimit": 0, + 'volumeButtonsScrollSpeed': { + 'type': 'int', + 'default': 200, + 'upperLimit': 1000000, + 'lowerLimit': 0, }, - "mousewheelScrollSpeed": { - "type": "double", - "default": 10.0, - "upperLimit": 20.0, - "lowerLimit": 0.1, + 'mousewheelScrollSpeed': { + 'type': 'double', + 'default': 10.0, + 'upperLimit': 20.0, + 'lowerLimit': 0.1, }, - "galleryAutoScrollTime": { - "type": "int", - "default": 4000, - "upperLimit": 100000, - "lowerLimit": 100, + 'galleryAutoScrollTime': { + 'type': 'int', + 'default': 4000, + 'upperLimit': 100000, + 'lowerLimit': 100, }, - "cacheSize": { - "type": "int", - "default": 3, - "upperLimit": 10, - "lowerLimit": 0, + 'cacheSize': { + 'type': 'int', + 'default': 3, + 'upperLimit': 10, + 'lowerLimit': 0, }, // double // bool - "jsonWrite": { - "type": "bool", - "default": false, + 'jsonWrite': { + 'type': 'bool', + 'default': false, }, - "autoPlayEnabled": { - "type": "bool", - "default": true, + 'autoPlayEnabled': { + 'type': 'bool', + 'default': true, }, - "loadingGif": { - "type": "bool", - "default": false, + 'loadingGif': { + 'type': 'bool', + 'default': false, }, - "thumbnailCache": { - "type": "bool", - "default": true, + 'thumbnailCache': { + 'type': 'bool', + 'default': true, }, - "mediaCache": { - "type": "bool", - "default": false, + 'mediaCache': { + 'type': 'bool', + 'default': false, }, - "autoHideImageBar": { - "type": "bool", - "default": false, + 'autoHideImageBar': { + 'type': 'bool', + 'default': false, }, - "dbEnabled": { - "type": "bool", - "default": true, + 'dbEnabled': { + 'type': 'bool', + 'default': true, }, - "indexesEnabled": { - "type": "bool", - "default": false, + 'indexesEnabled': { + 'type': 'bool', + 'default': false, }, - "searchHistoryEnabled": { - "type": "bool", - "default": true, + 'searchHistoryEnabled': { + 'type': 'bool', + 'default': true, }, - "filterHated": { - "type": "bool", - "default": false, + 'filterHated': { + 'type': 'bool', + 'default': false, }, - "filterFavourites": { - "type": "bool", - "default": false, + 'filterFavourites': { + 'type': 'bool', + 'default': false, }, - "useVolumeButtonsForScroll": { - "type": "bool", - "default": false, + 'useVolumeButtonsForScroll': { + 'type': 'bool', + 'default': false, }, - "shitDevice": { - "type": "bool", - "default": false, + 'shitDevice': { + 'type': 'bool', + 'default': false, }, - "disableVideo": { - "type": "bool", - "default": false, + 'disableVideo': { + 'type': 'bool', + 'default': false, }, - "enableDrawerMascot": { - "type": "bool", - "default": false, + 'enableDrawerMascot': { + 'type': 'bool', + 'default': false, }, - "allowSelfSignedCerts": { - "type": "bool", - "default": false, + 'allowSelfSignedCerts': { + 'type': 'bool', + 'default': false, }, - "disableImageScaling": { - "type": "bool", - "default": false, + 'disableImageScaling': { + 'type': 'bool', + 'default': false, }, - "gifsAsThumbnails": { - "type": "bool", - "default": false, + 'gifsAsThumbnails': { + 'type': 'bool', + 'default': false, }, - "desktopListsDrag": { - "type": "bool", - "default": false, + 'desktopListsDrag': { + 'type': 'bool', + 'default': false, }, - "wakeLockEnabled": { - "type": "bool", - "default": true, + 'wakeLockEnabled': { + 'type': 'bool', + 'default': true, }, - "tagTypeFetchEnabled": { - "type": "bool", - "default": true, + 'tagTypeFetchEnabled': { + 'type': 'bool', + 'default': true, }, - "downloadNotifications": { - "type": "bool", - "default": true, + 'downloadNotifications': { + 'type': 'bool', + 'default': true, }, // other - "buttonOrder": { - "type": "other", - "default": >[ - ["autoscroll", "AutoScroll"], - ["snatch", "Save"], - ["favourite", "Favourite"], - ["info", "Display Info"], - ["share", "Share"], - ["open", "Open in Browser"], - ["reloadnoscale", "Reload w/out scaling"] + 'buttonOrder': { + 'type': 'other', + 'default': >[ + ['autoscroll', 'AutoScroll'], + ['snatch', 'Save'], + ['favourite', 'Favourite'], + ['info', 'Display Info'], + ['share', 'Share'], + ['open', 'Open in Browser'], + ['reloadnoscale', 'Reload w/out scaling'] ], }, - "cacheDuration": { - "type": "duration", - "default": Duration.zero, - "options": >[ + 'cacheDuration': { + 'type': 'duration', + 'default': Duration.zero, + 'options': >[ {'label': 'Never', 'value': Duration.zero}, {'label': '30 minutes', 'value': const Duration(minutes: 30)}, {'label': '1 hour', 'value': const Duration(hours: 1)}, @@ -477,58 +477,58 @@ class SettingsHandler extends GetxController { }, // theme - "appMode": { - "type": "appMode", - "default": AppMode.defaultValue, - "options": AppMode.values, + 'appMode': { + 'type': 'appMode', + 'default': AppMode.defaultValue, + 'options': AppMode.values, }, - "handSide": { - "type": "handSide", - "default": HandSide.defaultValue, - "options": HandSide.values, + 'handSide': { + 'type': 'handSide', + 'default': HandSide.defaultValue, + 'options': HandSide.values, }, - "theme": { - "type": "theme", - "default": ThemeItem(name: "Pink", primary: Colors.pink[200], accent: Colors.pink[600]), - "options": [ - ThemeItem(name: "Pink", primary: Colors.pink[200], accent: Colors.pink[600]), - ThemeItem(name: "Purple", primary: Colors.deepPurple[600], accent: Colors.deepPurple[800]), - ThemeItem(name: "Blue", primary: Colors.lightBlue, accent: Colors.lightBlue[600]), - ThemeItem(name: "Teal", primary: Colors.teal, accent: Colors.teal[600]), - ThemeItem(name: "Red", primary: Colors.red[700], accent: Colors.red[800]), - ThemeItem(name: "Green", primary: Colors.green, accent: Colors.green[700]), - ThemeItem(name: "Custom", primary: null, accent: null), + 'theme': { + 'type': 'theme', + 'default': ThemeItem(name: 'Pink', primary: Colors.pink[200], accent: Colors.pink[600]), + 'options': [ + ThemeItem(name: 'Pink', primary: Colors.pink[200], accent: Colors.pink[600]), + ThemeItem(name: 'Purple', primary: Colors.deepPurple[600], accent: Colors.deepPurple[800]), + ThemeItem(name: 'Blue', primary: Colors.lightBlue, accent: Colors.lightBlue[600]), + ThemeItem(name: 'Teal', primary: Colors.teal, accent: Colors.teal[600]), + ThemeItem(name: 'Red', primary: Colors.red[700], accent: Colors.red[800]), + ThemeItem(name: 'Green', primary: Colors.green, accent: Colors.green[700]), + ThemeItem(name: 'Custom', primary: null, accent: null), ] }, - "themeMode": { - "type": "themeMode", - "default": ThemeMode.dark, - "options": ThemeMode.values, + 'themeMode': { + 'type': 'themeMode', + 'default': ThemeMode.dark, + 'options': ThemeMode.values, }, - "useMaterial3": { - "type": "rxbool", - "default": false.obs, + 'useMaterial3': { + 'type': 'rxbool', + 'default': false.obs, }, - "useDynamicColor": { - "type": "rxbool", - "default": false.obs, + 'useDynamicColor': { + 'type': 'rxbool', + 'default': false.obs, }, - "isAmoled": { - "type": "rxbool", - "default": false.obs, + 'isAmoled': { + 'type': 'rxbool', + 'default': false.obs, }, - "customPrimaryColor": { - "type": "rxcolor", - "default": Colors.pink[200], + 'customPrimaryColor': { + 'type': 'rxcolor', + 'default': Colors.pink[200], }, - "customAccentColor": { - "type": "rxcolor", - "default": Colors.pink[600], + 'customAccentColor': { + 'type': 'rxcolor', + 'default': Colors.pink[600], }, }; dynamic validateValue(String name, dynamic value, {bool toJSON = false}) { - Map? settingParams = map[name]; + final Map? settingParams = map[name]; if (toJSON) { value = getByString(name); @@ -543,32 +543,32 @@ class SettingsHandler extends GetxController { } try { - switch (settingParams["type"]) { + switch (settingParams['type']) { case 'stringFromList': - final String validValue = List.from(settingParams["options"]!).firstWhere((el) => el == value, orElse: () => ''); + final String validValue = List.from(settingParams['options']!).firstWhere((el) => el == value, orElse: () => ''); if (validValue != '') { return validValue; } else { - return settingParams["default"]; + return settingParams['default']; } case 'string': if (value is! String) { - throw 'value "$value" for $name is not a String'; + throw Exception('value "$value" for $name is not a String'); } else { return value; } case 'int': - int? parse = (value is String) ? int.tryParse(value) : (value is int ? value : null); + final int? parse = (value is String) ? int.tryParse(value) : (value is int ? value : null); if (parse == null) { - throw 'value "$value" of type ${value.runtimeType} for $name is not an int'; - } else if (parse < settingParams["lowerLimit"] || parse > settingParams["upperLimit"]) { + throw Exception('value "$value" of type ${value.runtimeType} for $name is not an int'); + } else if (parse < settingParams['lowerLimit'] || parse > settingParams['upperLimit']) { if (toJSON) { // force default value when not passing validation when saving - setByString(name, settingParams["default"]); + setByString(name, settingParams['default']); } - return settingParams["default"]; + return settingParams['default']; } else { return parse; } @@ -576,9 +576,9 @@ class SettingsHandler extends GetxController { case 'bool': if (value is! bool) { if (value is String && (value == 'true' || value == 'false')) { - return value == 'true' ? true : false; + return value == 'true'; } else { - throw 'value "$value" for $name is not a bool'; + throw Exception('value "$value" for $name is not a bool'); } } else { return value; @@ -595,7 +595,7 @@ class SettingsHandler extends GetxController { } else if (value is bool) { return value.obs; } else { - throw 'value "$value" for $name is not a rxbool'; + throw Exception('value "$value" for $name is not a rxbool'); } } @@ -608,7 +608,7 @@ class SettingsHandler extends GetxController { // string to rxobject return AppMode.fromString(value); } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -621,7 +621,7 @@ class SettingsHandler extends GetxController { // string to rxobject return HandSide.fromString(value); } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -632,9 +632,9 @@ class SettingsHandler extends GetxController { } else { if (value is List) { // list to list - return List.from(value).map((el) => LogTypes.fromString(el)).toList(); + return List.from(value).map(LogTypes.fromString).toList(); } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -646,10 +646,10 @@ class SettingsHandler extends GetxController { if (value is String) { // string to rxobject final ThemeItem findTheme = - List.from(settingParams["options"]!).firstWhere((el) => el.name == value, orElse: () => settingParams["default"]); + List.from(settingParams['options']!).firstWhere((el) => el.name == value, orElse: () => settingParams['default']); return findTheme; } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -666,10 +666,10 @@ class SettingsHandler extends GetxController { return findMode[0]; } else { // if not theme mode with given name - return settingParams["default"]; + return settingParams['default']; } } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -682,7 +682,7 @@ class SettingsHandler extends GetxController { if (value is int) { return Color(value); } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -696,7 +696,7 @@ class SettingsHandler extends GetxController { // int to Duration return Duration(seconds: value); } else { - return settingParams["default"]; + return settingParams['default']; } } @@ -706,14 +706,18 @@ class SettingsHandler extends GetxController { } } catch (err) { // return default value on exceptions - Logger.Inst().log('value validation error: $err', "SettingsHandler", "validateValue", null); - return settingParams["default"]; + Logger.Inst().log('value validation error: $err', 'SettingsHandler', 'validateValue', null); + return settingParams['default']; } } Future loadSettings() async { - if (path == "") await setConfigDir(); - if (cachePath == "") cachePath = await ServiceHandler.getCacheDir(); + if (path == '') { + await setConfigDir(); + } + if (cachePath == '') { + cachePath = await ServiceHandler.getCacheDir(); + } if (await checkForSettings()) { await loadSettingsJson(); @@ -731,13 +735,13 @@ class SettingsHandler extends GetxController { return true; } - Future checkForSettings() async { - final File settingsFile = File("${path}settings.json"); - return await settingsFile.exists(); + Future checkForSettings() { + final File settingsFile = File('${path}settings.json'); + return settingsFile.exists(); } Future loadSettingsJson() async { - final File settingsFile = File("${path}settings.json"); + final File settingsFile = File('${path}settings.json'); final String settings = await settingsFile.readAsString(); // print('loadJSON $settings'); await loadFromJSON(settings, true); @@ -1072,74 +1076,74 @@ class SettingsHandler extends GetxController { } Map toJson() { - Map json = { - "defTags": validateValue("defTags", null, toJSON: true), - "previewMode": validateValue("previewMode", null, toJSON: true), - "videoCacheMode": validateValue("videoCacheMode", null, toJSON: true), - "previewDisplay": validateValue("previewDisplay", null, toJSON: true), - "galleryMode": validateValue("galleryMode", null, toJSON: true), - "shareAction": validateValue("shareAction", null, toJSON: true), - "limit": validateValue("limit", null, toJSON: true), - "portraitColumns": validateValue("portraitColumns", null, toJSON: true), - "landscapeColumns": validateValue("landscapeColumns", null, toJSON: true), - "preloadCount": validateValue("preloadCount", null, toJSON: true), - "snatchCooldown": validateValue("snatchCooldown", null, toJSON: true), - "galleryBarPosition": validateValue("galleryBarPosition", null, toJSON: true), - "galleryScrollDirection": validateValue("galleryScrollDirection", null, toJSON: true), - "jsonWrite": validateValue("jsonWrite", null, toJSON: true), - "autoPlayEnabled": validateValue("autoPlayEnabled", null, toJSON: true), - "loadingGif": validateValue("loadingGif", null, toJSON: true), - "thumbnailCache": validateValue("thumbnailCache", null, toJSON: true), - "mediaCache": validateValue("mediaCache", null, toJSON: true), - "autoHideImageBar": validateValue("autoHideImageBar", null, toJSON: true), - "dbEnabled": validateValue("dbEnabled", null, toJSON: true), - "indexesEnabled": validateValue("indexesEnabled", null, toJSON: true), - "searchHistoryEnabled": validateValue("searchHistoryEnabled", null, toJSON: true), - "filterHated": validateValue("filterHated", null, toJSON: true), - "filterFavourites": validateValue("filterFavourites", null, toJSON: true), - "useVolumeButtonsForScroll": validateValue("useVolumeButtonsForScroll", null, toJSON: true), - "volumeButtonsScrollSpeed": validateValue("volumeButtonsScrollSpeed", null, toJSON: true), - "mousewheelScrollSpeed": validateValue("mousewheelScrollSpeed", null, toJSON: true), - "disableVideo": validateValue("disableVideo", null, toJSON: true), - "shitDevice": validateValue("shitDevice", null, toJSON: true), - "galleryAutoScrollTime": validateValue("galleryAutoScrollTime", null, toJSON: true), - "zoomButtonPosition": validateValue("zoomButtonPosition", null, toJSON: true), - "changePageButtonsPosition": validateValue("changePageButtonsPosition", null, toJSON: true), - "disableImageScaling": validateValue("disableImageScaling", null, toJSON: true), - "gifsAsThumbnails": validateValue("gifsAsThumbnails", null, toJSON: true), - "desktopListsDrag": validateValue("desktopListsDrag", null, toJSON: true), - "cacheDuration": validateValue("cacheDuration", null, toJSON: true), - "cacheSize": validateValue("cacheSize", null, toJSON: true), - "allowSelfSignedCerts": validateValue("allowSelfSignedCerts", null, toJSON: true), - "enabledLogTypes": validateValue("enabledLogTypes", null, toJSON: true), - "wakeLockEnabled": validateValue("wakeLockEnabled", null, toJSON: true), - "tagTypeFetchEnabled": validateValue("tagTypeFetchEnabled", null, toJSON: true), - "downloadNotifications": validateValue("downloadNotifications", null, toJSON: true), + final Map json = { + 'defTags': validateValue('defTags', null, toJSON: true), + 'previewMode': validateValue('previewMode', null, toJSON: true), + 'videoCacheMode': validateValue('videoCacheMode', null, toJSON: true), + 'previewDisplay': validateValue('previewDisplay', null, toJSON: true), + 'galleryMode': validateValue('galleryMode', null, toJSON: true), + 'shareAction': validateValue('shareAction', null, toJSON: true), + 'limit': validateValue('limit', null, toJSON: true), + 'portraitColumns': validateValue('portraitColumns', null, toJSON: true), + 'landscapeColumns': validateValue('landscapeColumns', null, toJSON: true), + 'preloadCount': validateValue('preloadCount', null, toJSON: true), + 'snatchCooldown': validateValue('snatchCooldown', null, toJSON: true), + 'galleryBarPosition': validateValue('galleryBarPosition', null, toJSON: true), + 'galleryScrollDirection': validateValue('galleryScrollDirection', null, toJSON: true), + 'jsonWrite': validateValue('jsonWrite', null, toJSON: true), + 'autoPlayEnabled': validateValue('autoPlayEnabled', null, toJSON: true), + 'loadingGif': validateValue('loadingGif', null, toJSON: true), + 'thumbnailCache': validateValue('thumbnailCache', null, toJSON: true), + 'mediaCache': validateValue('mediaCache', null, toJSON: true), + 'autoHideImageBar': validateValue('autoHideImageBar', null, toJSON: true), + 'dbEnabled': validateValue('dbEnabled', null, toJSON: true), + 'indexesEnabled': validateValue('indexesEnabled', null, toJSON: true), + 'searchHistoryEnabled': validateValue('searchHistoryEnabled', null, toJSON: true), + 'filterHated': validateValue('filterHated', null, toJSON: true), + 'filterFavourites': validateValue('filterFavourites', null, toJSON: true), + 'useVolumeButtonsForScroll': validateValue('useVolumeButtonsForScroll', null, toJSON: true), + 'volumeButtonsScrollSpeed': validateValue('volumeButtonsScrollSpeed', null, toJSON: true), + 'mousewheelScrollSpeed': validateValue('mousewheelScrollSpeed', null, toJSON: true), + 'disableVideo': validateValue('disableVideo', null, toJSON: true), + 'shitDevice': validateValue('shitDevice', null, toJSON: true), + 'galleryAutoScrollTime': validateValue('galleryAutoScrollTime', null, toJSON: true), + 'zoomButtonPosition': validateValue('zoomButtonPosition', null, toJSON: true), + 'changePageButtonsPosition': validateValue('changePageButtonsPosition', null, toJSON: true), + 'disableImageScaling': validateValue('disableImageScaling', null, toJSON: true), + 'gifsAsThumbnails': validateValue('gifsAsThumbnails', null, toJSON: true), + 'desktopListsDrag': validateValue('desktopListsDrag', null, toJSON: true), + 'cacheDuration': validateValue('cacheDuration', null, toJSON: true), + 'cacheSize': validateValue('cacheSize', null, toJSON: true), + 'allowSelfSignedCerts': validateValue('allowSelfSignedCerts', null, toJSON: true), + 'enabledLogTypes': validateValue('enabledLogTypes', null, toJSON: true), + 'wakeLockEnabled': validateValue('wakeLockEnabled', null, toJSON: true), + 'tagTypeFetchEnabled': validateValue('tagTypeFetchEnabled', null, toJSON: true), + 'downloadNotifications': validateValue('downloadNotifications', null, toJSON: true), //TODO - "buttonOrder": buttonOrder.map((e) => e[0]).toList(), - "hatedTags": cleanTagsList(hatedTags), - "lovedTags": cleanTagsList(lovedTags), - - "prefBooru": validateValue("prefBooru", null, toJSON: true), - "appMode": validateValue("appMode", null, toJSON: true), - "handSide": validateValue("handSide", null, toJSON: true), - "extPathOverride": validateValue("extPathOverride", null, toJSON: true), - "lastSyncIp": validateValue("lastSyncIp", null, toJSON: true), - "lastSyncPort": validateValue("lastSyncPort", null, toJSON: true), - "customUserAgent": validateValue("customUserAgent", null, toJSON: true), - - "theme": validateValue("theme", null, toJSON: true), - "themeMode": validateValue("themeMode", null, toJSON: true), - "useMaterial3": validateValue("useMaterial3", null, toJSON: true), - "useDynamicColor": validateValue("useDynamicColor", null, toJSON: true), - "isAmoled": validateValue("isAmoled", null, toJSON: true), - "enableDrawerMascot": validateValue("enableDrawerMascot", null, toJSON: true), - "drawerMascotPathOverride": validateValue("drawerMascotPathOverride", null, toJSON: true), - "customPrimaryColor": validateValue("customPrimaryColor", null, toJSON: true), - "customAccentColor": validateValue("customAccentColor", null, toJSON: true), - "version": Constants.appVersion, - "build": Constants.appBuildNumber, + 'buttonOrder': buttonOrder.map((e) => e[0]).toList(), + 'hatedTags': cleanTagsList(hatedTags), + 'lovedTags': cleanTagsList(lovedTags), + + 'prefBooru': validateValue('prefBooru', null, toJSON: true), + 'appMode': validateValue('appMode', null, toJSON: true), + 'handSide': validateValue('handSide', null, toJSON: true), + 'extPathOverride': validateValue('extPathOverride', null, toJSON: true), + 'lastSyncIp': validateValue('lastSyncIp', null, toJSON: true), + 'lastSyncPort': validateValue('lastSyncPort', null, toJSON: true), + 'customUserAgent': validateValue('customUserAgent', null, toJSON: true), + + 'theme': validateValue('theme', null, toJSON: true), + 'themeMode': validateValue('themeMode', null, toJSON: true), + 'useMaterial3': validateValue('useMaterial3', null, toJSON: true), + 'useDynamicColor': validateValue('useDynamicColor', null, toJSON: true), + 'isAmoled': validateValue('isAmoled', null, toJSON: true), + 'enableDrawerMascot': validateValue('enableDrawerMascot', null, toJSON: true), + 'drawerMascotPathOverride': validateValue('drawerMascotPathOverride', null, toJSON: true), + 'customPrimaryColor': validateValue('customPrimaryColor', null, toJSON: true), + 'customAccentColor': validateValue('customAccentColor', null, toJSON: true), + 'version': Constants.appVersion, + 'build': Constants.appBuildNumber, }; // print('JSON $json'); @@ -1158,7 +1162,7 @@ class SettingsHandler extends GetxController { // (don't allow user to exit the page until the value is correct? or just set to default (current behaviour)? mix of both?) try { - dynamic tempBtnOrder = json["buttonOrder"]; + dynamic tempBtnOrder = json['buttonOrder']; if (tempBtnOrder is List) { // print('btnorder is a list'); } else if (tempBtnOrder is String) { @@ -1168,9 +1172,9 @@ class SettingsHandler extends GetxController { // print('btnorder is a ${tempBtnOrder.runtimeType} type'); tempBtnOrder = []; } - List> btnOrder = List.from(tempBtnOrder) + final List> btnOrder = List.from(tempBtnOrder) .map((bstr) { - List button = buttonList.singleWhere((el) => el[0] == bstr, orElse: () => ['null', 'null']); + final List button = buttonList.singleWhere((el) => el[0] == bstr, orElse: () => ['null', 'null']); return button; }) .where((el) => el[0] != 'null') @@ -1186,7 +1190,7 @@ class SettingsHandler extends GetxController { } try { - dynamic tempHatedTags = json["hatedTags"]; + dynamic tempHatedTags = json['hatedTags']; if (tempHatedTags is List) { // print('hatedTags is a list'); } else if (tempHatedTags is String) { @@ -1196,7 +1200,7 @@ class SettingsHandler extends GetxController { // print('hatedTags is a ${tempHatedTags.runtimeType} type'); tempHatedTags = []; } - List hateTags = List.from(tempHatedTags); + final List hateTags = List.from(tempHatedTags); for (int i = 0; i < hateTags.length; i++) { if (!hatedTags.contains(hateTags.elementAt(i))) { hatedTags.add(hateTags.elementAt(i)); @@ -1207,7 +1211,7 @@ class SettingsHandler extends GetxController { } try { - dynamic tempLovedTags = json["lovedTags"]; + dynamic tempLovedTags = json['lovedTags']; if (tempLovedTags is List) { // print('lovedTags is a list'); } else if (tempLovedTags is String) { @@ -1217,7 +1221,7 @@ class SettingsHandler extends GetxController { // print('lovedTags is a ${tempLovedTags.runtimeType} type'); tempLovedTags = []; } - List loveTags = List.from(tempLovedTags); + final List loveTags = List.from(tempLovedTags); for (int i = 0; i < loveTags.length; i++) { if (!lovedTags.contains(loveTags.elementAt(i))) { lovedTags.add(loveTags.elementAt(i)); @@ -1227,8 +1231,8 @@ class SettingsHandler extends GetxController { Logger.Inst().log('Failed to parse loved tags $e', 'SettingsHandler', 'loadFromJSON', LogTypes.exception); } - List leftoverKeys = json.keys.where((element) => !['buttonOrder', 'hatedTags', 'lovedTags'].contains(element)).toList(); - for (String key in leftoverKeys) { + final List leftoverKeys = json.keys.where((element) => !['buttonOrder', 'hatedTags', 'lovedTags'].contains(element)).toList(); + for (final String key in leftoverKeys) { // TODO something causes rare exception which causes settings to reset try { setByString(key, json[key]); @@ -1254,9 +1258,11 @@ class SettingsHandler extends GetxController { Future saveSettings({required bool restate}) async { await getPerms(); - if (path == "") await setConfigDir(); + if (path == '') { + await setConfigDir(); + } await Directory(path).create(recursive: true); - final File settingsFile = File("${path}settings.json"); + final File settingsFile = File('${path}settings.json'); final writer = settingsFile.openWrite(); writer.write(jsonEncode(toJson())); await writer.close(); @@ -1268,9 +1274,11 @@ class SettingsHandler extends GetxController { } Future loadBoorus() async { - List tempList = []; + final List tempList = []; try { - if (path == "") await setConfigDir(); + if (path == '') { + await setConfigDir(); + } final Directory directory = Directory(boorusPath); List files = []; @@ -1280,7 +1288,7 @@ class SettingsHandler extends GetxController { if (files.isNotEmpty) { for (int i = 0; i < files.length; i++) { - if (files[i].path.contains(".json")) { + if (files[i].path.contains('.json')) { // && files[i].path != 'settings.json' // print(files[i].toString()); final File booruFile = files[i] as File; @@ -1300,7 +1308,7 @@ class SettingsHandler extends GetxController { } if (dbEnabled && tempList.isNotEmpty) { - tempList.add(Booru("Favourites", BooruType.Favourites, "", "", "")); + tempList.add(Booru('Favourites', BooruType.Favourites, '', '', '')); } } catch (e) { Logger.Inst().log('Failed to load boorus $e', 'SettingsHandler', 'loadBoorus', LogTypes.exception); @@ -1309,13 +1317,13 @@ class SettingsHandler extends GetxController { booruList.value = tempList.where((element) => !booruList.contains(element)).toList(); // filter due to possibility of duplicates if (tempList.isNotEmpty) { - sortBooruList(); + unawaited(sortBooruList()); } return true; } - void sortBooruList() async { - List sorted = [...booruList]; // spread the array just in case, to guarantee that we don't affect the original value + Future sortBooruList() async { + final List sorted = [...booruList]; // spread the array just in case, to guarantee that we don't affect the original value sorted.sort((a, b) { // sort alphabetically return a.name!.toLowerCase().compareTo(b.name!.toLowerCase()); @@ -1349,10 +1357,12 @@ class SettingsHandler extends GetxController { } Future saveBooru(Booru booru, {bool onlySave = false}) async { - if (path == "") await setConfigDir(); + if (path == '') { + await setConfigDir(); + } await Directory(boorusPath).create(recursive: true); - final File booruFile = File("$boorusPath${booru.name}.json"); + final File booruFile = File('$boorusPath${booru.name}.json'); final writer = booruFile.openWrite(); writer.write(jsonEncode(booru.toJson())); await writer.close(); @@ -1361,28 +1371,28 @@ class SettingsHandler extends GetxController { // used only to avoid duplication after migration to json format // TODO remove condition when migration logic is removed booruList.add(booru); - sortBooruList(); + unawaited(sortBooruList()); } return true; } Future deleteBooru(Booru booru) async { - final File booruFile = File("$boorusPath${booru.name}.json"); + final File booruFile = File('$boorusPath${booru.name}.json'); await booruFile.delete(); if (prefBooru == booru.name) { - prefBooru = ""; + prefBooru = ''; await saveSettings(restate: true); } booruList.remove(booru); - sortBooruList(); + unawaited(sortBooruList()); return true; } List> parseTagsList(List itemTags, {bool isCapped = true}) { - List cleanItemTags = cleanTagsList(itemTags); - List hatedInItem = hatedTags.where((tag) => cleanItemTags.contains(tag)).toList(); - List lovedInItem = lovedTags.where((tag) => cleanItemTags.contains(tag)).toList(); - List soundInItem = ['sound', 'sound_edit', 'has_audio', 'voice_acted'].where((tag) => cleanItemTags.contains(tag)).toList(); + final List cleanItemTags = cleanTagsList(itemTags); + List hatedInItem = hatedTags.where(cleanItemTags.contains).toList(); + List lovedInItem = lovedTags.where(cleanItemTags.contains).toList(); + final List soundInItem = ['sound', 'sound_edit', 'has_audio', 'voice_acted'].where(cleanItemTags.contains).toList(); // TODO add more sound tags? if (isCapped) { @@ -1440,28 +1450,29 @@ class SettingsHandler extends GetxController { return cleanTags; } - void checkUpdate({bool withMessage = false}) async { + Future checkUpdate({bool withMessage = false}) async { if (Tools.isTestMode) { return; } - const String changelog = r"""Changelog"""; - Map fakeUpdate = { - "version_name": "2.2.0", - "build_number": 170, - "title": "Title", - "changelog": changelog, - "is_in_store": true, // is app still in store - "is_update_in_store": + const String changelog = '''Changelog'''; + // ignore: unused_local_variable + final Map fakeUpdate = { + 'version_name': '2.2.0', + 'build_number': 170, + 'title': 'Title', + 'changelog': changelog, + 'is_in_store': true, // is app still in store + 'is_update_in_store': true, // is update available in store [LEGACY], after 2.2.0 hits the store - left this in update.json as true for backwards compatibility with pre-2.2 - "is_important": false, // is update important => force open dialog on start - "store_package": "com.noaisu.play.loliSnatcher", // custom app package name, to allow to redirect store users to new app if it will be needed - "github_url": "https://github.com/NO-ob/LoliSnatcher_Droid/releases/latest" + 'is_important': false, // is update important => force open dialog on start + 'store_package': 'com.noaisu.play.loliSnatcher', // custom app package name, to allow to redirect store users to new app if it will be needed + 'github_url': 'https://github.com/NO-ob/LoliSnatcher_Droid/releases/latest' }; // fake update json for tests // String fakeUpdate = '123'; // broken string try { - const String updateFileName = EnvironmentConfig.isFromStore ? "update_store.json" : "update.json"; + const String updateFileName = EnvironmentConfig.isFromStore ? 'update_store.json' : 'update.json'; final response = await DioNetwork.get('https://raw.githubusercontent.com/NO-ob/LoliSnatcher_Droid/master/$updateFileName'); final json = jsonDecode(response.data); // final json = jsonDecode(jsonEncode(fakeUpdate)); @@ -1470,17 +1481,17 @@ class SettingsHandler extends GetxController { Logger.Inst().log(jsonEncode(json), 'SettingsHandler', 'checkUpdate', LogTypes.settingsError); updateInfo.value = UpdateInfo( - versionName: json["version_name"] ?? '0.0.0', - buildNumber: json["build_number"] ?? 0, - title: json["title"] ?? '...', - changelog: json["changelog"] ?? '...', - isInStore: json["is_in_store"] ?? false, - isImportant: json["is_important"] ?? false, - storePackage: json["store_package"] ?? '', - githubURL: json["github_url"] ?? 'https://github.com/NO-ob/LoliSnatcher_Droid/releases/latest', + versionName: json['version_name'] ?? '0.0.0', + buildNumber: json['build_number'] ?? 0, + title: json['title'] ?? '...', + changelog: json['changelog'] ?? '...', + isInStore: json['is_in_store'] ?? false, + isImportant: json['is_important'] ?? false, + storePackage: json['store_package'] ?? '', + githubURL: json['github_url'] ?? 'https://github.com/NO-ob/LoliSnatcher_Droid/releases/latest', ); - String? discordFromGithub = json["discord_url"]; + final String? discordFromGithub = json['discord_url']; if (discordFromGithub != null && discordFromGithub.isNotEmpty) { // overwrite included discord url if it's not the same as the one in update info if (discordFromGithub != discordURL.value) { @@ -1513,7 +1524,7 @@ class SettingsHandler extends GetxController { if (withMessage) { FlashElements.showSnackbar( title: const Text( - "Update Check Error!", + 'Update Check Error!', style: TextStyle(fontSize: 20), ), content: Text( @@ -1531,7 +1542,7 @@ class SettingsHandler extends GetxController { if (withMessage) { FlashElements.showSnackbar( title: const Text( - "You already have the latest version!", + 'You already have the latest version!', style: TextStyle(fontSize: 20), ), sideColor: Colors.green, @@ -1576,7 +1587,7 @@ class SettingsHandler extends GetxController { // } on PlatformException catch(e) { // ServiceHandler.launchURL("https://play.google.com/store/apps/details?id=" + updateInfo.value!.storePackage); // } - ServiceHandler.launchURL("https://play.google.com/store/apps/details?id=${updateInfo.value!.storePackage}"); + ServiceHandler.launchURL('https://play.google.com/store/apps/details?id=${updateInfo.value!.storePackage}'); Navigator.of(context).pop(true); }, icon: const Icon(Icons.play_arrow), @@ -1626,7 +1637,7 @@ class SettingsHandler extends GetxController { Logger.Inst().log(e.toString(), 'SettingsHandler', 'initialize', LogTypes.settingsError); FlashElements.showSnackbar( title: const Text( - "Initialization Error!", + 'Initialization Error!', style: TextStyle(fontSize: 20), ), content: Text( @@ -1647,7 +1658,7 @@ class SettingsHandler extends GetxController { darkTheme: false, // TODO on true - throws theme exception when opening inspector? ); - checkUpdate(withMessage: false); + unawaited(checkUpdate(withMessage: false)); isInit.value = true; return; } diff --git a/lib/src/handlers/snatch_handler.dart b/lib/src/handlers/snatch_handler.dart index f3ce07a9..554dfd1e 100644 --- a/lib/src/handlers/snatch_handler.dart +++ b/lib/src/handlers/snatch_handler.dart @@ -11,39 +11,36 @@ import 'package:lolisnatcher/src/services/image_writer.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; - - class SnatchHandler extends GetxController { + SnatchHandler() { + queuedList.listen((List list) { + trySnatch(); + }); + } static SnatchHandler get instance => Get.find(); RxBool active = false.obs; - RxString status = "".obs; + RxString status = ''.obs; RxInt queueProgress = 0.obs; RxInt received = 0.obs; RxInt total = 0.obs; RxList queuedList = RxList([]); - SnatchHandler() { - queuedList.listen((List list) { - trySnatch(); - }); - } - Stream> writeMultipleFake(List items, Booru booru, int cooldown) async* { int snatchedCounter = 0; for (int i = 0; i < items.length; i++) { await Future.delayed(const Duration(milliseconds: 2000), () async {}); snatchedCounter++; yield { - "snatched": snatchedCounter, + 'snatched': snatchedCounter, }; } yield { - "snatched": snatchedCounter, - "exists": 0, - "failed": 0, + 'snatched': snatchedCounter, + 'exists': 0, + 'failed': 0, }; } @@ -54,7 +51,7 @@ class SnatchHandler extends GetxController { Future snatch(SnatchItem item) async { active.value = true; - status.value = queuedList.isNotEmpty ? "0/${item.booruItems.length}/${queuedList.length}" : "0/${item.booruItems.length}"; + status.value = queuedList.isNotEmpty ? '0/${item.booruItems.length}/${queuedList.length}' : '0/${item.booruItems.length}'; // writeMultipleFake(item.booruItems, item.booru, item.cooldown).listen( ImageWriter() @@ -67,11 +64,11 @@ class SnatchHandler extends GetxController { ) .listen( (Map data) { - final int snatched = data["snatched"]! as int; - final List? exists = data["exists"]; - final List? failed = data["failed"]; + final int snatched = data['snatched']! as int; + final List? exists = data['exists']; + final List? failed = data['failed']; - status.value = queuedList.isNotEmpty ? "$snatched/${item.booruItems.length}/${queuedList.length}" : "$snatched/${item.booruItems.length}"; + status.value = queuedList.isNotEmpty ? '$snatched/${item.booruItems.length}/${queuedList.length}' : '$snatched/${item.booruItems.length}'; if (exists == null && failed == null) { // record progress only when snatched changes @@ -88,7 +85,7 @@ class SnatchHandler extends GetxController { FlashElements.showSnackbar( duration: const Duration(seconds: 2), position: Positions.top, - title: const Text("Snatching Complete", style: TextStyle(fontSize: 20)), + title: const Text('Snatching Complete', style: TextStyle(fontSize: 20)), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -110,7 +107,7 @@ class SnatchHandler extends GetxController { snatch(queuedList.removeLast()); } else { active.value = false; - status.value = ""; + status.value = ''; queueProgress.value = 0; received.value = 0; total.value = 0; @@ -136,13 +133,13 @@ class SnatchHandler extends GetxController { bool ignoreExists, ) { if (booruItems.isNotEmpty) { - SnatchItem item = SnatchItem(booruItems, cooldown, booru, ignoreExists); + final SnatchItem item = SnatchItem(booruItems, cooldown, booru, ignoreExists); queuedList.add(item); if (booruItems.length > 1) { if (SettingsHandler.instance.downloadNotifications) { FlashElements.showSnackbar( - title: const Text("Added to snatch queue", style: TextStyle(fontSize: 20)), + title: const Text('Added to snatch queue', style: TextStyle(fontSize: 20)), position: Positions.top, duration: const Duration(seconds: 2), content: Column( @@ -159,7 +156,7 @@ class SnatchHandler extends GetxController { } else { if (SettingsHandler.instance.downloadNotifications) { FlashElements.showSnackbar( - title: const Text("Added to snatch queue", style: TextStyle(fontSize: 20)), + title: const Text('Added to snatch queue', style: TextStyle(fontSize: 20)), position: Positions.top, duration: const Duration(seconds: 2), content: Column( @@ -187,17 +184,17 @@ class SnatchHandler extends GetxController { limit = 100; } - List temp = BooruHandlerFactory().getBooruHandler([booru], limit); + final List temp = BooruHandlerFactory().getBooruHandler([booru], limit); booruHandler = temp[0] as BooruHandler; booruHandler.pageNum = temp[1] as int; booruHandler.pageNum++; FlashElements.showSnackbar( - title: const Text("Snatching Images", style: TextStyle(fontSize: 20)), + title: const Text('Snatching Images', style: TextStyle(fontSize: 20)), position: Positions.top, - content: Column( + content: const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ Text('Do not close the app!'), ], ), @@ -207,7 +204,7 @@ class SnatchHandler extends GetxController { ); while (count < int.parse(amount) && !booruHandler.locked) { - booruItems = (await booruHandler.search(tags, null) ?? []); + booruItems = await booruHandler.search(tags, null) ?? []; booruHandler.pageNum++; count = booruItems.length; // TODO error handling? diff --git a/lib/src/handlers/tag_handler.dart b/lib/src/handlers/tag_handler.dart index dfbfc622..02bf6110 100644 --- a/lib/src/handlers/tag_handler.dart +++ b/lib/src/handlers/tag_handler.dart @@ -21,6 +21,11 @@ class UntypedCollection { } class TagHandler extends GetxController { + TagHandler() { + untypedQueue.listen((List list) { + tryGetTagTypes(); + }); + } static TagHandler get instance => Get.find(); int prevLength = 0; @@ -30,11 +35,6 @@ class TagHandler extends GetxController { RxList untypedQueue = RxList([]); RxBool tagFetchActive = false.obs; bool tagSaveActive = false; - TagHandler() { - untypedQueue.listen((List list) { - tryGetTagTypes(); - }); - } bool hasTag(String tagString) { return tagMap.containsKey(tagString.toLowerCase()); @@ -56,27 +56,29 @@ class TagHandler extends GetxController { if (hasTag(tagString)) { tag = tagMap[tagString]; } - tag ??= Tag(tagString, tagType: TagType.none); - return tag; + return tag ?? Tag(tagString, tagType: TagType.none); } - Future putTag(Tag tag, {bool useDB = true, bool preferTypeIfNone = false}) async{ + Future putTag(Tag tag, {bool useDB = true, bool preferTypeIfNone = false}) async { // TODO sanitize tagString? - if(tag.fullString.isEmpty) { + if (tag.fullString.isEmpty) { return; } tag.fullString = tag.fullString.toLowerCase(); - if (preferTypeIfNone && hasTag(tag.fullString)){ - if(getTag(tag.fullString).tagType != TagType.none && tag.tagType == TagType.none){ + if (preferTypeIfNone && hasTag(tag.fullString)) { + if (getTag(tag.fullString).tagType != TagType.none && tag.tagType == TagType.none) { Logger.Inst().log( - "Skipped tag ${tag.fullString}", "TagHandler", "putTag", - LogTypes.tagHandlerInfo); + 'Skipped tag ${tag.fullString}', + 'TagHandler', + 'putTag', + LogTypes.tagHandlerInfo, + ); return; } } _tagMap[tag.fullString] = tag; - if (SettingsHandler.instance.dbEnabled && useDB){ + if (SettingsHandler.instance.dbEnabled && useDB) { await SettingsHandler.instance.dbHandler.updateTagsFromObjects([tag]); } return; @@ -91,20 +93,20 @@ class TagHandler extends GetxController { } Future getTagTypes(UntypedCollection untyped) async { - if (SettingsHandler.instance.tagTypeFetchEnabled){ - Logger.Inst().log("Snatching tags: ${untyped.tags}", "TagHandler", "getTagTypes", LogTypes.tagHandlerInfo); + if (SettingsHandler.instance.tagTypeFetchEnabled) { + Logger.Inst().log('Snatching tags: ${untyped.tags}', 'TagHandler', 'getTagTypes', LogTypes.tagHandlerInfo); tagFetchActive.value = true; - List temp = BooruHandlerFactory().getBooruHandler([untyped.booru], null); - BooruHandler booruHandler = temp[0]; + final List temp = BooruHandlerFactory().getBooruHandler([untyped.booru], null); + final BooruHandler booruHandler = temp[0]; int tagCounter = 0; while (untyped.tags.isNotEmpty) { - List workingTags = []; - int tagMaxLimit = 100; - int tagMax = (untyped.tags.length > tagMaxLimit) ? tagMaxLimit : untyped.tags.length; + final List workingTags = []; + const int tagMaxLimit = 100; + final int tagMax = (untyped.tags.length > tagMaxLimit) ? tagMaxLimit : untyped.tags.length; for (int i = 0; i < tagMax; i++) { if (untyped.tags.isNotEmpty) { - String tag = untyped.tags.removeLast(); + final String tag = untyped.tags.removeLast(); if (!hasTagAndNotStale(tag) && !workingTags.contains(tag)) { workingTags.add(tag); } @@ -112,25 +114,25 @@ class TagHandler extends GetxController { } if (workingTags.isNotEmpty) { - List newTags = await booruHandler.genTagObjects(workingTags); - for (Tag tag in newTags) { + final List newTags = await booruHandler.genTagObjects(workingTags); + for (final Tag tag in newTags) { await putTag(tag); //TODO write tag to database - tagCounter ++; + tagCounter++; } - await Future.delayed(Duration(milliseconds: untyped.cooldown), () async{}); + await Future.delayed(Duration(milliseconds: untyped.cooldown), () async {}); } } - Logger.Inst().log("Got $tagCounter tag types, untyped list length was: ${untyped.tags.length}", "TagHandler", "getTagTypes", LogTypes.tagHandlerInfo); + Logger.Inst().log('Got $tagCounter tag types, untyped list length was: ${untyped.tags.length}', 'TagHandler', 'getTagTypes', LogTypes.tagHandlerInfo); tagFetchActive.value = false; } tryGetTagTypes(); } /// Stores given tags list with given type, if tag is already in the tag map - update it's type, but only if the type was "none" - void addTagsWithType(List tags, TagType type) async { - for(String tag in tags) { + Future addTagsWithType(List tags, TagType type) async { + for (final String tag in tags) { if (!hasTagAndNotStale(tag)) { await putTag(Tag(tag, tagType: type)); } else if (type != TagType.none) { @@ -142,7 +144,7 @@ class TagHandler extends GetxController { } void queue(List untypedTags, Booru booru, int cooldown) { - Logger.Inst().log("Added ${untypedTags.length} tags to queue from ${booru.name}", "TagHandler", "queue", LogTypes.tagHandlerInfo); + Logger.Inst().log('Added ${untypedTags.length} tags to queue from ${booru.name}', 'TagHandler', 'queue', LogTypes.tagHandlerInfo); if (untypedTags.isNotEmpty) { untypedQueue.add(UntypedCollection(untypedTags, cooldown, booru)); } @@ -157,9 +159,9 @@ class TagHandler extends GetxController { Future loadTags() async { try { - if (SettingsHandler.instance.dbEnabled){ - List tags = await SettingsHandler.instance.dbHandler.getAllTags(); - for (Tag tag in tags){ + if (SettingsHandler.instance.dbEnabled) { + final List tags = await SettingsHandler.instance.dbHandler.getAllTags(); + for (final Tag tag in tags) { await putTag(tag, useDB: false); } } else { @@ -168,51 +170,55 @@ class TagHandler extends GetxController { } } } catch (e) { - Logger.Inst().log("Error loading tags: $e", "TagHandler", "loadTags", LogTypes.exception); + Logger.Inst().log('Error loading tags: $e', 'TagHandler', 'loadTags', LogTypes.exception); } return true; } - Future checkForTagsFile() async { - File tagFile = File("${SettingsHandler.instance.path}tags.json"); - return await tagFile.exists(); + Future checkForTagsFile() { + final File tagFile = File('${SettingsHandler.instance.path}tags.json'); + return tagFile.exists(); } Future loadTagsFile() async { - File tagFile = File("${SettingsHandler.instance.path}tags.json"); - String jsonString = await tagFile.readAsString(); + final File tagFile = File('${SettingsHandler.instance.path}tags.json'); + final String jsonString = await tagFile.readAsString(); await loadFromJSON(jsonString); return; } - - Future loadFromJSON(String jsonString, {bool preferTagTypeIfNone = false}) async { - try { - List jsonList = jsonDecode(jsonString); - for (Map rawTag in jsonList) { - try { - Tag tagObject = Tag.fromJson(rawTag); - await putTag(tagObject, preferTypeIfNone: preferTagTypeIfNone); - } catch (e) { - Logger.Inst().log( - "Error parsing tag: $rawTag", "TagHandler", "loadFromJSON", - LogTypes.exception); - } + try { + final List jsonList = jsonDecode(jsonString); + for (final Map rawTag in jsonList) { + try { + final Tag tagObject = Tag.fromJson(rawTag); + await putTag(tagObject, preferTypeIfNone: preferTagTypeIfNone); + } catch (e) { + Logger.Inst().log( + 'Error parsing tag: $rawTag', + 'TagHandler', + 'loadFromJSON', + LogTypes.exception, + ); } - return true; - } catch (e) { - Logger.Inst().log( - "Error loading tags from JSON: $e", "TagHandler", "loadFromJSON", - LogTypes.exception); - return false; } + return true; + } catch (e) { + Logger.Inst().log( + 'Error loading tags from JSON: $e', + 'TagHandler', + 'loadFromJSON', + LogTypes.exception, + ); + return false; + } } List toList() { - List tagList = []; - tagMap.forEach((key,value) => tagList.add(value)); + final List tagList = []; + tagMap.forEach((key, value) => tagList.add(value)); return tagList; } @@ -222,26 +228,27 @@ class TagHandler extends GetxController { Future saveTags() async { tagSaveActive = true; - SettingsHandler settings = SettingsHandler.instance; + final SettingsHandler settings = SettingsHandler.instance; await getPerms(); prevLength = tagMap.entries.length; - if(settings.dbEnabled){ + if (settings.dbEnabled) { //await settings.dbHandler.updateTagsFromObjects(toList()); } else { try { - if (settings.path == "") await settings.setConfigDir(); - await Directory(settings.path).create(recursive:true); - File tagFile = File("${settings.path}tags.json"); - var writer = tagFile.openWrite(); + if (settings.path == '') { + await settings.setConfigDir(); + } + await Directory(settings.path).create(recursive: true); + final File tagFile = File('${settings.path}tags.json'); + final writer = tagFile.openWrite(); writer.write(jsonEncode(toList())); await writer.flush(); await writer.close(); - } catch(e) { - Logger.Inst().log("FAILED TO WRITE TAG FILE: $e", "TagHandler", "saveTags", LogTypes.exception); + } catch (e) { + Logger.Inst().log('FAILED TO WRITE TAG FILE: $e', 'TagHandler', 'saveTags', LogTypes.exception); } } tagSaveActive = false; return; } } - diff --git a/lib/src/handlers/theme_handler.dart b/lib/src/handlers/theme_handler.dart index a3325975..96537761 100644 --- a/lib/src/handlers/theme_handler.dart +++ b/lib/src/handlers/theme_handler.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart' show SchedulerBinding; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -15,11 +14,14 @@ class ThemeHandler { required this.useMaterial3, required this.isAmoled, }) { - isDark = themeMode == ThemeMode.dark || (themeMode == ThemeMode.system && SchedulerBinding.instance.window.platformBrightness == Brightness.dark); + final platformBrigtness = WidgetsBinding.instance.platformDispatcher.views.first.platformDispatcher.platformBrightness; + isDark = themeMode == ThemeMode.dark || (themeMode == ThemeMode.system && platformBrigtness == Brightness.dark); - Brightness primaryBrightness = ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().primary : theme.primary!); + final Brightness primaryBrightness = + ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().primary : theme.primary!); primaryIsDark = primaryBrightness == Brightness.dark; - Brightness accentBrightness = ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().secondary : theme.accent!); + final Brightness accentBrightness = + ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().secondary : theme.accent!); accentIsDark = accentBrightness == Brightness.dark; } @@ -38,11 +40,14 @@ class ThemeHandler { lightDynamic = light; darkDynamic = dark; - isDark = themeMode == ThemeMode.dark || (themeMode == ThemeMode.system && SchedulerBinding.instance.window.platformBrightness == Brightness.dark); + final platformBrigtness = WidgetsBinding.instance.platformDispatcher.views.first.platformDispatcher.platformBrightness; + isDark = themeMode == ThemeMode.dark || (themeMode == ThemeMode.system && platformBrigtness == Brightness.dark); - Brightness primaryBrightness = ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().primary : theme.primary!); + final Brightness primaryBrightness = + ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().primary : theme.primary!); primaryIsDark = primaryBrightness == Brightness.dark; - Brightness accentBrightness = ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().secondary : theme.accent!); + final Brightness accentBrightness = + ThemeData.estimateBrightnessForColor((isDark ? darkDynamic : lightDynamic) != null ? colorScheme().secondary : theme.accent!); accentIsDark = accentBrightness == Brightness.dark; } @@ -155,8 +160,8 @@ class ThemeHandler { if (useMaterial3) { return ColorScheme.fromSeed( seedColor: theme.accent!, - primary: theme.primary!, - secondary: theme.accent!, + primary: theme.primary, + secondary: theme.accent, brightness: isDark ? Brightness.dark : Brightness.light, error: Colors.redAccent, ); @@ -322,8 +327,8 @@ class ThemeHandler { if (Platform.isAndroid || Platform.isIOS) { return 8; } else { - List goodStates = [MaterialState.hovered, MaterialState.focused, MaterialState.pressed]; - for (MaterialState state in states) { + final List goodStates = [MaterialState.hovered, MaterialState.focused, MaterialState.pressed]; + for (final MaterialState state in states) { if (goodStates.contains(state)) { return 8; } @@ -336,8 +341,8 @@ class ThemeHandler { if (Platform.isAndroid || Platform.isIOS) { return true; } else { - List goodStates = [MaterialState.hovered, MaterialState.focused, MaterialState.pressed]; - for (MaterialState state in states) { + final List goodStates = [MaterialState.hovered, MaterialState.focused, MaterialState.pressed]; + for (final MaterialState state in states) { if (goodStates.contains(state)) { return true; } @@ -346,10 +351,10 @@ class ThemeHandler { } }), thumbColor: MaterialStateProperty.resolveWith((states) { - List goodStates = [MaterialState.hovered, MaterialState.focused, MaterialState.pressed]; + final List goodStates = [MaterialState.hovered, MaterialState.focused, MaterialState.pressed]; Color color = isDark ? Colors.grey[300]! : Colors.grey[900]!; color = colorScheme.secondary; - for (MaterialState state in states) { + for (final MaterialState state in states) { if (goodStates.contains(state)) { return color.withOpacity(0.75); } @@ -368,11 +373,10 @@ class ThemeHandler { CheckboxThemeData checkboxTheme(ColorScheme colorScheme) => CheckboxThemeData( fillColor: MaterialStateProperty.resolveWith((states) { - bool isHovered = states.contains(MaterialState.hovered); + final bool isHovered = states.contains(MaterialState.hovered); if (states.contains(MaterialState.selected)) { - Color color = colorScheme.secondary; - color = isHovered ? Color.lerp(color, Colors.black, 0.15)! : color; - return color; + final Color color = colorScheme.secondary; + return isHovered ? Color.lerp(color, Colors.black, 0.15)! : color; } else { return isHovered ? Colors.grey[600] : Colors.grey; } @@ -383,21 +387,19 @@ class ThemeHandler { SwitchThemeData switchTheme(ColorScheme colorScheme) => SwitchThemeData( thumbColor: MaterialStateProperty.resolveWith((states) { - bool isHovered = states.contains(MaterialState.hovered); + final bool isHovered = states.contains(MaterialState.hovered); if (states.contains(MaterialState.selected)) { - Color color = colorScheme.secondary; - color = isHovered ? Color.lerp(color, Colors.black, 0.2)! : color; - return color; + final Color color = colorScheme.secondary; + return isHovered ? Color.lerp(color, Colors.black, 0.2)! : color; } else { return isHovered ? Colors.grey[600] : Colors.grey[500]; } }), trackColor: MaterialStateProperty.resolveWith((states) { - bool isHovered = states.contains(MaterialState.hovered); + final bool isHovered = states.contains(MaterialState.hovered); if (states.contains(MaterialState.selected)) { - Color color = Color.lerp(colorScheme.secondary, Colors.white, 0.3)!; - color = isHovered ? Color.lerp(color, Colors.black, 0.2)! : color; - return color; + final Color color = Color.lerp(colorScheme.secondary, Colors.white, 0.3)!; + return isHovered ? Color.lerp(color, Colors.black, 0.2)! : color; } else { return isHovered ? Colors.grey[400] : Colors.grey[300]; } diff --git a/lib/src/handlers/viewer_handler.dart b/lib/src/handlers/viewer_handler.dart index 57ac9aea..e4725efc 100644 --- a/lib/src/handlers/viewer_handler.dart +++ b/lib/src/handlers/viewer_handler.dart @@ -14,7 +14,6 @@ import 'package:lolisnatcher/src/widgets/video/video_viewer_placeholder.dart'; class ViewerHandler extends GetxController { static ViewerHandler get instance => Get.find(); - // Keys to get states of all currently built viewers RxList activeKeys = RxList([]); // Key of the currently viewed media widget @@ -22,26 +21,38 @@ class ViewerHandler extends GetxController { // Add media widget key void addViewed(Key? key) { - if(key == null) return; - if(activeKeys.contains(key)) return; + if (key == null || activeKeys.contains(key)) { + return; + } - if(key is GlobalKey) activeKeys.add(key); + if (key is GlobalKey) { + activeKeys.add(key); + } } + // Remove media widget key void removeViewed(Key? key) { - if(key == null) return; - if(!activeKeys.contains(key)) return; + if (key == null || !activeKeys.contains(key)) { + return; + } - if(key is GlobalKey) activeKeys.remove(key); + if (key is GlobalKey) { + activeKeys.remove(key); + } } + // Set the key of current viewed widget void setCurrent(Key? key) { - if(key == null) return; - if(currentKey.value == key) return; + if (key == null || currentKey.value == key) { + return; + } - if(key is GlobalKey) currentKey.value = key; + if (key is GlobalKey) { + currentKey.value = key; + } setNewState(key); } + // Drop the key of viewed widget and reset the handler state // (used when user exits the viewerpage) void dropCurrent() { @@ -72,29 +83,30 @@ class ViewerHandler extends GetxController { // Get zoom state of new current item void setNewState(Key? key) { - if(key == null) return; - if(currentKey.value != key) return; + if (key == null || currentKey.value != key) { + return; + } // addPostFrameCallback waits until widget is built to avoid calling setState in it while other setState is active WidgetsBinding.instance.addPostFrameCallback((_) { - var state = currentKey.value?.currentState; + final state = currentKey.value?.currentState; switch (state?.widget.runtimeType) { case ImageViewer: - var widgetState = state as ImageViewerState; + final widgetState = state! as ImageViewerState; isZoomed.value = widgetState.isZoomed; isLoaded.value = widgetState.isLoaded; isFullscreen.value = false; viewState.value = widgetState.viewController.value; break; case VideoViewer: - var widgetState = state as VideoViewerState; + final widgetState = state! as VideoViewerState; isZoomed.value = widgetState.isZoomed; isLoaded.value = widgetState.isVideoInited; isFullscreen.value = widgetState.chewieController?.isFullScreen ?? false; viewState.value = widgetState.viewController.value; break; case VideoViewerDesktop: - var widgetState = state as VideoViewerDesktopState; + final widgetState = state! as VideoViewerDesktopState; isZoomed.value = widgetState.isZoomed; // TODO find a way to get video loaded state isLoaded.value = true; @@ -107,52 +119,56 @@ class ViewerHandler extends GetxController { isFullscreen.value = false; viewState.value = null; break; - default: break; + default: + break; } }); } + // Set zoom state, called by the current item itself void setZoomed(Key? key, bool isZoom) { - if(key == null) return; - if(currentKey.value != key) return; + if (key == null || currentKey.value != key) { + return; + } isZoomed.value = isZoom; } + // toggle item's zoom state void toggleZoom() { isZoomed.value ? resetZoom() : doubleTapZoom(); } + void resetZoom() { - dynamic state = currentKey.value?.currentState; + final dynamic state = currentKey.value?.currentState; state?.resetZoom?.call(); } + void doubleTapZoom() { - dynamic state = currentKey.value?.currentState; + final dynamic state = currentKey.value?.currentState; state?.doubleTapZoom?.call(); } void setViewState(Key? key, PhotoViewControllerValue value) { - if(key == null) return; - if(currentKey.value != key) return; + if (key == null || currentKey.value != key) { + return; + } viewState.value = value; } void setLoaded(Key? key, bool value) { - if(key == null) return; - if(currentKey.value != key) return; + if (key == null || currentKey.value != key) { + return; + } isLoaded.value = value; } - - // Related to videos // TODO check if mute is forced when there are two videos in a row and you mute on the first video and then go to the second video bool videoAutoMute = false; // hold volume button in VideoViewer to mute videos globally - double videoVolume = 1; - - + double videoVolume = 1; // ViewerHandler() { // // debug: print keys list changes @@ -227,5 +243,3 @@ class ViewerHandler extends GetxController { // ); // }); // } - - diff --git a/lib/src/pages/about_page.dart b/lib/src/pages/about_page.dart index 0de7e7cb..08790197 100644 --- a/lib/src/pages/about_page.dart +++ b/lib/src/pages/about_page.dart @@ -8,7 +8,7 @@ import 'package:lolisnatcher/src/widgets/common/discord_button.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class AboutPage extends StatelessWidget { - const AboutPage({Key? key}) : super(key: key); + const AboutPage({super.key}); @override Widget build(BuildContext context) { @@ -16,7 +16,7 @@ class AboutPage extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text("LoliSnatcher"), + title: const Text('LoliSnatcher'), ), body: Center( child: ListView( @@ -24,7 +24,7 @@ class AboutPage extends StatelessWidget { Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), child: const Text( - "LoliSnatcher is open source and licensed under GPLv3 the source code is available on github. Please report any issues or feature requests in the issues section of the repo.", + 'LoliSnatcher is open source and licensed under GPLv3 the source code is available on github. Please report any issues or feature requests in the issues section of the repo.', ), ), SettingsButton( @@ -32,7 +32,7 @@ class AboutPage extends StatelessWidget { icon: const Icon(Icons.public), trailingIcon: const Icon(Icons.exit_to_app), action: () { - ServiceHandler.launchURL("https://github.com/NO-ob/LoliSnatcher_Droid"); + ServiceHandler.launchURL('https://github.com/NO-ob/LoliSnatcher_Droid'); }, ), const DiscordButton(overrideText: 'Visit our Discord Server'), @@ -41,14 +41,14 @@ class AboutPage extends StatelessWidget { icon: const Icon(Icons.email), trailingIcon: const Icon(Icons.exit_to_app), action: () { - ServiceHandler.launchURL("mailto:$email"); + ServiceHandler.launchURL('mailto:$email'); // Clipboard.setData(ClipboardData(text: email)); }, ), // Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), - child: const Text("A big thanks to Showers-U for letting me use their artwork for the app logo please check them out on pixiv"), + child: const Text('A big thanks to Showers-U for letting me use their artwork for the app logo please check them out on pixiv'), ), if (!EnvironmentConfig.isFromStore) SettingsButton( @@ -56,20 +56,20 @@ class AboutPage extends StatelessWidget { icon: const Icon(Icons.public), trailingIcon: const Icon(Icons.exit_to_app), action: () { - ServiceHandler.launchURL("https://www.pixiv.net/en/users/28366691"); + ServiceHandler.launchURL('https://www.pixiv.net/en/users/28366691'); }, ), // Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), - child: const Text("Developers:"), + child: const Text('Developers:'), ), SettingsButton( name: 'NO-ob - Github', icon: const Icon(Icons.public), trailingIcon: const Icon(Icons.exit_to_app), action: () { - ServiceHandler.launchURL("https://github.com/NO-ob"); + ServiceHandler.launchURL('https://github.com/NO-ob'); }, ), SettingsButton( @@ -77,20 +77,20 @@ class AboutPage extends StatelessWidget { icon: const Icon(Icons.public), trailingIcon: const Icon(Icons.exit_to_app), action: () { - ServiceHandler.launchURL("https://github.com/NANI-SORE"); + ServiceHandler.launchURL('https://github.com/NANI-SORE'); }, ), // Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), - child: const Text("Latest version and full changelogs can be found at the Github Releases page:"), + child: const Text('Latest version and full changelogs can be found at the Github Releases page:'), ), SettingsButton( name: 'Releases', icon: const Icon(Icons.public), trailingIcon: const Icon(Icons.exit_to_app), action: () { - ServiceHandler.launchURL("https://github.com/NO-ob/LoliSnatcher_Droid/releases"); + ServiceHandler.launchURL('https://github.com/NO-ob/LoliSnatcher_Droid/releases'); }, ), SettingsButton( diff --git a/lib/src/pages/desktop_home_page.dart b/lib/src/pages/desktop_home_page.dart index 548c72a3..31498d14 100644 --- a/lib/src/pages/desktop_home_page.dart +++ b/lib/src/pages/desktop_home_page.dart @@ -21,8 +21,7 @@ import 'package:lolisnatcher/src/widgets/tabs/tab_buttons.dart'; import 'package:lolisnatcher/src/widgets/tabs/tab_selector.dart'; class DesktopHome extends StatelessWidget { - const DesktopHome({Key? key}) : super(key: key); - + const DesktopHome({super.key}); @override Widget build(BuildContext context) { @@ -45,10 +44,10 @@ class DesktopHome extends StatelessWidget { Obx(() { if (settingsHandler.booruList.isNotEmpty && searchHandler.list.isNotEmpty) { // return const SizedBox(width: 5); - return Expanded( + return const Expanded( child: Row( mainAxisSize: MainAxisSize.max, - children: const [ + children: [ SizedBox(width: 15), TagSearchBox(), TagSearchButton(), @@ -110,8 +109,8 @@ class DesktopHome extends StatelessWidget { } else { FlashElements.showSnackbar( context: context, - title: const Text("No items selected", style: TextStyle(fontSize: 20)), - overrideLeadingIconWidget: const Text(" (」°ロ°)」 ", style: TextStyle(fontSize: 18)), + title: const Text('No items selected', style: TextStyle(fontSize: 20)), + overrideLeadingIconWidget: const Text(' (」°ロ°)」 ', style: TextStyle(fontSize: 18)), ); } }, @@ -170,7 +169,7 @@ class DesktopHome extends StatelessWidget { } class DesktopTagListener extends StatelessWidget { - const DesktopTagListener({Key? key}) : super(key: key); + const DesktopTagListener({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/src/pages/gallery_view_page.dart b/lib/src/pages/gallery_view_page.dart index e6de8d91..b8b9a0c3 100644 --- a/lib/src/pages/gallery_view_page.dart +++ b/lib/src/pages/gallery_view_page.dart @@ -57,16 +57,16 @@ class _GalleryViewPageState extends State { kbFocusNode.requestFocus(); // enable volume buttons if opened page is a video AND appbar is visible - BooruItem item = searchHandler.currentFetched[widget.initialIndex]; - bool isVideo = item.mediaType.value.isVideo; + final BooruItem item = searchHandler.currentFetched[widget.initialIndex]; + final bool isVideo = item.mediaType.value.isVideo; // bool isHated = item.isHated.value; - bool isVolumeAllowed = !settingsHandler.useVolumeButtonsForScroll || (isVideo && viewerHandler.displayAppbar.value); + final bool isVolumeAllowed = !settingsHandler.useVolumeButtonsForScroll || (isVideo && viewerHandler.displayAppbar.value); ServiceHandler.setVolumeButtons(isVolumeAllowed); setVolumeListener(); } void toggleToolbar(bool isLongTap) { - bool newAppbarVisibility = !viewerHandler.displayAppbar.value; + final bool newAppbarVisibility = !viewerHandler.displayAppbar.value; viewerHandler.displayAppbar.value = newAppbarVisibility; if (isLongTap) { @@ -74,8 +74,8 @@ class _GalleryViewPageState extends State { } // enable volume buttons if current page is a video AND appbar is set to visible - bool isVideo = searchHandler.currentFetched[searchHandler.viewedIndex.value].mediaType.value.isVideo; - bool isVolumeAllowed = !settingsHandler.useVolumeButtonsForScroll || (isVideo && newAppbarVisibility); + final bool isVideo = searchHandler.currentFetched[searchHandler.viewedIndex.value].mediaType.value.isVideo; + final bool isVolumeAllowed = !settingsHandler.useVolumeButtonsForScroll || (isVideo && newAppbarVisibility); ServiceHandler.setVolumeButtons(isVolumeAllowed); } @@ -154,105 +154,107 @@ class _GalleryViewPageState extends State { } } }, - child: Stack(children: [ - Obx(() { - if (searchHandler.currentFetched.isEmpty) { - return const Center( - child: Text("No items", style: TextStyle(color: Colors.white)), - ); - } + child: Stack( + children: [ + Obx(() { + if (searchHandler.currentFetched.isEmpty) { + return const Center( + child: Text('No items', style: TextStyle(color: Colors.white)), + ); + } - return PreloadPageView.builder( - controller: controller, - preloadPagesCount: settingsHandler.preloadCount, - // allowImplicitScrolling: true, - scrollDirection: settingsHandler.galleryScrollDirection == 'Vertical' ? Axis.vertical : Axis.horizontal, - physics: const AlwaysScrollableScrollPhysics(parent: ClampingScrollPhysics()), - itemCount: searchHandler.currentFetched.length, - itemBuilder: (context, index) { - BooruItem item = searchHandler.currentFetched[index]; - // String fileURL = item.fileURL; - bool isVideo = item.mediaType.value.isVideo; - bool isImage = item.mediaType.value.isImageOrAnimation; - bool isNeedsExtraRequest = item.mediaType.value.isNeedsExtraRequest; - // print(fileURL); - // print('isVideo: '+isVideo.toString()); - - late Widget itemWidget; - if (isImage) { - itemWidget = ImageViewer(item, key: item.key); - } else if (isVideo) { - if (settingsHandler.disableVideo) { - itemWidget = const Center(child: Text("Video Disabled", style: TextStyle(fontSize: 20))); - } else { - if (Platform.isAndroid || Platform.isIOS) { - itemWidget = VideoViewer(item, enableFullscreen: true, key: item.key); - } else if (Platform.isWindows || Platform.isLinux) { - // itemWidget = VideoViewerPlaceholder(item: item); - itemWidget = VideoViewerDesktop(item, key: item.key); + return PreloadPageView.builder( + controller: controller, + preloadPagesCount: settingsHandler.preloadCount, + // allowImplicitScrolling: true, + scrollDirection: settingsHandler.galleryScrollDirection == 'Vertical' ? Axis.vertical : Axis.horizontal, + physics: const AlwaysScrollableScrollPhysics(parent: ClampingScrollPhysics()), + itemCount: searchHandler.currentFetched.length, + itemBuilder: (context, index) { + final BooruItem item = searchHandler.currentFetched[index]; + // String fileURL = item.fileURL; + final bool isVideo = item.mediaType.value.isVideo; + final bool isImage = item.mediaType.value.isImageOrAnimation; + final bool isNeedsExtraRequest = item.mediaType.value.isNeedsExtraRequest; + // print(fileURL); + // print('isVideo: '+isVideo.toString()); + + late Widget itemWidget; + if (isImage) { + itemWidget = ImageViewer(item, key: item.key); + } else if (isVideo) { + if (settingsHandler.disableVideo) { + itemWidget = const Center(child: Text('Video Disabled', style: TextStyle(fontSize: 20))); } else { - itemWidget = VideoViewerPlaceholder(item: item); + if (Platform.isAndroid || Platform.isIOS) { + itemWidget = VideoViewer(item, enableFullscreen: true, key: item.key); + } else if (Platform.isWindows || Platform.isLinux) { + // itemWidget = VideoViewerPlaceholder(item: item); + itemWidget = VideoViewerDesktop(item, key: item.key); + } else { + itemWidget = VideoViewerPlaceholder(item: item); + } } - } - } else if (isNeedsExtraRequest) { - itemWidget = GuessExtensionViewer( - itemKey: item.key, - item: item, - index: index, - onMediaTypeGuessed: (MediaType mediaType) { - item.mediaType.value = mediaType; - setState(() {}); - }, - ); - } else { - itemWidget = UnknownViewerPlaceholder(item: item); - } - - return Obx(() { - bool isViewed = index == searchHandler.viewedIndex.value; - bool isNear = (searchHandler.viewedIndex.value - index).abs() <= settingsHandler.preloadCount; - // print('!! preloadpageview item build $index $isViewed $isNear !!'); - if (!isViewed && !isNear) { - // don't render if out of preload range - return Center(child: Container(color: Colors.black)); + } else if (isNeedsExtraRequest) { + itemWidget = GuessExtensionViewer( + itemKey: item.key, + item: item, + index: index, + onMediaTypeGuessed: (MediaType mediaType) { + item.mediaType.value = mediaType; + setState(() {}); + }, + ); + } else { + itemWidget = UnknownViewerPlaceholder(item: item); } - // Cut to the size of the container, prevents overlapping - return ClipRect( - // Stack/Buttons Temp fix for desktop pageview only scrollable on like 2px at edges of screen. Think its a windows only bug - child: GestureDetector( - onTap: () { - toggleToolbar(false); - }, - onLongPress: () { - toggleToolbar(true); - }, - child: itemWidget, - ), - ); - }); - }, - onPageChanged: (int index) { - // rehide system ui on every page change - ServiceHandler.disableSleep(); - - searchHandler.setViewedItem(index); - kbFocusNode.requestFocus(); - - viewerHandler.setCurrent(searchHandler.currentFetched[index].key); - - // enable volume buttons if new page is a video AND appbar is visible - bool isVideo = searchHandler.currentFetched[index].mediaType.value.isVideo; - bool isVolumeAllowed = !settingsHandler.useVolumeButtonsForScroll || (isVideo && viewerHandler.displayAppbar.value); - ServiceHandler.setVolumeButtons(isVolumeAllowed); - // print('Page changed ' + index.toString()); - }, - ); - }), - NotesRenderer(controller), - GalleryButtons(pageController: controller), - const ViewerTutorial(), - ]), + return Obx(() { + final bool isViewed = index == searchHandler.viewedIndex.value; + final bool isNear = (searchHandler.viewedIndex.value - index).abs() <= settingsHandler.preloadCount; + // print('!! preloadpageview item build $index $isViewed $isNear !!'); + if (!isViewed && !isNear) { + // don't render if out of preload range + return Center(child: Container(color: Colors.black)); + } + + // Cut to the size of the container, prevents overlapping + return ClipRect( + // Stack/Buttons Temp fix for desktop pageview only scrollable on like 2px at edges of screen. Think its a windows only bug + child: GestureDetector( + onTap: () { + toggleToolbar(false); + }, + onLongPress: () { + toggleToolbar(true); + }, + child: itemWidget, + ), + ); + }); + }, + onPageChanged: (int index) { + // rehide system ui on every page change + ServiceHandler.disableSleep(); + + searchHandler.setViewedItem(index); + kbFocusNode.requestFocus(); + + viewerHandler.setCurrent(searchHandler.currentFetched[index].key); + + // enable volume buttons if new page is a video AND appbar is visible + final bool isVideo = searchHandler.currentFetched[index].mediaType.value.isVideo; + final bool isVolumeAllowed = !settingsHandler.useVolumeButtonsForScroll || (isVideo && viewerHandler.displayAppbar.value); + ServiceHandler.setVolumeButtons(isVolumeAllowed); + // print('Page changed ' + index.toString()); + }, + ); + }), + NotesRenderer(controller), + GalleryButtons(pageController: controller), + const ViewerTutorial(), + ], + ), ), ), ), @@ -287,7 +289,7 @@ class _GalleryViewPageState extends State { if (kbFocusNode.hasFocus && dir != 0) { // disable volume scrolling when not in focus // lastScrolledTo = math.max(math.min(lastScrolledTo + dir, searchHandler.currentFetched.length), 0); - int toPage = searchHandler.viewedIndex.value + dir; // lastScrolledTo; + final int toPage = searchHandler.viewedIndex.value + dir; // lastScrolledTo; // controller.animateToPage(toPage, duration: Duration(milliseconds: 30), curve: Curves.easeInOut); if (toPage >= 0 && toPage < searchHandler.currentFetched.length) { controller.jumpToPage(toPage); diff --git a/lib/src/pages/lockscreen_page.dart b/lib/src/pages/lockscreen_page.dart index 9ac05a4f..1c600a6e 100644 --- a/lib/src/pages/lockscreen_page.dart +++ b/lib/src/pages/lockscreen_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; // import 'package:lolisnatcher/src/handlers/local_auth_handler.dart'; class LockScreen extends StatelessWidget { - const LockScreen({Key? key}) : super(key: key); + const LockScreen({super.key}); @override Widget build(BuildContext context) { @@ -11,7 +11,7 @@ class LockScreen extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text("LoliSnatcher"), + title: const Text('LoliSnatcher'), ), body: Center( child: InkWell( diff --git a/lib/src/pages/loli_sync_page.dart b/lib/src/pages/loli_sync_page.dart index 720b3605..34de77bd 100644 --- a/lib/src/pages/loli_sync_page.dart +++ b/lib/src/pages/loli_sync_page.dart @@ -23,7 +23,7 @@ enum SyncSide { } class LoliSyncPage extends StatefulWidget { - const LoliSyncPage({Key? key}) : super(key: key); + const LoliSyncPage({super.key}); @override State createState() => _LoliSyncPageState(); @@ -41,9 +41,11 @@ class _LoliSyncPageState extends State { final LoliSync testSync = LoliSync(); + StreamSubscription? sub; + SyncSide syncSide = SyncSide.none; - bool favourites = false, favouritesv2 = false, settings = false, booru = false, tabs = false, tags=false; + bool favourites = false, favouritesv2 = false, settings = false, booru = false, tabs = false, tags = false; int favToggleCount = 0; String tabsMode = 'Merge'; String tagsMode = 'PreferTypeIfNone'; @@ -62,7 +64,7 @@ class _LoliSyncPageState extends State { settingsHandler.lastSyncIp = ipController.text; settingsHandler.lastSyncPort = portController.text; - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); return result; } @@ -82,27 +84,34 @@ class _LoliSyncPageState extends State { setState(() {}); } - void getFavCount() async { + @override + void dispose() { + testSync.killSync(); + sub?.cancel(); + super.dispose(); + } + + Future getFavCount() async { favCount = await settingsHandler.dbHandler.getFavouritesCount(); updateState(); } - void getIPList() async { - List temp = await ServiceHandler.getIPList(); + Future getIPList() async { + final List temp = await ServiceHandler.getIPList(); ipList.addAll(temp); ipListNames.addAll(temp.map((e) => e.name).toList()); updateState(); } - void sendTestRequest() async { + Future sendTestRequest() async { if (ipController.text.isEmpty || portController.text.isEmpty) { FlashElements.showSnackbar( context: context, - title: const Text("Error!", style: TextStyle(fontSize: 20)), - content: Column( + title: const Text('Error!', style: TextStyle(fontSize: 20)), + content: const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text("Please enter IP address and port."), + children: [ + Text('Please enter IP address and port.'), ], ), sideColor: Colors.red, @@ -113,15 +122,19 @@ class _LoliSyncPageState extends State { } testSync.killSync(); - Stream stream = testSync.startSync(ipController.text, portController.text, ["Test"], 0, 'Merge', 'PreferTypeIfNone'); - StreamSubscription sub = stream.listen( + final Stream stream = testSync.startSync( + ipController.text, + portController.text, + ['Test'], + 0, + 'Merge', + 'PreferTypeIfNone', + ); + sub = stream.listen( (data) { // print(data); }, - onDone: () { - // print('Done'); - testSync.killSync(); - }, + onDone: testSync.killSync, onError: (e) { // print('Error $e'); testSync.killSync(); @@ -169,12 +182,13 @@ class _LoliSyncPageState extends State { Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), child: const Text( - "Start the server on another device it will show an ip and port, fill those in and then hit start sync to send data from this device to the other"), + 'Start the server on another device it will show an ip and port, fill those in and then hit start sync to send data from this device to the other', + ), ), SettingsTextInput( controller: ipController, title: 'IP Address', - hintText: "Host IP Address (i.e. 192.168.1.1)", + hintText: 'Host IP Address (i.e. 192.168.1.1)', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9.]'))], clearable: true, @@ -182,7 +196,7 @@ class _LoliSyncPageState extends State { SettingsTextInput( controller: portController, title: 'Port', - hintText: "Host Port (i.e. 7777)", + hintText: 'Host Port (i.e. 7777)', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], clearable: true, @@ -190,7 +204,9 @@ class _LoliSyncPageState extends State { SettingsToggle( value: favouritesv2, onChanged: (newValue) { - if (newValue) favToggleCount++; + if (newValue) { + favToggleCount++; + } setState(() { favouritesv2 = newValue; @@ -198,7 +214,7 @@ class _LoliSyncPageState extends State { }, title: 'Send Favourites', subtitle: Text('Favorites: ${favCount ?? '...'}'), - drawBottomBorder: favouritesv2 == true ? false : true, + drawBottomBorder: !favouritesv2, ), if (favToggleCount > 2) SettingsToggle( @@ -210,7 +226,7 @@ class _LoliSyncPageState extends State { }, title: 'Send Favourites (Legacy)', subtitle: Text('Favorites: ${favCount ?? '...'}'), - drawBottomBorder: favourites == true ? false : true, + drawBottomBorder: !favourites, ), AnimatedSwitcher( duration: const Duration(milliseconds: 200), @@ -218,7 +234,7 @@ class _LoliSyncPageState extends State { ? SettingsTextInput( controller: favouritesSkipController, title: 'Start Favs Sync from #...', - hintText: "Start Favs Sync from #...", + hintText: 'Start Favs Sync from #...', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], clearable: true, @@ -277,7 +293,7 @@ class _LoliSyncPageState extends State { }, title: 'Send Tabs', subtitle: Text('Tabs: ${searchHandler.total}'), - drawBottomBorder: tabs == true ? false : true, + drawBottomBorder: !tabs, ), AnimatedSwitcher( duration: const Duration(milliseconds: 200), @@ -301,7 +317,8 @@ class _LoliSyncPageState extends State { title: Text('Tabs Sync Mode'), contentItems: [ Text( - 'Merge: Merge the tabs from this device on the other device, tabs with unknown boorus and already existing tabs will be ignored'), + 'Merge: Merge the tabs from this device on the other device, tabs with unknown boorus and already existing tabs will be ignored', + ), Text(''), Text('Replace: Completely replace the tabs on the other device with the tabs from this device'), ], @@ -322,49 +339,48 @@ class _LoliSyncPageState extends State { }, title: 'Send Tags', subtitle: Text('Tags: ${tagHandler.tagMap.entries.length}'), - drawBottomBorder: tags == true ? false : true, + drawBottomBorder: !tags, ), AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: tags ? SettingsDropdown( - value: tagsMode, - items: tagsModesList, - onChanged: (String? newValue) { - setState(() { - tagsMode = newValue!; - }); - }, - title: 'Tags Sync Mode', - trailingIcon: IconButton( - icon: const Icon(Icons.help_outline), - onPressed: () { - showDialog( - context: context, - builder: (context) { - return const SettingsDialog( - title: Text('Tabs Sync Mode'), - contentItems: [ - Text("PreferTypeIfNone: If the tag exists with a tag type on the other device and it doesn't on this device it will be skipped"), - Text(''), - Text( - 'Overwrite: Tags will be added, if a tag and tag type exists on the other device it will be overwritten'), - ], - ); + value: tagsMode, + items: tagsModesList, + onChanged: (String? newValue) { + setState(() { + tagsMode = newValue!; + }); }, - ); - }, - ), - ) + title: 'Tags Sync Mode', + trailingIcon: IconButton( + icon: const Icon(Icons.help_outline), + onPressed: () { + showDialog( + context: context, + builder: (context) { + return const SettingsDialog( + title: Text('Tabs Sync Mode'), + contentItems: [ + Text("PreferTypeIfNone: If the tag exists with a tag type on the other device and it doesn't on this device it will be skipped"), + Text(''), + Text( + 'Overwrite: Tags will be added, if a tag and tag type exists on the other device it will be overwritten', + ), + ], + ); + }, + ); + }, + ), + ) : Container(), ), const SettingsButton(name: '', enabled: false), SettingsButton( name: 'Test Connection', icon: const Icon(Icons.wifi_tethering), - action: () { - sendTestRequest(); - }, + action: sendTestRequest, trailingIcon: IconButton( icon: const Icon(Icons.help_outline), onPressed: () { @@ -387,9 +403,9 @@ class _LoliSyncPageState extends State { name: 'Start Sync', icon: const Icon(Icons.send_to_mobile), action: () async { - bool isAddressEntered = ipController.text.isNotEmpty && portController.text.isNotEmpty; - bool isAnySyncSelected = favouritesv2 || favourites || settings || booru || tabs || tags; - bool syncAllowed = isAddressEntered && isAnySyncSelected; + final bool isAddressEntered = ipController.text.isNotEmpty && portController.text.isNotEmpty; + final bool isAnySyncSelected = favouritesv2 || favourites || settings || booru || tabs || tags; + final bool syncAllowed = isAddressEntered && isAnySyncSelected; if (syncAllowed) { await SettingsPageOpen( @@ -419,7 +435,7 @@ class _LoliSyncPageState extends State { } FlashElements.showSnackbar( context: context, - title: const Text("Error!", style: TextStyle(fontSize: 20)), + title: const Text('Error!', style: TextStyle(fontSize: 20)), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -445,23 +461,23 @@ class _LoliSyncPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("Stats of this device:"), + const Text('Stats of this device:'), Text("Favourites: ${favCount ?? '...'}"), - Text("Boorus: ${settingsHandler.booruList.length}"), - Text("Tabs: ${searchHandler.total}"), + Text('Boorus: ${settingsHandler.booruList.length}'), + Text('Tabs: ${searchHandler.total}'), ], ), ), Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), - child: const Text("Start the server if you want to recieve data from another device, do not use this on public wifi as you might get pozzed"), + child: const Text('Start the server if you want to recieve data from another device, do not use this on public wifi as you might get pozzed'), ), SettingsDropdown( value: selectedInterface, items: ipListNames, onChanged: (String? newValue) { selectedInterface = newValue!; - NetworkInterface? findInterface = ipList.firstWhereOrNull((el) => el.name == newValue); + final NetworkInterface? findInterface = ipList.firstWhereOrNull((el) => el.name == newValue); if (newValue == 'Localhost') { selectedAddress = '127.0.0.1'; @@ -523,7 +539,7 @@ class _LoliSyncPageState extends State { child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("LoliSync"), + title: const Text('LoliSync'), ), body: Center(child: conditionalBuild()), ), diff --git a/lib/src/pages/loli_sync_progress_page.dart b/lib/src/pages/loli_sync_progress_page.dart index 12b22466..30736989 100644 --- a/lib/src/pages/loli_sync_progress_page.dart +++ b/lib/src/pages/loli_sync_progress_page.dart @@ -9,7 +9,6 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class LoliSyncProgressPage extends StatefulWidget { const LoliSyncProgressPage({ - super.key, required this.type, // sender or receiver required this.ip, required this.port, @@ -19,9 +18,10 @@ class LoliSyncProgressPage extends StatefulWidget { this.settings = false, this.booru = false, this.tabs = false, - this.tabsMode = "Merge", + this.tabsMode = 'Merge', this.tags = false, - this.tagsMode = "PreferTypeIfNone", + this.tagsMode = 'PreferTypeIfNone', + super.key, }); final String type, port, tabsMode, tagsMode; @@ -60,22 +60,22 @@ class _LoliSyncProgressPageState extends State { super.initState(); if (widget.type == 'sender') { if (widget.favourites) { - toSync.add("Favourites"); + toSync.add('Favourites'); } if (widget.favouritesv2) { - toSync.add("Favouritesv2"); + toSync.add('Favouritesv2'); } if (widget.settings) { - toSync.add("Settings"); + toSync.add('Settings'); } if (widget.booru) { - toSync.add("Booru"); + toSync.add('Booru'); } if (widget.tabs) { - toSync.add("Tabs"); + toSync.add('Tabs'); } if (widget.tags) { - toSync.add("Tags"); + toSync.add('Tags'); } progressStream = loliSync.startSync(widget.ip!, widget.port, toSync, widget.favSkip, widget.tabsMode, widget.tagsMode); @@ -135,7 +135,7 @@ class _LoliSyncProgressPageState extends State { child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - title: const Text("LoliSync"), + title: const Text('LoliSync'), leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () async { @@ -149,24 +149,24 @@ class _LoliSyncProgressPageState extends State { child: StreamBuilder( stream: progressStream, builder: (BuildContext context, AsyncSnapshot snapshot) { - String status = ""; + String status = ''; if (snapshot.hasError) { - status = "Error ${snapshot.error}"; + status = 'Error ${snapshot.error}'; } else { switch (snapshot.connectionState) { case ConnectionState.none: - status = "No connection"; + status = 'No connection'; break; case ConnectionState.waiting: if (widget.type == 'sender') { - status = "Waiting for connection..."; + status = 'Waiting for connection...'; } else { - status = "Starting server..."; + status = 'Starting server...'; } break; case ConnectionState.active: case ConnectionState.done: - status = "${snapshot.data}"; + status = '${snapshot.data}'; break; } } @@ -187,7 +187,7 @@ class _LoliSyncProgressPageState extends State { }, ), Padding( - padding: const EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4), child: Text(status, style: const TextStyle(fontSize: 18)), ), // show history of messages @@ -198,8 +198,13 @@ class _LoliSyncProgressPageState extends State { itemBuilder: (BuildContext context, int index) { final int indexWoutStart = index + 1; return Padding( - padding: const EdgeInsets.all(4.0), - child: Center(child: Text(messagesHistory[indexWoutStart], style: const TextStyle(fontSize: 18))), + padding: const EdgeInsets.all(4), + child: Center( + child: Text( + messagesHistory[indexWoutStart], + style: const TextStyle(fontSize: 18), + ), + ), ); }, ), diff --git a/lib/src/pages/mobile_home_page.dart b/lib/src/pages/mobile_home_page.dart index 3e3a0ef6..026433e7 100644 --- a/lib/src/pages/mobile_home_page.dart +++ b/lib/src/pages/mobile_home_page.dart @@ -22,7 +22,7 @@ import 'package:lolisnatcher/src/widgets/tabs/tab_buttons.dart'; import 'package:lolisnatcher/src/widgets/tabs/tab_selector.dart'; class MobileHome extends StatefulWidget { - const MobileHome({Key? key}) : super(key: key); + const MobileHome({super.key}); @override State createState() => _MobileHomeState(); @@ -126,7 +126,7 @@ class _MobileHomeState extends State { //When setting the vertical offset, be sure to use only top or bottom offset: IDOffset.only( - bottom: 0.0, + bottom: 0, right: orientation == Orientation.landscape ? 0 : 0.5, left: orientation == Orientation.landscape ? 0 : 0.5, ), @@ -177,10 +177,12 @@ class _MobileHomeState extends State { alignment: Alignment.topCenter, children: [ const MediaPreviews(), - Obx(() => MainAppBar( - leading: settingsHandler.handSide.value.isLeft ? menuButton(InnerDrawerDirection.start) : null, - trailing: settingsHandler.handSide.value.isRight ? menuButton(InnerDrawerDirection.end) : null, - )), + Obx( + () => MainAppBar( + leading: settingsHandler.handSide.value.isLeft ? menuButton(InnerDrawerDirection.start) : null, + trailing: settingsHandler.handSide.value.isRight ? menuButton(InnerDrawerDirection.end) : null, + ), + ), ], ), ), @@ -199,7 +201,7 @@ class _MobileHomeState extends State { } class MainDrawer extends StatelessWidget { - const MainDrawer({Key? key}) : super(key: key); + const MainDrawer({super.key}); @override Widget build(BuildContext context) { @@ -208,7 +210,7 @@ class MainDrawer extends StatelessWidget { // print('build drawer'); - return Container( + return ColoredBox( color: Theme.of(context).colorScheme.background, child: SafeArea( child: Drawer( @@ -219,9 +221,9 @@ class MainDrawer extends StatelessWidget { return Container( margin: const EdgeInsets.fromLTRB(2, 15, 2, 15), width: double.infinity, - child: Row( + child: const Row( mainAxisSize: MainAxisSize.max, - children: const [ + children: [ //Tags/Search field TagSearchBox(), TagSearchButton(), @@ -248,7 +250,7 @@ class MainDrawer extends StatelessWidget { const TabBooruSelector(true), const MergeBooruToggle(), Obx(() { - bool hasTabsAndTabHasSecondaryBoorus = + final bool hasTabsAndTabHasSecondaryBoorus = searchHandler.list.isNotEmpty && (searchHandler.currentTab.secondaryBoorus?.isNotEmpty ?? false); if (settingsHandler.booruList.length > 1 && hasTabsAndTabHasSecondaryBoorus) { return const TabBooruSelector(false); @@ -260,7 +262,7 @@ class MainDrawer extends StatelessWidget { Obx(() { if (settingsHandler.booruList.isNotEmpty && searchHandler.list.isNotEmpty) { return SettingsButton( - name: "Snatcher", + name: 'Snatcher', icon: const Icon(Icons.download_sharp), page: () => const SnatcherPage(), drawTopBorder: true, @@ -269,11 +271,11 @@ class MainDrawer extends StatelessWidget { return const SizedBox.shrink(); } }), - // + // Obx(() { if (settingsHandler.isDebug.value) { return SettingsButton( - name: "Open Alice", + name: 'Open Alice', icon: const Icon(Icons.developer_board), action: () { settingsHandler.alice.showInspector(); @@ -286,9 +288,9 @@ class MainDrawer extends StatelessWidget { }), // Obx(() { - if(settingsHandler.isDebug.value && settingsHandler.enabledLogTypes.isNotEmpty) { + if (settingsHandler.isDebug.value && settingsHandler.enabledLogTypes.isNotEmpty) { return SettingsButton( - name: "Open Logger", + name: 'Open Logger', icon: const Icon(Icons.print), action: () { LogConsole.open( @@ -308,7 +310,7 @@ class MainDrawer extends StatelessWidget { } }), SettingsButton( - name: "Settings", + name: 'Settings', icon: const Icon(Icons.settings), page: () => const SettingsPage(), ), @@ -360,7 +362,7 @@ class MainDrawer extends StatelessWidget { } class MergeBooruToggle extends StatelessWidget { - const MergeBooruToggle({Key? key}) : super(key: key); + const MergeBooruToggle({super.key}); @override Widget build(BuildContext context) { @@ -380,12 +382,12 @@ class MergeBooruToggle extends StatelessWidget { FlashElements.showSnackbar( context: context, title: const Text( - "Error!", + 'Error!', style: TextStyle(fontSize: 20), ), - content: Column( + content: const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ Text('You need at least 2 booru configs to use this feature!'), ], ), @@ -393,7 +395,7 @@ class MergeBooruToggle extends StatelessWidget { leadingIconColor: Colors.red, ); } else { - var firstAvailableBooru = settingsHandler.booruList.firstWhereOrNull((booru) => booru != searchHandler.currentBooru); + final firstAvailableBooru = settingsHandler.booruList.firstWhereOrNull((booru) => booru != searchHandler.currentBooru); if (firstAvailableBooru != null) { searchHandler.mergeAction(newValue ? [firstAvailableBooru] : null); } diff --git a/lib/src/pages/settings/backup_restore_page.dart b/lib/src/pages/settings/backup_restore_page.dart index f6c58027..922679ba 100644 --- a/lib/src/pages/settings/backup_restore_page.dart +++ b/lib/src/pages/settings/backup_restore_page.dart @@ -16,7 +16,7 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class BackupRestorePage extends StatefulWidget { - const BackupRestorePage({Key? key}) : super(key: key); + const BackupRestorePage({super.key}); @override State createState() => _BackupRestorePageState(); @@ -26,7 +26,7 @@ class _BackupRestorePageState extends State { final SettingsHandler settingsHandler = SettingsHandler.instance; final SearchHandler searchHandler = SearchHandler.instance; final TagHandler tagHandler = TagHandler.instance; - String backupPath = ""; + String backupPath = ''; void showSnackbar(BuildContext context, String text, bool isError) { FlashElements.showSnackbar( @@ -46,7 +46,7 @@ class _BackupRestorePageState extends State { } Future detectedDuplicateFile(String fileName) async { - bool? res = await showDialog( + final bool? res = await showDialog( context: context, builder: (context) { return AlertDialog( @@ -86,7 +86,7 @@ class _BackupRestorePageState extends State { child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - title: const Text("Backup & Restore"), + title: const Text('Backup & Restore'), ), body: Center( child: ListView( @@ -110,7 +110,7 @@ class _BackupRestorePageState extends State { child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - title: const Text("Backup & Restore"), + title: const Text('Backup & Restore'), ), body: Center( child: ListView( @@ -155,7 +155,7 @@ class _BackupRestorePageState extends State { } } if (backupPath.isNotEmpty) { - await ServiceHandler.writeImage(await file.readAsBytes(), "settings", "text/json", "json", backupPath); + await ServiceHandler.writeImage(await file.readAsBytes(), 'settings', 'text/json', 'json', backupPath); showSnackbar(context, 'Settings saved to settings.json', false); } else { showSnackbar(context, 'No Access to backup folder!', true); @@ -172,7 +172,7 @@ class _BackupRestorePageState extends State { action: () async { try { if (backupPath.isNotEmpty) { - Uint8List? settingsFileBytes = await ServiceHandler.getFileFromSAFDirectory(backupPath, "settings.json"); + final Uint8List? settingsFileBytes = await ServiceHandler.getFileFromSAFDirectory(backupPath, 'settings.json'); if (settingsFileBytes != null) { final File newFile = File('${await ServiceHandler.getConfigDir()}settings.json'); if (!(await newFile.exists())) { @@ -206,7 +206,7 @@ class _BackupRestorePageState extends State { } } if (backupPath.isNotEmpty) { - await ServiceHandler.writeImage(await file.readAsBytes(), "store", "application/x-sqlite3", "db", backupPath); + await ServiceHandler.writeImage(await file.readAsBytes(), 'store', 'application/x-sqlite3', 'db', backupPath); showSnackbar(context, 'Database saved to store.db', false); } else { showSnackbar(context, 'No Access to backup folder!', true); @@ -267,8 +267,7 @@ class _BackupRestorePageState extends State { name: 'Backup Boorus', action: () async { try { - print(json.encode(settingsHandler.booruList)); - List booruList = settingsHandler.booruList.where((e) => e.type != BooruType.Favourites).toList(); + final List booruList = settingsHandler.booruList.where((e) => e.type != BooruType.Favourites).toList(); if (await ServiceHandler.existsFileFromSAFDirectory(backupPath, 'boorus.json')) { final bool res = await detectedDuplicateFile('boorus.json'); if (!res) { @@ -277,14 +276,13 @@ class _BackupRestorePageState extends State { } } if (backupPath.isNotEmpty) { - await ServiceHandler.writeImage(utf8.encode(json.encode(booruList)), "boorus", "text", "json", backupPath); + await ServiceHandler.writeImage(utf8.encode(json.encode(booruList)), 'boorus', 'text', 'json', backupPath); showSnackbar(context, 'Boorus saved to boorus.json', false); } else { showSnackbar(context, 'No Access to backup folder!', true); } } catch (e) { showSnackbar(context, 'Error while saving boorus! $e', true); - print(e); } }, ), @@ -294,13 +292,13 @@ class _BackupRestorePageState extends State { action: () async { try { if (backupPath.isNotEmpty) { - Uint8List? booruFileBytes = await ServiceHandler.getFileFromSAFDirectory(backupPath, "boorus.json"); - String boorusJSONString = ""; + final Uint8List? booruFileBytes = await ServiceHandler.getFileFromSAFDirectory(backupPath, 'boorus.json'); + String boorusJSONString = ''; if (booruFileBytes != null) { boorusJSONString = String.fromCharCodes(booruFileBytes); } if (boorusJSONString.isNotEmpty) { - List json = jsonDecode(boorusJSONString); + final List json = jsonDecode(boorusJSONString); final String configBoorusPath = '${await ServiceHandler.getConfigDir()}boorus/'; final Directory configBoorusDir = await Directory(configBoorusPath).create(recursive: true); if (json.isNotEmpty) { @@ -310,7 +308,7 @@ class _BackupRestorePageState extends State { settingsHandler.booruList.indexWhere((el) => el.baseURL == booru.baseURL && el.name == booru.name) != -1; final bool isAllowed = booru.type != BooruType.Favourites; if (!alreadyExists && isAllowed) { - final File booruFile = File("${configBoorusDir.path}${booru.name}.json"); + final File booruFile = File('${configBoorusDir.path}${booru.name}.json'); final writer = booruFile.openWrite(); writer.write(jsonEncode(booru.toJson())); await writer.close(); @@ -325,7 +323,6 @@ class _BackupRestorePageState extends State { } } catch (e) { showSnackbar(context, 'Error while restoring boorus! $e', true); - print(e); } }, ), @@ -334,7 +331,6 @@ class _BackupRestorePageState extends State { name: 'Backup Tags', action: () async { try { - print(json.encode(settingsHandler.booruList)); if (await ServiceHandler.existsFileFromSAFDirectory(backupPath, 'tags.json')) { final bool res = await detectedDuplicateFile('tags.json'); if (!res) { @@ -343,14 +339,13 @@ class _BackupRestorePageState extends State { } } if (backupPath.isNotEmpty) { - await ServiceHandler.writeImage(utf8.encode(json.encode(tagHandler.toList())), "tags", "text", "json", backupPath); + await ServiceHandler.writeImage(utf8.encode(json.encode(tagHandler.toList())), 'tags', 'text', 'json', backupPath); showSnackbar(context, 'Tags saved to tags.json', false); } else { showSnackbar(context, 'No Access to backup folder!', true); } } catch (e) { showSnackbar(context, 'Error while saving tags! $e', true); - print(e); } }, ), @@ -360,8 +355,8 @@ class _BackupRestorePageState extends State { action: () async { try { if (backupPath.isNotEmpty) { - Uint8List? tagFileBytes = await ServiceHandler.getFileFromSAFDirectory(backupPath, "tags.json"); - String tagJSONString = ""; + final Uint8List? tagFileBytes = await ServiceHandler.getFileFromSAFDirectory(backupPath, 'tags.json'); + String tagJSONString = ''; if (tagFileBytes != null) { tagJSONString = String.fromCharCodes(tagFileBytes); } @@ -374,7 +369,6 @@ class _BackupRestorePageState extends State { } } catch (e) { showSnackbar(context, 'Error while restoring tags! $e', true); - print(e); } }, ), diff --git a/lib/src/pages/settings/booru_edit_page.dart b/lib/src/pages/settings/booru_edit_page.dart index 574bd4c9..969a213b 100644 --- a/lib/src/pages/settings/booru_edit_page.dart +++ b/lib/src/pages/settings/booru_edit_page.dart @@ -24,7 +24,11 @@ import 'package:lolisnatcher/src/widgets/webview/webview_page.dart'; /// This is the booru editor page. class BooruEdit extends StatefulWidget { - BooruEdit(this.booru, {Key? key}) : super(key: key); + BooruEdit( + this.booru, { + super.key, + }); + Booru booru; BooruType? booruType; @@ -49,12 +53,12 @@ class _BooruEditState extends State { String convertSiteUrlToApi() { final String url = booruURLController.text; - if (url.contains("chan.sankakucomplex.com")) { + if (url.contains('chan.sankakucomplex.com')) { // Sankaku api override - return "https://capi-v2.sankakucomplex.com"; + return 'https://capi-v2.sankakucomplex.com'; // booruFaviconController.text = "https://chan.sankakucomplex.com/favicon.ico"; - } else if (url.contains("idol.sankakucomplex.com")) { - return "https://iapi.sankakucomplex.com"; + } else if (url.contains('idol.sankakucomplex.com')) { + return 'https://iapi.sankakucomplex.com'; // booruFaviconController.text = "https://idol.sankakucomplex.com/favicon.ico"; } @@ -66,7 +70,7 @@ class _BooruEditState extends State { @override void initState() { //Load settings from the Booru instance parsed to the widget and populate the text fields - if (widget.booru.name != "New") { + if (widget.booru.name != 'New') { booruNameController.text = widget.booru.name!; booruURLController.text = widget.booru.baseURL!; booruFaviconController.text = widget.booru.faviconURL!; @@ -83,7 +87,7 @@ class _BooruEditState extends State { return Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Booru Editor"), + title: const Text('Booru Editor'), actions: const [], ), body: Center( @@ -96,14 +100,13 @@ class _BooruEditState extends State { SettingsTextInput( controller: booruNameController, title: 'Name', - hintText: "Enter Booru Name", - inputType: TextInputType.text, + hintText: 'Enter Booru Name', clearable: true, ), SettingsTextInput( controller: booruURLController, title: 'URL', - hintText: "Enter Booru URL", + hintText: 'Enter Booru URL', inputType: TextInputType.url, clearable: true, ), @@ -116,20 +119,19 @@ class _BooruEditState extends State { }); }, title: 'Booru Type', - itemTitleBuilder: (p0) => (p0 as BooruType).alias, + itemTitleBuilder: (BooruType p0) => p0.alias, ), SettingsTextInput( controller: booruFaviconController, title: 'Favicon', - hintText: "(Autofills if blank)", + hintText: '(Autofills if blank)', inputType: TextInputType.url, clearable: true, ), SettingsTextInput( controller: booruDefTagsController, title: 'Default Tags', - hintText: "Default search for booru", - inputType: TextInputType.text, + hintText: 'Default search for booru', clearable: true, ), Container( @@ -156,16 +158,14 @@ class _BooruEditState extends State { SettingsTextInput( controller: booruUserIDController, title: getUserIDTitle(), - hintText: "(Can be blank)", - inputType: TextInputType.text, + hintText: '(Can be blank)', clearable: true, drawTopBorder: true, ), SettingsTextInput( controller: booruAPIKeyController, title: getApiKeyTitle(), - hintText: "(Can be blank)", - inputType: TextInputType.text, + hintText: '(Can be blank)', clearable: true, ), SizedBox(height: MediaQuery.of(context).size.height * 0.2), @@ -230,7 +230,7 @@ class _BooruEditState extends State { sanitizeBooruName(); // name and url are required - if (booruNameController.text == "") { + if (booruNameController.text == '') { FlashElements.showSnackbar( context: context, title: const Text( @@ -244,7 +244,7 @@ class _BooruEditState extends State { return; } - if (booruURLController.text == "") { + if (booruURLController.text == '') { FlashElements.showSnackbar( context: context, title: const Text( @@ -259,16 +259,16 @@ class _BooruEditState extends State { } // add https if not specified - if (!booruURLController.text.contains("http://") && !booruURLController.text.contains("https://")) { - booruURLController.text = "https://${booruURLController.text}"; + if (!booruURLController.text.contains('http://') && !booruURLController.text.contains('https://')) { + booruURLController.text = 'https://${booruURLController.text}'; } // autofill favicon if not specified - if (booruFaviconController.text == "") { - booruFaviconController.text = "${booruURLController.text}/favicon.ico"; + if (booruFaviconController.text == '') { + booruFaviconController.text = '${booruURLController.text}/favicon.ico'; } // TODO make a list of default favicons for boorus where ^default^ one won't work - if (booruURLController.text.contains("agn.ph")) { - booruFaviconController.text = "https://agn.ph/skin/Retro/favicon.ico"; + if (booruURLController.text.contains('agn.ph')) { + booruFaviconController.text = 'https://agn.ph/skin/Retro/favicon.ico'; } // some boorus have their api url different from main host @@ -276,7 +276,7 @@ class _BooruEditState extends State { //Call the booru test Booru testBooru; - if (booruAPIKeyController.text == "") { + if (booruAPIKeyController.text == '') { testBooru = Booru( booruNameController.text, widget.booruType, @@ -299,7 +299,7 @@ class _BooruEditState extends State { setState(() {}); final List testResults = await booruTest(testBooru, selectedBooruType); final BooruType? booruType = testResults[0]; - final String errorString = testResults[1].isNotEmpty ? 'Error text: "${testResults[1]}"' : ""; + final String errorString = testResults[1].isNotEmpty ? 'Error text: "${testResults[1]}"' : ''; // If a booru type is returned set the widget state if (booruType != null) { @@ -363,7 +363,7 @@ class _BooruEditState extends State { } await getPerms(); - final Booru newBooru = (booruAPIKeyController.text == "" && booruUserIDController.text == "") + final Booru newBooru = (booruAPIKeyController.text == '' && booruUserIDController.text == '') ? Booru( booruNameController.text, widget.booruType, @@ -390,7 +390,7 @@ class _BooruEditState extends State { final bool sameNameExists = settingsHandler.booruList.any((element) => element.name == newBooru.name); final bool sameURLExists = settingsHandler.booruList.any((element) => element.baseURL == newBooru.baseURL); - if (widget.booru.name == "New") { + if (widget.booru.name == 'New') { if (alreadyExists || sameNameExists || sameURLExists) { booruExists = true; } @@ -409,7 +409,7 @@ class _BooruEditState extends State { } final bool oldEditBooruExists = - (settingsHandler.booruList[i].baseURL == widget.booru.baseURL && settingsHandler.booruList[i].name == widget.booru.name); + settingsHandler.booruList[i].baseURL == widget.booru.baseURL && settingsHandler.booruList[i].name == widget.booru.name; if (!booruExists && oldEditBooruExists) { // remove the old config (same url and name as the start booru) settingsHandler.booruList.removeAt(i); @@ -460,7 +460,7 @@ class _BooruEditState extends State { null) { // if the booru is already selected in any tab, update the booru to a new one // (only if their type and baseurl are the same, otherwise main booru selector will set the value to null and user has to reselect the booru) - for (var tab in searchHandler.list) { + for (final tab in searchHandler.list) { if (tab.selectedBooru.value.type == newBooru.type && tab.selectedBooru.value.baseURL == newBooru.baseURL) { tab.selectedBooru.value = newBooru; } @@ -485,7 +485,7 @@ class _BooruEditState extends State { bool withCaptchaCheck = true, }) async { BooruType? booruType; - String? errorString = ""; + String? errorString = ''; BooruHandler test; List testFetched = []; booru.type = userBooruType; @@ -499,7 +499,7 @@ class _BooruEditState extends State { } if (userBooruType == BooruType.AutoDetect) { - List typeList = BooruType.detectable; + final List typeList = BooruType.detectable; for (int i = 1; i < typeList.length; i++) { booruType ??= (await booruTest( booru, @@ -514,7 +514,7 @@ class _BooruEditState extends State { test.pageNum++; testFetched = (await test.search( - " ", + ' ', null, withCaptchaCheck: withCaptchaCheck, )) ?? @@ -529,7 +529,7 @@ class _BooruEditState extends State { if (booruType == null) { if (testFetched.isNotEmpty) { booruType = userBooruType; - print("Found Results as $userBooruType"); + print('Found Results as $userBooruType'); return [booruType, '']; } } @@ -542,7 +542,6 @@ class _HydrusAccessKeyWidget extends StatelessWidget { const _HydrusAccessKeyWidget({ required this.urlController, required this.apiKeyController, - super.key, }); final TextEditingController urlController; @@ -559,16 +558,16 @@ class _HydrusAccessKeyWidget extends StatelessWidget { onPressed: () async { final HydrusHandler hydrus = HydrusHandler( Booru( - "Hydrus", + 'Hydrus', BooruType.Hydrus, - "Hydrus", + 'Hydrus', urlController.text, - "", + '', ), 5, ); final String accessKey = await hydrus.getAccessKey(); - if (accessKey != "") { + if (accessKey != '') { FlashElements.showSnackbar( context: context, title: const Text( @@ -601,14 +600,14 @@ class _HydrusAccessKeyWidget extends StatelessWidget { ); } }, - child: const Text("Get Hydrus Api Key"), + child: const Text('Get Hydrus Api Key'), ), ), Container( margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), width: double.infinity, child: const Text( - "To get the Hydrus key you need to open the request dialog in the hydrus client. services > review services > client api > add > from api request", + 'To get the Hydrus key you need to open the request dialog in the hydrus client. services > review services > client api > add > from api request', ), ), ], @@ -621,7 +620,6 @@ class _R34HSignInWidget extends StatelessWidget { required this.urlController, required this.loginController, required this.passwordController, - super.key, }); final TextEditingController urlController; @@ -645,8 +643,8 @@ class _R34HSignInWidget extends StatelessWidget { child: ElevatedButton.icon( onPressed: () async { // add https if not specified - if (!urlController.text.contains("http://") && !urlController.text.contains("https://")) { - urlController.text = "https://${urlController.text}"; + if (!urlController.text.contains('http://') && !urlController.text.contains('https://')) { + urlController.text = 'https://${urlController.text}'; } if (urlController.text.isEmpty || loginController.text.isEmpty || passwordController.text.isEmpty) { @@ -665,11 +663,11 @@ class _R34HSignInWidget extends StatelessWidget { final R34HentaiHandler r34h = R34HentaiHandler( Booru( - "R34Hentai", + 'R34Hentai', BooruType.R34Hentai, - "R34Hentai", + 'R34Hentai', urlController.text, - "", + '', ), 5, ); @@ -707,12 +705,11 @@ class _R34HSignInWidget extends StatelessWidget { ), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, - sideColor: Colors.red, ); } }, icon: const Icon(Icons.login), - label: const Text("Sign in"), + label: const Text('Sign in'), ), ), // button that shows simple cookie viewer dialog @@ -723,8 +720,8 @@ class _R34HSignInWidget extends StatelessWidget { child: ElevatedButton.icon( onPressed: () async { // add https if not specified - if (!urlController.text.contains("http://") && !urlController.text.contains("https://")) { - urlController.text = "https://${urlController.text}"; + if (!urlController.text.contains('http://') && !urlController.text.contains('https://')) { + urlController.text = 'https://${urlController.text}'; } if (urlController.text.isEmpty) { @@ -749,7 +746,7 @@ class _R34HSignInWidget extends StatelessWidget { context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text("Cookies"), + title: const Text('Cookies'), content: SizedBox( width: MediaQuery.of(context).size.width * 0.8, child: ListView.builder( @@ -768,7 +765,7 @@ class _R34HSignInWidget extends StatelessWidget { onPressed: () { Navigator.of(context).pop(); }, - child: const Text("Close"), + child: const Text('Close'), ), ], ); @@ -788,7 +785,7 @@ class _R34HSignInWidget extends StatelessWidget { } }, icon: const Icon(Icons.cookie), - label: const Text("View cookies"), + label: const Text('View cookies'), ), ), Container( @@ -797,8 +794,8 @@ class _R34HSignInWidget extends StatelessWidget { child: ElevatedButton.icon( onPressed: () async { // add https if not specified - if (!urlController.text.contains("http://") && !urlController.text.contains("https://")) { - urlController.text = "https://${urlController.text}"; + if (!urlController.text.contains('http://') && !urlController.text.contains('https://')) { + urlController.text = 'https://${urlController.text}'; } if (urlController.text.isEmpty) { @@ -817,11 +814,11 @@ class _R34HSignInWidget extends StatelessWidget { final R34HentaiHandler r34h = R34HentaiHandler( Booru( - "R34Hentai", + 'R34Hentai', BooruType.R34Hentai, - "R34Hentai", + 'R34Hentai', urlController.text, - "", + '', ), 5, ); @@ -867,7 +864,7 @@ class _R34HSignInWidget extends StatelessWidget { } }, icon: const Icon(Icons.logout), - label: const Text("Sign out and clear cookies"), + label: const Text('Sign out and clear cookies'), ), ), ], diff --git a/lib/src/pages/settings/booru_page.dart b/lib/src/pages/settings/booru_page.dart index 387e74d1..4ddcd545 100644 --- a/lib/src/pages/settings/booru_page.dart +++ b/lib/src/pages/settings/booru_page.dart @@ -26,7 +26,7 @@ import 'package:lolisnatcher/src/widgets/webview/webview_page.dart'; // TODO move all buttons to separate widgets/unified functions to be used in other places? class BooruPage extends StatefulWidget { - const BooruPage({Key? key}) : super(key: key); + const BooruPage({super.key}); @override State createState() => _BooruPageState(); @@ -78,9 +78,9 @@ class _BooruPageState extends State { Future _onWillPop() async { settingsHandler.defTags = defaultTagsController.text; if (int.parse(limitController.text) > 100) { - limitController.text = "100"; + limitController.text = '100'; } else if (int.parse(limitController.text) < 10) { - limitController.text = "10"; + limitController.text = '10'; } if (selectedBooru == null && settingsHandler.booruList.isNotEmpty) { @@ -98,7 +98,7 @@ class _BooruPageState extends State { } settingsHandler.limit = int.parse(limitController.text); final bool result = await settingsHandler.saveSettings(restate: false); - settingsHandler.sortBooruList(); + await settingsHandler.sortBooruList(); return result; } @@ -106,7 +106,7 @@ class _BooruPageState extends State { return SettingsButton( name: 'Add New Booru', icon: const Icon(Icons.add), - page: () => BooruEdit(Booru("New", null, "", "", "")), + page: () => BooruEdit(Booru('New', null, '', '', '')), ); } @@ -131,9 +131,9 @@ class _BooruPageState extends State { return const SettingsDialog( title: Text('Booru'), contentItems: [ - Text("The Booru selected here will be set as default after saving."), + Text('The Booru selected here will be set as default after saving.'), Text(''), - Text("The default Booru will be first to appear in the dropdown boxes."), + Text('The default Booru will be first to appear in the dropdown boxes.'), ], ); }, @@ -162,7 +162,7 @@ class _BooruPageState extends State { "Booru Config of '${selectedBooru?.name}' will be converted to a link ${Platform.isAndroid ? 'and share dialog will open' : 'which will be copied to clipboard'}.", ), const Text(''), - const Text("Should login/apikey data be included?"), + const Text('Should login/apikey data be included?'), ], actionButtons: [ const CancelButton(), @@ -195,16 +195,14 @@ class _BooruPageState extends State { // TODO more explanations about booru sharing const Text(''), if (Platform.isAndroid) ...[ - const Text("How to automatically open booru config links in the app on Android 12 and higher:"), + const Text('How to automatically open booru config links in the app on Android 12 and higher:'), const Text('1) Tap button below to open system app settings'), const Text('2) Go to "Open by default"'), const Text('3) Tap on "Add link"/Plus icon and select all available options'), const SizedBox(height: 20), - ElevatedButton( - onPressed: () { - openAppSettings(); - }, - child: const Text('Go to settings'), + const ElevatedButton( + onPressed: openAppSettings, + child: Text('Go to settings'), ), ], ], @@ -260,7 +258,7 @@ class _BooruPageState extends State { FlashElements.showSnackbar( context: context, title: const Text("Can't delete this Booru!", style: TextStyle(fontSize: 20)), - content: const Text("Remove all tabs which use it first!", style: TextStyle(fontSize: 16)), + content: const Text('Remove all tabs which use it first!', style: TextStyle(fontSize: 16)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -274,7 +272,7 @@ class _BooruPageState extends State { return SettingsDialog( title: const Text('Are you sure?'), contentItems: [ - Text("Delete Booru: ${selectedBooru?.name}?"), + Text('Delete Booru: ${selectedBooru?.name}?'), ], actionButtons: [ const CancelButton(), @@ -297,7 +295,7 @@ class _BooruPageState extends State { if (await settingsHandler.deleteBooru(tempSelected)) { FlashElements.showSnackbar( context: context, - title: const Text("Booru Deleted!", style: TextStyle(fontSize: 20)), + title: const Text('Booru Deleted!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.delete_forever, leadingIconColor: Colors.red, sideColor: Colors.yellow, @@ -306,12 +304,12 @@ class _BooruPageState extends State { // restore selected and prefbooru if something went wrong selectedBooru = tempSelected; settingsHandler.prefBooru = tempSelected.name ?? ''; - settingsHandler.sortBooruList(); + await settingsHandler.sortBooruList(); FlashElements.showSnackbar( context: context, - title: const Text("Error!", style: TextStyle(fontSize: 20)), - content: const Text("Something went wrong during deletion of a booru config!", style: TextStyle(fontSize: 16)), + title: const Text('Error!', style: TextStyle(fontSize: 20)), + content: const Text('Something went wrong during deletion of a booru config!', style: TextStyle(fontSize: 16)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -361,11 +359,11 @@ class _BooruPageState extends State { FlashElements.showSnackbar( context: context, title: const Text( - "Need Login and Password", + 'Need Login and Password', style: TextStyle(fontSize: 20), ), content: const Text( - "Please change this booru config to have both login AND password fields!", + 'Please change this booru config to have both login AND password fields!', style: TextStyle(fontSize: 16), ), leadingIcon: Icons.warning_amber, @@ -384,7 +382,7 @@ class _BooruPageState extends State { FlashElements.showSnackbar( context: context, - title: const Text("Relogin complete!", style: TextStyle(fontSize: 20)), + title: const Text('Relogin complete!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.login, leadingIconColor: Colors.yellow, sideColor: Colors.yellow, @@ -401,7 +399,7 @@ class _BooruPageState extends State { // FlashElements.showSnackbar(title: Text('Deep Link: $url'), duration: null); final ClipboardData? cdata = await Clipboard.getData(Clipboard.kTextPlain); final String url = cdata?.text ?? ''; - Logger.Inst().log(url, "BooruPage", "getBooruFromClipboard", LogTypes.settingsLoad); + Logger.Inst().log(url, 'BooruPage', 'getBooruFromClipboard', LogTypes.settingsLoad); if (url.isNotEmpty) { if (url.contains('loli.snatcher')) { final Booru booru = Booru.fromLink(url); @@ -419,7 +417,7 @@ class _BooruPageState extends State { } else { FlashElements.showSnackbar( context: context, - title: const Text("Invalid URL!", style: TextStyle(fontSize: 20)), + title: const Text('Invalid URL!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -445,7 +443,7 @@ class _BooruPageState extends State { child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Boorus & Search"), + title: const Text('Boorus & Search'), ), body: Center( child: ListView( @@ -453,7 +451,7 @@ class _BooruPageState extends State { SettingsTextInput( controller: defaultTagsController, title: 'Default Tags', - hintText: "Tags searched when app opens", + hintText: 'Tags searched when app opens', inputType: TextInputType.text, clearable: true, resetText: () => 'rating:safe', @@ -461,7 +459,7 @@ class _BooruPageState extends State { SettingsTextInput( controller: limitController, title: 'Items per Page', - hintText: "Items to fetch per page 10-100", + hintText: 'Items to fetch per page 10-100', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['limit']!['default']!.toString(), @@ -470,7 +468,7 @@ class _BooruPageState extends State { numberMin: 10, numberMax: 100, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); + final int? parse = int.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { @@ -509,7 +507,7 @@ class _BooruPageState extends State { Future askToChangePrefBooru(Booru? initBooru, Booru selectedBooru) async { if (initBooru != null && initBooru.name != selectedBooru.name) { - return await showDialog( + return showDialog( context: NavigationHandler.instance.navigatorKey.currentContext!, builder: (BuildContext context) { return SettingsDialog( diff --git a/lib/src/pages/settings/database_page.dart b/lib/src/pages/settings/database_page.dart index d8f77ed3..36e7b515 100644 --- a/lib/src/pages/settings/database_page.dart +++ b/lib/src/pages/settings/database_page.dart @@ -20,7 +20,7 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class DatabasePage extends StatefulWidget { - const DatabasePage({Key? key}) : super(key: key); + const DatabasePage({super.key}); @override State createState() => _DatabasePageState(); @@ -296,9 +296,7 @@ class _DatabasePageState extends State { ignoring: changingIndexes, child: SettingsToggle( value: indexesEnabled, - onChanged: (newValue) { - changeIndexes(newValue); - }, + onChanged: changeIndexes, title: 'Enable Indexes', trailingIcon: IconButton( icon: const Icon(Icons.help_outline), @@ -598,9 +596,7 @@ class _DatabasePageState extends State { SettingsButton( name: 'Update Sankaku URLs', trailingIcon: const Icon(Icons.image), - action: () { - updateSankakuItems(); - }, + action: updateSankakuItems, ), ], ), @@ -668,9 +664,7 @@ class _DatabasePageState extends State { trailingIcon: const Icon(Icons.delete_forever), drawTopBorder: true, action: () { - setState(() { - purgeFailedSankakuItems(); - }); + setState(purgeFailedSankakuItems); }, ), SettingsButton( diff --git a/lib/src/pages/settings/debug_page.dart b/lib/src/pages/settings/debug_page.dart index ef85c118..1b2dce79 100644 --- a/lib/src/pages/settings/debug_page.dart +++ b/lib/src/pages/settings/debug_page.dart @@ -24,7 +24,7 @@ import 'package:lolisnatcher/src/widgets/tags_manager/tm_dialog.dart'; import 'package:lolisnatcher/src/widgets/webview/webview_page.dart'; class DebugPage extends StatefulWidget { - const DebugPage({Key? key}) : super(key: key); + const DebugPage({super.key}); @override State createState() => _DebugPageState(); @@ -47,7 +47,7 @@ class _DebugPageState extends State { } Future showTagsManager(BuildContext context) async { - return await SettingsPageOpen( + return SettingsPageOpen( context: context, page: () => const TagsManagerDialog(), ).open(); @@ -56,7 +56,7 @@ class _DebugPageState extends State { //called when page is closed, sets settingshandler variables and then writes settings to disk Future _onWillPop() async { settingsHandler.allowSelfSignedCerts = allowSelfSignedCerts; - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); if (allowSelfSignedCerts) { HttpOverrides.global = MyHttpOverrides(); } else { @@ -72,7 +72,7 @@ class _DebugPageState extends State { child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Debug"), + title: const Text('Debug'), ), body: Center( child: ListView( @@ -120,7 +120,7 @@ class _DebugPageState extends State { settingsHandler.gifsAsThumbnails = newValue; }); }, - title: "GIF thumbnails", + title: 'GIF thumbnails', subtitle: const Text('Requires "Don\'t scale images"'), ), SettingsToggle( @@ -148,7 +148,7 @@ class _DebugPageState extends State { settingsHandler.desktopListsDrag = newValue; }); }, - title: "Enable drag scroll on lists [Desktop only]", + title: 'Enable drag scroll on lists [Desktop only]', ), SettingsButton( @@ -156,7 +156,7 @@ class _DebugPageState extends State { icon: const Icon(Icons.timelapse), action: () { const List speeds = [0.25, 0.5, 0.75, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20]; - int currentIndex = speeds.indexOf(timeDilation); + final int currentIndex = speeds.indexOf(timeDilation); int newIndex = 0; if ((currentIndex + 1) <= (speeds.length - 1)) { newIndex = currentIndex + 1; @@ -226,7 +226,7 @@ class _DebugPageState extends State { }, ), Padding( - padding: const EdgeInsets.only(left: 8.0), + padding: const EdgeInsets.only(left: 8), child: Text('$vDuration'), ), ], @@ -275,7 +275,7 @@ class _DebugPageState extends State { }, ), Padding( - padding: const EdgeInsets.only(left: 8.0), + padding: const EdgeInsets.only(left: 8), child: Text('$vAmplitude'), ), ], @@ -337,7 +337,7 @@ class _DebugPageState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Copied to clipboard!", style: TextStyle(fontSize: 20)), + title: const Text('Copied to clipboard!', style: TextStyle(fontSize: 20)), content: Text( str, style: const TextStyle(fontSize: 16), @@ -374,7 +374,7 @@ class _DebugPageState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Restored session from string!", style: TextStyle(fontSize: 20)), + title: const Text('Restored session from string!', style: TextStyle(fontSize: 20)), content: Text( sessionStrController.text, style: const TextStyle(fontSize: 16), diff --git a/lib/src/pages/settings/dir_picker_page.dart b/lib/src/pages/settings/dir_picker_page.dart index 4f3d47b3..ef83d99f 100644 --- a/lib/src/pages/settings/dir_picker_page.dart +++ b/lib/src/pages/settings/dir_picker_page.dart @@ -9,28 +9,29 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class DirPicker extends StatefulWidget { - const DirPicker(this.path, {Key? key}) : super(key: key); + const DirPicker(this.path, {super.key}); final String path; @override State createState() => _DirPickerState(); } + // Need to make this return the seslected directory to previous page. Might use getx variable not sure yet // Might also use a grid and add folder icons isntead of listing text // Need to make a dialog to create a new folder class _DirPickerState extends State { final SettingsHandler settingsHandler = SettingsHandler.instance; final newDirNameController = TextEditingController(); - String path = ""; + String path = ''; @override - void initState(){ + void initState() { super.initState(); path = widget.path; } Future _onWillPop() async { - if (path == "/storage"){ + if (path == '/storage') { final shouldPop = await showDialog( context: context, builder: (context) { @@ -58,9 +59,9 @@ class _DirPickerState extends State { ); return shouldPop; } else { - if(path.lastIndexOf("/") > -1) { + if (path.lastIndexOf('/') > -1) { setState(() { - path = path.substring(0,path.lastIndexOf("/")); + path = path.substring(0, path.lastIndexOf('/')); print(path); }); } @@ -68,38 +69,38 @@ class _DirPickerState extends State { } } - void mkdir(){ - var dir = Directory("$path/${newDirNameController.text}"); - if (!dir.existsSync()){ + void mkdir() { + final dir = Directory('$path/${newDirNameController.text}'); + if (!dir.existsSync()) { dir.createSync(recursive: true); } - if (dir.existsSync()){ + if (dir.existsSync()) { setState(() { - path += "/${newDirNameController.text}"; - newDirNameController.text = ""; + path += '/${newDirNameController.text}'; + newDirNameController.text = ''; }); } else { - newDirNameController.text = ""; + newDirNameController.text = ''; FlashElements.showSnackbar( context: context, title: const Text( - "Error!", - style: TextStyle(fontSize: 20) + 'Error!', + style: TextStyle(fontSize: 20), ), content: const Text( - "Failed to create directory!", - style: TextStyle(fontSize: 16) + 'Failed to create directory!', + style: TextStyle(fontSize: 16), ), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, ); } - } - bool isWritable(){ - File file = File("$path/test.txt"); + + bool isWritable() { + final File file = File('$path/test.txt'); try { file.createSync(); file.writeAsStringSync('', mode: FileMode.write, flush: true); @@ -109,12 +110,12 @@ class _DirPickerState extends State { FlashElements.showSnackbar( context: context, title: const Text( - "Error!", - style: TextStyle(fontSize: 20) + 'Error!', + style: TextStyle(fontSize: 20), ), content: const Text( - "Directory is not writable!", - style: TextStyle(fontSize: 16) + 'Directory is not writable!', + style: TextStyle(fontSize: 16), ), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, @@ -123,21 +124,23 @@ class _DirPickerState extends State { return false; } } - Future> getDirs() async{ - List dirs = []; - var dir = Directory(path); + + Future> getDirs() async { + final List dirs = []; + final dir = Directory(path); await dir.list(recursive: false).forEach((file) { - if (file is Directory){ - dirs.add(file.path.replaceAll(path,"")); + if (file is Directory) { + dirs.add(file.path.replaceAll(path, '')); } }); return dirs; } + @override Widget build(BuildContext context) { - String title = "Select a Directory"; - if (path != widget.path){ - title = path.substring(path.lastIndexOf("/")+1); + String title = 'Select a Directory'; + if (path != widget.path) { + title = path.substring(path.lastIndexOf('/') + 1); } return WillPopScope( onWillPop: _onWillPop, @@ -148,77 +151,82 @@ class _DirPickerState extends State { ), body: Center( child: FutureBuilder( - future: getDirs(), - builder: (BuildContext context, AsyncSnapshot snapshot){ - if (snapshot.connectionState == ConnectionState.done){ - if (snapshot.data.length > 0){ - return ListView.builder( - padding: const EdgeInsets.all(8), - itemCount: snapshot.data.length, - itemBuilder: (BuildContext context, int index) { - return GestureDetector( - onTap: (){ - setState(() { - if (!path.endsWith(snapshot.data[index])){ - path += snapshot.data[index]; - print(path); - } - settingsHandler.extPathOverride = "$path/"; - }); - }, - child: SizedBox( - height: 50, - child: Center(child: Text(snapshot.data[index])), - ), - ); - } - ); - } else if (title == "emulated"){ - return ListView(children: [ - GestureDetector( - onTap: (){ + future: getDirs(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data.length > 0) { + return ListView.builder( + padding: const EdgeInsets.all(8), + itemCount: snapshot.data.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onTap: () { + setState(() { + if (!path.endsWith(snapshot.data[index])) { + path += snapshot.data[index]; + print(path); + } + settingsHandler.extPathOverride = '$path/'; + }); + }, + child: SizedBox( + height: 50, + child: Center(child: Text(snapshot.data[index])), + ), + ); + }, + ); + } else if (title == 'emulated') { + return ListView( + children: [ + GestureDetector( + onTap: () { setState(() { - if (!path.endsWith("0")){ - path += "/0"; + if (!path.endsWith('0')) { + path += '/0'; print(path); } - settingsHandler.extPathOverride = "$path/"; + settingsHandler.extPathOverride = '$path/'; }); }, child: const SizedBox( - height: 50, - child: Center(child: Text("/0")), - ), + height: 50, + child: Center(child: Text('/0')), ), - ],); - } else { - return Container(); - } - } else { - return const Center( - child: CircularProgressIndicator() + ), + ], ); + } else { + return Container(); } + } else { + return const Center( + child: CircularProgressIndicator(), + ); } + }, ), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ - path == widget.path - ? Container() - : FloatingActionButton( + if (path == widget.path) + Container() + else + FloatingActionButton( heroTag: null, onPressed: () { - if (isWritable()){ + if (isWritable()) { Navigator.of(context).pop('$path/'); } }, backgroundColor: Theme.of(context).colorScheme.secondary, child: const Icon(Icons.check), ), - Container(width: 5,), + Container( + width: 5, + ), FloatingActionButton( heroTag: null, onPressed: () { @@ -232,9 +240,7 @@ class _DirPickerState extends State { title: 'Directory Name', hintText: 'Directory Name', onlyInput: true, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp('[aA-zZ]')) - ], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[aA-zZ]'))], ), actionButtons: [ const CancelButton(), @@ -254,7 +260,7 @@ class _DirPickerState extends State { child: const Icon(Icons.add), ), ], - ) + ), ), ); } diff --git a/lib/src/pages/settings/gallery_page.dart b/lib/src/pages/settings/gallery_page.dart index 1632f9db..825d2fe5 100644 --- a/lib/src/pages/settings/gallery_page.dart +++ b/lib/src/pages/settings/gallery_page.dart @@ -6,7 +6,7 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class GalleryPage extends StatefulWidget { - const GalleryPage({Key? key}) : super(key: key); + const GalleryPage({super.key}); @override State createState() => _GalleryPageState(); } @@ -37,7 +37,9 @@ class _GalleryPageState extends State { galleryScrollDirection = settingsHandler.galleryScrollDirection; shareAction = settingsHandler.shareAction; - if (!settingsHandler.hasHydrus && settingsHandler.shareAction == "Hydrus") shareAction = "Ask"; + if (!settingsHandler.hasHydrus && settingsHandler.shareAction == 'Hydrus') { + shareAction = 'Ask'; + } zoomButtonPosition = settingsHandler.zoomButtonPosition; changePageButtonsPosition = settingsHandler.changePageButtonsPosition; @@ -70,10 +72,10 @@ class _GalleryPageState extends State { settingsHandler.useVolumeButtonsForScroll = useVolumeButtonsForScroll; settingsHandler.wakeLockEnabled = wakeLockEnabled; if (int.parse(scrollSpeedController.text) < 100) { - scrollSpeedController.text = "100"; + scrollSpeedController.text = '100'; } if (int.parse(galleryAutoScrollController.text) < 800) { - galleryAutoScrollController.text = "800"; + galleryAutoScrollController.text = '800'; } settingsHandler.volumeButtonsScrollSpeed = int.parse(scrollSpeedController.text); settingsHandler.galleryAutoScrollTime = int.parse(galleryAutoScrollController.text); @@ -82,7 +84,7 @@ class _GalleryPageState extends State { } settingsHandler.preloadCount = int.parse(preloadController.text); // Set settingshandler values here - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); return result; } @@ -99,7 +101,7 @@ class _GalleryPageState extends State { child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Gallery"), + title: const Text('Gallery'), ), body: Center( child: ListView( @@ -107,7 +109,7 @@ class _GalleryPageState extends State { SettingsTextInput( controller: preloadController, title: 'Gallery View Preload', - hintText: "Images to preload", + hintText: 'Images to preload', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['preloadCount']!['default']!.toString(), @@ -116,7 +118,7 @@ class _GalleryPageState extends State { numberMin: 0, numberMax: 5, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); + final int? parse = int.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { @@ -146,10 +148,10 @@ class _GalleryPageState extends State { return const SettingsDialog( title: Text('Gallery Quality'), contentItems: [ - Text("The gallery quality changes the resolution of images in the gallery viewer."), + Text('The gallery quality changes the resolution of images in the gallery viewer.'), Text(''), - Text(" - Sample - Medium resolution"), - Text(" - Full Res - Full resolution"), + Text(' - Sample - Medium resolution'), + Text(' - Full Res - Full resolution'), ], ); }, @@ -185,17 +187,17 @@ class _GalleryPageState extends State { return SettingsDialog( title: const Text('Share Actions'), contentItems: [ - const Text("- Ask - always ask what to share"), - const Text("- Post URL"), - const Text("- File URL - shares direct link to the original file (may not work with some sites, e.g. Sankaku)"), - const Text("- File - shares viewed file itself"), - if (hasHydrus) const Text("- Hydrus - sends the post url to Hydrus for import"), + const Text('- Ask - always ask what to share'), + const Text('- Post URL'), + const Text('- File URL - shares direct link to the original file (may not work with some sites, e.g. Sankaku)'), + const Text('- File - shares viewed file itself'), + if (hasHydrus) const Text('- Hydrus - sends the post url to Hydrus for import'), const Text(''), const Text( - "[Note]: If File is saved in cache, it will be loaded from there. Otherwise it will be loaded again from network which can take some time.", + '[Note]: If File is saved in cache, it will be loaded from there. Otherwise it will be loaded again from network which can take some time.', ), const Text(''), - const Text("[Tip]: You can open Share Actions Menu by long pressing Share button.") + const Text('[Tip]: You can open Share Actions Menu by long pressing Share button.') ], ); }, @@ -259,9 +261,9 @@ class _GalleryPageState extends State { return const SettingsDialog( title: Text('Buttons Order'), contentItems: [ - Text("Long press to change item order."), - Text("First 4 buttons from this list will be always visible on Toolbar."), - Text("Other buttons will be in overflow (three dots) menu."), + Text('Long press to change item order.'), + Text('First 4 buttons from this list will be always visible on Toolbar.'), + Text('Other buttons will be in overflow (three dots) menu.'), ], ); }, @@ -280,7 +282,7 @@ class _GalleryPageState extends State { FlashElements.showSnackbar( context: context, title: const Text( - "Long Press to move items", + 'Long Press to move items', style: TextStyle(fontSize: 20), ), leadingIcon: Icons.warning_amber, @@ -326,7 +328,7 @@ class _GalleryPageState extends State { return const SettingsDialog( title: Text('Disable Video'), contentItems: [ - Text("Useful on low end devices that crash when trying to load videos."), + Text('Useful on low end devices that crash when trying to load videos.'), Text("Replaces video with text that says 'Video disabled'."), ], ); @@ -362,8 +364,8 @@ class _GalleryPageState extends State { setState(() { shitDevice = newValue; if (shitDevice) { - preloadController.text = "0"; - galleryMode = "Sample"; + preloadController.text = '0'; + galleryMode = 'Sample'; autoPlay = false; // TODO set thumbnails quality to low? } @@ -379,13 +381,13 @@ class _GalleryPageState extends State { return const SettingsDialog( title: Text('Low Performance Mode'), contentItems: [ - Text("Recommended for old devices and devices with RAM < 2GB."), + Text('Recommended for old devices and devices with RAM < 2GB.'), Text(''), - Text("- Disables loading progress information"), - Text("- Sets optimal settings for:"), - Text(" - Gallery Quality"), - Text(" - Gallery Preload"), - Text(" - Video Auto Play"), + Text('- Disables loading progress information'), + Text('- Sets optimal settings for:'), + Text(' - Gallery Quality'), + Text(' - Gallery Preload'), + Text(' - Video Auto Play'), ], ); }, @@ -412,14 +414,14 @@ class _GalleryPageState extends State { return const SettingsDialog( title: Text('Volume Buttons Scrolling'), contentItems: [ - Text("Allows to scroll through previews grid and gallery items using volume buttons"), + Text('Allows to scroll through previews grid and gallery items using volume buttons'), Text(''), - Text(" - Volume Down - next item"), - Text(" - Volume Up - previous item"), + Text(' - Volume Down - next item'), + Text(' - Volume Up - previous item'), Text(''), - Text("On videos:"), - Text(" - App Bar visible - controls volume"), - Text(" - App Bar hidden - controls scrolling"), + Text('On videos:'), + Text(' - App Bar visible - controls volume'), + Text(' - App Bar hidden - controls scrolling'), ], ); }, @@ -430,7 +432,7 @@ class _GalleryPageState extends State { SettingsTextInput( controller: scrollSpeedController, title: 'Buttons Scroll Speed', - hintText: "Scroll Speed", + hintText: 'Scroll Speed', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['volumeButtonsScrollSpeed']!['default']!.toString(), @@ -439,7 +441,7 @@ class _GalleryPageState extends State { numberMin: 100, numberMax: double.infinity, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); + final int? parse = int.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { @@ -455,7 +457,7 @@ class _GalleryPageState extends State { SettingsTextInput( controller: galleryAutoScrollController, title: 'AutoScroll Timeout (in ms)', - hintText: "AutoScroll Timeout (in ms)", + hintText: 'AutoScroll Timeout (in ms)', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['galleryAutoScrollTime']!['default']!.toString(), @@ -464,7 +466,7 @@ class _GalleryPageState extends State { numberMin: 100, numberMax: double.infinity, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); + final int? parse = int.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { @@ -482,7 +484,7 @@ class _GalleryPageState extends State { return const SettingsDialog( title: Text('AutoScroll / Slideshow'), contentItems: [ - Text("[WIP] Videos and gifs must be scrolled manually for now."), + Text('[WIP] Videos and gifs must be scrolled manually for now.'), ], ); }, diff --git a/lib/src/pages/settings/logger_page.dart b/lib/src/pages/settings/logger_page.dart index cd80c91f..d56c3192 100644 --- a/lib/src/pages/settings/logger_page.dart +++ b/lib/src/pages/settings/logger_page.dart @@ -10,7 +10,7 @@ import 'package:lolisnatcher/src/utils/logger.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class LoggerPage extends StatefulWidget { - const LoggerPage({Key? key}) : super(key: key); + const LoggerPage({super.key}); @override State createState() => _LoggerPageState(); } @@ -38,14 +38,14 @@ class _LoggerPageState extends State { @override Widget build(BuildContext context) { - bool allLogTypesEnabled = enabledLogTypes.toSet().toList().length == LogTypes.values.length; + final bool allLogTypesEnabled = enabledLogTypes.toSet().toList().length == LogTypes.values.length; return WillPopScope( onWillPop: _onWillPop, - child:Scaffold( + child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Logger"), + title: const Text('Logger'), actions: [ Switch( value: allLogTypesEnabled, @@ -53,21 +53,21 @@ class _LoggerPageState extends State { setState(() { if (newValue) { enabledLogTypes = [...LogTypes.values]; - Logger.Inst().log("Enabled all log types", "LoggerPage", "build", LogTypes.settingsLoad); + Logger.Inst().log('Enabled all log types', 'LoggerPage', 'build', LogTypes.settingsLoad); } else { enabledLogTypes = []; - Logger.Inst().log("Disabled all log types", "LoggerPage", "build", LogTypes.settingsLoad); + Logger.Inst().log('Disabled all log types', 'LoggerPage', 'build', LogTypes.settingsLoad); } }); - } - ), - ], + }, ), + ], + ), body: Center( child: ListView.builder( itemCount: LogTypes.values.length + 2, itemBuilder: (context, index) { - if(index == 0) { + if (index == 0) { return SettingsButton( name: 'Open Logger Output', action: () { @@ -85,7 +85,7 @@ class _LoggerPageState extends State { ); } - if(index == 1) { + if (index == 1) { return const SettingsButton( name: '', enabled: false, @@ -98,12 +98,12 @@ class _LoggerPageState extends State { value: enabledLogTypes.contains(logType), onChanged: (newValue) { setState(() { - if (enabledLogTypes.contains(logType)){ + if (enabledLogTypes.contains(logType)) { enabledLogTypes.remove(logType); - Logger.Inst().log("Disabled logging for $logType", "LoggerPage", "build", LogTypes.settingsLoad); + Logger.Inst().log('Disabled logging for $logType', 'LoggerPage', 'build', LogTypes.settingsLoad); } else { enabledLogTypes.add(logType); - Logger.Inst().log("Enabled logging for $logType", "LoggerPage", "build", LogTypes.settingsLoad); + Logger.Inst().log('Enabled logging for $logType', 'LoggerPage', 'build', LogTypes.settingsLoad); } }); }, @@ -116,4 +116,3 @@ class _LoggerPageState extends State { ); } } - diff --git a/lib/src/pages/settings/save_cache_page.dart b/lib/src/pages/settings/save_cache_page.dart index 0d1f454f..dd6a33f0 100644 --- a/lib/src/pages/settings/save_cache_page.dart +++ b/lib/src/pages/settings/save_cache_page.dart @@ -15,7 +15,7 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class SaveCachePage extends StatefulWidget { - const SaveCachePage({Key? key}) : super(key: key); + const SaveCachePage({super.key}); @override State createState() => _SaveCachePageState(); @@ -28,10 +28,10 @@ class _SaveCachePageState extends State { final TextEditingController snatchCooldownController = TextEditingController(); final TextEditingController cacheSizeController = TextEditingController(); final TextEditingController userAgentController = TextEditingController(); - + late String videoCacheMode, extPathOverride; bool jsonWrite = false, thumbnailCache = true, mediaCache = false, downloadNotifications = true; - + static const List<_CacheType> cacheTypes = [ _CacheType('Total', null), // TODO ask before deleting favicons, since they cause unneeded network requests on each render if not cached @@ -57,7 +57,7 @@ class _SaveCachePageState extends State { jsonWrite = settingsHandler.jsonWrite; cacheDuration = settingsHandler.cacheDuration; cacheDurationSelected = settingsHandler.map['cacheDuration']!['options']!.firstWhere((dur) { - return dur["value"].inSeconds == cacheDuration.inSeconds; + return dur['value'].inSeconds == cacheDuration.inSeconds; }); cacheSizeController.text = settingsHandler.cacheSize.toString(); downloadNotifications = settingsHandler.downloadNotifications; @@ -73,17 +73,17 @@ class _SaveCachePageState extends State { super.dispose(); } - void getCacheStats(String? folder) async { - if(folder != null) { + Future getCacheStats(String? folder) async { + if (folder != null) { // delete selected folder stats + global cacheStats.removeWhere((e) => e['type'] == folder || e['type'] == '' || e['type'] == null); } else { cacheStats = []; } - var cacheTypesToGet = folder == null ? cacheTypes : cacheTypes.where((e) => e.folder == folder || e.folder == null).toList(); + final cacheTypesToGet = folder == null ? cacheTypes : cacheTypes.where((e) => e.folder == folder || e.folder == null).toList(); - for(_CacheType type in cacheTypesToGet) { + for (final _CacheType type in cacheTypesToGet) { final ReceivePort receivePort = ReceivePort(); isolate = await Isolate.spawn(_isolateEntry, receivePort.sendPort); @@ -94,9 +94,9 @@ class _SaveCachePageState extends State { 'path': await ServiceHandler.getCacheDir(), 'type': type.folder, }); - }else { + } else { cacheStats.add(data); - setState(() { }); + setState(() {}); } } }); @@ -104,7 +104,7 @@ class _SaveCachePageState extends State { return; } - static _isolateEntry(dynamic d) async { + static Future _isolateEntry(dynamic d) async { final ReceivePort receivePort = ReceivePort(); d.send(receivePort.sendPort); @@ -124,36 +124,38 @@ class _SaveCachePageState extends State { settingsHandler.extPathOverride = extPathOverride; settingsHandler.downloadNotifications = downloadNotifications; settingsHandler.customUserAgent = userAgentController.text; - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); return result; } void setPath(String path) { - print("path is $path"); if (path.isNotEmpty) { settingsHandler.extPathOverride = path; } } Widget buildCacheButton(_CacheType type) { - Map stat = cacheStats.firstWhere((stat) => stat['type'] == type.folder, orElse: () => ({'type': 'loading', 'totalSize': -1, 'fileNum': -1})); - String? folder = type.folder; - String label = type.label; - String size = Tools.formatBytes(stat['totalSize']!, 2); - int fileCount = stat['fileNum'] ?? 0; - bool isEmpty = stat['fileNum'] == 0 || stat['totalSize'] == 0; - bool isLoading = stat['type'] == 'loading'; - String text = isLoading - ? 'Loading...' - : (isEmpty ? 'Empty' : '$size in ${fileCount.toString()} ${Tools.pluralize('file', fileCount)}'); + final Map stat = cacheStats.firstWhere( + (stat) => stat['type'] == type.folder, + orElse: () => { + 'type': 'loading', + 'totalSize': -1, + 'fileNum': -1, + }, + ); + final String? folder = type.folder; + final String label = type.label; + final String size = Tools.formatBytes(stat['totalSize']!, 2); + final int fileCount = stat['fileNum'] ?? 0; + final bool isEmpty = stat['fileNum'] == 0 || stat['totalSize'] == 0; + final bool isLoading = stat['type'] == 'loading'; + final String text = isLoading ? 'Loading...' : (isEmpty ? 'Empty' : '$size in $fileCount ${Tools.pluralize('file', fileCount)}'); - bool allowedToClear = folder != null && folder != 'favicons' && !isEmpty; + final bool allowedToClear = folder != null && folder != 'favicons' && !isEmpty; return SettingsButton( name: '$label: $text', - icon: isLoading - ? const CircularProgressIndicator() - : Icon(allowedToClear ? Icons.delete_forever : null), + icon: isLoading ? const CircularProgressIndicator() : Icon(allowedToClear ? Icons.delete_forever : null), action: () async { if (allowedToClear) { FlashElements.showSnackbar( @@ -174,7 +176,7 @@ class _SaveCachePageState extends State { sideColor: Colors.yellow, ); await imageWriter.deleteCacheFolder(folder); - getCacheStats(folder); + await getCacheStats(folder); } }, ); @@ -184,10 +186,10 @@ class _SaveCachePageState extends State { Widget build(BuildContext context) { return WillPopScope( onWillPop: _onWillPop, - child:Scaffold( + child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Snatching & Caching"), + title: const Text('Snatching & Caching'), ), body: Center( child: ListView( @@ -195,23 +197,21 @@ class _SaveCachePageState extends State { SettingsTextInput( controller: snatchCooldownController, title: 'Snatch Cooldown (ms)', - hintText: "Timeout between snatching images in ms", + hintText: 'Timeout between snatching images in ms', inputType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], + inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['snatchCooldown']!['default']!.toString(), numberButtons: true, numberStep: 50, numberMin: 0, numberMax: double.infinity, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); - if(value == null || value.isEmpty) { + final int? parse = int.tryParse(value ?? ''); + if (value == null || value.isEmpty) { return 'Please enter a value'; - } else if(parse == null) { + } else if (parse == null) { return 'Please enter a valid timeout value'; - } else if(parse < 10) { + } else if (parse < 10) { return 'Please enter a value bigger than 10ms'; } else { return null; @@ -236,7 +236,6 @@ class _SaveCachePageState extends State { }, title: 'Write Image Data to JSON on save', ), - SettingsButton( name: 'Set Storage Directory', subtitle: Text(extPathOverride.isEmpty ? '...' : 'Current: $extPathOverride'), @@ -247,7 +246,7 @@ class _SaveCachePageState extends State { if (Platform.isAndroid) { final String newPath = await ServiceHandler.setExtDir(); extPathOverride = newPath; - setState(() { }); + setState(() {}); // TODO Store uri in settings and make another button so can set seetings dir and pictures dir } else { // TODO need to update dir picker to work on desktop @@ -270,7 +269,6 @@ class _SaveCachePageState extends State { // } // setPath(value ?? ""); - FlashElements.showSnackbar( context: context, title: const Text( @@ -288,7 +286,7 @@ class _SaveCachePageState extends State { } }, ), - if(extPathOverride.isNotEmpty) + if (extPathOverride.isNotEmpty) SettingsButton( name: 'Reset Storage Directory', icon: const Icon(Icons.refresh), @@ -299,7 +297,6 @@ class _SaveCachePageState extends State { }, ), const SettingsButton(name: '', enabled: false), - SettingsToggle( value: thumbnailCache, onChanged: (newValue) { @@ -337,56 +334,51 @@ class _SaveCachePageState extends State { title: Text('Video Cache Modes'), contentItems: [ Text("- Stream - Don't cache, start playing as soon as possible"), - Text("- Cache - Saves the file to device storage, plays only when download is complete"), - Text("- Stream+Cache - Mix of both, but currently leads to double download"), + Text('- Cache - Saves the file to device storage, plays only when download is complete'), + Text('- Stream+Cache - Mix of both, but currently leads to double download'), Text(''), Text("[Note]: Videos will cache only if 'Cache Media' is enabled."), Text(''), - Text("[Warning]: On desktop builds Stream mode can work incorrectly for some Boorus.") + Text('[Warning]: On desktop builds Stream mode can work incorrectly for some Boorus.') ], ); - } + }, ); }, ), ), - SettingsDropdown( - value: (cacheDurationSelected?["label"] ?? '') as String, - items: List.from(settingsHandler.map['cacheDuration']!['options'].map((dur) { - return dur["label"]; - })), + value: (cacheDurationSelected?['label'] ?? '') as String, + items: List.from( + settingsHandler.map['cacheDuration']!['options'].map((dur) { + return dur['label']; + }), + ), onChanged: (String? newValue) { setState(() { cacheDurationSelected = settingsHandler.map['cacheDuration']!['options'].firstWhere((dur) { - return dur["label"] == newValue; + return dur['label'] == newValue; }); - cacheDuration = cacheDurationSelected!["value"]; + cacheDuration = cacheDurationSelected!['value']; }); }, title: 'Delete Cache after:', ), - SettingsTextInput( controller: cacheSizeController, title: 'Cache Size Limit (in GB)', - hintText: "Maximum Total Cache Size", + hintText: 'Maximum Total Cache Size', inputType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], + inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['cacheSize']!['default']!.toString(), numberButtons: true, numberStep: 1, numberMin: 0, numberMax: double.infinity, ), - const SettingsButton(name: '', enabled: false), - const SettingsButton(name: 'Cache Stats:'), ...cacheTypes.map(buildCacheButton), - SettingsButton( name: 'Clear cache completely', icon: Icon(Icons.delete_forever, color: Theme.of(context).colorScheme.error), @@ -418,13 +410,11 @@ class _SaveCachePageState extends State { ); await imageWriter.deleteCacheFolder(''); // await serviceHandler.emptyCache(); - getCacheStats(null); + await getCacheStats(null); }, drawBottomBorder: false, ), - const SettingsButton(name: '', enabled: false), - SettingsTextInput( controller: userAgentController, title: 'Custom User Agent', @@ -465,7 +455,6 @@ class _SaveCachePageState extends State { } } - class _CacheType { const _CacheType( this.label, @@ -474,4 +463,4 @@ class _CacheType { final String label; final String? folder; -} \ No newline at end of file +} diff --git a/lib/src/pages/settings/settings_template.dart b/lib/src/pages/settings/settings_template.dart index a297fcd6..0228bc8c 100644 --- a/lib/src/pages/settings/settings_template.dart +++ b/lib/src/pages/settings/settings_template.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; class SettingsTemplate extends StatefulWidget { - const SettingsTemplate({Key? key}) : super(key: key); + const SettingsTemplate({super.key}); @override State createState() => _SettingsTemplateState(); } @@ -12,14 +12,14 @@ class _SettingsTemplateState extends State { final SettingsHandler settingsHandler = SettingsHandler.instance; @override - void initState(){ + void initState() { super.initState(); } //called when page is clsoed, sets settingshandler variables and then writes settings to disk Future _onWillPop() async { // Set settingshandler values here - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); return result; } @@ -27,10 +27,10 @@ class _SettingsTemplateState extends State { Widget build(BuildContext context) { return WillPopScope( onWillPop: _onWillPop, - child:Scaffold( + child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - title: const Text("Title"), + title: const Text('Title'), ), body: Center( child: ListView( diff --git a/lib/src/pages/settings/tags_filters_page.dart b/lib/src/pages/settings/tags_filters_page.dart index 77af170e..88a43bee 100644 --- a/lib/src/pages/settings/tags_filters_page.dart +++ b/lib/src/pages/settings/tags_filters_page.dart @@ -10,7 +10,7 @@ import 'package:lolisnatcher/src/widgets/tags_filters/tf_list.dart'; import 'package:lolisnatcher/src/widgets/tags_filters/tf_settings_list.dart'; class TagsFiltersPage extends StatefulWidget { - const TagsFiltersPage({Key? key}) : super(key: key); + const TagsFiltersPage({super.key}); @override State createState() => _TagsFiltersPageState(); @@ -58,11 +58,11 @@ class _TagsFiltersPageState extends State with SingleTickerProv settingsHandler.lovedTags = settingsHandler.cleanTagsList(lovedList); settingsHandler.filterHated = filterHated; settingsHandler.filterFavourites = filterFavourites; - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); return result; } - List getTagsList(type) { + List getTagsList(String type) { List tagsList = []; if (type == 'Hated') { tagsList = hatedList; @@ -77,11 +77,11 @@ class _TagsFiltersPageState extends State with SingleTickerProv } void addTag(String newTag, String type) { - if(newTag.isEmpty) { + if (newTag.isEmpty) { return; } - List changedList = getTagsList(type); + final List changedList = getTagsList(type); if (changedList.contains(newTag)) { duplicateMessage(newTag, type); } else { @@ -92,15 +92,15 @@ class _TagsFiltersPageState extends State with SingleTickerProv } void editTag(String oldTag, String newTag, String type) { - if(newTag.isEmpty) { + if (newTag.isEmpty) { return; } - List changedList = getTagsList(type); + final List changedList = getTagsList(type); if (changedList.contains(newTag)) { duplicateMessage(newTag, type); } else { - int index = changedList.indexOf(oldTag); + final int index = changedList.indexOf(oldTag); changedList[index] = newTag; changedList.sort(sortTags); updateState(); @@ -108,7 +108,7 @@ class _TagsFiltersPageState extends State with SingleTickerProv } void deleteTag(String tag, String type) { - List changedList = getTagsList(type); + final List changedList = getTagsList(type); changedList.remove(tag); changedList.sort(sortTags); updateState(); @@ -117,7 +117,7 @@ class _TagsFiltersPageState extends State with SingleTickerProv void duplicateMessage(String tag, String type) { FlashElements.showSnackbar( context: context, - title: const Text("Duplicate tag!", style: TextStyle(fontSize: 20)), + title: const Text('Duplicate tag!', style: TextStyle(fontSize: 20)), content: Text("'$tag' is already in $type list", style: const TextStyle(fontSize: 16)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.yellow, @@ -155,7 +155,7 @@ class _TagsFiltersPageState extends State with SingleTickerProv child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Filters Editor"), + title: const Text('Filters Editor'), bottom: TabBar( controller: tabController, indicatorColor: Theme.of(context).colorScheme.secondary, diff --git a/lib/src/pages/settings/theme_page.dart b/lib/src/pages/settings/theme_page.dart index b373414e..a0f0f5d1 100644 --- a/lib/src/pages/settings/theme_page.dart +++ b/lib/src/pages/settings/theme_page.dart @@ -15,7 +15,7 @@ import 'package:lolisnatcher/src/utils/debouncer.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class ThemePage extends StatefulWidget { - const ThemePage({Key? key}) : super(key: key); + const ThemePage({super.key}); @override State createState() => _ThemePageState(); @@ -53,7 +53,7 @@ class _ThemePageState extends State { checkSdk(); } - void checkSdk() async { + Future checkSdk() async { if (Platform.isAndroid) { currentSdk = await ServiceHandler.getAndroidSDKVersion(); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -92,11 +92,11 @@ class _ThemePageState extends State { } else { settingsHandler.drawerMascotPathOverride = mascotPathOverride; } - bool result = await settingsHandler.saveSettings(restate: withRestate); + final bool result = await settingsHandler.saveSettings(restate: withRestate); return result; } - void updateTheme({bool withRestate = false}) async { + Future updateTheme({bool withRestate = false}) async { // instantly do local restate setState(() {}); @@ -179,7 +179,7 @@ class _ThemePageState extends State { child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Themes"), + title: const Text('Themes'), ), body: Center( child: ListView( @@ -197,36 +197,42 @@ class _ThemePageState extends State { const double size = 40; switch (prettyValue) { - case ("Dark"): - return Row(children: [ - const SizedBox(width: size, child: Icon(Icons.dark_mode)), - Text(prettyValue), - ]); - case ("Light"): - return Row(children: [ - const SizedBox(width: size, child: Icon(Icons.light_mode)), - Text(prettyValue), - ]); - case ("System"): - return Row(children: [ - SizedBox( - width: size, - child: Stack( - alignment: Alignment.center, - children: [ - ClipPath( - clipper: SunClipper(), - child: const Padding(padding: EdgeInsets.only(right: 8), child: Icon(Icons.light_mode)), - ), - ClipPath( - clipper: MoonClipper(), - child: const Padding(padding: EdgeInsets.only(left: 5), child: Icon(Icons.dark_mode)), - ), - ], + case 'Dark': + return Row( + children: [ + const SizedBox(width: size, child: Icon(Icons.dark_mode)), + Text(prettyValue), + ], + ); + case 'Light': + return Row( + children: [ + const SizedBox(width: size, child: Icon(Icons.light_mode)), + Text(prettyValue), + ], + ); + case 'System': + return Row( + children: [ + SizedBox( + width: size, + child: Stack( + alignment: Alignment.center, + children: [ + ClipPath( + clipper: SunClipper(), + child: const Padding(padding: EdgeInsets.only(right: 8), child: Icon(Icons.light_mode)), + ), + ClipPath( + clipper: MoonClipper(), + child: const Padding(padding: EdgeInsets.only(left: 5), child: Icon(Icons.dark_mode)), + ), + ], + ), ), - ), - Text(prettyValue), - ]); + Text(prettyValue), + ], + ); default: return Text(prettyValue); } @@ -273,42 +279,44 @@ class _ThemePageState extends State { }, title: 'Theme', itemBuilder: (String value) { - ThemeItem theme = settingsHandler.map['theme']!['options'].firstWhere((e) => e.name == value); - Color? primary = theme.name == 'Custom' ? primaryPickerColor : theme.primary; - Color? accent = theme.name == 'Custom' ? accentPickerColor : theme.accent; + final ThemeItem theme = settingsHandler.map['theme']!['options'].firstWhere((e) => e.name == value); + final Color? primary = theme.name == 'Custom' ? primaryPickerColor : theme.primary; + final Color? accent = theme.name == 'Custom' ? accentPickerColor : theme.accent; - return Row(children: [ - Center( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.grey.shade500, width: 1.5), - shape: BoxShape.rectangle, - color: primary, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - color: primary, - height: 10, - width: 60, - ), - Container( - color: accent, - height: 10, - width: 60, - ), - ], + return Row( + children: [ + Center( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade500, width: 1.5), + shape: BoxShape.rectangle, + color: primary, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + color: primary, + height: 10, + width: 60, + ), + Container( + color: accent, + height: 10, + width: 60, + ), + ], + ), ), ), ), - ), - const SizedBox(width: 10), - Text(value), - ]); + const SizedBox(width: 10), + Text(value), + ], + ); }, ), if (theme.name == 'Custom' && !useDynamicColor) @@ -332,7 +340,7 @@ class _ThemePageState extends State { }, )) { primaryPickerColor = colorBeforeDialog; - updateTheme(); + await updateTheme(); } }, trailingIcon: ColorIndicator( @@ -365,7 +373,7 @@ class _ThemePageState extends State { }, )) { accentPickerColor = colorBeforeDialog; - updateTheme(); + await updateTheme(); } }, trailingIcon: ColorIndicator( @@ -383,7 +391,7 @@ class _ThemePageState extends State { icon: const Icon(Icons.refresh), drawTopBorder: true, action: () { - ThemeItem theme = settingsHandler.map['theme']!['default']; + final ThemeItem theme = settingsHandler.map['theme']!['default']; primaryPickerColor = theme.primary; accentPickerColor = theme.accent; updateTheme(); @@ -415,11 +423,11 @@ class _ThemePageState extends State { icon: const Icon(Icons.delete_forever), drawTopBorder: true, action: () async { - File file = File(mascotPathOverride); + final File file = File(mascotPathOverride); if (await file.exists()) { await file.delete(); } - mascotPathOverride = ""; + mascotPathOverride = ''; setState(() {}); }, ), @@ -434,7 +442,7 @@ class _ThemePageState extends State { class SunClipper extends CustomClipper { @override Path getClip(Size size) { - var path = Path(); + final path = Path(); path.lineTo(0, size.height); path.lineTo(size.width * 0.37, size.height); path.lineTo(size.width * 0.37, 0); @@ -450,7 +458,7 @@ class SunClipper extends CustomClipper { class MoonClipper extends CustomClipper { @override Path getClip(Size size) { - var path = Path(); + final path = Path(); path.lineTo(size.width, 0); path.lineTo(size.width, size.height); path.lineTo(size.width * 0.45, size.height); diff --git a/lib/src/pages/settings/user_interface_page.dart b/lib/src/pages/settings/user_interface_page.dart index b2a56cdd..eba8ca32 100644 --- a/lib/src/pages/settings/user_interface_page.dart +++ b/lib/src/pages/settings/user_interface_page.dart @@ -9,7 +9,7 @@ import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class UserInterfacePage extends StatefulWidget { - const UserInterfacePage({Key? key}) : super(key: key); + const UserInterfacePage({super.key}); @override State createState() => _UserInterfacePageState(); @@ -53,7 +53,7 @@ class _UserInterfacePageState extends State { settingsHandler.landscapeColumns = int.parse(columnsLandscapeController.text); settingsHandler.portraitColumns = int.parse(columnsPortraitController.text); settingsHandler.mousewheelScrollSpeed = double.parse(mouseSpeedController.text); - bool result = await settingsHandler.saveSettings(restate: false); + final bool result = await settingsHandler.saveSettings(restate: false); return result; } @@ -64,7 +64,7 @@ class _UserInterfacePageState extends State { child: Scaffold( resizeToAvoidBottomInset: true, appBar: AppBar( - title: const Text("Interface"), + title: const Text('Interface'), ), body: Center( child: ListView( @@ -87,14 +87,14 @@ class _UserInterfacePageState extends State { return const SettingsDialog( title: Text('App UI Mode'), contentItems: [ - Text("- Mobile - Normal Mobile UI"), - Text("- Desktop - Ahoviewer Style UI"), + Text('- Mobile - Normal Mobile UI'), + Text('- Desktop - Ahoviewer Style UI'), SizedBox(height: 10), Text( - "[Warning]: Do not set UI Mode to Desktop on a phone you might break the app and might have to wipe your settings including booru configs.", + '[Warning]: Do not set UI Mode to Desktop on a phone you might break the app and might have to wipe your settings including booru configs.', ), - Text("If you are on android versions smaller than 11 you can remove the appMode line from /LoliSnatcher/config/settings.json"), - Text("If you are on android 11 or higher you will have to wipe app data via system settings"), + Text('If you are on android versions smaller than 11 you can remove the appMode line from /LoliSnatcher/config/settings.json'), + Text('If you are on android 11 or higher you will have to wipe app data via system settings'), ], ); }, @@ -120,7 +120,7 @@ class _UserInterfacePageState extends State { return const SettingsDialog( title: Text('Hand Side'), contentItems: [ - Text("Moves some parts of the UI to the selected side of the screen"), + Text('Moves some parts of the UI to the selected side of the screen'), Text('Currently only changes the position of the main drawer button'), Text('[This is a WIP feature, will include more changes in the future versions]') ], @@ -133,7 +133,7 @@ class _UserInterfacePageState extends State { SettingsTextInput( controller: columnsPortraitController, title: 'Preview Columns Portrait', - hintText: "Columns in Portrait orientation", + hintText: 'Columns in Portrait orientation', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], onChanged: (String? text) { @@ -145,7 +145,7 @@ class _UserInterfacePageState extends State { numberMin: 1, numberMax: double.infinity, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); + final int? parse = int.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { @@ -160,7 +160,7 @@ class _UserInterfacePageState extends State { SettingsTextInput( controller: columnsLandscapeController, title: 'Preview Columns Landscape', - hintText: "Columns in Landscape orientation", + hintText: 'Columns in Landscape orientation', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['landscapeColumns']!['default']!.toString(), @@ -169,7 +169,7 @@ class _UserInterfacePageState extends State { numberMin: 1, numberMax: double.infinity, validator: (String? value) { - int? parse = int.tryParse(value ?? ''); + final int? parse = int.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { @@ -197,11 +197,11 @@ class _UserInterfacePageState extends State { return const SettingsDialog( title: Text('Preview Quality'), contentItems: [ - Text("This setting changes the resolution of images in the preview grid"), - Text(" - Sample - Medium resolution, app will also load a Thumbnail quality as a placeholder while higher quality loads"), - Text(" - Thumbnail - Low resolution"), - Text(" "), - Text("[Note]: Sample quality can noticeably degrade performance, especially if you have too many columns in preview grid") + Text('This setting changes the resolution of images in the preview grid'), + Text(' - Sample - Medium resolution, app will also load a Thumbnail quality as a placeholder while higher quality loads'), + Text(' - Thumbnail - Low resolution'), + Text(' '), + Text('[Note]: Sample quality can noticeably degrade performance, especially if you have too many columns in preview grid') ], ); }, @@ -222,16 +222,16 @@ class _UserInterfacePageState extends State { SettingsTextInput( controller: mouseSpeedController, title: 'Mouse Wheel Scroll Modifer', - hintText: "Scroll modifier", + hintText: 'Scroll modifier', inputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.map['mousewheelScrollSpeed']!['default']!.toString(), numberButtons: true, numberStep: 0.5, numberMin: 0.1, - numberMax: 20.0, + numberMax: 20, validator: (String? value) { - double? parse = double.tryParse(value ?? ''); + final double? parse = double.tryParse(value ?? ''); if (value == null || value.isEmpty) { return 'Please enter a value'; } else if (parse == null) { diff --git a/lib/src/pages/settings_page.dart b/lib/src/pages/settings_page.dart index ac641ed3..148adc80 100644 --- a/lib/src/pages/settings_page.dart +++ b/lib/src/pages/settings_page.dart @@ -26,11 +26,11 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; /// Then settings page is pretty self explanatory it will display, allow the user to edit and save settings class SettingsPage extends StatelessWidget { - const SettingsPage({Key? key}) : super(key: key); + const SettingsPage({super.key}); Future _onWillPop() async { final SettingsHandler settingsHandler = SettingsHandler.instance; - bool result = await settingsHandler.saveSettings(restate: true); + final bool result = await settingsHandler.saveSettings(restate: true); await settingsHandler.loadSettings(); // await settingsHandler.getBooru(); return result; @@ -45,14 +45,15 @@ class SettingsPage extends StatelessWidget { child: Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - title: const Text("Settings"), + title: const Text('Settings'), leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () async { - if (await _onWillPop()) { - Navigator.of(context).pop(); - } - }), + icon: const Icon(Icons.arrow_back), + onPressed: () async { + if (await _onWillPop()) { + Navigator.of(context).pop(); + } + }, + ), ), body: Center( child: ListView( @@ -106,19 +107,18 @@ class SettingsPage extends StatelessWidget { FlashElements.showSnackbar( context: context, title: const Text( - "Error!", + 'Error!', style: TextStyle(fontSize: 20), ), content: const Text( - "Database must be enabled to use LoliSync"), + 'Database must be enabled to use LoliSync', + ), leadingIcon: Icons.error_outline, leadingIconColor: Colors.red, sideColor: Colors.red, ); }, - page: settingsHandler.dbEnabled - ? () => const LoliSyncPage() - : null, + page: settingsHandler.dbEnabled ? () => const LoliSyncPage() : null, ), const DiscordButton(), SettingsButton( @@ -138,7 +138,8 @@ class SettingsPage extends StatelessWidget { icon: const Icon(Icons.help_center_outlined), action: () { ServiceHandler.launchURL( - "https://github.com/NO-ob/LoliSnatcher_Droid/wiki"); + 'https://github.com/NO-ob/LoliSnatcher_Droid/wiki', + ); }, trailingIcon: const Icon(Icons.exit_to_app), ), @@ -165,7 +166,7 @@ class SettingsPage extends StatelessWidget { } class VersionButton extends StatefulWidget { - const VersionButton({Key? key}) : super(key: key); + const VersionButton({super.key}); @override State createState() => _VersionButtonState(); @@ -178,21 +179,20 @@ class _VersionButtonState extends State { Widget build(BuildContext context) { final SettingsHandler settingsHandler = SettingsHandler.instance; - final String verText = - "Version: ${Constants.appVersion} (${Constants.appBuildNumber})"; - const String buildTypeText = EnvironmentConfig.isFromStore - ? "/ Play" - : (kDebugMode ? "/ Debug" : ""); + const String verText = 'Version: ${Constants.appVersion} (${Constants.appBuildNumber})'; + const String buildTypeText = EnvironmentConfig.isFromStore ? '/ Play' : (kDebugMode ? '/ Debug' : ''); return SettingsButton( - name: "$verText $buildTypeText".trim(), + name: '$verText $buildTypeText'.trim(), icon: const Icon(null), // to align with other items action: () { if (settingsHandler.isDebug.value) { FlashElements.showSnackbar( context: context, - title: const Text("Debug mode is already enabled!", - style: TextStyle(fontSize: 18)), + title: const Text( + 'Debug mode is already enabled!', + style: TextStyle(fontSize: 18), + ), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.yellow, sideColor: Colors.yellow, @@ -203,8 +203,10 @@ class _VersionButtonState extends State { settingsHandler.isDebug.value = true; FlashElements.showSnackbar( context: context, - title: const Text("Debug mode is enabled!", - style: TextStyle(fontSize: 18)), + title: const Text( + 'Debug mode is enabled!', + style: TextStyle(fontSize: 18), + ), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.green, sideColor: Colors.green, @@ -215,14 +217,16 @@ class _VersionButtonState extends State { setState(() {}); }, onLongPress: () { - if (!settingsHandler.isDebug.value) return; + if (!settingsHandler.isDebug.value) { + return; + } // debugTaps = 0; settingsHandler.isDebug.value = false; FlashElements.showSnackbar( context: context, title: const Text( - "Debug mode is disabled!", + 'Debug mode is disabled!', style: TextStyle(fontSize: 18), ), leadingIcon: Icons.warning_amber, @@ -236,7 +240,7 @@ class _VersionButtonState extends State { } class LogsEnabledWarning extends StatelessWidget { - const LogsEnabledWarning({Key? key}) : super(key: key); + const LogsEnabledWarning({super.key}); @override Widget build(BuildContext context) { @@ -250,7 +254,7 @@ class LogsEnabledWarning extends StatelessWidget { } return SettingsButton( - name: "You have enabled logging for:", + name: 'You have enabled logging for:', subtitle: Text( '${enabledLogTypes.map((e) => e.toString())}', style: const TextStyle(fontSize: 12), @@ -260,11 +264,10 @@ class LogsEnabledWarning extends StatelessWidget { FlashElements.showSnackbar( context: context, title: const Text( - "Logging enabled", + 'Logging enabled', style: TextStyle(fontSize: 18), ), - content: - const Text("You can disable logging in the debug settings"), + content: const Text('You can disable logging in the debug settings'), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.yellow, sideColor: Colors.yellow, diff --git a/lib/src/pages/snatcher_page.dart b/lib/src/pages/snatcher_page.dart index d6ed8013..fa956743 100644 --- a/lib/src/pages/snatcher_page.dart +++ b/lib/src/pages/snatcher_page.dart @@ -9,10 +9,9 @@ import 'package:lolisnatcher/src/services/get_perms.dart'; import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; - /// This is the page which allows the user to batch download images class SnatcherPage extends StatefulWidget { - const SnatcherPage({Key? key}) : super(key: key); + const SnatcherPage({super.key}); @override State createState() => _SnatcherPageState(); @@ -44,7 +43,7 @@ class _SnatcherPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Snatcher") + title: const Text('Snatcher'), ), resizeToAvoidBottomInset: true, body: Center( @@ -53,18 +52,16 @@ class _SnatcherPageState extends State { SettingsTextInput( controller: snatcherTagsController, title: 'Tags', - hintText: "Enter Tags", + hintText: 'Enter Tags', inputType: TextInputType.text, clearable: true, ), SettingsTextInput( controller: snatcherAmountController, title: 'Amount', - hintText: "Amount of Files to Snatch", + hintText: 'Amount of Files to Snatch', inputType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], + inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => 10.toString(), numberButtons: true, numberStep: 10, @@ -74,11 +71,9 @@ class _SnatcherPageState extends State { SettingsTextInput( controller: snatcherSleepController, title: 'Delay (in ms)', - hintText: "Delay between each download", + hintText: 'Delay between each download', inputType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], + inputFormatters: [FilteringTextInputFormatter.digitsOnly], resetText: () => settingsHandler.snatchCooldown.toString(), numberButtons: true, numberStep: 50, @@ -94,21 +89,20 @@ class _SnatcherPageState extends State { }, title: 'Booru', ), - const SettingsButton(name: '', enabled: false), SettingsButton( name: 'Snatch Files', icon: const Icon(Icons.download), action: () { - if (snatcherSleepController.text.isEmpty){ + if (snatcherSleepController.text.isEmpty) { snatcherSleepController.text = 0.toString(); } - if(selectedBooru == null) { + if (selectedBooru == null) { FlashElements.showSnackbar( context: context, title: const Text( - "No Booru Selected!", - style: TextStyle(fontSize: 18) + 'No Booru Selected!', + style: TextStyle(fontSize: 18), ), leadingIcon: Icons.error_outline, leadingIconColor: Colors.red, @@ -124,7 +118,6 @@ class _SnatcherPageState extends State { selectedBooru, ); - Navigator.of(context).pop(); // TODO make a page which shows progress of all files + list of previous downloads }, diff --git a/lib/src/services/dio_downloader.dart b/lib/src/services/dio_downloader.dart index a293402f..24086a66 100644 --- a/lib/src/services/dio_downloader.dart +++ b/lib/src/services/dio_downloader.dart @@ -3,7 +3,6 @@ import 'dart:isolate'; import 'dart:typed_data'; import 'package:dio/dio.dart'; -import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:lolisnatcher/src/handlers/service_handler.dart'; import 'package:lolisnatcher/src/services/image_writer.dart'; @@ -78,72 +77,52 @@ class DioDownloader { ); } - static void writeToCache(dynamic d) async { + static Future writeToCache(dynamic d) async { final ReceivePort receivePort = ReceivePort(); d.send(receivePort.sendPort); final IsolateCacheConfig config = IsolateCacheConfig.fromHost(await receivePort.first); - File? file = await (ImageWriterIsolate(config.cacheRootPath).writeCacheFromBytes( + final File? file = await ImageWriterIsolate(config.cacheRootPath).writeCacheFromBytes( config.fileURL, config.bytes, config.typeFolder, - clearName: config.typeFolder == 'favicons' ? false : true, + clearName: config.typeFolder != 'favicons', fileNameExtras: config.fileNameExtras, - )); + ); d.send(file); } - static void readFileFromCache(dynamic d) async { + static Future readFileFromCache(dynamic d) async { final ReceivePort receivePort = ReceivePort(); d.send(receivePort.sendPort); final IsolateCacheConfig config = IsolateCacheConfig.fromHost(await receivePort.first); - File? file = await (ImageWriterIsolate(config.cacheRootPath).readFileFromCache( + final File? file = await ImageWriterIsolate(config.cacheRootPath).readFileFromCache( config.fileURL, config.typeFolder, - clearName: config.typeFolder == 'favicons' ? false : true, + clearName: config.typeFolder != 'favicons', fileNameExtras: config.fileNameExtras, - )); + ); d.send(file); } - static void readBytesFromCache(dynamic d) async { + static Future readBytesFromCache(dynamic d) async { final ReceivePort receivePort = ReceivePort(); d.send(receivePort.sendPort); final IsolateCacheConfig config = IsolateCacheConfig.fromHost(await receivePort.first); - Uint8List? file = await (ImageWriterIsolate(config.cacheRootPath).readBytesFromCache( + final Uint8List? file = await ImageWriterIsolate(config.cacheRootPath).readBytesFromCache( config.fileURL, config.typeFolder, - clearName: config.typeFolder == 'favicons' ? false : true, + clearName: config.typeFolder != 'favicons', fileNameExtras: config.fileNameExtras, - )); + ); d.send(file); } - Future getCookies() async { - String cookieString = ''; - if (Platform.isAndroid || Platform.isIOS) { - // TODO add when there is desktop support? - try { - final CookieManager cookieManager = CookieManager.instance(); - final List cookies = await cookieManager.getCookies(url: Uri.parse(url)); - for (Cookie cookie in cookies) { - cookieString += '${cookie.name}=${cookie.value}; '; - } - } catch (e) { - Logger.Inst().log(e.toString(), 'DioDownloader', "getCookies", LogTypes.exception); - } - } - - cookieString = cookieString.trim(); - - return cookieString; - } - Future> getHeaders() async { - Map resultHeaders = {...headers ?? {}}; - final String cookieString = await getCookies(); + final Map resultHeaders = {...headers ?? {}}; + final String cookieString = await Tools.getCookies(Uri.parse(url)); if (cookieString.isNotEmpty) { resultHeaders['Cookie'] = cookieString; } @@ -154,8 +133,12 @@ class DioDownloader { try { final String resolved = Uri.base.resolve(url).toString(); - final String? filePath = - await ImageWriter().getCachePath(resolved, cacheFolder, clearName: cacheFolder == 'favicons' ? false : true, fileNameExtras: fileNameExtras); + final String? filePath = await ImageWriter().getCachePath( + resolved, + cacheFolder, + clearName: cacheFolder != 'favicons', + fileNameExtras: fileNameExtras, + ); if (filePath != null) { // read from cache final File fileToCache = File(filePath); @@ -199,7 +182,7 @@ class DioDownloader { onEvent?.call('isFromNetwork', null); currentClient = DioNetwork.getClient(); final Response response = await currentClient!.get( - resolved.toString(), + resolved, options: Options(responseType: ResponseType.bytes, headers: await getHeaders(), sendTimeout: timeoutDuration, receiveTimeout: timeoutDuration), cancelToken: cancelToken, onReceiveProgress: onProgress, @@ -214,7 +197,7 @@ class DioDownloader { } if (response.data == null || response.data.lengthInBytes == 0) { - throw DioLoadException(url: resolved, message: "File did not load"); + throw DioLoadException(url: resolved, message: 'File did not load'); } if (cacheEnabled) { @@ -239,7 +222,14 @@ class DioDownloader { return; } catch (e) { final bool isCancelError = e is DioException && CancelToken.isCancel(e); - if (!isCancelError) Logger.Inst().log('Error downloading $url :: $e', runtimeType.toString(), 'runRequestIsolate', LogTypes.imageLoadingError); + if (!isCancelError) { + Logger.Inst().log( + 'Error downloading $url :: $e', + runtimeType.toString(), + 'runRequestIsolate', + LogTypes.imageLoadingError, + ); + } if (e is Exception) { onError?.call(e); } else { @@ -254,8 +244,12 @@ class DioDownloader { try { final String resolved = Uri.base.resolve(url).toString(); - final String? filePath = - await imageWriter.getCachePath(resolved, cacheFolder, clearName: cacheFolder == 'favicons' ? false : true, fileNameExtras: fileNameExtras); + final String? filePath = await imageWriter.getCachePath( + resolved, + cacheFolder, + clearName: cacheFolder != 'favicons', + fileNameExtras: fileNameExtras, + ); if (filePath != null) { // read from cache final File file = File(filePath); @@ -289,7 +283,7 @@ class DioDownloader { onEvent?.call('isFromNetwork', null); currentClient = DioNetwork.getClient(); final Response response = await currentClient!.get( - resolved.toString(), + resolved, options: Options(responseType: ResponseType.bytes, headers: await getHeaders(), sendTimeout: timeoutDuration, receiveTimeout: timeoutDuration), cancelToken: cancelToken, onReceiveProgress: onProgress, @@ -304,7 +298,7 @@ class DioDownloader { } if (response.data == null || response.data.lengthInBytes == 0) { - throw DioLoadException(url: resolved, message: "File did not load"); + throw DioLoadException(url: resolved, message: 'File did not load'); } File? tempFile; @@ -313,7 +307,7 @@ class DioDownloader { resolved, response.data as Uint8List, cacheFolder, - clearName: cacheFolder == 'favicons' ? false : true, + clearName: cacheFolder != 'favicons', fileNameExtras: fileNameExtras, ); if (tempFile != null) { @@ -332,7 +326,14 @@ class DioDownloader { return; } catch (e) { final bool isCancelError = e is DioException && CancelToken.isCancel(e); - if (!isCancelError) Logger.Inst().log('Error downloading $url :: $e', runtimeType.toString(), 'runRequest', LogTypes.imageLoadingError); + if (!isCancelError) { + Logger.Inst().log( + 'Error downloading $url :: $e', + runtimeType.toString(), + 'runRequest', + LogTypes.imageLoadingError, + ); + } if (e is Exception) { onError?.call(e); } else { @@ -347,8 +348,12 @@ class DioDownloader { try { final String resolved = Uri.base.resolve(url).toString(); - final String? filePath = - await imageWriter.getCachePath(resolved, cacheFolder, clearName: cacheFolder == 'favicons' ? false : true, fileNameExtras: fileNameExtras); + final String? filePath = await imageWriter.getCachePath( + resolved, + cacheFolder, + clearName: cacheFolder != 'favicons', + fileNameExtras: fileNameExtras, + ); if (filePath != null) { // read from cache final File file = File(filePath); @@ -379,9 +384,13 @@ class DioDownloader { onEvent?.call('isFromNetwork', null); currentClient = DioNetwork.getClient(); final Response response = await currentClient!.download( - resolved.toString(), - await imageWriter.getCachePathString(resolved.toString(), cacheFolder, - clearName: cacheFolder == 'favicons' ? false : true, fileNameExtras: fileNameExtras), + resolved, + await imageWriter.getCachePathString( + resolved, + cacheFolder, + clearName: cacheFolder != 'favicons', + fileNameExtras: fileNameExtras, + ), options: Options(headers: await getHeaders(), sendTimeout: timeoutDuration, receiveTimeout: timeoutDuration), cancelToken: cancelToken, onReceiveProgress: onProgress, @@ -398,8 +407,12 @@ class DioDownloader { File? tempFile; if (cacheEnabled) { - final String? tempFilePath = - await imageWriter.getCachePath(resolved, cacheFolder, clearName: cacheFolder == 'favicons' ? false : true, fileNameExtras: fileNameExtras); + final String? tempFilePath = await imageWriter.getCachePath( + resolved, + cacheFolder, + clearName: cacheFolder != 'favicons', + fileNameExtras: fileNameExtras, + ); if (tempFilePath != null) { tempFile = File(tempFilePath); // onEvent?.call('isFromCache'); @@ -414,7 +427,14 @@ class DioDownloader { return; } catch (e) { final bool isCancelError = e is DioException && CancelToken.isCancel(e); - if (!isCancelError) Logger.Inst().log('Error downloading $url :: $e', runtimeType.toString(), 'runRequest', LogTypes.imageLoadingError); + if (!isCancelError) { + Logger.Inst().log( + 'Error downloading $url :: $e', + runtimeType.toString(), + 'runRequest', + LogTypes.imageLoadingError, + ); + } if (e is Exception) { onError?.call(e); } else { @@ -431,7 +451,7 @@ class DioDownloader { currentClient = DioNetwork.getClient(); final Response response = await currentClient!.head( - resolved.toString(), + resolved, options: Options(responseType: ResponseType.bytes, headers: await getHeaders(), sendTimeout: timeoutDuration, receiveTimeout: timeoutDuration), cancelToken: cancelToken, ); @@ -451,7 +471,14 @@ class DioDownloader { return; } catch (e) { final bool isCancelError = e is DioException && CancelToken.isCancel(e); - if (!isCancelError) Logger.Inst().log('Error downloading $url :: $e', runtimeType.toString(), 'runRequestSize', LogTypes.imageLoadingError); + if (!isCancelError) { + Logger.Inst().log( + 'Error downloading $url :: $e', + runtimeType.toString(), + 'runRequestSize', + LogTypes.imageLoadingError, + ); + } if (e is Exception) { onError?.call(e); } else { @@ -476,13 +503,13 @@ class DioLoadException implements Exception { } class IsolateCacheConfig { - final String cacheRootPath; - final String fileURL; - final List bytes; - final String typeFolder; - final String fileNameExtras; - - IsolateCacheConfig({required this.cacheRootPath, required this.fileURL, required this.bytes, required this.typeFolder, required this.fileNameExtras}); + IsolateCacheConfig({ + required this.cacheRootPath, + required this.fileURL, + required this.bytes, + required this.typeFolder, + required this.fileNameExtras, + }); IsolateCacheConfig.fromHost(dynamic data) : cacheRootPath = data['cacheRootPath'] as String, @@ -490,4 +517,10 @@ class IsolateCacheConfig { bytes = data['bytes'] as List? ?? [], typeFolder = data['typeFolder'] as String, fileNameExtras = data['fileNameExtras'] as String; + + final String cacheRootPath; + final String fileURL; + final List bytes; + final String typeFolder; + final String fileNameExtras; } diff --git a/lib/src/services/get_perms.dart b/lib/src/services/get_perms.dart index fb264cc4..500bbb11 100644 --- a/lib/src/services/get_perms.dart +++ b/lib/src/services/get_perms.dart @@ -7,13 +7,13 @@ import 'package:lolisnatcher/src/handlers/service_handler.dart'; // TODO expand to have more control over permissions /// This launches the permissions dialogue to get storage permissions from the user -/// +/// /// it is called before every operation which would require writing to storage which is why its in its own function -/// +/// /// The dialog will not show if the user has already accepted perms or android sdk is below 33 Future getPerms() async { if ((Platform.isAndroid && await ServiceHandler.getAndroidSDKVersion() < 33) || Platform.isIOS) { - return await Permission.storage.request().isGranted; + return Permission.storage.request().isGranted; } // print(Platform.environment['HOME']); } diff --git a/lib/src/services/image_writer.dart b/lib/src/services/image_writer.dart index 2afb156e..cf512301 100644 --- a/lib/src/services/image_writer.dart +++ b/lib/src/services/image_writer.dart @@ -101,11 +101,11 @@ class ImageWriter { await ServiceHandler.closeStreamToFileFromSAFDirectory(safPath); } } else { - final File jsonFile = File("${path!}$fileNameWoutExt.json"); + final File jsonFile = File('${path!}$fileNameWoutExt.json'); await jsonFile.writeAsString(jsonEncode(item.toJson()), flush: true); } } - print("Image written: ${path!}$fileName"); + print('Image written: ${path!}$fileName'); item.isSnatched.value = true; if (settingsHandler.dbEnabled) { await settingsHandler.dbHandler.updateBooruItem(item, BooruUpdateMode.local); @@ -119,7 +119,7 @@ class ImageWriter { throw Exception('SAF file not found'); } } else { - ServiceHandler.callMediaScanner(image.path); + await ServiceHandler.callMediaScanner(image.path); } } } catch (e, s) { @@ -170,8 +170,7 @@ class ImageWriter { bool ignoreExists, ) async* { int snatchedCounter = 1; - List existsList = []; - List failedList = []; + final List existsList = [], failedList = []; for (int i = 0; i < snatched.length; i++) { await Future.delayed(Duration(milliseconds: cooldown), () async { final snatchResult = await write( @@ -199,11 +198,17 @@ class ImageWriter { }; } - Future writeCacheFromBytes(String fileURL, List bytes, String typeFolder, {required String fileNameExtras, bool clearName = true}) async { + Future writeCacheFromBytes( + String fileURL, + List bytes, + String typeFolder, { + required String fileNameExtras, + bool clearName = true, + }) async { File? image; try { await setPaths(); - final String cachePath = "${cacheRootPath!}$typeFolder/"; + final String cachePath = '${cacheRootPath!}$typeFolder/'; // print("write cahce from bytes:: cache path is $cachePath"); await Directory(cachePath).create(recursive: true); @@ -219,10 +224,14 @@ class ImageWriter { // Deletes file from given cache folder // returns true if successful, false if there was an exception and null if file didn't exist - Future deleteFileFromCache(String fileURL, String typeFolder, {required String fileNameExtras}) async { + Future deleteFileFromCache( + String fileURL, + String typeFolder, { + required String fileNameExtras, + }) async { try { await setPaths(); - final String cachePath = "${cacheRootPath!}$typeFolder/"; + final String cachePath = '${cacheRootPath!}$typeFolder/'; final String fileName = sanitizeName(parseThumbUrlToName(fileURL), fileNameExtras: fileNameExtras); final File file = File(cachePath + fileName); if (await file.exists()) { @@ -240,7 +249,7 @@ class ImageWriter { Future deleteCacheFolder(String typeFolder) async { try { await setPaths(); - final String cachePath = "${cacheRootPath!}$typeFolder/"; + final String cachePath = '${cacheRootPath!}$typeFolder/'; final Directory folder = Directory(cachePath); if (await folder.exists()) { await folder.delete(recursive: true); @@ -258,7 +267,7 @@ class ImageWriter { String cachePath; try { await setPaths(); - cachePath = "${cacheRootPath!}$typeFolder/"; + cachePath = '${cacheRootPath!}$typeFolder/'; final String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); final File cacheFile = File(cachePath + fileName); @@ -279,10 +288,15 @@ class ImageWriter { } } - Future getCachePathString(String fileURL, String typeFolder, {required String fileNameExtras, bool clearName = true}) async { + Future getCachePathString( + String fileURL, + String typeFolder, { + required String fileNameExtras, + bool clearName = true, + }) async { await setPaths(); String cachePath; - cachePath = "${cacheRootPath!}$typeFolder/"; + cachePath = '${cacheRootPath!}$typeFolder/'; final String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); return cachePath + fileName; @@ -299,7 +313,7 @@ class ImageWriter { final Directory cacheDir = Directory(cacheDirPath); if (await cacheDir.exists()) { - List files = await cacheDir.list(recursive: true, followLinks: false).toList(); + final List files = await cacheDir.list(recursive: true, followLinks: false).toList(); for (final FileSystemEntity file in files) { if (file is File) { fileNum++; @@ -331,7 +345,7 @@ class ImageWriter { final Directory cacheDir = Directory(cacheDirPath); if (await cacheDir.exists()) { - List files = await cacheDir.list(recursive: true, followLinks: false).toList(); + final List files = await cacheDir.list(recursive: true, followLinks: false).toList(); for (final FileSystemEntity file in files) { if (file is File) { final bool isNotExcludedExt = Tools.getFileExt(file.path) != 'ico'; @@ -367,7 +381,7 @@ class ImageWriter { } String cacheDirPath; - List toDelete = []; + final List toDelete = []; int toDeleteSize = 0; int currentCacheSize = 0; try { @@ -376,7 +390,7 @@ class ImageWriter { final Directory cacheDir = Directory(cacheDirPath); if (await cacheDir.exists()) { - List files = (await cacheDir.list(recursive: true, followLinks: false).toList()).whereType().toList(); + final List files = (await cacheDir.list(recursive: true, followLinks: false).toList()).whereType().toList(); for (final File file in files) { currentCacheSize += await file.length(); } @@ -432,13 +446,13 @@ class ImageWriter { } String sanitizeName(String fileName, {required String fileNameExtras}) { - return "${Tools.sanitize(fileNameExtras)}${Tools.sanitize(fileName)}"; + return '${Tools.sanitize(fileNameExtras)}${Tools.sanitize(fileName)}'; } Future writeMascotImage(String contentUri) async { await setPaths(); if (contentUri.isNotEmpty) { - Uint8List? fileBytes = await ServiceHandler.getSAFFile(contentUri); + final Uint8List? fileBytes = await ServiceHandler.getSAFFile(contentUri); final String fileExt = await ServiceHandler.getSAFFileExtension(contentUri); if (fileBytes != null && fileExt.isNotEmpty) { final String path = await ServiceHandler.getConfigDir(); diff --git a/lib/src/services/image_writer_isolate.dart b/lib/src/services/image_writer_isolate.dart index be9d4573..f85ed7d5 100644 --- a/lib/src/services/image_writer_isolate.dart +++ b/lib/src/services/image_writer_isolate.dart @@ -5,75 +5,90 @@ import 'dart:typed_data'; import 'package:lolisnatcher/src/utils/tools.dart'; class ImageWriterIsolate { - final String cacheRootPath; - ImageWriterIsolate(this.cacheRootPath); + final String cacheRootPath; - Future writeCacheFromBytes(String fileURL, List bytes, String typeFolder, {bool clearName = true, required String fileNameExtras}) async{ + Future writeCacheFromBytes( + String fileURL, + List bytes, + String typeFolder, { + required String fileNameExtras, + bool clearName = true, + }) async { File? image; try { - String cachePath = "$cacheRootPath$typeFolder/"; - await Directory(cachePath).create(recursive:true); + final String cachePath = '$cacheRootPath$typeFolder/'; + await Directory(cachePath).create(recursive: true); - String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); + final String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); image = File(cachePath + fileName); - print("found image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate :: readFileFromCache"); + print('found image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate :: readFileFromCache'); await image.writeAsBytes(bytes, flush: true); } catch (e) { - print("Image Writer Isolate Exception :: cache write bytes :: $e"); + print('Image Writer Isolate Exception :: cache write bytes :: $e'); return null; } return image; } - Future readFileFromCache(String fileURL, String typeFolder, {bool clearName = true,required String fileNameExtras}) async { + Future readFileFromCache( + String fileURL, + String typeFolder, { + required String fileNameExtras, + bool clearName = true, + }) async { File? image; try { - String cachePath = "$cacheRootPath$typeFolder/"; - String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); + final String cachePath = '$cacheRootPath$typeFolder/'; + final String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); image = File(cachePath + fileName); // TODO is readBytes required here? - print("found image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate /:: readFileFromCache"); - if(await image.exists()) { + print('found image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate /:: readFileFromCache'); + if (await image.exists()) { await image.readAsBytes(); } - } catch (e){ - print("Image Writer Isolate Exception :: cache write :: $e"); + } catch (e) { + print('Image Writer Isolate Exception :: cache write :: $e'); return null; } return image; } - Future readBytesFromCache(String fileURL, String typeFolder, {bool clearName = true, required String fileNameExtras}) async { + Future readBytesFromCache( + String fileURL, + String typeFolder, { + required String fileNameExtras, + bool clearName = true, + }) async { Uint8List? imageBytes; try { - String cachePath = "$cacheRootPath$typeFolder/"; - String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); - File image = File(cachePath + fileName); + final String cachePath = '$cacheRootPath$typeFolder/'; + final String fileName = sanitizeName(clearName ? parseThumbUrlToName(fileURL) : fileURL, fileNameExtras: fileNameExtras); + final File image = File(cachePath + fileName); - if(await image.exists()) { + if (await image.exists()) { imageBytes = await image.readAsBytes(); - print("found image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate :: readBytesFromCache"); + print('found image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate :: readBytesFromCache'); } else { - print("couldn't find image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate :: readBytesFromCache"); + print('could not find image at: ${cachePath + fileName} for $fileURL :: ImageWriterIsolate :: readBytesFromCache'); } - } catch (e){ - print("Image Writer Isolate Exception :: read bytes cache :: $e"); + } catch (e) { + print('Image Writer Isolate Exception :: read bytes cache :: $e'); return null; } return imageBytes; } String parseThumbUrlToName(String thumbURL) { - int queryLastIndex = thumbURL.lastIndexOf("?"); // Sankaku fix - int lastIndex = queryLastIndex != -1 ? queryLastIndex : thumbURL.length; - String result = thumbURL.substring(thumbURL.lastIndexOf("/") + 1, lastIndex); - if(result.startsWith('thumb.')) { //Paheal/shimmie(?) fix - String unthumbedURL = thumbURL.replaceAll('/thumb', ''); - result = unthumbedURL.substring(unthumbedURL.lastIndexOf("/") + 1); + final int queryLastIndex = thumbURL.lastIndexOf('?'); // Sankaku fix + final int lastIndex = queryLastIndex != -1 ? queryLastIndex : thumbURL.length; + String result = thumbURL.substring(thumbURL.lastIndexOf('/') + 1, lastIndex); + if (result.startsWith('thumb.')) { + //Paheal/shimmie(?) fix + final String unthumbedURL = thumbURL.replaceAll('/thumb', ''); + result = unthumbedURL.substring(unthumbedURL.lastIndexOf('/') + 1); } return result; - } // calculates cache (total or by type) size and file count @@ -82,13 +97,13 @@ class ImageWriterIsolate { int fileNum = 0; int totalSize = 0; try { - cacheDirPath = "$cacheRootPath${typeFolder ?? ''}/"; + cacheDirPath = '$cacheRootPath${typeFolder ?? ''}/'; - Directory cacheDir = Directory(cacheDirPath); - bool dirExists = await cacheDir.exists(); + final Directory cacheDir = Directory(cacheDirPath); + final bool dirExists = await cacheDir.exists(); if (dirExists) { - List files = await cacheDir.list(recursive: true, followLinks: false).toList(); - for (FileSystemEntity file in files) { + final List files = await cacheDir.list(recursive: true, followLinks: false).toList(); + for (final FileSystemEntity file in files) { if (file is File) { fileNum++; totalSize += await file.length(); @@ -96,7 +111,7 @@ class ImageWriterIsolate { } } } catch (e) { - print("Image Writer Isolate Exception :: cache stat :: $e"); + print('Image Writer Isolate Exception :: cache stat :: $e'); } return { @@ -107,6 +122,6 @@ class ImageWriterIsolate { } String sanitizeName(String fileName, {required String fileNameExtras}) { - return "${Tools.sanitize(fileNameExtras)}${Tools.sanitize(fileName)}"; + return '${Tools.sanitize(fileNameExtras)}${Tools.sanitize(fileName)}'; } -} \ No newline at end of file +} diff --git a/lib/src/utils/debouncer.dart b/lib/src/utils/debouncer.dart index 484477ac..bed4fb0d 100644 --- a/lib/src/utils/debouncer.dart +++ b/lib/src/utils/debouncer.dart @@ -3,9 +3,9 @@ import 'dart:async'; // based on https://github.com/magnuswikhog/easy_debounce class Debounce { + const Debounce(); // ignore: prefer_final_fields static Map _debounceMap = {}; - Debounce(); /// Debounce a [callback] function by [duration] static void debounce({ @@ -83,7 +83,7 @@ class Debounce { static void cancelAllStartingWith(String substring) { // cancel all debounce operations that have tag, starting with the given substring // i.e. loading_element_progress_[fileUrl] where [fileUrl] is not included in the substring - _debounceMap.keys.where((key) => key.startsWith(substring)).forEach((key) => cancel(key)); + _debounceMap.keys.where((key) => key.startsWith(substring)).forEach(cancel); } static void cancelAll() { @@ -95,9 +95,13 @@ class Debounce { } class DebounceOperation { + DebounceOperation({ + required this.callback, + required this.timer, + this.startedAt, + }); + final void Function() callback; final Timer timer; final int? startedAt; - - DebounceOperation({required this.callback, required this.timer, this.startedAt}); } diff --git a/lib/src/utils/dio_network.dart b/lib/src/utils/dio_network.dart index 73bd0337..24627c91 100644 --- a/lib/src/utils/dio_network.dart +++ b/lib/src/utils/dio_network.dart @@ -6,7 +6,6 @@ import 'package:dio/dio.dart'; import 'package:lolisnatcher/src/handlers/service_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; -import 'package:lolisnatcher/src/utils/logger.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; class DioNetwork { @@ -42,7 +41,7 @@ class DioNetwork { final usedOptions = options ?? defaultOptions; return usedOptions.copyWith( headers: { - ...(headers ?? usedOptions.headers ?? defaultOptions.headers!), + ...headers ?? usedOptions.headers ?? defaultOptions.headers!, }, ); } @@ -57,13 +56,13 @@ class DioNetwork { final String cleanUrl = temp.replace(queryParameters: {}).toString(); final Map queryParams = { ...temp.queryParameters, - ...(givenQueryParams ?? {}), + ...givenQueryParams ?? {}, }; // TODO create a separate class for this? return { - "url": Uri.encodeFull(cleanUrl), - "query": queryParams.isEmpty ? null : queryParams, + 'url': Uri.encodeFull(cleanUrl), + 'query': queryParams.isEmpty ? null : queryParams, }; } @@ -76,9 +75,9 @@ class DioNetwork { return handler.next(response); } - String oldCookie = response.requestOptions.headers['Cookie'] as String? ?? ''; - String newCookie = await Tools.getCookies(response.requestOptions.uri); - var headers = { + final String oldCookie = response.requestOptions.headers['Cookie'] as String? ?? ''; + final String newCookie = await Tools.getCookies(response.requestOptions.uri); + final headers = { ...response.requestOptions.headers, 'Cookie': '${oldCookie.replaceAll('cf_clearance', 'cf_clearance_old')} $newCookie'.trim(), Tools.captchaCheckHeader: 'done', @@ -107,9 +106,9 @@ class DioNetwork { return handler.next(error); } - String oldCookie = error.requestOptions.headers['Cookie'] as String? ?? ''; - String newCookie = await Tools.getCookies(error.requestOptions.uri); - var headers = { + final String oldCookie = error.requestOptions.headers['Cookie'] as String? ?? ''; + final String newCookie = await Tools.getCookies(error.requestOptions.uri); + final headers = { ...error.requestOptions.headers, 'Cookie': '${oldCookie.replaceAll('cf_clearance', 'cf_clearance_old')} $newCookie'.trim(), Tools.captchaCheckHeader: 'done', @@ -130,7 +129,7 @@ class DioNetwork { return handler.resolve(cloneReq); } catch (e) { return handler.next(error); - } + } }, ), ); @@ -221,7 +220,7 @@ class DioNetwork { ); } - static download( + static Future download( String url, String savePath, { Object? data, @@ -306,7 +305,7 @@ class DioNetwork { total = int.parse(response.headers.value(lengthHeader) ?? '-1'); } - String? fileUri = await ServiceHandler.createFileStreamFromSAFDirectory( + final String? fileUri = await ServiceHandler.createFileStreamFromSAFDirectory( fileNameWoutExt, mediaType, fileExt, @@ -383,11 +382,12 @@ class DioNetwork { }, cancelOnError: true, ); - // ignore: unawaited_futures - cancelToken?.whenCancel.then((_) async { - await subscription.cancel(); - await closeAndDelete(); - }); + unawaited( + cancelToken?.whenCancel.then((_) async { + await subscription.cancel(); + await closeAndDelete(); + }), + ); final timeout = response.requestOptions.receiveTimeout; if (timeout != null) { diff --git a/lib/src/utils/extensions.dart b/lib/src/utils/extensions.dart index 2d28cf71..3dc9d7c7 100644 --- a/lib/src/utils/extensions.dart +++ b/lib/src/utils/extensions.dart @@ -17,7 +17,7 @@ extension UIExtras on Widget { Widget withOpacity(double opacity) => Opacity(opacity: opacity, child: this); - Widget withColor(Color color) => Container(color: color, child: this); + Widget withColor(Color color) => ColoredBox(color: color, child: this); } extension StringExtras on String { @@ -25,7 +25,7 @@ extension StringExtras on String { return count == 1 ? this : '${this}s'; } - String capitalize() => "${this[0].toUpperCase()}${substring(1)}"; + String capitalize() => '${this[0].toUpperCase()}${substring(1)}'; String toTitleCase() => split(' ').map((s) => s.capitalize()).join(' '); @@ -41,9 +41,9 @@ extension StringExtras on String { } extension IntExtras on int { - bool isEven() => this % 2 == 0; + bool isEven() => this.isEven; - bool isOdd() => this % 2 == 1; + bool isOdd() => this.isOdd; int clamp(int min, int max) => (min > this ? min : (max < this ? max : this)); diff --git a/lib/src/utils/html_color.dart b/lib/src/utils/html_color.dart index 38696feb..0a6fdf7c 100644 --- a/lib/src/utils/html_color.dart +++ b/lib/src/utils/html_color.dart @@ -153,7 +153,9 @@ const _kColorMap = { Color parse(String text) { text = text.trim(); - if (text.isEmpty) throw FormatException('Empty html color: $text'); + if (text.isEmpty) { + throw FormatException('Empty html color: $text'); + } if (text.codeUnitAt(0) == '#'.codeUnitAt(0)) { if (text.length >= 7) { @@ -170,8 +172,8 @@ Color parse(String text) { } else if (text.startsWith('rgb(') && text.endsWith(')')) { // rgb(255, 0, 0) // rgb(100%, 0%, 0%) - String str = text.substring(4, text.length - 1); - List colors = str.split(','); + final String str = text.substring(4, text.length - 1); + final List colors = str.split(','); if (colors.length >= 3) { final r = _parseRGB(colors[0]); final g = _parseRGB(colors[1]); @@ -181,19 +183,19 @@ Color parse(String text) { } else if (text.startsWith('rgba(') && text.endsWith(')')) { // rgba(255, 0, 0, 0.6) // rgba(100%, 0%, 0%, 0.6) - String str = text.substring(5, text.length - 1); - List colors = str.split(','); + final String str = text.substring(5, text.length - 1); + final List colors = str.split(','); if (colors.length >= 4) { - int r = _parseRGB(colors[0]); - int g = _parseRGB(colors[1]); - int b = _parseRGB(colors[2]); - int a = _parseA(colors[3]); + final int r = _parseRGB(colors[0]); + final int g = _parseRGB(colors[1]); + final int b = _parseRGB(colors[2]); + final int a = _parseA(colors[3]); return Color.fromARGB(a, r, g, b); } } else if (text.startsWith('hsl(') && text.endsWith(')')) { // hsl(120, 100%, 50%) - String str = text.substring(4, text.length - 1); - List colors = str.split(','); + final String str = text.substring(4, text.length - 1); + final List colors = str.split(','); if (colors.length >= 3) { final h = _parseH(colors[0]); final s = _parseSL(colors[1]); @@ -202,8 +204,8 @@ Color parse(String text) { } } else if (text.startsWith('hsla(') && text.endsWith(')')) { // hsla(120, 100%, 25%, 0.3) - String str = text.substring(5, text.length - 1); - List colors = str.split(','); + final String str = text.substring(5, text.length - 1); + final List colors = str.split(','); if (colors.length >= 4) { final h = _parseH(colors[0]); final s = _parseSL(colors[1]); @@ -214,7 +216,9 @@ Color parse(String text) { } final color = _kColorMap[text.toLowerCase()]; - if (color == null) throw FormatException('Invalid html color: $text'); + if (color == null) { + throw FormatException('Invalid html color: $text'); + } return color; } @@ -302,4 +306,4 @@ Color _hslaToColor(int h, double s, double l, int a) { b = b.clamp(0, 255); return Color.fromARGB(a, r, g, b); -} \ No newline at end of file +} diff --git a/lib/src/utils/html_parse.dart b/lib/src/utils/html_parse.dart index 74fdc69b..35c0abae 100644 --- a/lib/src/utils/html_parse.dart +++ b/lib/src/utils/html_parse.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' as parser; -import 'package:lolisnatcher/src/utils/html_color.dart' as htmlColor; +import 'package:lolisnatcher/src/utils/html_color.dart' as html_color; // Original code: https://gist.github.com/seven332/9dc76255d959c8c5194cbd92068b0f60 @@ -31,7 +31,7 @@ InlineSpan _parseRecursive(dynamic node, TextStyle style, bool styleChanged, boo String _fixWhitespaceInText(String text) { final sb = StringBuffer(); int pre = ' '.codeUnitAt(0); - for (int c in text.codeUnits) { + for (final int c in text.codeUnits) { if (c == ' '.codeUnitAt(0) || c == '\n'.codeUnitAt(0)) { if (pre != ' '.codeUnitAt(0) && pre != '\n'.codeUnitAt(0)) { sb.writeCharCode(' '.codeUnitAt(0)); @@ -46,8 +46,10 @@ String _fixWhitespaceInText(String text) { } InlineSpan _parseText(dom.Text text, TextStyle style, bool styleChanged, bool isBordered) { - var t = text.data; - if (t.isEmpty) return const TextSpan(text: ''); + final t = text.data; + if (t.isEmpty) { + return const TextSpan(text: ''); + } // t = _fixWhitespaceInText(t); return TextSpan(text: t, style: styleChanged ? style : null); } @@ -66,70 +68,70 @@ InlineSpan _parseElement(dom.Element element, TextStyle style, bool styleChanged GestureRecognizer? recognizer; switch (tag) { - case "body": - case "span": //? - case "div": //? + case 'body': + case 'span': //? + case 'div': //? // Ignore break; - case "br": - return TextSpan(text: "\n", style: styleChanged ? style : null); - case "strong": - case "b": + case 'br': + return TextSpan(text: '\n', style: styleChanged ? style : null); + case 'strong': + case 'b': style = style.copyWith(fontWeight: FontWeight.bold); styleChanged = true; break; - case "em": - case "cite": - case "dfn": - case "i": + case 'em': + case 'cite': + case 'dfn': + case 'i': style = style.copyWith(fontStyle: FontStyle.italic); styleChanged = true; break; - case "u": - case "ins": + case 'u': + case 'ins': style = style.copyWith(decoration: _combine(style.decoration, TextDecoration.underline)); styleChanged = true; break; - case "del": - case "s": - case "strike": + case 'del': + case 's': + case 'strike': style = style.copyWith(decoration: _combine(style.decoration, TextDecoration.lineThrough)); styleChanged = true; break; - case "small": + case 'small': style = style.copyWith(fontSize: (style.fontSize ?? 14) - 2); styleChanged = true; break; - case "big": + case 'big': style = style.copyWith(fontSize: (style.fontSize ?? 14) + 2); styleChanged = true; break; - case "tn": + case 'tn': element.text = 'Translator note: ${element.text}'; style = style.copyWith(fontSize: (style.fontSize ?? 14) - 2); styleChanged = true; break; - case "font": - Color? color = htmlColor.tryParse(element.attributes['color'] ?? ''); + case 'font': + final Color? color = html_color.tryParse(element.attributes['color'] ?? ''); if (color != null) { style = style.copyWith(color: color); styleChanged = true; } break; default: - print("Unhandled tag: $tag"); + print('Unhandled tag: $tag'); break; } - if(isBordered) { - Paint paint = Paint() + if (isBordered) { + final Paint paint = Paint() ..color = Colors.black.withOpacity(0.75) ..style = PaintingStyle.fill ..strokeCap = StrokeCap.round ..strokeWidth = 2.0; style = style.copyWith( - background: paint + background: paint, ); styleChanged = true; } @@ -149,29 +151,30 @@ InlineSpan _parseParent( bool isBordered, ) { final List children = []; - for (var item in node.nodes) { + for (final item in node.nodes) { // The change of style is applied below - var span = _parseRecursive(item, style, false, isBordered); + final span = _parseRecursive(item, style, false, isBordered); children.add(span); } // Avoid TextSpan with no child - if (children.isEmpty) return const TextSpan(text: ''); + if (children.isEmpty) { + return const TextSpan(text: ''); + } // Avoid TextSpan with only one child if (children.length == 1) { final span = children.single; if (span is TextSpan) { // Keep origin style/recognizer, or use parent style/recognizer - if ((span.style != null || !styleChanged) && - (span.recognizer != null || recognizer == null)) { + if ((span.style != null || !styleChanged) && (span.recognizer != null || recognizer == null)) { return span; } else { return TextSpan( - text: span.text, - style: span.style ?? style, - recognizer: span.recognizer ?? recognizer, - ); + text: span.text, + style: span.style ?? style, + recognizer: span.recognizer ?? recognizer, + ); } } // TODO what if it's not TextSpan @@ -182,4 +185,4 @@ InlineSpan _parseParent( style: styleChanged ? style : null, recognizer: recognizer, ); -} \ No newline at end of file +} diff --git a/lib/src/utils/http_overrides.dart b/lib/src/utils/http_overrides.dart index bd7897a0..d2a470ba 100644 --- a/lib/src/utils/http_overrides.dart +++ b/lib/src/utils/http_overrides.dart @@ -1,9 +1,8 @@ import 'dart:io'; -class MyHttpOverrides extends HttpOverrides{ +class MyHttpOverrides extends HttpOverrides { @override - HttpClient createHttpClient(SecurityContext? context){ - return super.createHttpClient(context) - ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true; + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context)..badCertificateCallback = (X509Certificate cert, String host, int port) => true; } -} \ No newline at end of file +} diff --git a/lib/src/utils/logger.dart b/lib/src/utils/logger.dart index 246b36f7..29d47e16 100644 --- a/lib/src/utils/logger.dart +++ b/lib/src/utils/logger.dart @@ -1,6 +1,7 @@ import 'dart:math' as math; import 'package:dio/dio.dart'; +// ignore: library_prefixes import 'package:logger_fork/logger_fork.dart' as LogLib; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; @@ -16,13 +17,16 @@ class Logger { return _loggerInstance!; } - void log(dynamic logStr, String callerClass, String callerFunction, - LogTypes? logType) { + void log( + dynamic logStr, + String callerClass, + String callerFunction, + LogTypes? logType, + ) { if (!Tools.isTestMode) { // don't call handlers when in test mode // don't check which types are ignored in test mode and output everything - final bool allowedToLog = logType == null || - SettingsHandler.instance.enabledLogTypes.contains(logType); + final bool allowedToLog = logType == null || SettingsHandler.instance.enabledLogTypes.contains(logType); if (!allowedToLog) { // Ignore unselected log types return; @@ -102,9 +106,7 @@ class Logger { if (!Tools.isTestMode) { // don't call handlers when in test mode // don't check which types are ignored in test mode and output everything - final bool allowedToLog = ignoreTypeCheck || - t == null || - SettingsHandler.instance.enabledLogTypes.contains(t); + final bool allowedToLog = ignoreTypeCheck || t == null || SettingsHandler.instance.enabledLogTypes.contains(t); if (!allowedToLog) { // Ignore unselected log types return; @@ -115,8 +117,7 @@ class Logger { final usedLogger = withStack ? _logger : _loggerNoStack; final title = e == null ? '$t' : '$e :: $t'; // string/list/map/set can be handled automatically, other objects need to be converted to string - final message = - (m is String || m is List || m is Map || m is Set) ? m : '$m'; + final message = (m is String || m is List || m is Map || m is Set) ? m : '$m'; if (logLevel == LogLib.Level.info) { usedLogger.i(message, title, s); @@ -172,13 +173,25 @@ class CustomLogFilter extends LogLib.LogFilter { } class CustomPrettyDioLogger extends Interceptor { + CustomPrettyDioLogger({ + this.request = true, + this.requestHeader = false, + this.requestBody = false, + this.responseHeader = false, + this.responseBody = true, + this.error = true, + this.maxWidth = 90, + this.compact = true, + this.logPrint = print, + }); + /// Print request [Options] final bool request; /// Print request header [Options.headers] final bool requestHeader; - /// Print request data [Options.data] + /// Print request data [RequestOptions.data] final bool requestBody; /// Print [Response.data] @@ -207,17 +220,6 @@ class CustomPrettyDioLogger extends Interceptor { /// you can also write log in a file. void Function(Object object) logPrint; - CustomPrettyDioLogger( - {this.request = true, - this.requestHeader = false, - this.requestBody = false, - this.responseHeader = false, - this.responseBody = true, - this.error = true, - this.maxWidth = 90, - this.compact = true, - this.logPrint = print}); - @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { if (request) { @@ -238,7 +240,9 @@ class CustomPrettyDioLogger extends Interceptor { if (requestBody && options.method != 'GET') { final dynamic data = options.data; if (data != null) { - if (data is Map) _printMapAsTable(options.data as Map?, header: 'Body'); + if (data is Map) { + _printMapAsTable(options.data as Map?, header: 'Body'); + } if (data is FormData) { final formDataMap = {} ..addEntries(data.fields) @@ -258,11 +262,11 @@ class CustomPrettyDioLogger extends Interceptor { if (err.type == DioExceptionType.badResponse) { final uri = err.response?.requestOptions.uri; _printBoxed( - header: - 'DioException ║ Status: ${err.response?.statusCode} ${err.response?.statusMessage}', - text: uri.toString()); + header: 'DioException ║ Status: ${err.response?.statusCode} ${err.response?.statusMessage}', + text: uri.toString(), + ); if (err.response != null && err.response?.data != null) { - logPrint('╔ ${err.type.toString()}'); + logPrint('╔ ${err.type}'); _printResponse(err.response!); } _printLine('╚'); @@ -279,8 +283,7 @@ class CustomPrettyDioLogger extends Interceptor { _printResponseHeader(response); if (responseHeader) { final responseHeaders = {}; - response.headers - .forEach((k, list) => responseHeaders[k] = list.toString()); + response.headers.forEach((k, list) => responseHeaders[k] = list.toString()); _printMapAsTable(responseHeaders, header: 'Headers'); } @@ -307,7 +310,7 @@ class CustomPrettyDioLogger extends Interceptor { _printPrettyMap(response.data as Map); } else if (response.data is List) { logPrint('║${_indent()}['); - if(response.data is List && response.requestOptions.responseType == ResponseType.bytes) { + if (response.data is List && response.requestOptions.responseType == ResponseType.bytes) { _printBlock('[response bytes list]'); } else { _printList(response.data as List); @@ -323,9 +326,9 @@ class CustomPrettyDioLogger extends Interceptor { final uri = response.requestOptions.uri; final method = response.requestOptions.method; _printBoxed( - header: - 'Response ║ $method ║ Status: ${response.statusCode} ${response.statusMessage}', - text: uri.toString()); + header: 'Response ║ $method ║ Status: ${response.statusCode} ${response.statusMessage}', + text: uri.toString(), + ); } void _printRequestHeader(RequestOptions options) { @@ -334,8 +337,7 @@ class CustomPrettyDioLogger extends Interceptor { _printBoxed(header: 'Request ║ $method ', text: uri.toString()); } - void _printLine([String pre = '', String suf = '╝']) => - logPrint('$pre${'═' * maxWidth}$suf'); + void _printLine([String pre = '', String suf = '╝']) => logPrint('$pre${'═' * maxWidth}$suf'); void _printKV(String? key, Object? v) { final pre = '╟ $key: '; @@ -352,9 +354,13 @@ class CustomPrettyDioLogger extends Interceptor { void _printBlock(String msg) { final lines = (msg.length / maxWidth).ceil(); for (var i = 0; i < lines; ++i) { - logPrint((i >= 0 ? '║ ' : '') + - msg.substring(i * maxWidth, - math.min(i * maxWidth + maxWidth, msg.length))); + logPrint( + (i >= 0 ? '║ ' : '') + + msg.substring( + i * maxWidth, + math.min(i * maxWidth + maxWidth, msg.length), + ), + ); } } @@ -366,46 +372,49 @@ class CustomPrettyDioLogger extends Interceptor { bool isListItem = false, bool isLast = false, }) { - var _tabs = tabs; - final isRoot = _tabs == initialTab; - final initialIndent = _indent(_tabs); - _tabs++; + var tabs0 = tabs; + final isRoot = tabs0 == initialTab; + final initialIndent = _indent(tabs0); + tabs0++; - if (isRoot || isListItem) logPrint('║$initialIndent{'); + if (isRoot || isListItem) { + logPrint('║$initialIndent{'); + } data.keys.toList().asMap().forEach((index, dynamic key) { final isLast = index == data.length - 1; dynamic value = data[key]; if (value is String) { - value = '"${value.toString().replaceAll(RegExp(r'(\r|\n)+'), " ")}"'; + value = '"${value.replaceAll(RegExp(r'(\r|\n)+'), " ")}"'; } if (value is Map) { if (compact && _canFlattenMap(value)) { - logPrint('║${_indent(_tabs)} $key: $value${!isLast ? ',' : ''}'); + logPrint('║${_indent(tabs0)} $key: $value${!isLast ? ',' : ''}'); } else { - logPrint('║${_indent(_tabs)} $key: {'); - _printPrettyMap(value, tabs: _tabs); + logPrint('║${_indent(tabs0)} $key: {'); + _printPrettyMap(value, tabs: tabs0); } } else if (value is List) { if (compact && _canFlattenList(value)) { - logPrint('║${_indent(_tabs)} $key: ${value.toString()}'); + logPrint('║${_indent(tabs0)} $key: $value'); } else { - logPrint('║${_indent(_tabs)} $key: ['); - _printList(value, tabs: _tabs); - logPrint('║${_indent(_tabs)} ]${isLast ? '' : ','}'); + logPrint('║${_indent(tabs0)} $key: ['); + _printList(value, tabs: tabs0); + logPrint('║${_indent(tabs0)} ]${isLast ? '' : ','}'); } } else { final msg = value.toString().replaceAll('\n', ''); - final indent = _indent(_tabs); + final indent = _indent(tabs0); final linWidth = maxWidth - indent.length; if (msg.length + indent.length > linWidth) { final lines = (msg.length / linWidth).ceil(); for (var i = 0; i < lines; ++i) { logPrint( - '║${_indent(_tabs)} ${msg.substring(i * linWidth, math.min(i * linWidth + linWidth, msg.length))}'); + '║${_indent(tabs0)} ${msg.substring(i * linWidth, math.min(i * linWidth + linWidth, msg.length))}', + ); } } else { - logPrint('║${_indent(_tabs)} $key: $msg${!isLast ? ',' : ''}'); + logPrint('║${_indent(tabs0)} $key: $msg${!isLast ? ',' : ''}'); } } }); @@ -429,10 +438,7 @@ class CustomPrettyDioLogger extends Interceptor { } bool _canFlattenMap(Map map) { - return map.values - .where((dynamic val) => val is Map || val is List) - .isEmpty && - map.toString().length < maxWidth; + return map.values.where((dynamic val) => val is Map || val is List).isEmpty && map.toString().length < maxWidth; } bool _canFlattenList(List list) { @@ -440,10 +446,13 @@ class CustomPrettyDioLogger extends Interceptor { } void _printMapAsTable(Map? map, {String? header}) { - if (map == null || map.isEmpty) return; + if (map == null || map.isEmpty) { + return; + } logPrint('╔ $header '); map.forEach( - (dynamic key, dynamic value) => _printKV(key.toString(), value)); + (dynamic key, dynamic value) => _printKV(key.toString(), value), + ); _printLine('╚'); } } @@ -472,7 +481,7 @@ enum LogTypes { } static LogTypes fromString(String str) { - return LogTypes.values.firstWhere((element) => element.name == str,orElse: () => LogTypes.exception); + return LogTypes.values.firstWhere((element) => element.name == str, orElse: () => LogTypes.exception); } LogLib.Level get logLevel { diff --git a/lib/src/utils/timed_progress_controller.dart b/lib/src/utils/timed_progress_controller.dart index 7d33fa33..38b27277 100644 --- a/lib/src/utils/timed_progress_controller.dart +++ b/lib/src/utils/timed_progress_controller.dart @@ -3,6 +3,9 @@ import 'dart:async'; // code from https://stackoverflow.com/questions/56853554/show-timer-progress-on-a-circularprogressindicator-in-flutter class TimedProgressController { + TimedProgressController({ + required this.duration, + }) : tickPeriod = _calculateTickPeriod(duration); static const double smoothnessConstant = 250; final Duration duration; @@ -17,9 +20,6 @@ class TimedProgressController { double get progress => _progress; double _progress = 0; - TimedProgressController({required this.duration}) - : tickPeriod = _calculateTickPeriod(duration); - void start() { _timer = Timer(duration, () { _cancelTimers(); @@ -29,7 +29,7 @@ class TimedProgressController { _periodicTimer = Timer.periodic( tickPeriod, (Timer timer) { - double progress = _calculateProgress(timer); + final double progress = _calculateProgress(timer); _setProgressAndNotify(progress); }, ); @@ -51,10 +51,14 @@ class TimedProgressController { } double _calculateProgress(Timer timer) { - double progress = timer.tick / smoothnessConstant; - - if (progress > 1) return 1; - if (progress < 0) return 0; + final double progress = timer.tick / smoothnessConstant; + + if (progress > 1) { + return 1; + } + if (progress < 0) { + return 0; + } return progress; } @@ -64,16 +68,22 @@ class TimedProgressController { } Future _cancelStreams() async { - if (!_progressController.isClosed) await _progressController.close(); + if (!_progressController.isClosed) { + await _progressController.close(); + } } void _cancelTimers() { - if (_timer?.isActive == true) _timer?.cancel(); - if (_periodicTimer?.isActive == true) _periodicTimer?.cancel(); + if (_timer?.isActive == true) { + _timer?.cancel(); + } + if (_periodicTimer?.isActive == true) { + _periodicTimer?.cancel(); + } } static Duration _calculateTickPeriod(Duration duration) { - double tickPeriodMs = duration.inMilliseconds / smoothnessConstant; + final double tickPeriodMs = duration.inMilliseconds / smoothnessConstant; return Duration(milliseconds: tickPeriodMs.toInt()); } -} \ No newline at end of file +} diff --git a/lib/src/utils/tools.dart b/lib/src/utils/tools.dart index fb2ccced..1098a090 100644 --- a/lib/src/utils/tools.dart +++ b/lib/src/utils/tools.dart @@ -6,19 +6,22 @@ import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/constants.dart'; import 'package:lolisnatcher/src/handlers/navigation_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; +import 'package:lolisnatcher/src/utils/logger.dart'; import 'package:lolisnatcher/src/widgets/webview/webview_page.dart'; class Tools { // code taken from: https://gist.github.com/zzpmaster/ec51afdbbfa5b2bf6ced13374ff891d9 static String formatBytes(int bytes, int decimals) { - if (bytes <= 0) return "0 B"; - const suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + if (bytes <= 0) { + return '0 B'; + } + const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; final i = (log(bytes) / log(1024)).floor(); return '${(bytes / pow(1024, i)).toStringAsFixed(decimals)} ${suffixes[i]}'; } @@ -28,24 +31,24 @@ class Tools { } static bool intToBool(int boolean) { - return boolean != 0 ? true : false; + return boolean != 0; } static bool stringToBool(String boolean) { - return boolean == "true" ? true : false; + return boolean == 'true'; } static String getFileExt(String fileURL) { - final int queryLastIndex = fileURL.lastIndexOf("?"); // if has GET query parameters + final int queryLastIndex = fileURL.lastIndexOf('?'); // if has GET query parameters final int lastIndex = queryLastIndex != -1 ? queryLastIndex : fileURL.length; - final String fileExt = fileURL.substring(fileURL.lastIndexOf(".") + 1, lastIndex); + final String fileExt = fileURL.substring(fileURL.lastIndexOf('.') + 1, lastIndex); return fileExt; } static String getFileName(String fileURL) { - final int queryLastIndex = fileURL.lastIndexOf("?"); // if has GET query parameters + final int queryLastIndex = fileURL.lastIndexOf('?'); // if has GET query parameters final int lastIndex = queryLastIndex != -1 ? queryLastIndex : fileURL.length; - final String fileExt = fileURL.substring(fileURL.lastIndexOf("/") + 1, lastIndex); + final String fileExt = fileURL.substring(fileURL.lastIndexOf('/') + 1, lastIndex); return fileExt; } @@ -63,7 +66,7 @@ class Tools { .replaceAll(reservedRe, replacement) .replaceAll(windowsReservedRe, replacement) .replaceAll(windowsTrailingRe, replacement) - .replaceAll("%20", "_"); + .replaceAll('%20', '_'); } // unified http headers list generator for dio in thumb/media/video loaders @@ -72,19 +75,19 @@ class Tools { bool checkForReferer = false, }) async { // a few boorus don't work without a browser useragent - Map headers = {"User-Agent": browserUserAgent}; - if (booru.baseURL?.contains("danbooru.donmai.us") ?? false) { - headers["User-Agent"] = appUserAgent; + final Map headers = {'User-Agent': browserUserAgent}; + if (booru.baseURL?.contains('danbooru.donmai.us') ?? false) { + headers['User-Agent'] = appUserAgent; } - if (booru.baseURL?.contains("sankakucomplex.com") ?? false) { - headers["User-Agent"] = Constants.defaultBrowserUserAgent; + if (booru.baseURL?.contains('sankakucomplex.com') ?? false) { + headers['User-Agent'] = Constants.defaultBrowserUserAgent; } if (!isTestMode) { try { final cookiesStr = await getCookies(Uri.parse(booru.baseURL!)); if (cookiesStr.isNotEmpty) { - headers["Cookie"] = cookiesStr; + headers['Cookie'] = cookiesStr; } } catch (e) { print('Error getting cookies: $e'); @@ -96,9 +99,9 @@ class Tools { switch (booru.type) { case BooruType.World: if (booru.baseURL!.contains('rule34.xyz')) { - headers["Referer"] = "https://rule34xyz.b-cdn.net"; + headers['Referer'] = 'https://rule34xyz.b-cdn.net'; } else if (booru.baseURL!.contains('rule34.world')) { - headers["Referer"] = "https://rule34storage.b-cdn.net"; + headers['Referer'] = 'https://rule34storage.b-cdn.net'; } break; @@ -128,7 +131,9 @@ class Tools { static void forceClearMemoryCache({bool withLive = false}) { // clears memory image cache on timer or when changing tabs PaintingBinding.instance.imageCache.clear(); - if (withLive) PaintingBinding.instance.imageCache.clearLiveImages(); + if (withLive) { + PaintingBinding.instance.imageCache.clearLiveImages(); + } } static String pluralize(String str, int count) { @@ -140,7 +145,7 @@ class Tools { } // TODO move to separate class (something with the name like "Constants") - static const String appUserAgent = "LoliSnatcher_Droid/${Constants.appVersion}"; + static const String appUserAgent = 'LoliSnatcher_Droid/${Constants.appVersion}'; static String get browserUserAgent { return isTestMode ? appUserAgent : (SettingsHandler.instance.customUserAgent.isNotEmpty ? SettingsHandler.instance.customUserAgent : appUserAgent); } @@ -150,7 +155,9 @@ class Tools { static const String captchaCheckHeader = 'LSCaptchaCheck'; static Future checkForCaptcha(Response? response, Uri uri, {String? customUserAgent}) async { - if (captchaScreenActive || isTestMode || response?.requestOptions.headers.containsKey(captchaCheckHeader) == true) return false; + if (captchaScreenActive || isTestMode || response?.requestOptions.headers.containsKey(captchaCheckHeader) == true) { + return false; + } if (response?.statusCode == 503 || response?.statusCode == 403) { captchaScreenActive = true; @@ -162,7 +169,7 @@ class Tools { userAgent: customUserAgent, title: 'Captcha check', subtitle: - 'Possible captcha detected, please solve it and press back after that. If there is no captcha then it\'s probably some other authentication issue. [Beta]', + "Possible captcha detected, please solve it and press back after that. If there is no captcha then it's probably some other authentication issue. [Beta]", ), ), ); @@ -179,17 +186,15 @@ class Tools { try { final CookieManager cookieManager = CookieManager.instance(); final List cookies = await cookieManager.getCookies(url: uri); - for (Cookie cookie in cookies) { + for (final Cookie cookie in cookies) { cookieString += '${cookie.name}=${cookie.value}; '; } } catch (e) { - // + Logger.Inst().log(e.toString(), 'Tools', 'getCookies', LogTypes.exception); } } - cookieString = cookieString.trim(); - - return cookieString; + return cookieString.trim(); } } diff --git a/lib/src/widgets/common/animated_progress_indicator.dart b/lib/src/widgets/common/animated_progress_indicator.dart index d1a13fc5..9aa7fa0a 100644 --- a/lib/src/widgets/common/animated_progress_indicator.dart +++ b/lib/src/widgets/common/animated_progress_indicator.dart @@ -7,11 +7,10 @@ enum IndicatorStyle { linear; } -const double _kMinCircularProgressIndicatorSize = 36.0; +const double _kMinCircularProgressIndicatorSize = 36; class AnimatedProgressIndicator extends StatefulWidget { const AnimatedProgressIndicator({ - Key? key, required this.value, this.animationDuration, this.backgroundColor, @@ -19,7 +18,8 @@ class AnimatedProgressIndicator extends StatefulWidget { this.minHeight, this.strokeWidth = 4, this.indicatorStyle = IndicatorStyle.circular, - }) : super(key: key); + super.key, + }); final double value; final Duration? animationDuration; @@ -74,7 +74,7 @@ class AnimatedProgressIndicatorState extends State wi if (widget.value != oldWidget.value) { // Try to start with the previous tween's end value. This ensures that we // have a smooth transition from where the previous animation reached. - double beginValue = valueTween.evaluate(curve); + final double beginValue = valueTween.evaluate(curve); valueTween = Tween( begin: beginValue, @@ -122,8 +122,7 @@ class AnimatedProgressIndicatorState extends State wi widget.backgroundColor ?? (widget.indicatorStyle == IndicatorStyle.circular ? indicatorTheme.circularTrackColor : indicatorTheme.linearTrackColor) ?? Theme.of(context).colorScheme.background; - final Color valueColor = - valueColorTween?.evaluate(curve) ?? widget.valueColor ?? indicatorTheme.color ?? Theme.of(context).colorScheme.background; + final Color valueColor = valueColorTween?.evaluate(curve) ?? widget.valueColor ?? indicatorTheme.color ?? Theme.of(context).colorScheme.background; final double minHeight = widget.minHeight ?? indicatorTheme.linearMinHeight ?? 4.0; if (widget.indicatorStyle == IndicatorStyle.circular) { @@ -182,7 +181,7 @@ class CircularProgressIndicatorPainter extends CustomPainter { final Offset center = size.center(Offset.zero); final double shortestSide = min(size.width - strokeWidth, size.height - strokeWidth); - final double radius = (shortestSide / 2); + final double radius = shortestSide / 2; const double startAngle = -(2 * pi * 0.25); final double sweepAngle = 2 * pi * value; @@ -209,7 +208,7 @@ class CircularProgressIndicatorPainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) { - final oldPainter = (oldDelegate as CircularProgressIndicatorPainter); + final oldPainter = oldDelegate as CircularProgressIndicatorPainter; return oldPainter.value != value || oldPainter.backgroundColor != backgroundColor || oldPainter.valueColor != valueColor || @@ -250,7 +249,7 @@ class LinearProgressIndicatorPainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) { - final oldPainter = (oldDelegate as LinearProgressIndicatorPainter); + final oldPainter = oldDelegate as LinearProgressIndicatorPainter; return oldPainter.value != value || oldPainter.backgroundColor != backgroundColor || oldPainter.valueColor != valueColor || diff --git a/lib/src/widgets/common/bordered_text.dart b/lib/src/widgets/common/bordered_text.dart index 4c4b9854..c3980705 100644 --- a/lib/src/widgets/common/bordered_text.dart +++ b/lib/src/widgets/common/bordered_text.dart @@ -19,13 +19,13 @@ import 'package:flutter/widgets.dart'; /// ``` class BorderedText extends StatelessWidget { const BorderedText({ - Key? key, required this.child, this.strokeCap = StrokeCap.round, this.strokeJoin = StrokeJoin.round, this.strokeWidth = 6.0, this.strokeColor = const Color.fromRGBO(0, 0, 0, 1), - }) : super(key: key); + super.key, + }); /// the stroke cap style final StrokeCap strokeCap; @@ -86,4 +86,4 @@ class BorderedText extends StatelessWidget { ], ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/common/cancel_button.dart b/lib/src/widgets/common/cancel_button.dart index c9cd320d..bb087fa7 100644 --- a/lib/src/widgets/common/cancel_button.dart +++ b/lib/src/widgets/common/cancel_button.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; class CancelButton extends StatelessWidget { const CancelButton({ - Key? key, this.text = 'Cancel', this.returnData, this.withIcon = false, - }) : super(key: key); + super.key, + }); final String text; final dynamic returnData; diff --git a/lib/src/widgets/common/custom_scroll_bar_thumb.dart b/lib/src/widgets/common/custom_scroll_bar_thumb.dart index c6fc44d8..a4348b29 100644 --- a/lib/src/widgets/common/custom_scroll_bar_thumb.dart +++ b/lib/src/widgets/common/custom_scroll_bar_thumb.dart @@ -2,12 +2,12 @@ import 'package:flutter/material.dart'; class CustomScrollBarThumb extends StatelessWidget { const CustomScrollBarThumb({ - Key? key, + super.key, this.backgroundColor = Colors.black, this.drawColor = Colors.black, this.height = 50, this.title = '', - }) : super(key: key); + }); final Color backgroundColor; final Color drawColor; @@ -44,14 +44,14 @@ class CustomScrollBarThumb extends StatelessWidget { child: Container( decoration: BoxDecoration( color: backgroundColor, - borderRadius: const BorderRadius.all(Radius.circular(12.0)), + borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: Container( - width: 24.0, + width: 24, height: height, decoration: BoxDecoration( color: drawColor, - borderRadius: const BorderRadius.all(Radius.circular(12.0)), + borderRadius: const BorderRadius.all(Radius.circular(12)), ), ), ), @@ -75,10 +75,10 @@ class CustomScrollBarThumb extends StatelessWidget { } } +// ignore: unused_element class _ArrowCustomPainter extends CustomPainter { - final Color drawColor; - _ArrowCustomPainter(this.drawColor); + final Color drawColor; @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; @@ -110,16 +110,16 @@ class _ArrowCustomPainter extends CustomPainter { class _ArrowClipper extends CustomClipper { @override Path getClip(Size size) { - double arrowWidth = 8.0; - double startPointX = (size.width - arrowWidth) / 2; - double startPointY1 = size.height / 2 - arrowWidth / 2; - double startPointY2 = size.height / 2 + arrowWidth / 2; + const double arrowWidth = 8; + final double startPointX = (size.width - arrowWidth) / 2; + final double startPointY1 = size.height / 2 - arrowWidth / 2; + final double startPointY2 = size.height / 2 + arrowWidth / 2; return Path() - ..lineTo(0.0, size.height) + ..lineTo(0, size.height) ..lineTo(size.width, size.height) - ..lineTo(size.width, 0.0) - ..lineTo(0.0, 0.0) + ..lineTo(size.width, 0) + ..lineTo(0, 0) ..close() ..moveTo(startPointX, startPointY1) ..lineTo(startPointX + arrowWidth / 2, startPointY1 - arrowWidth / 2) diff --git a/lib/src/widgets/common/discord_button.dart b/lib/src/widgets/common/discord_button.dart index dc87a17d..17b6d1dc 100644 --- a/lib/src/widgets/common/discord_button.dart +++ b/lib/src/widgets/common/discord_button.dart @@ -9,9 +9,9 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class DiscordButton extends StatelessWidget { const DiscordButton({ - Key? key, this.overrideText, - }) : super(key: key); + super.key, + }); final String? overrideText; diff --git a/lib/src/widgets/common/flash_elements.dart b/lib/src/widgets/common/flash_elements.dart index 555f1393..37180eaf 100644 --- a/lib/src/widgets/common/flash_elements.dart +++ b/lib/src/widgets/common/flash_elements.dart @@ -46,8 +46,8 @@ class FlashElements { /// /// [ignoreDesktopCheck] - should we ignore desktop specific style checks static FutureOr showSnackbar({ - BuildContext? context, required Widget title, + BuildContext? context, Widget content = const SizedBox(height: 20), Color sideColor = Colors.red, IconData? leadingIcon = Icons.info_outline, @@ -65,9 +65,11 @@ class FlashElements { Widget? Function(FlashController)? primaryActionBuilder, }) async { // do nothing if in test mode - if (Tools.isTestMode) return; + if (Tools.isTestMode) { + return; + } - bool inViewer = ViewerHandler.instance.inViewer.value; + final bool inViewer = ViewerHandler.instance.inViewer.value; if (!allowInViewer && inViewer) { return; } @@ -76,27 +78,27 @@ class FlashElements { return; } - BuildContext contextToUse = context ?? NavigationHandler.instance.navigatorKey.currentContext!; + final BuildContext contextToUse = context ?? NavigationHandler.instance.navigatorKey.currentContext!; // TODO can this cause an exception? maybe change to WidgetsBinding ? - MediaQueryData mediaQueryData = MediaQuery.of(contextToUse); + final MediaQueryData mediaQueryData = MediaQuery.of(contextToUse); // Get theme here instead of inside the dialogs themselves, since the dialog could close after the page is changed // therefore causing an exception, because this context is not available anymore - ThemeData themeData = Theme.of(contextToUse); + final ThemeData themeData = Theme.of(contextToUse); - bool isDesktop = !ignoreDesktopCheck && (SettingsHandler.instance.appMode.value.isDesktop || Platform.isWindows || Platform.isLinux); - bool isDark = themeData.brightness == Brightness.dark; + final bool isDesktop = !ignoreDesktopCheck && (SettingsHandler.instance.appMode.value.isDesktop || Platform.isWindows || Platform.isLinux); + final bool isDark = themeData.brightness == Brightness.dark; - FlashPosition flashPosition = position == Positions.bottom ? FlashPosition.bottom : FlashPosition.top; + final FlashPosition flashPosition = position == Positions.bottom ? FlashPosition.bottom : FlashPosition.top; if (asDialog) { return showModalFlash( context: contextToUse, builder: (context, controller) { return SettingsDialog( - titlePadding: const EdgeInsets.all(0), - buttonPadding: const EdgeInsets.all(0), - contentPadding: const EdgeInsets.all(0), - insetPadding: const EdgeInsets.all(0), + titlePadding: EdgeInsets.zero, + buttonPadding: EdgeInsets.zero, + contentPadding: EdgeInsets.zero, + insetPadding: EdgeInsets.zero, borderRadius: const BorderRadius.all(Radius.circular(8)), backgroundColor: Colors.transparent, surfaceTintColor: Colors.transparent, diff --git a/lib/src/widgets/common/long_press_repeater.dart b/lib/src/widgets/common/long_press_repeater.dart index 16de4507..1ea6d834 100644 --- a/lib/src/widgets/common/long_press_repeater.dart +++ b/lib/src/widgets/common/long_press_repeater.dart @@ -6,32 +6,38 @@ import 'package:lolisnatcher/src/handlers/service_handler.dart'; class LongPressRepeater extends StatefulWidget { /// This widget detects long press on [child] and repeats given action every [tick] milliseconds. - /// + /// /// After [fasterAfter] amount of ticks, uses faster [tick] interval - [fastTick]. const LongPressRepeater({ - Key? key, required this.onStart, + required this.child, this.onStop, this.onTap, this.tick = 200, this.fastTick = 100, this.fasterAfter = -1, this.behavior, - required this.child, - }) : super(key: key); + super.key, + }); /// called on every tick of long press final VoidCallback onStart; + /// called afrter user stops long press final VoidCallback? onStop; + /// called when user just taps on widget final VoidCallback? onTap; + /// delay in ms between each callback during long press final int tick; + /// delay in ms between each callback after [fasterAfter] amount of ticks final int fastTick; + /// after how many ticks action gets called faster final int fasterAfter; + /// see [GestureDetector.behavior] final HitTestBehavior? behavior; final Widget child; @@ -55,7 +61,9 @@ class _LongPressRepeaterState extends State { return GestureDetector( onLongPressStart: (details) { // repeat every 100ms if the user holds down the button - if (longPressTimer != null) return; + if (longPressTimer != null) { + return; + } longPressTimer = Timer.periodic(Duration(milliseconds: widget.tick), (timer) { widget.onStart(); if (repeatCount > 0) { diff --git a/lib/src/widgets/common/marquee_text.dart b/lib/src/widgets/common/marquee_text.dart index dbbbe1d2..058d5d4b 100644 --- a/lib/src/widgets/common/marquee_text.dart +++ b/lib/src/widgets/common/marquee_text.dart @@ -34,11 +34,11 @@ class MarqueeText extends StatelessWidget { this.pauseAfterRound = const Duration(milliseconds: 1500), this.isExpanded = true, super.key, - }) : text = null, - fontSize = 0, - fontWeight = FontWeight.normal, - fontStyle = FontStyle.normal, - color = null; + }) : text = null, + fontSize = 0, + fontWeight = FontWeight.normal, + fontStyle = FontStyle.normal, + color = null; final String? text; final TextSpan? textSpan; @@ -57,20 +57,18 @@ class MarqueeText extends StatelessWidget { @override Widget build(BuildContext context) { - return isExpanded - ? Expanded(child: innerBox(context)) - : innerBox(context); + return isExpanded ? Expanded(child: innerBox(context)) : innerBox(context); } Widget marquee(BuildContext context) { // This one can detect when text overflows by itself, but I'll leave AutoSize to resize text a bit when nearing overflow return Marquee( - text: text!, + text: text, blankSpace: blankSpace, curve: curve, velocity: velocity, startPadding: startPadding, - fadingEdgeStartFraction: 0.0, + fadingEdgeStartFraction: 0, fadingEdgeEndFraction: 0.15, showFadingOnlyWhenScrolling: false, startAfter: startAfter, @@ -86,12 +84,12 @@ class MarqueeText extends StatelessWidget { Widget marqueeRich(BuildContext context) { return Marquee.rich( - textSpan: textSpan!, + textSpan: textSpan, blankSpace: blankSpace, curve: curve, velocity: velocity, startPadding: startPadding, - fadingEdgeStartFraction: 0.0, + fadingEdgeStartFraction: 0, fadingEdgeEndFraction: 0.15, showFadingOnlyWhenScrolling: false, startAfter: startAfter, @@ -100,7 +98,7 @@ class MarqueeText extends StatelessWidget { } Widget innerBox(BuildContext context) { - if(textSpan != null) { + if (textSpan != null) { return Container( alignment: Alignment.centerLeft, child: marqueeRich(context), @@ -112,7 +110,8 @@ class MarqueeText extends StatelessWidget { height: (fontSize + addedHeight) * MediaQuery.of(context).textScaleFactor, // +X to not trigger overflow on short strings child: AutoSizeText( text!, - minFontSize: (fontSize * 0.8).ceilToDouble(), // allow text to shrink a bit, so that strings can exceed a few symbols in length before starting to scroll + minFontSize: + (fontSize * 0.8).ceilToDouble(), // allow text to shrink a bit, so that strings can exceed a few symbols in length before starting to scroll maxFontSize: fontSize, maxLines: 2, style: TextStyle( diff --git a/lib/src/widgets/common/mascot_image.dart b/lib/src/widgets/common/mascot_image.dart index 61b1f43d..24ddad58 100644 --- a/lib/src/widgets/common/mascot_image.dart +++ b/lib/src/widgets/common/mascot_image.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; class MascotImage extends StatelessWidget { - const MascotImage({Key? key}) : super(key: key); + const MascotImage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/common/media_loading.dart b/lib/src/widgets/common/media_loading.dart index 69ff4ff3..0f9361ba 100644 --- a/lib/src/widgets/common/media_loading.dart +++ b/lib/src/widgets/common/media_loading.dart @@ -13,21 +13,21 @@ import 'package:lolisnatcher/src/widgets/common/bordered_text.dart'; class MediaLoading extends StatefulWidget { const MediaLoading({ - Key? key, required this.item, required this.hasProgress, required this.isFromCache, required this.isDone, - this.isTooBig = false, required this.isStopped, - this.stopReasons = const [], required this.isViewed, required this.total, required this.received, required this.startedAt, required this.startAction, required this.stopAction, - }) : super(key: key); + this.isTooBig = false, + this.stopReasons = const [], + super.key, + }); final BooruItem item; @@ -149,9 +149,9 @@ class _MediaLoadingState extends State { @override Widget build(BuildContext context) { - int nowMils = DateTime.now().millisecondsSinceEpoch; - int sinceStart = _startedAt == 0 ? 0 : nowMils - _startedAt; - bool showLoading = !widget.isDone && (widget.isStopped || (widget.isViewed && sinceStart > 999)); + final int nowMils = DateTime.now().millisecondsSinceEpoch; + final int sinceStart = _startedAt == 0 ? 0 : nowMils - _startedAt; + final bool showLoading = !widget.isDone && (widget.isStopped || (widget.isViewed && sinceStart > 999)); // delay showing loading info a bit, so we don't clutter interface for fast loading files // return buildElement(context); @@ -184,11 +184,11 @@ class _MediaLoadingState extends State { } } - bool hasProgressData = widget.hasProgress && (_total > 0); - int expectedBytes = hasProgressData ? _received : 0; - int totalBytes = hasProgressData ? _total : 0; + final bool hasProgressData = widget.hasProgress && (_total > 0); + final int expectedBytes = hasProgressData ? _received : 0; + final int totalBytes = hasProgressData ? _total : 0; - double speedCheckInterval = 1000 / 4; + const double speedCheckInterval = 1000 / 4; if (hasProgressData && (nowMils - _lastTime) > speedCheckInterval) { _prevAmount = _lastAmount; _lastAmount = expectedBytes; @@ -197,11 +197,11 @@ class _MediaLoadingState extends State { _lastTime = nowMils; } - double percentDone = hasProgressData ? (expectedBytes / totalBytes) : 0; - String loadedSize = hasProgressData ? Tools.formatBytes(expectedBytes, 1) : ''; - String expectedSize = hasProgressData ? Tools.formatBytes(totalBytes, 1) : ''; + final double percentDone = hasProgressData ? (expectedBytes / totalBytes) : 0; + final String loadedSize = hasProgressData ? Tools.formatBytes(expectedBytes, 1) : ''; + final String expectedSize = hasProgressData ? Tools.formatBytes(totalBytes, 1) : ''; - bool isVideo = widget.item.mediaType.value.isVideo; + final bool isVideo = widget.item.mediaType.value.isVideo; String percentDoneText = ''; if (hasProgressData) { @@ -219,22 +219,22 @@ class _MediaLoadingState extends State { } } - String filesizeText = (hasProgressData && percentDone < 1) ? ('$loadedSize / $expectedSize') : ''; + final String filesizeText = (hasProgressData && percentDone < 1) ? ('$loadedSize / $expectedSize') : ''; int expectedSpeed = 0; if (hasProgressData && _prevAmount > 0 && _lastAmount > 0) { expectedSpeed = ((_lastAmount - _prevAmount) * (1000 / (nowMils - _prevTime))).round(); // expectedSpeed = ((_lastAmount - _prevAmount) * (1000 / speedCheckInterval)).round(); } - String expectedSpeedText = (hasProgressData && percentDone < 1) ? ('${Tools.formatBytes(expectedSpeed, 1)}/s') : ''; + final String expectedSpeedText = (hasProgressData && percentDone < 1) ? ('${Tools.formatBytes(expectedSpeed, 1)}/s') : ''; - double expectedTime = hasProgressData ? (expectedSpeed == 0 ? double.infinity : ((totalBytes - expectedBytes) / expectedSpeed)) : 0; - String expectedTimeText = (hasProgressData && expectedTime > 0 && percentDone < 1) ? ("~${expectedTime.toStringAsFixed(1)} s") : ''; + final double expectedTime = hasProgressData ? (expectedSpeed == 0 ? double.infinity : ((totalBytes - expectedBytes) / expectedSpeed)) : 0; + final String expectedTimeText = (hasProgressData && expectedTime > 0 && percentDone < 1) ? ('~${expectedTime.toStringAsFixed(1)} s') : ''; - int sinceStartSeconds = (sinceStart / 1000).floor(); - String sinceStartText = (!widget.isDone && percentDone < 1) ? 'Started ${sinceStartSeconds.toString()}s ago' : ''; + final int sinceStartSeconds = (sinceStart / 1000).floor(); + final String sinceStartText = (!widget.isDone && percentDone < 1) ? 'Started ${sinceStartSeconds}s ago' : ''; - bool isMovedBelow = settingsHandler.previewMode == 'Sample' && !widget.item.isHated.value; + final bool isMovedBelow = settingsHandler.previewMode == 'Sample' && !widget.item.isHated.value; // print('$percentDone | $percentDoneText'); @@ -376,11 +376,11 @@ class _MediaLoadingState extends State { class LoadingText extends StatelessWidget { const LoadingText({ - Key? key, required this.text, required this.fontSize, this.color = Colors.white, - }) : super(key: key); + super.key, + }); final String text; final double fontSize; @@ -405,19 +405,19 @@ class LoadingText extends StatelessWidget { ); // TODO animate text value changes? - return AnimatedSwitcher( - duration: const Duration(milliseconds: 50), - child: BorderedText( - key: ValueKey(text), - strokeWidth: 3, - child: Text( - text, - style: TextStyle( - fontSize: fontSize, - color: color, - ), - ), - ), - ); + // return AnimatedSwitcher( + // duration: const Duration(milliseconds: 50), + // child: BorderedText( + // key: ValueKey(text), + // strokeWidth: 3, + // child: Text( + // text, + // style: TextStyle( + // fontSize: fontSize, + // color: color, + // ), + // ), + // ), + // ); } } diff --git a/lib/src/widgets/common/restartable_progress_indicator.dart b/lib/src/widgets/common/restartable_progress_indicator.dart index ffa5e896..391c8499 100644 --- a/lib/src/widgets/common/restartable_progress_indicator.dart +++ b/lib/src/widgets/common/restartable_progress_indicator.dart @@ -8,10 +8,10 @@ import 'package:lolisnatcher/src/utils/timed_progress_controller.dart'; class RestartableProgressIndicator extends StatefulWidget { const RestartableProgressIndicator({ - Key? key, required this.controller, this.onTimeout, - }) : super(key: key); + super.key, + }); final TimedProgressController controller; final VoidCallback? onTimeout; @@ -45,8 +45,8 @@ class _RestartableProgressIndicatorState extends State { String? displayText; int counter = 0, pauseCounter = 0, pauseThreshold = 8, maxAllowedSize = 0; - String bufferText = ""; + String bufferText = ''; bool forward = true, disposed = false; static const int stepDelay = 200; @override - void initState(){ + void initState() { + super.initState(); counter = 0; pauseCounter = 0; maxAllowedSize = widget.size; - bufferText = ""; + bufferText = ''; forward = true; - super.initState(); } + @override - void dispose(){ + void dispose() { disposed = true; super.dispose(); } @override Widget build(BuildContext context) { - if ((counter + maxAllowedSize) > widget.text.length){ - if(counter != 0) { - setState(() { - initState(); - }); + if ((counter + maxAllowedSize) > widget.text.length) { + if (counter != 0) { + setState(initState); } else { return Text( - widget.text, - textAlign: TextAlign.left, + widget.text, + textAlign: TextAlign.left, style: TextStyle( color: widget.textColor, - ) + ), ); } } else { - switch(widget.mode){ - case ("bounce"): + switch (widget.mode) { + case 'bounce': bounce(); break; - case ("scroll"): + case 'scroll': scroll(); break; - case ("infinite"): + case 'infinite': infinite(); break; - case ("infiniteWithPause"): + case 'infiniteWithPause': infiniteWithPause(); break; } } return Text( - displayText!, - textAlign: TextAlign.left, + displayText!, + textAlign: TextAlign.left, style: TextStyle( color: widget.textColor, - ) + ), ); } - void bounce(){ + + void bounce() { Future.delayed(const Duration(milliseconds: stepDelay), () { - if (!disposed){ + if (!disposed) { if ((counter + maxAllowedSize) >= widget.text.length) { setState(() { forward = false; }); - } else if (!forward && counter == 0){ + } else if (!forward && counter == 0) { setState(() { forward = true; }); } - if (forward){ + if (forward) { setState(() { - counter ++; + counter++; }); } else { setState(() { - counter --; + counter--; }); } } }); displayText = widget.text.substring(counter, counter + maxAllowedSize); } + void scroll() { Future.delayed(const Duration(milliseconds: stepDelay), () { - if (!disposed){ + if (!disposed) { if ((counter + maxAllowedSize) < widget.text.length) { setState(() { counter++; @@ -112,10 +119,11 @@ class _ScrollingTextState extends State { }); displayText = widget.text.substring(counter, counter + maxAllowedSize); } + void infinite() { Future.delayed(const Duration(milliseconds: stepDelay), () { - if (!disposed){ - if (bufferText.isEmpty){ + if (!disposed) { + if (bufferText.isEmpty) { if ((counter + maxAllowedSize) < widget.text.length) { setState(() { counter++; @@ -126,33 +134,34 @@ class _ScrollingTextState extends State { }); } } else { - if (bufferText.length > 1){ + if (bufferText.length > 1) { setState(() { - bufferText = bufferText.substring(1,bufferText.length); + bufferText = bufferText.substring(1, bufferText.length); }); } else { setState(() { - bufferText = ""; + bufferText = ''; counter = 0; }); } } } }); - if (bufferText.isEmpty){ + if (bufferText.isEmpty) { displayText = widget.text.substring(counter, counter + maxAllowedSize); } else { - displayText = "$bufferText ${widget.text.substring(0, maxAllowedSize - (bufferText.length - 1))}"; + displayText = '$bufferText ${widget.text.substring(0, maxAllowedSize - (bufferText.length - 1))}'; } } - void infiniteWithPause() async { + + Future infiniteWithPause() async { Future.delayed(const Duration(milliseconds: stepDelay), () { if (!disposed) { - if(counter == 0 && pauseCounter <= pauseThreshold) { + if (counter == 0 && pauseCounter <= pauseThreshold) { setState(() { pauseCounter++; }); - } else if (bufferText.isEmpty){ + } else if (bufferText.isEmpty) { if ((counter + maxAllowedSize) < widget.text.length) { setState(() { counter++; @@ -163,13 +172,13 @@ class _ScrollingTextState extends State { }); } } else { - if (bufferText.length > 1){ + if (bufferText.length > 1) { setState(() { - bufferText = bufferText.substring(1,bufferText.length); + bufferText = bufferText.substring(1, bufferText.length); }); } else { setState(() { - bufferText = ""; + bufferText = ''; counter = 0; pauseCounter = 0; }); @@ -178,15 +187,14 @@ class _ScrollingTextState extends State { } }); - if (bufferText.isEmpty){ + if (bufferText.isEmpty) { displayText = widget.text.substring(counter, counter + maxAllowedSize); } else { - displayText = "$bufferText ${widget.text.substring(0, maxAllowedSize - (bufferText.length - 1))}"; + displayText = '$bufferText ${widget.text.substring(0, maxAllowedSize - (bufferText.length - 1))}'; } - if(counter == 0 && pauseCounter <= pauseThreshold) { + if (counter == 0 && pauseCounter <= pauseThreshold) { displayText = '${displayText!}...'; } - } } diff --git a/lib/src/widgets/common/settings_widgets.dart b/lib/src/widgets/common/settings_widgets.dart index 083bec6d..84bdcd70 100644 --- a/lib/src/widgets/common/settings_widgets.dart +++ b/lib/src/widgets/common/settings_widgets.dart @@ -3,8 +3,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/widgets/common/long_press_repeater.dart'; @@ -15,7 +15,6 @@ const double borderWidth = 1; class SettingsButton extends StatelessWidget { const SettingsButton({ - Key? key, required this.name, this.icon, this.subtitle, @@ -29,7 +28,8 @@ class SettingsButton extends StatelessWidget { this.enabled = true, // disable button interaction (will also change text color to grey) this.iconOnly = false, this.dense = false, - }) : super(key: key); + super.key, + }); final String name; final Widget? icon; @@ -113,13 +113,15 @@ class SettingsPageOpen { final bool asBottomSheet; Future open() async { - if (!condition) return null; + if (!condition) { + return null; + } - SettingsHandler settingsHandler = SettingsHandler.instance; + final SettingsHandler settingsHandler = SettingsHandler.instance; - bool isTooNarrow = MediaQuery.of(context).size.width < 550; - bool isDesktop = settingsHandler.appMode.value.isDesktop || Platform.isWindows || Platform.isLinux || Platform.isMacOS; - bool useDesktopMode = !isTooNarrow && isDesktop; + final bool isTooNarrow = MediaQuery.of(context).size.width < 550; + final bool isDesktop = settingsHandler.appMode.value.isDesktop || Platform.isWindows || Platform.isLinux || Platform.isMacOS; + final bool useDesktopMode = !isTooNarrow && isDesktop; dynamic result; if (useDesktopMode) { @@ -172,7 +174,6 @@ class SettingsPageOpen { class SettingsToggle extends StatelessWidget { const SettingsToggle({ - Key? key, required this.value, required this.onChanged, required this.title, @@ -180,7 +181,8 @@ class SettingsToggle extends StatelessWidget { this.drawTopBorder = false, this.drawBottomBorder = true, this.trailingIcon, - }) : super(key: key); + super.key, + }); final bool value; final void Function(bool) onChanged; @@ -222,7 +224,6 @@ class SettingsToggle extends StatelessWidget { class SettingsDropdown extends StatelessWidget { const SettingsDropdown({ - Key? key, required this.value, required this.items, required this.onChanged, @@ -233,7 +234,8 @@ class SettingsDropdown extends StatelessWidget { this.trailingIcon, this.itemBuilder, this.itemTitleBuilder, - }) : super(key: key); + super.key, + }); final T value; final List items; @@ -285,7 +287,7 @@ class SettingsDropdown extends StatelessWidget { }).toList(); }, items: items.map>((T item) { - bool isCurrent = item == value; + final bool isCurrent = item == value; return DropdownMenuItem( value: item, @@ -322,14 +324,14 @@ class SettingsDropdown extends StatelessWidget { class SettingsBooruDropdown extends StatelessWidget { const SettingsBooruDropdown({ - Key? key, required this.value, required this.onChanged, required this.title, this.drawTopBorder = false, this.drawBottomBorder = true, this.trailingIcon, - }) : super(key: key); + super.key, + }); final Booru? value; final void Function(Booru?)? onChanged; @@ -340,7 +342,7 @@ class SettingsBooruDropdown extends StatelessWidget { @override Widget build(BuildContext context) { - List boorus = SettingsHandler.instance.booruList; + final List boorus = SettingsHandler.instance.booruList; return SettingsDropdown( value: value, @@ -353,7 +355,10 @@ class SettingsBooruDropdown extends StatelessWidget { itemBuilder: (Booru? booru) { return Row( children: [ - booru == null ? const Icon(null) : (booru.type == BooruType.Favourites ? const Icon(Icons.favorite, color: Colors.red, size: 18) : Favicon(booru)), + if (booru == null) + const Icon(null) + else + booru.type == BooruType.Favourites ? const Icon(Icons.favorite, color: Colors.red, size: 18) : Favicon(booru), Text(" ${booru?.name ?? ''}".trim()), ], ); @@ -364,12 +369,11 @@ class SettingsBooruDropdown extends StatelessWidget { class SettingsTextInput extends StatefulWidget { const SettingsTextInput({ - Key? key, required this.controller, + required this.title, this.inputType = TextInputType.text, this.inputFormatters, this.validator, - required this.title, this.hintText = '', this.autofocus = false, this.onChanged, @@ -385,13 +389,14 @@ class SettingsTextInput extends StatefulWidget { this.numberMax = 100, this.trailingIcon, this.onlyInput = false, - }) : super(key: key); + super.key, + }); final TextEditingController controller; + final String title; final TextInputType inputType; final List? inputFormatters; final String? Function(String?)? validator; - final String title; final String hintText; final bool autofocus; final void Function(String)? onChanged; @@ -407,6 +412,7 @@ class SettingsTextInput extends StatefulWidget { final double numberMax; final Widget? trailingIcon; final bool onlyInput; + @override State createState() => _SettingsTextInputState(); } @@ -439,8 +445,8 @@ class _SettingsTextInputState extends State { void stepNumberDown() { if (widget.numberButtons) { - double valueWithStep = (double.tryParse(widget.controller.text) ?? 0) - widget.numberStep; - double newValue = valueWithStep >= widget.numberMin ? valueWithStep : widget.numberMin; + final double valueWithStep = (double.tryParse(widget.controller.text) ?? 0) - widget.numberStep; + final double newValue = valueWithStep >= widget.numberMin ? valueWithStep : widget.numberMin; widget.controller.text = newValue.toStringAsFixed(newValue.truncateToDouble() == newValue ? 0 : 1); onChangedCallback(widget.controller.text); } @@ -448,8 +454,8 @@ class _SettingsTextInputState extends State { void stepNumberUp() { if (widget.numberButtons) { - double valueWithStep = (double.tryParse(widget.controller.text) ?? 0) + widget.numberStep; - double newValue = valueWithStep <= widget.numberMax ? valueWithStep : widget.numberMax; + final double valueWithStep = (double.tryParse(widget.controller.text) ?? 0) + widget.numberStep; + final double newValue = valueWithStep <= widget.numberMax ? valueWithStep : widget.numberMax; widget.controller.text = newValue.toStringAsFixed(newValue.truncateToDouble() == newValue ? 0 : 1); onChangedCallback(widget.controller.text); } @@ -504,22 +510,23 @@ class _SettingsTextInputState extends State { onChangedCallback(widget.controller.text); }, ), - isFocused - ? IconButton( - key: const Key('submit-button'), - icon: Icon(widget.onSubmitted != null ? Icons.send : Icons.done, color: Theme.of(context).colorScheme.onSurface), - onPressed: () { - if (widget.onSubmitted != null) widget.onSubmitted!(widget.controller.text); - _focusNode.unfocus(); - }, - ) - : IconButton( - key: const Key('edit-button'), - icon: Icon(Icons.edit, color: Theme.of(context).colorScheme.onSurface), - onPressed: () { - _focusNode.requestFocus(); - }, - ), + if (isFocused) + IconButton( + key: const Key('submit-button'), + icon: Icon(widget.onSubmitted != null ? Icons.send : Icons.done, color: Theme.of(context).colorScheme.onSurface), + onPressed: () { + if (widget.onSubmitted != null) { + widget.onSubmitted?.call(widget.controller.text); + } + _focusNode.unfocus(); + }, + ) + else + IconButton( + key: const Key('edit-button'), + icon: Icon(Icons.edit, color: Theme.of(context).colorScheme.onSurface), + onPressed: _focusNode.requestFocus, + ), ], ); } @@ -572,7 +579,6 @@ class _SettingsTextInputState extends State { class SettingsDialog extends StatelessWidget { const SettingsDialog({ - Key? key, this.title, this.content, this.contentItems, @@ -580,12 +586,13 @@ class SettingsDialog extends StatelessWidget { this.titlePadding, this.contentPadding = const EdgeInsets.fromLTRB(16, 20, 16, 16), this.buttonPadding, - this.insetPadding = const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0), + this.insetPadding = const EdgeInsets.symmetric(horizontal: 40, vertical: 24), this.borderRadius, this.backgroundColor, this.surfaceTintColor, this.scrollable = true, - }) : super(key: key); + super.key, + }); final Widget? title; final Widget? content; @@ -625,7 +632,6 @@ class SettingsDialog extends StatelessWidget { class SettingsBottomSheet extends StatelessWidget { const SettingsBottomSheet({ - Key? key, this.title, this.content, this.contentItems, @@ -636,7 +642,8 @@ class SettingsBottomSheet extends StatelessWidget { this.borderRadius, this.backgroundColor, this.showCloseButton = true, - }) : super(key: key); + super.key, + }); final Widget? title; final Widget? content; @@ -713,12 +720,12 @@ class SettingsBottomSheet extends StatelessWidget { class SettingsPageDialog extends StatelessWidget { const SettingsPageDialog({ - Key? key, this.title, this.content, this.actions, this.fab, - }) : super(key: key); + super.key, + }); final Widget? title; final Widget? content; diff --git a/lib/src/widgets/common/text_expander.dart b/lib/src/widgets/common/text_expander.dart index 0d541a92..d52ab804 100644 --- a/lib/src/widgets/common/text_expander.dart +++ b/lib/src/widgets/common/text_expander.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; class TextExpander extends StatefulWidget { - const TextExpander({Key? key, required this.title, required this.bodyList}) : super(key: key); + const TextExpander({ + required this.title, + required this.bodyList, + super.key, + }); + final String title; final List bodyList; @@ -43,15 +48,10 @@ class _TextExpanderState extends State { duration: const Duration(milliseconds: 200), child: showText ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: widget.bodyList, - ) - : const Center( - child: SizedBox( - width: 0, - height: 0, - ), - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: widget.bodyList, + ) + : const Center(child: SizedBox.shrink()), ), Divider( height: 10, diff --git a/lib/src/widgets/common/thumbnail_loading.dart b/lib/src/widgets/common/thumbnail_loading.dart index 2669b5e9..0160dd93 100644 --- a/lib/src/widgets/common/thumbnail_loading.dart +++ b/lib/src/widgets/common/thumbnail_loading.dart @@ -11,7 +11,6 @@ import 'package:lolisnatcher/src/widgets/common/bordered_text.dart'; class ThumbnailLoading extends StatefulWidget { const ThumbnailLoading({ - Key? key, required this.item, required this.hasProgress, required this.isFromCache, @@ -22,7 +21,8 @@ class ThumbnailLoading extends StatefulWidget { required this.startedAt, required this.restartAction, this.errorCode, - }) : super(key: key); + super.key, + }); final BooruItem item; @@ -82,9 +82,7 @@ class _ThumbnailLoadingState extends State { final bool isDone = _total > 0 && _received >= _total; Debounce.delay( tag: 'loading_thumbnail_progress_${widget.item.thumbnailURL}', - callback: () { - updateState(); - }, + callback: updateState, duration: Duration(milliseconds: isDone ? 0 : 250), ); } @@ -114,9 +112,9 @@ class _ThumbnailLoadingState extends State { @override Widget build(BuildContext context) { - int nowMils = DateTime.now().millisecondsSinceEpoch; - int sinceStart = _startedAt == 0 ? 0 : nowMils - _startedAt; - bool showLoading = !widget.isDone && (widget.isFailed || (sinceStart > 499)); + final int nowMils = DateTime.now().millisecondsSinceEpoch; + final int sinceStart = _startedAt == 0 ? 0 : nowMils - _startedAt; + final bool showLoading = !widget.isDone && (widget.isFailed || (sinceStart > 499)); // bool showLoading = !widget.isDone || widget.isFailed; // delay showing loading info a bit, so we don't clutter interface for fast loading files @@ -193,11 +191,11 @@ class _ThumbnailLoadingState extends State { ); } - bool hasProgressData = widget.hasProgress && (_total > 0); - int expectedBytes = hasProgressData ? _received : 0; - int totalBytes = hasProgressData ? _total : 0; + final bool hasProgressData = widget.hasProgress && (_total > 0); + final int expectedBytes = hasProgressData ? _received : 0; + final int totalBytes = hasProgressData ? _total : 0; - double percentDone = hasProgressData ? (expectedBytes / totalBytes) : 0; + final double percentDone = hasProgressData ? (expectedBytes / totalBytes) : 0; // String? percentDoneText = hasProgressData // ? ((percentDone ?? 0) == 1 ? null : '${(percentDone! * 100).toStringAsFixed(2)}%') // : (isFromCache == true ? '...' : null); @@ -222,7 +220,7 @@ class _ThumbnailLoadingState extends State { ), ), ), - // + // SizedBox( width: 1, child: RotatedBox( diff --git a/lib/src/widgets/common/transparent_pointer.dart b/lib/src/widgets/common/transparent_pointer.dart index 6ca57667..8f32ff44 100644 --- a/lib/src/widgets/common/transparent_pointer.dart +++ b/lib/src/widgets/common/transparent_pointer.dart @@ -51,10 +51,10 @@ class TransparentPointer extends SingleChildRenderObjectWidget { /// Creates a widget that is invisible for its parent to hit testing, but still /// allows its subtree to receive pointer events. const TransparentPointer({ - Key? key, + required super.child, this.transparent = true, - required Widget child, - }) : super(key: key, child: child); + super.key, + }); /// Whether this widget is invisible to its parent during hit testing. final bool transparent; @@ -89,7 +89,9 @@ class RenderTransparentPointer extends RenderProxyBox { bool _transparent; set transparent(bool value) { - if (value == _transparent) return; + if (value == _transparent) { + return; + } _transparent = value; } @@ -104,4 +106,4 @@ class RenderTransparentPointer extends RenderProxyBox { super.debugFillProperties(properties); properties.add(DiagnosticsProperty('transparent', transparent)); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/desktop/desktop_image_listener.dart b/lib/src/widgets/desktop/desktop_image_listener.dart index af2ddd57..33846157 100644 --- a/lib/src/widgets/desktop/desktop_image_listener.dart +++ b/lib/src/widgets/desktop/desktop_image_listener.dart @@ -25,7 +25,7 @@ import 'package:lolisnatcher/src/widgets/video/video_viewer_placeholder.dart'; /// If the file url isn't empty it will return a current media widget for the fileURL /// class DesktopImageListener extends StatefulWidget { - const DesktopImageListener(this.searchTab, {Key? key}) : super(key: key); + const DesktopImageListener(this.searchTab, {super.key}); final SearchTab searchTab; @override @@ -183,7 +183,7 @@ class _DesktopImageListenerState extends State { onPressed: () async { viewerHandler.isDesktopFullscreen.value = true; updateState(); - delayedZoomReset(); + unawaited(delayedZoomReset()); await showDialog( context: context, @@ -215,7 +215,7 @@ class _DesktopImageListenerState extends State { viewerHandler.isDesktopFullscreen.value = false; updateState(); - delayedZoomReset(); + unawaited(delayedZoomReset()); }, child: const Icon(Icons.fullscreen), ), diff --git a/lib/src/widgets/desktop/desktop_scroll_wrap.dart b/lib/src/widgets/desktop/desktop_scroll_wrap.dart index 7a3b7713..8961a670 100644 --- a/lib/src/widgets/desktop/desktop_scroll_wrap.dart +++ b/lib/src/widgets/desktop/desktop_scroll_wrap.dart @@ -9,7 +9,7 @@ import 'package:lolisnatcher/src/handlers/settings_handler.dart'; ScrollPhysics? getListPhysics() { final SettingsHandler settingsHandler = SettingsHandler.instance; - bool isDesktopPlatform = Platform.isWindows || Platform.isLinux || Platform.isMacOS; + final bool isDesktopPlatform = Platform.isWindows || Platform.isLinux || Platform.isMacOS; if (settingsHandler.desktopListsDrag == false && isDesktopPlatform) { return const NeverScrollableScrollPhysics(); } else { @@ -18,9 +18,14 @@ ScrollPhysics? getListPhysics() { } class DesktopScrollWrap extends StatelessWidget { - final Widget child; + const DesktopScrollWrap({ + required this.controller, + required this.child, + super.key, + }); + final ScrollController controller; - const DesktopScrollWrap({Key? key, required this.child, required this.controller}) : super(key: key); + final Widget child; @override Widget build(BuildContext context) { @@ -30,7 +35,7 @@ class DesktopScrollWrap extends StatelessWidget { // on child element wrapped in this // otherwise, it will not affect how scroll works - bool isDesktopPlatform = Platform.isWindows || Platform.isLinux || Platform.isMacOS; + final bool isDesktopPlatform = Platform.isWindows || Platform.isLinux || Platform.isMacOS; if (isDesktopPlatform) { return ImprovedScrolling( @@ -53,7 +58,7 @@ class DesktopScrollWrap extends StatelessWidget { // customScrollCursor: useSystemCursor ? null : const DefaultCustomScrollCursor(), ), keyboardScrollConfig: KeyboardScrollConfig( - arrowsScrollAmount: 250.0, + arrowsScrollAmount: 250, homeScrollDurationBuilder: (currentScrollOffset, minScrollOffset) { return const Duration(milliseconds: 100); }, @@ -62,9 +67,8 @@ class DesktopScrollWrap extends StatelessWidget { }, ), customMouseWheelScrollConfig: CustomMouseWheelScrollConfig( - scrollAmountMultiplier: SettingsHandler.instance.appMode.value.isDesktop == true - ? (settingsHandler.mousewheelScrollSpeed / 3) - : settingsHandler.mousewheelScrollSpeed, + scrollAmountMultiplier: + SettingsHandler.instance.appMode.value.isDesktop == true ? (settingsHandler.mousewheelScrollSpeed / 3) : settingsHandler.mousewheelScrollSpeed, ), child: child, ); diff --git a/lib/src/widgets/desktop/desktop_tabs.dart b/lib/src/widgets/desktop/desktop_tabs.dart index 36634892..16035e08 100644 --- a/lib/src/widgets/desktop/desktop_tabs.dart +++ b/lib/src/widgets/desktop/desktop_tabs.dart @@ -5,9 +5,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/widgets/common/marquee_text.dart'; import 'package:lolisnatcher/src/widgets/image/favicon.dart'; @@ -15,7 +15,7 @@ import 'package:lolisnatcher/src/widgets/image/favicon.dart'; // Experimental: attempt to do chrome-like tabs list for desktop view class DesktopTabs extends StatefulWidget { - const DesktopTabs({Key? key}) : super(key: key); + const DesktopTabs({super.key}); @override State createState() => _DesktopTabsState(); @@ -40,7 +40,7 @@ class _DesktopTabsState extends State { super.dispose(); } - void jumpToTab(int index) async { + Future jumpToTab(int index) async { scrollController.jumpTo(index * (scrollController.position.maxScrollExtent / searchHandler.list.length)); await Future.delayed(const Duration(milliseconds: 50)); await scrollController.scrollToIndex( @@ -57,13 +57,13 @@ class _DesktopTabsState extends State { } Widget buildRow(SearchTab tab) { - bool isNotEmptyBooru = tab.selectedBooru.value.faviconURL != null; + final bool isNotEmptyBooru = tab.selectedBooru.value.faviconURL != null; - int? totalCount = tab.booruHandler.totalCount.value; - String totalCountText = (totalCount > 0) ? " ($totalCount)" : ""; - String tagText = "${tab.tags == "" ? "[No Tags]" : tab.tags}$totalCountText"; + final int totalCount = tab.booruHandler.totalCount.value; + final String totalCountText = (totalCount > 0) ? ' ($totalCount)' : ''; + final String tagText = "${tab.tags == "" ? "[No Tags]" : tab.tags}$totalCountText"; - bool isSelected = searchHandler.currentIndex == searchHandler.list.indexOf(tab); + final bool isSelected = searchHandler.currentIndex == searchHandler.list.indexOf(tab); return Container( decoration: BoxDecoration( @@ -77,17 +77,16 @@ class _DesktopTabsState extends State { width: double.maxFinite, child: Row( children: [ - isNotEmptyBooru - ? (tab.selectedBooru.value.type == BooruType.Favourites - ? const Icon(Icons.favorite, color: Colors.red, size: 18) - : Favicon(tab.selectedBooru.value)) - : const Icon(CupertinoIcons.question, size: 18), + if (isNotEmptyBooru) + tab.selectedBooru.value.type == BooruType.Favourites ? const Icon(Icons.favorite, color: Colors.red, size: 18) : Favicon(tab.selectedBooru.value) + else + const Icon(CupertinoIcons.question, size: 18), const SizedBox(width: 3), MarqueeText( key: ValueKey(tagText), text: tagText, fontSize: 16, - color: tab.tags == "" ? Colors.grey : null, + color: tab.tags == '' ? Colors.grey : null, ), const SizedBox(width: 3), IconButton( diff --git a/lib/src/widgets/desktop/resizable_split_view.dart b/lib/src/widgets/desktop/resizable_split_view.dart index 378a44ce..b187f1cf 100644 --- a/lib/src/widgets/desktop/resizable_split_view.dart +++ b/lib/src/widgets/desktop/resizable_split_view.dart @@ -7,7 +7,6 @@ enum SplitDirection { class ResizableSplitView extends StatefulWidget { const ResizableSplitView({ - Key? key, required this.firstChild, required this.secondChild, this.startRatio = 0.5, @@ -15,11 +14,9 @@ class ResizableSplitView extends StatefulWidget { this.maxRatio = 1, this.direction = SplitDirection.horizontal, this.onRatioChange, - }) : assert(startRatio >= 0), - assert(startRatio <= 1), - assert(minRatio >= 0), - assert(minRatio <= 1), - super(key: key); + super.key, + }) : assert(startRatio >= 0 && startRatio <= 1, 'startRatio must be between 0 and 1'), + assert(minRatio >= 0 && minRatio <= 1, 'minRatio must be between 0 and 1'); final Widget firstChild; final Widget secondChild; @@ -27,7 +24,7 @@ class ResizableSplitView extends StatefulWidget { final double minRatio; final double maxRatio; final SplitDirection direction; - final void Function(double)? onRatioChange; + final void Function(double)? onRatioChange; @override State createState() => _ResizableSplitViewState(); @@ -76,53 +73,56 @@ class _ResizableSplitViewState extends State { widget.onRatioChange?.call(_ratio); - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } } @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, BoxConstraints constraints) { - assert(_ratio <= 1); - assert(_ratio >= 0); - final double directionSize = widget.direction == SplitDirection.horizontal ? constraints.maxWidth : constraints.maxHeight; - _maxSize ??= directionSize - _dividerWidth; - if (_maxSize != directionSize) { - _maxSize = directionSize - _dividerWidth; - } - - return SizedBox( - width: widget.direction == SplitDirection.horizontal ? constraints.maxWidth : null, - height: widget.direction == SplitDirection.horizontal ? null : constraints.maxHeight, - child: directionWidget( - children: [ - SizedBox( - width: widget.direction == SplitDirection.horizontal ? _size1 : null, - height: widget.direction == SplitDirection.horizontal ? null : _size1, - child: widget.firstChild, - ), - MouseRegion( - cursor: SystemMouseCursors.grab, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onPanUpdate: updateRatio, - child: SizedBox( - width: widget.direction == SplitDirection.horizontal ? _dividerWidth : constraints.maxWidth, - height: widget.direction == SplitDirection.horizontal ? constraints.maxHeight : _dividerWidth, - child: RotationTransition( - turns: widget.direction == SplitDirection.horizontal ? const AlwaysStoppedAnimation(0.25) : const AlwaysStoppedAnimation(0.50), - child: const Icon(Icons.drag_handle), + return LayoutBuilder( + builder: (context, BoxConstraints constraints) { + assert(_ratio <= 1 && _ratio >= 0, 'ratio must be between 0 and 1'); + final double directionSize = widget.direction == SplitDirection.horizontal ? constraints.maxWidth : constraints.maxHeight; + _maxSize ??= directionSize - _dividerWidth; + if (_maxSize != directionSize) { + _maxSize = directionSize - _dividerWidth; + } + + return SizedBox( + width: widget.direction == SplitDirection.horizontal ? constraints.maxWidth : null, + height: widget.direction == SplitDirection.horizontal ? null : constraints.maxHeight, + child: directionWidget( + children: [ + SizedBox( + width: widget.direction == SplitDirection.horizontal ? _size1 : null, + height: widget.direction == SplitDirection.horizontal ? null : _size1, + child: widget.firstChild, + ), + MouseRegion( + cursor: SystemMouseCursors.grab, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onPanUpdate: updateRatio, + child: SizedBox( + width: widget.direction == SplitDirection.horizontal ? _dividerWidth : constraints.maxWidth, + height: widget.direction == SplitDirection.horizontal ? constraints.maxHeight : _dividerWidth, + child: RotationTransition( + turns: widget.direction == SplitDirection.horizontal ? const AlwaysStoppedAnimation(0.25) : const AlwaysStoppedAnimation(0.50), + child: const Icon(Icons.drag_handle), + ), ), ), ), - ), - SizedBox( - width: widget.direction == SplitDirection.horizontal ? _size2 : null, - height: widget.direction == SplitDirection.horizontal ? null : _size2, - child: widget.secondChild, - ), - ], - ), - ); - }); + SizedBox( + width: widget.direction == SplitDirection.horizontal ? _size2 : null, + height: widget.direction == SplitDirection.horizontal ? null : _size2, + child: widget.secondChild, + ), + ], + ), + ); + }, + ); } } diff --git a/lib/src/widgets/dialogs/comments_dialog.dart b/lib/src/widgets/dialogs/comments_dialog.dart index b5fe16da..e667587a 100644 --- a/lib/src/widgets/dialogs/comments_dialog.dart +++ b/lib/src/widgets/dialogs/comments_dialog.dart @@ -19,10 +19,10 @@ import 'package:lolisnatcher/src/widgets/thumbnail/thumbnail_build.dart'; class CommentsDialog extends StatefulWidget { const CommentsDialog({ - Key? key, required this.index, required this.item, - }) : super(key: key); + super.key, + }); final int index; final BooruItem item; @@ -52,7 +52,7 @@ class _CommentsDialogState extends State { // no timezone support in DateFormat? see: https://stackoverflow.com/questions/56189407/dart-parse-date-timezone-gives-unimplementederror/56190055 // remove timezones from strings until they fix it DateTime parsedDate; - if (format == "unix") { + if (format == 'unix') { parsedDate = DateTime.fromMillisecondsSinceEpoch(int.parse(date) * 1000); } else { date = date.replaceAll(RegExp(r'(?:\+|\-)\d{4}'), ''); @@ -67,7 +67,9 @@ class _CommentsDialogState extends State { } String parseContent(String? content) { - if (content == null) return ''; + if (content == null) { + return ''; + } // TODO more rules // TODO maybe there is a better/more optimized way to do this? @@ -82,7 +84,7 @@ class _CommentsDialogState extends State { // move three dots away from everything .replaceAll('...', ' ... ') // multiple spaces to one - .replaceAll(RegExp(r' +'), ' ') + .replaceAll(RegExp(' +'), ' ') // multiple new lines to one .replaceAll(RegExp(r'\n+'), '\n'); } @@ -108,11 +110,11 @@ class _CommentsDialogState extends State { } } - void getComments() async { + Future getComments() async { isLoading = true; if (widget.item.serverId != null) { setState(() {}); // set state to update the loading indicator - List fetched = await searchHandler.currentBooruHandler.getComments(widget.item.serverId!, 0); + final List fetched = await searchHandler.currentBooruHandler.getComments(widget.item.serverId!, 0); comments = fetched; } else { notSupported = true; @@ -136,7 +138,7 @@ class _CommentsDialogState extends State { } Widget mainBuild() { - bool areThereErrors = isLoading || notSupported || comments.isEmpty; + final bool areThereErrors = isLoading || notSupported || comments.isEmpty; return ClipRRect( borderRadius: BorderRadius.circular(10), @@ -151,7 +153,7 @@ class _CommentsDialogState extends State { strokeWidth: 4, color: Theme.of(context).colorScheme.secondary, onRefresh: () async { - getComments(); + await getComments(); }, child: DesktopScrollWrap( controller: scrollController, @@ -172,21 +174,21 @@ class _CommentsDialogState extends State { } Widget listEntryBuild(BuildContext context, int index) { - if(index == 0) { + if (index == 0) { return listHeader(); } index = index - 1; - CommentItem currentEntry = comments[index]; + final CommentItem currentEntry = comments[index]; - Widget entryRow = Container( + final Widget entryRow = Container( margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), child: Ink( child: InkWell( onTap: () {}, child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -215,26 +217,28 @@ class _CommentsDialogState extends State { children: [ if (currentEntry.authorName?.isNotEmpty == true) Padding( - padding: const EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4), child: SelectableText(currentEntry.authorName!, style: const TextStyle(fontWeight: FontWeight.bold)), ), Padding( - padding: const EdgeInsets.all(4.0), - child: Row(children: [ - if (currentEntry.title?.isNotEmpty == true) - SelectableText(currentEntry.title!, style: const TextStyle(fontWeight: FontWeight.bold)), - const SizedBox(width: 5), - if (currentEntry.createDate?.isNotEmpty == true) - Text( - formatDate(currentEntry.createDate!, currentEntry.createDateFormat!), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + padding: const EdgeInsets.all(4), + child: Row( + children: [ + if (currentEntry.title?.isNotEmpty == true) + SelectableText(currentEntry.title!, style: const TextStyle(fontWeight: FontWeight.bold)), + const SizedBox(width: 5), + if (currentEntry.createDate?.isNotEmpty == true) + Text( + formatDate(currentEntry.createDate!, currentEntry.createDateFormat!), + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + ), ), - ), - const SizedBox(width: 15), - scoreText(currentEntry.score), - ]), + const SizedBox(width: 15), + scoreText(currentEntry.score), + ], + ), ), ], ) @@ -242,7 +246,7 @@ class _CommentsDialogState extends State { ), const SizedBox(height: 8), Container( - padding: const EdgeInsets.all(12.0), + padding: const EdgeInsets.all(12), width: double.maxFinite, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, @@ -273,19 +277,23 @@ class _CommentsDialogState extends State { ), ); - return Column(children: [ - Row(children: [ - Expanded(child: entryRow), - ]), - Divider(height: 1, thickness: 1, color: Colors.grey[800]), - ]); + return Column( + children: [ + Row( + children: [ + Expanded(child: entryRow), + ], + ), + Divider(height: 1, thickness: 1, color: Colors.grey[800]), + ], + ); } Widget listHeader() { return Row( children: [ Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Container( constraints: const BoxConstraints( maxHeight: 150, @@ -299,7 +307,7 @@ class _CommentsDialogState extends State { } Widget errorEntryBuild(BuildContext context, int index) { - if(index == 0) { + if (index == 0) { return listHeader(); } @@ -308,21 +316,21 @@ class _CommentsDialogState extends State { if (isLoading) const Center( child: Padding( - padding: EdgeInsets.all(18.0), + padding: EdgeInsets.all(18), child: CircularProgressIndicator(), ), ) else if (notSupported) const Center( child: Padding( - padding: EdgeInsets.all(8.0), - child: Text('This Booru doesn\'t have comments or there is no API for them.'), + padding: EdgeInsets.all(8), + child: Text("This Booru doesn't have comments or there is no API for them."), ), ) else if (comments.isEmpty) const Center( child: Padding( - padding: EdgeInsets.all(8.0), + padding: EdgeInsets.all(8), child: Text('No comments.'), ), ), @@ -337,9 +345,7 @@ class _CommentsDialogState extends State { content: mainBuild(), actions: [ IconButton( - onPressed: () { - getComments(); - }, + onPressed: getComments, icon: const Icon(Icons.refresh), ), if (widget.item.postURL.isNotEmpty) diff --git a/lib/src/widgets/dialogs/page_number_dialog.dart b/lib/src/widgets/dialogs/page_number_dialog.dart index 4d9167d3..1d1d7605 100644 --- a/lib/src/widgets/dialogs/page_number_dialog.dart +++ b/lib/src/widgets/dialogs/page_number_dialog.dart @@ -5,7 +5,7 @@ import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class PageNumberDialog extends StatelessWidget { - const PageNumberDialog({Key? key}) : super(key: key); + const PageNumberDialog({super.key}); @override Widget build(BuildContext context) { @@ -26,24 +26,24 @@ class PageNumberDialog extends StatelessWidget { title: const Text('Page changer'), contentItems: [ SettingsTextInput( - title: "Page #", - hintText: "Page #", + title: 'Page #', + hintText: 'Page #', onlyInput: true, controller: pageNumberController, autofocus: true, - inputType: const TextInputType.numberWithOptions(signed: false, decimal: false), + inputType: TextInputType.number, numberButtons: true, numberStep: 1, numberMin: -1, numberMax: double.infinity, ), SettingsTextInput( - title: "Delay between loadings", - hintText: "Delay in ms", + title: 'Delay between loadings', + hintText: 'Delay in ms', onlyInput: true, controller: delayController, autofocus: false, - inputType: const TextInputType.numberWithOptions(signed: false, decimal: false), + inputType: TextInputType.number, numberButtons: true, numberStep: 100, numberMin: 0, @@ -81,7 +81,7 @@ class PageNumberDialog extends StatelessWidget { if (pageNumberController.text.isNotEmpty) { searchHandler.searchCurrentTabUntilPageNumber( (int.tryParse(pageNumberController.text) ?? 0) - 1, - customDelay: (int.tryParse(delayController.text) ?? 200), + customDelay: int.tryParse(delayController.text) ?? 200, ); Navigator.of(context).pop(); } diff --git a/lib/src/widgets/gallery/change_page_buttons.dart b/lib/src/widgets/gallery/change_page_buttons.dart index 987283c6..411031f1 100644 --- a/lib/src/widgets/gallery/change_page_buttons.dart +++ b/lib/src/widgets/gallery/change_page_buttons.dart @@ -6,7 +6,12 @@ import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/widgets/common/long_press_repeater.dart'; class ChangePageButtons extends StatelessWidget { - const ChangePageButtons({Key? key, required this.controller, required this.isPrev}) : super(key: key); + const ChangePageButtons({ + required this.controller, + required this.isPrev, + super.key, + }); + final PreloadPageController? controller; final bool isPrev; diff --git a/lib/src/widgets/gallery/gallery_buttons.dart b/lib/src/widgets/gallery/gallery_buttons.dart index f201750a..d9c763e4 100644 --- a/lib/src/widgets/gallery/gallery_buttons.dart +++ b/lib/src/widgets/gallery/gallery_buttons.dart @@ -10,7 +10,7 @@ import 'package:lolisnatcher/src/widgets/gallery/change_page_buttons.dart'; import 'package:lolisnatcher/src/widgets/gallery/zoom_button.dart'; class GalleryButtons extends StatefulWidget { - const GalleryButtons({Key? key, this.pageController}) : super(key: key); + const GalleryButtons({super.key, this.pageController}); final PreloadPageController? pageController; @override @@ -62,6 +62,7 @@ class _GalleryButtonsState extends State { @override void dispose() { appbarListener?.cancel(); + loadedListener?.cancel(); super.dispose(); } @@ -121,7 +122,7 @@ class _GalleryButtonsState extends State { left: distanceFromSide, child: ClipRRect( borderRadius: BorderRadius.circular(20), - child: Container( + child: ColoredBox( color: Theme.of(context).colorScheme.background.withOpacity(0.33), child: isVerticalDirection ? Column( @@ -140,7 +141,7 @@ class _GalleryButtonsState extends State { right: distanceFromSide, child: ClipRRect( borderRadius: BorderRadius.circular(20), - child: Container( + child: ColoredBox( color: Theme.of(context).colorScheme.background.withOpacity(0.33), child: isVerticalDirection ? Column( diff --git a/lib/src/widgets/gallery/hideable_appbar.dart b/lib/src/widgets/gallery/hideable_appbar.dart index ddce0566..af9e950f 100644 --- a/lib/src/widgets/gallery/hideable_appbar.dart +++ b/lib/src/widgets/gallery/hideable_appbar.dart @@ -7,13 +7,13 @@ import 'package:flutter/services.dart'; import 'package:dio/dio.dart'; import 'package:get/get.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; -import 'package:lolisnatcher/src/handlers/database_handler.dart'; import 'package:preload_page_view/preload_page_view.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/boorus/hydrus_handler.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; +import 'package:lolisnatcher/src/handlers/database_handler.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/handlers/service_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; @@ -44,7 +44,7 @@ class HideableAppBar extends StatefulWidget implements PreferredSizeWidget { final PreloadPageController pageController; final VoidCallback onOpenDrawer; - final double defaultHeight = kToolbarHeight; //56.0 + double get defaultHeight => kToolbarHeight; //56.0 @override Size get preferredSize => Size.fromHeight(defaultHeight); @@ -151,7 +151,7 @@ class _HideableAppBarState extends State { ////////// Toolbar Stuff //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// List getActions() { - List> filteredButtonOrder = settingsHandler.buttonOrder.where((btn) { + final List> filteredButtonOrder = settingsHandler.buttonOrder.where((btn) { if (searchHandler.viewedIndex.value == -1) { return false; } @@ -167,7 +167,7 @@ class _HideableAppBarState extends State { return isScaleAllowed && isFavAllowed; }).toList(); - List actions = []; + final List actions = []; List> overFlowList = []; List> buttonList = []; // first 4 buttons will show on toolbar @@ -232,7 +232,7 @@ class _HideableAppBarState extends State { final String name = value[0]; return PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: EdgeInsets.zero, value: name, child: SizedBox( width: double.infinity, // force button to take full width @@ -357,7 +357,7 @@ class _HideableAppBarState extends State { } String buttonText(List actionAndLabel) { - String action = actionAndLabel[0], defaultLabel = actionAndLabel[1]; + final String action = actionAndLabel[0], defaultLabel = actionAndLabel[1]; late String label; if (searchHandler.viewedIndex.value == -1) { diff --git a/lib/src/widgets/gallery/interactive_viewer.dart b/lib/src/widgets/gallery/interactive_viewer.dart index ff7bcd62..f7ba64a4 100644 --- a/lib/src/widgets/gallery/interactive_viewer.dart +++ b/lib/src/widgets/gallery/interactive_viewer.dart @@ -1 +1 @@ -// TODO make photo_view part independant from media type \ No newline at end of file +// TODO make photo_view part independant from media type diff --git a/lib/src/widgets/gallery/notes_renderer.dart b/lib/src/widgets/gallery/notes_renderer.dart index dbc3c88e..63fbb595 100644 --- a/lib/src/widgets/gallery/notes_renderer.dart +++ b/lib/src/widgets/gallery/notes_renderer.dart @@ -6,7 +6,6 @@ import 'package:get/get.dart'; import 'package:preload_page_view/preload_page_view.dart'; import 'package:lolisnatcher/src/data/booru_item.dart'; -import 'package:lolisnatcher/src/data/note_item.dart'; import 'package:lolisnatcher/src/handlers/navigation_handler.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; @@ -18,7 +17,7 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; import 'package:lolisnatcher/src/widgets/common/transparent_pointer.dart'; class NotesRenderer extends StatefulWidget { - const NotesRenderer(this.pageController, {Key? key}) : super(key: key); + const NotesRenderer(this.pageController, {super.key}); final PreloadPageController? pageController; @override @@ -56,8 +55,8 @@ class _NotesRendererState extends State { void initState() { super.initState(); - screenWidth = WidgetsBinding.instance.window.physicalSize.width; - screenHeight = WidgetsBinding.instance.window.physicalSize.height; + screenWidth = WidgetsBinding.instance.platformDispatcher.views.first.physicalSize.width; + screenHeight = WidgetsBinding.instance.platformDispatcher.views.first.physicalSize.height; screenRatio = screenWidth / screenHeight; item = searchHandler.viewedItem.value; @@ -93,7 +92,7 @@ class _NotesRendererState extends State { super.dispose(); } - void loadNotes() async { + Future loadNotes() async { final handler = searchHandler.currentBooruHandler; final bool hasSupport = handler.hasNotesSupport; final bool hasNotes = item.hasNotes == true; @@ -142,8 +141,8 @@ class _NotesRendererState extends State { if (settingsHandler.disableImageScaling || item.isNoScale.value || !item.mediaType.value.isImageOrAnimation) { // do nothing } else { - Size screenSize = WidgetsBinding.instance.window.physicalSize; - double pixelRatio = WidgetsBinding.instance.window.devicePixelRatio; + final Size screenSize = WidgetsBinding.instance.platformDispatcher.views.first.physicalSize; + final double pixelRatio = WidgetsBinding.instance.platformDispatcher.views.first.devicePixelRatio; // image size can change if scaling is allowed and it's size is too big widthLimit = screenSize.width * pixelRatio * 2; if (imageWidth > widthLimit) { @@ -159,7 +158,7 @@ class _NotesRendererState extends State { final double page = widget.pageController?.hasClients == true ? (widget.pageController!.page ?? 0) : 0; pageOffset = ((page * 10000).toInt() % 10000) / 10000; pageOffset = pageOffset > 0.5 ? (1 - pageOffset) : (0 - pageOffset); - bool isVertical = settingsHandler.galleryScrollDirection == 'Vertical'; + final bool isVertical = settingsHandler.galleryScrollDirection == 'Vertical'; offsetX = (screenWidth / 2) - (imageWidth / 2 * screenToImageRatio); offsetX = isVertical ? offsetX : (offsetX + (pageOffset * screenWidth)); @@ -173,68 +172,72 @@ class _NotesRendererState extends State { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { - if (screenWidth != constraints.maxWidth || screenHeight != constraints.maxHeight) { - screenWidth = constraints.maxWidth; - screenHeight = constraints.maxHeight; - screenRatio = screenWidth / screenHeight; - triggerCalculations(); - } + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + if (screenWidth != constraints.maxWidth || screenHeight != constraints.maxHeight) { + screenWidth = constraints.maxWidth; + screenHeight = constraints.maxHeight; + screenRatio = screenWidth / screenHeight; + triggerCalculations(); + } - return Obx(() { - if (!viewerHandler.isLoaded.value || !viewerHandler.showNotes.value || item.fileURL.isEmpty) { - return const SizedBox(); - } else { - return Stack( - children: [ - if (loading) - Positioned( - left: 60, - top: kToolbarHeight * 1.5, - child: SizedBox( - width: 30, - height: 30, - child: Stack( - alignment: Alignment.center, - children: [ - const CircularProgressIndicator( - strokeWidth: 2, - ), - Icon( - Icons.note_add, - size: 18, - color: Theme.of(context).colorScheme.secondary, - ), - ], + return Obx(() { + if (!viewerHandler.isLoaded.value || !viewerHandler.showNotes.value || item.fileURL.isEmpty) { + return const SizedBox(); + } else { + return Stack( + children: [ + if (loading) + Positioned( + left: 60, + top: kToolbarHeight * 1.5, + child: SizedBox( + width: 30, + height: 30, + child: Stack( + alignment: Alignment.center, + children: [ + const CircularProgressIndicator( + strokeWidth: 2, + ), + Icon( + Icons.note_add, + size: 18, + color: Theme.of(context).colorScheme.secondary, + ), + ], + ), ), - ), - ) - else - // TODO change to animated transform? - ...item.notes.map((note) => NoteBuild( + ) + else + // TODO change to animated transform? + ...item.notes.map( + (note) => NoteBuild( text: note.content, left: (note.posX * screenToImageRatio * ratioDiff) + offsetX + viewOffsetX, top: (note.posY * screenToImageRatio * ratioDiff) + offsetY + viewOffsetY, width: note.width * screenToImageRatio * ratioDiff, height: note.height * screenToImageRatio * ratioDiff, - )), - ], - ); - } - }); - }); + ), + ), + ], + ); + } + }); + }, + ); } } class NoteBuild extends StatefulWidget { const NoteBuild({ - Key? key, required this.text, required this.left, required this.top, required this.width, required this.height, - }) : super(key: key); + super.key, + }); final String? text; final double left; @@ -335,8 +338,8 @@ class _NoteBuildState extends State { } class NotesDialog extends StatelessWidget { + const NotesDialog(this.item, {super.key}); final BooruItem item; - const NotesDialog(this.item, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/gallery/snatched_status_icon.dart b/lib/src/widgets/gallery/snatched_status_icon.dart index f64ddf10..44c02f60 100644 --- a/lib/src/widgets/gallery/snatched_status_icon.dart +++ b/lib/src/widgets/gallery/snatched_status_icon.dart @@ -32,9 +32,9 @@ class _SnatchedStatusIconState extends State { super.dispose(); } - void fileExistsCheck() async { + Future fileExistsCheck() async { final String extPath = SettingsHandler.instance.extPathOverride; - if(extPath.isNotEmpty) { + if (extPath.isNotEmpty) { fileExists = await ServiceHandler.existsFileFromSAFDirectory(extPath, ImageWriter().getFilename(widget.item, widget.booru)); } else { fileExists = await File(await ImageWriter().getFilePath(widget.item, widget.booru)).exists(); @@ -48,7 +48,7 @@ class _SnatchedStatusIconState extends State { void didUpdateWidget(covariant SnatchedStatusIcon oldWidget) { super.didUpdateWidget(oldWidget); - if(oldWidget.item != widget.item) { + if (oldWidget.item != widget.item) { fileExists = false; WidgetsBinding.instance.addPostFrameCallback((_) { setState(() {}); @@ -59,6 +59,10 @@ class _SnatchedStatusIconState extends State { @override Widget build(BuildContext context) { - return Icon(Icons.save_alt, size: Theme.of(context).buttonTheme.height / 2.1, color: fileExists ? Colors.green : Colors.white,); + return Icon( + Icons.save_alt, + size: Theme.of(context).buttonTheme.height / 2.1, + color: fileExists ? Colors.green : Colors.white, + ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/gallery/tag_view.dart b/lib/src/widgets/gallery/tag_view.dart index 0d62dfd8..5387f030 100644 --- a/lib/src/widgets/gallery/tag_view.dart +++ b/lib/src/widgets/gallery/tag_view.dart @@ -25,7 +25,7 @@ import 'package:lolisnatcher/src/widgets/dialogs/comments_dialog.dart'; import 'package:lolisnatcher/src/widgets/gallery/notes_renderer.dart'; class TagView extends StatefulWidget { - const TagView({Key? key}) : super(key: key); + const TagView({super.key}); @override State createState() => _TagViewState(); @@ -75,8 +75,10 @@ class _TagViewState extends State { } List filterTags(List tagsToFilter) { - List filteredTags = []; - if (searchController.text.isEmpty) return tagsToFilter; + final List filteredTags = []; + if (searchController.text.isEmpty) { + return tagsToFilter; + } for (int i = 0; i < tagsToFilter.length; i++) { if (tagsToFilter[i].toLowerCase().contains(searchController.text.toLowerCase())) { @@ -99,8 +101,8 @@ class _TagViewState extends State { } void groupTagsList() { - Map> tagMap = {}; - List groupedTags = []; + final Map> tagMap = {}; + final List groupedTags = []; for (int i = 0; i < TagType.values.length; i++) { tagMap[TagType.values[i]] = []; } @@ -115,7 +117,7 @@ class _TagViewState extends State { // tagMap.forEach((key, value) => { // print("Type: $key Tags: $value") // }); - for (var value in tagMap.values) { + for (final value in tagMap.values) { groupedTags.addAll(value); } tags = groupedTags; @@ -154,7 +156,7 @@ class _TagViewState extends State { // no timezone support in DateFormat? see: https://stackoverflow.com/questions/56189407/dart-parse-date-timezone-gives-unimplementederror/56190055 // remove timezones from strings until they fix it DateTime parsedDate; - if (postDateFormat == "unix") { + if (postDateFormat == 'unix') { parsedDate = DateTime.fromMillisecondsSinceEpoch(int.parse(postDate) * 1000); } else { postDate = postDate.replaceAll(RegExp(r'(?:\+|\-)\d{4}'), ''); @@ -170,7 +172,7 @@ class _TagViewState extends State { return SliverList( delegate: SliverChildListDelegate( [ - if(settingsHandler.isDebug.value) infoText('Filename', fileName), + if (settingsHandler.isDebug.value) infoText('Filename', fileName), infoText('URL', fileUrl), infoText('ID', itemId), infoText('Rating', rating), @@ -214,26 +216,29 @@ class _TagViewState extends State { name: 'Tags', subtitle: Text(searchController.text.isEmpty ? '${tags.length}' : '${filteredTags.length} / ${tags.length}'), trailingIcon: Container( - margin: const EdgeInsets.only(left: 10), - child: Transform( - alignment: Alignment.center, - transform: sortTags == true ? Matrix4.rotationX(pi) : Matrix4.rotationX(0), - child: IconButton( - icon: Icon((sortTags == true || sortTags == false) ? Icons.sort : Icons.sort_by_alpha, color: Theme.of(context).iconTheme.color,), - onPressed: () { - if (sortTags == true) { - sortTags = false; - } else if (sortTags == false) { - sortTags = null; - } else { - sortTags = true; - } - sortAndGroupTagsList(); - setState(() {}); - }, - ), - ), + margin: const EdgeInsets.only(left: 10), + child: Transform( + alignment: Alignment.center, + transform: sortTags == true ? Matrix4.rotationX(pi) : Matrix4.rotationX(0), + child: IconButton( + icon: Icon( + (sortTags == true || sortTags == false) ? Icons.sort : Icons.sort_by_alpha, + color: Theme.of(context).iconTheme.color, ), + onPressed: () { + if (sortTags == true) { + sortTags = false; + } else if (sortTags == false) { + sortTags = null; + } else { + sortTags = true; + } + sortAndGroupTagsList(); + setState(() {}); + }, + ), + ), + ), drawBottomBorder: false, ); } @@ -249,7 +254,10 @@ class _TagViewState extends State { return SettingsButton( name: 'Comments', - icon: Icon(icon, color: Theme.of(context).iconTheme.color,), + icon: Icon( + icon, + color: Theme.of(context).iconTheme.color, + ), action: () { SettingsPageOpen( context: context, @@ -275,7 +283,10 @@ class _TagViewState extends State { if (item.notes.isNotEmpty) { return SettingsButton( name: '${viewerHandler.showNotes.value ? 'Hide' : 'Show'} Notes (${item.notes.length})', - icon: Icon(Icons.note_add, color: Theme.of(context).iconTheme.color,), + icon: Icon( + Icons.note_add, + color: Theme.of(context).iconTheme.color, + ), action: () { viewerHandler.showNotes.toggle(); }, @@ -292,7 +303,10 @@ class _TagViewState extends State { } else { return SettingsButton( name: 'Load notes', - icon: Icon(Icons.note_add, color: Theme.of(context).iconTheme.color,), + icon: Icon( + Icons.note_add, + color: Theme.of(context).iconTheme.color, + ), action: () async { item.notes.value = await searchHandler.currentBooruHandler.getNotes(item.serverId!); }, @@ -329,7 +343,7 @@ class _TagViewState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Copied source to clipboard!", style: TextStyle(fontSize: 20)), + title: const Text('Copied source to clipboard!', style: TextStyle(fontSize: 20)), content: Text(link, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.copy, sideColor: Colors.green, @@ -381,7 +395,7 @@ class _TagViewState extends State { context: context, duration: const Duration(seconds: 2), title: Text( - "Copied $title to clipboard!", + 'Copied $title to clipboard!', style: const TextStyle(fontSize: 20), ), content: Text( @@ -446,15 +460,18 @@ class _TagViewState extends State { ), const SizedBox(height: 10), ListTile( - leading: Icon(Icons.copy, color: Theme.of(context).iconTheme.color,), - title: const Text("Copy"), + leading: Icon( + Icons.copy, + color: Theme.of(context).iconTheme.color, + ), + title: const Text('Copy'), onTap: () { Clipboard.setData(ClipboardData(text: tag)); FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), title: const Text( - "Copied to clipboard!", + 'Copied to clipboard!', style: TextStyle(fontSize: 20), ), content: Text( @@ -469,8 +486,11 @@ class _TagViewState extends State { ), if (isInSearch) ListTile( - leading: Icon(Icons.remove, color: Theme.of(context).iconTheme.color,), - title: const Text("Remove from Search"), + leading: Icon( + Icons.remove, + color: Theme.of(context).iconTheme.color, + ), + title: const Text('Remove from Search'), onTap: () { searchHandler.removeTagFromSearch(tag); Navigator.of(context).pop(true); @@ -479,7 +499,7 @@ class _TagViewState extends State { if (!isInSearch) ListTile( leading: const Icon(Icons.add, color: Colors.green), - title: const Text("Add to Search"), + title: const Text('Add to Search'), onTap: () { searchHandler.addTagToSearch(tag); @@ -487,7 +507,7 @@ class _TagViewState extends State { context: context, duration: const Duration(seconds: 2), title: const Text( - "Added to search bar:", + 'Added to search bar:', style: TextStyle(fontSize: 20), ), content: Text( @@ -504,7 +524,7 @@ class _TagViewState extends State { if (!isInSearch) ListTile( leading: const Icon(Icons.add, color: Colors.red), - title: const Text("Add to Search (Exclude)"), + title: const Text('Add to Search (Exclude)'), onTap: () { searchHandler.addTagToSearch('-$tag'); @@ -512,7 +532,7 @@ class _TagViewState extends State { context: context, duration: const Duration(seconds: 2), title: const Text( - "Added to search bar (Exclude):", + 'Added to search bar (Exclude):', style: TextStyle(fontSize: 20), ), content: Text( @@ -529,7 +549,7 @@ class _TagViewState extends State { if (!isHated && !isLoved) ListTile( leading: const Icon(Icons.star, color: Colors.yellow), - title: const Text("Add to Loved"), + title: const Text('Add to Loved'), onTap: () { settingsHandler.addTagToList('loved', tag); parseSortGroupTags(); @@ -539,7 +559,7 @@ class _TagViewState extends State { if (!isHated && !isLoved) ListTile( leading: const Icon(CupertinoIcons.eye_slash, color: Colors.red), - title: const Text("Add to Hated"), + title: const Text('Add to Hated'), onTap: () { settingsHandler.addTagToList('hated', tag); parseSortGroupTags(); @@ -548,8 +568,11 @@ class _TagViewState extends State { ), if (isLoved) ListTile( - leading: Icon(Icons.star_border, color: Theme.of(context).iconTheme.color,), - title: const Text("Remove from Loved"), + leading: Icon( + Icons.star_border, + color: Theme.of(context).iconTheme.color, + ), + title: const Text('Remove from Loved'), onTap: () { settingsHandler.removeTagFromList('loved', tag); parseSortGroupTags(); @@ -558,8 +581,11 @@ class _TagViewState extends State { ), if (isHated) ListTile( - leading: Icon(CupertinoIcons.eye_slash, color: Theme.of(context).iconTheme.color,), - title: const Text("Remove from Hated"), + leading: Icon( + CupertinoIcons.eye_slash, + color: Theme.of(context).iconTheme.color, + ), + title: const Text('Remove from Hated'), onTap: () { settingsHandler.removeTagFromList('hated', tag); parseSortGroupTags(); @@ -601,17 +627,20 @@ class _TagViewState extends State { final HasTabWithTagResult hasTabWithTag = searchHandler.hasTabWithTag(currentTag); final List tagIconAndColor = []; - if (isSound) tagIconAndColor.add(TagInfoIcon(Icons.volume_up_rounded, Theme.of(context).colorScheme.onBackground)); - if (isHated) tagIconAndColor.add(TagInfoIcon(CupertinoIcons.eye_slash, Colors.red)); - if (isLoved) tagIconAndColor.add(TagInfoIcon(Icons.star, Colors.yellow)); + if (isSound) { + tagIconAndColor.add(TagInfoIcon(Icons.volume_up_rounded, Theme.of(context).colorScheme.onBackground)); + } + if (isHated) { + tagIconAndColor.add(TagInfoIcon(CupertinoIcons.eye_slash, Colors.red)); + } + if (isLoved) { + tagIconAndColor.add(TagInfoIcon(Icons.star, Colors.yellow)); + } if (currentTag != '') { - return Column(children: [ - Container( - // decoration: BoxDecoration( - // border: Border(left: BorderSide(width: 10.0, color: tagHandler.getTag(currentTag).getColour())), - // ), - child: InkWell( + return Column( + children: [ + InkWell( onTap: () { tagDialog( tag: currentTag, @@ -638,11 +667,13 @@ class _TagViewState extends State { isExpanded: true, ), if (tagIconAndColor.isNotEmpty) ...[ - ...tagIconAndColor.map((t) => Icon( - t.icon, - color: t.color, - size: 20, - )), + ...tagIconAndColor.map( + (t) => Icon( + t.icon, + color: t.color, + size: 20, + ), + ), const SizedBox(width: 5), ], IconButton( @@ -662,11 +693,11 @@ class _TagViewState extends State { ], ), onPressed: () { - if(isInSearch) { + if (isInSearch) { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("This tag is already in the current search query:", style: TextStyle(fontSize: 18)), + title: const Text('This tag is already in the current search query:', style: TextStyle(fontSize: 18)), content: Text(currentTag, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.yellow, @@ -679,7 +710,7 @@ class _TagViewState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Added to current search query:", style: TextStyle(fontSize: 20)), + title: const Text('Added to current search query:', style: TextStyle(fontSize: 20)), content: Text(currentTag, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.add, sideColor: Colors.green, @@ -719,32 +750,34 @@ class _TagViewState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Added new tab:", style: TextStyle(fontSize: 20)), + title: const Text('Added new tab:', style: TextStyle(fontSize: 20)), content: Text(currentTag, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.fiber_new, sideColor: Colors.green, primaryActionBuilder: (controller) { - return Row(children: [ - IconButton( - onPressed: () { - ServiceHandler.vibrate(); - if (settingsHandler.appMode.value.isMobile && viewerHandler.inViewer.value) { - Navigator.of(context).pop(true); // exit drawer - Navigator.of(context).pop(true); // exit viewer - } - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - searchHandler.changeTabIndex(searchHandler.list.length - 1); - }); - controller.dismiss(); - }, - icon: Icon(Icons.arrow_forward_rounded, color: Theme.of(context).colorScheme.onBackground), - ), - const SizedBox(width: 4), - IconButton( - onPressed: () => controller.dismiss(), - icon: Icon(Icons.close, color: Theme.of(context).colorScheme.onBackground), - ), - ]); + return Row( + children: [ + IconButton( + onPressed: () { + ServiceHandler.vibrate(); + if (settingsHandler.appMode.value.isMobile && viewerHandler.inViewer.value) { + Navigator.of(context).pop(true); // exit drawer + Navigator.of(context).pop(true); // exit viewer + } + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + searchHandler.changeTabIndex(searchHandler.list.length - 1); + }); + controller.dismiss(); + }, + icon: Icon(Icons.arrow_forward_rounded, color: Theme.of(context).colorScheme.onBackground), + ), + const SizedBox(width: 4), + IconButton( + onPressed: () => controller.dismiss(), + icon: Icon(Icons.close, color: Theme.of(context).colorScheme.onBackground), + ), + ], + ); }, ); sortAndGroupTagsList(); @@ -756,13 +789,13 @@ class _TagViewState extends State { ], ), ), - ), - Divider( - color: Colors.grey[800], - height: 1, - thickness: 1, - ), - ]); + Divider( + color: Colors.grey[800], + height: 1, + thickness: 1, + ), + ], + ); } else { // Render nothing if currentTag is an empty string return const SizedBox(); @@ -794,8 +827,8 @@ class _TagViewState extends State { // TODO move to own/model file class TagInfoIcon { + TagInfoIcon(this.icon, this.color); + final IconData icon; final Color color; - - TagInfoIcon(this.icon, this.color); } diff --git a/lib/src/widgets/gallery/viewer_tutorial.dart b/lib/src/widgets/gallery/viewer_tutorial.dart index 6b83beab..464b8f93 100644 --- a/lib/src/widgets/gallery/viewer_tutorial.dart +++ b/lib/src/widgets/gallery/viewer_tutorial.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:lolisnatcher/src/widgets/common/transparent_pointer.dart'; class ViewerTutorial extends StatefulWidget { - const ViewerTutorial({Key? key}) : super(key: key); + const ViewerTutorial({super.key}); @override State createState() => _ViewerTutorialState(); @@ -43,53 +43,55 @@ class _ViewerTutorialState extends State { @override Widget build(BuildContext context) { - if(!_isVisible) { + if (!_isVisible) { return const SizedBox(); } - return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { - return TransparentPointer( - child: Container( - width: constraints.maxWidth, - height: constraints.maxHeight, - color: Colors.black.withOpacity(0.66), - child: getTutorialPage(constraints), - ), - ); - }); + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return TransparentPointer( + child: Container( + width: constraints.maxWidth, + height: constraints.maxHeight, + color: Colors.black.withOpacity(0.66), + child: getTutorialPage(constraints), + ), + ); + }, + ); } } class ImageTutorial extends StatelessWidget { + const ImageTutorial(this.constraints, this.onNext, {super.key}); final BoxConstraints constraints; final VoidCallback onNext; - const ImageTutorial(this.constraints, this.onNext, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), - child: Column( + child: const Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Row( mainAxisSize: MainAxisSize.min, - children: const [ + children: [ Icon(Icons.image_outlined, size: 28), SizedBox(width: 8), Text('Images', style: TextStyle(color: Colors.white, fontSize: 28)), ], ), ), - const SizedBox(height: 15), - const Text( + SizedBox(height: 15), + Text( 'Tap/Long tap: toggle immersive mode', style: TextStyle(color: Colors.white, fontSize: 20), ), - const SizedBox(height: 10), - const Text( + SizedBox(height: 10), + Text( 'Double tap: fit to screen / original size / reset zoom', style: TextStyle(color: Colors.white, fontSize: 20), ), @@ -100,9 +102,9 @@ class ImageTutorial extends StatelessWidget { } class VideoTutorial extends StatelessWidget { + const VideoTutorial(this.constraints, this.onNext, {super.key}); final BoxConstraints constraints; final VoidCallback onNext; - const VideoTutorial(this.constraints, this.onNext, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/gallery/zoom_button.dart b/lib/src/widgets/gallery/zoom_button.dart index fb300199..138ef2fe 100644 --- a/lib/src/widgets/gallery/zoom_button.dart +++ b/lib/src/widgets/gallery/zoom_button.dart @@ -5,7 +5,7 @@ import 'package:get/get.dart'; import 'package:lolisnatcher/src/handlers/viewer_handler.dart'; class ZoomButton extends StatelessWidget { - const ZoomButton({Key? key}) : super(key: key); + const ZoomButton({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/history/history.dart b/lib/src/widgets/history/history.dart index 87ce2e3c..ae0c2689 100644 --- a/lib/src/widgets/history/history.dart +++ b/lib/src/widgets/history/history.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math'; import 'package:flutter/cupertino.dart'; @@ -6,9 +7,9 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:huge_listview/huge_listview.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/data/history_item.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; @@ -24,7 +25,7 @@ import 'package:lolisnatcher/src/widgets/image/favicon.dart'; // TODO split in smaller widgets class HistoryList extends StatefulWidget { - const HistoryList({Key? key}) : super(key: key); + const HistoryList({super.key}); @override State createState() => _HistoryListState(); @@ -50,13 +51,13 @@ class _HistoryListState extends State { getHistory(); } - void getHistory() async { + Future getHistory() async { isLoading = true; setState(() {}); - history = ((settingsHandler.dbEnabled && settingsHandler.searchHistoryEnabled) ? await settingsHandler.dbHandler.getSearchHistory() : []) - .map((e) => HistoryItem.fromMap(e)) - .toList(); + final List> rawHistory = + (settingsHandler.dbEnabled && settingsHandler.searchHistoryEnabled) ? await settingsHandler.dbHandler.getSearchHistory() : []; + history = List.from(rawHistory.map(HistoryItem.fromMap)); history.sort(compareFavourites); filteredHistory = history; @@ -127,7 +128,7 @@ class _HistoryListState extends State { final String hourStr = searchDate.hour.toString().padLeft(2, '0'); final String minuteStr = searchDate.minute.toString().padLeft(2, '0'); final String secondStr = searchDate.second.toString().padLeft(2, '0'); - final String searchDateStr = withTime ? "$dayStr.$monthStr.$yearStr $hourStr:$minuteStr:$secondStr" : "$dayStr.$monthStr.$yearStr"; + final String searchDateStr = withTime ? '$dayStr.$monthStr.$yearStr $hourStr:$minuteStr:$secondStr' : '$dayStr.$monthStr.$yearStr'; return searchDateStr; } @@ -138,7 +139,7 @@ class _HistoryListState extends State { return SettingsDialog( contentItems: [ SizedBox(width: double.maxFinite, child: row), - Text("Last searched on: ${formatDate(entry.timestamp)}", textAlign: TextAlign.center), + Text('Last searched on: ${formatDate(entry.timestamp)}', textAlign: TextAlign.center), // const SizedBox(height: 20), ListTile( @@ -155,7 +156,7 @@ class _HistoryListState extends State { } else { FlashElements.showSnackbar( context: context, - title: const Text("Unknown Booru type!", style: TextStyle(fontSize: 20)), + title: const Text('Unknown Booru type!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -187,7 +188,7 @@ class _HistoryListState extends State { } else { FlashElements.showSnackbar( context: context, - title: const Text("Unknown Booru type!", style: TextStyle(fontSize: 20)), + title: const Text('Unknown Booru type!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -234,7 +235,7 @@ class _HistoryListState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Copied to clipboard!", style: TextStyle(fontSize: 20)), + title: const Text('Copied to clipboard!', style: TextStyle(fontSize: 20)), content: Text(entry.searchText, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.copy, sideColor: Colors.green, @@ -442,7 +443,7 @@ class _HistoryListState extends State { margin: const EdgeInsets.all(10), child: ElevatedButton.icon( icon: const Icon(Icons.select_all), - label: const Text("Select all"), + label: const Text('Select all'), onPressed: () { // create new list through spread to avoid modifying the original list selectedEntries = [...filteredHistory]; @@ -472,7 +473,7 @@ class _HistoryListState extends State { } final Widget deleteDialog = SettingsDialog( - title: const Text("Delete History Entries"), + title: const Text('Delete History Entries'), scrollable: false, content: SizedBox( width: double.maxFinite, @@ -484,21 +485,21 @@ class _HistoryListState extends State { ...selectedEntries.map((HistoryItem entry) { final int index = history.indexOf(entry); return buildEntry(index, false, false); - }).toList(), + }), ], ), ), actionButtons: [ const CancelButton(), ElevatedButton.icon( - label: const Text("Delete"), + label: const Text('Delete'), icon: const Icon(Icons.delete_forever), onPressed: () async { for (int i = 0; i < selectedEntries.length; i++) { await deleteEntry(selectedEntries[i]); } selectedEntries.clear(); - getHistory(); + unawaited(getHistory()); Navigator.of(context).pop(); }, ), @@ -517,7 +518,7 @@ class _HistoryListState extends State { Expanded( child: ElevatedButton.icon( icon: const Icon(Icons.border_clear), - label: const Text("Clear selection"), + label: const Text('Clear selection'), onPressed: () { selectedEntries.clear(); setState(() {}); diff --git a/lib/src/widgets/image/abstract_custom_network_image.dart b/lib/src/widgets/image/abstract_custom_network_image.dart index 6e03f4e4..9c0f918b 100644 --- a/lib/src/widgets/image/abstract_custom_network_image.dart +++ b/lib/src/widgets/image/abstract_custom_network_image.dart @@ -1,9 +1,15 @@ +// ignore_for_file: deprecated_member_use + import 'package:flutter/material.dart'; import 'package:lolisnatcher/src/widgets/image/custom_network_image.dart' as custom_network_image; abstract class CustomNetworkImage extends ImageProvider { - const factory CustomNetworkImage(String url, { double scale, Map? headers }) = custom_network_image.CustomNetworkImage; + const factory CustomNetworkImage( + String url, { + double scale, + Map? headers, + }) = custom_network_image.CustomNetworkImage; String get url; @@ -16,4 +22,4 @@ abstract class CustomNetworkImage extends ImageProvider { @override ImageStreamCompleter loadBuffer(CustomNetworkImage key, DecoderBufferCallback decode); -} \ No newline at end of file +} diff --git a/lib/src/widgets/image/custom_network_image.dart b/lib/src/widgets/image/custom_network_image.dart index ede3b230..c2310df1 100644 --- a/lib/src/widgets/image/custom_network_image.dart +++ b/lib/src/widgets/image/custom_network_image.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use + import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; @@ -26,7 +28,7 @@ class CustomNetworkImage extends ImageProvider { ); } - void onError(Object error) async { + Future onError(Object error) async { //// Error handling if (error is DioException && CancelToken.isCancel(error)) { // } else { if (error is Exception && (error as dynamic).message == 'Invalid image data') { - ((mainProvider as ResizeImage).imageProvider as CustomNetworkImage).deleteCacheFile(); + await ((mainProvider! as ResizeImage).imageProvider as CustomNetworkImage).deleteCacheFile(); disposables(); } if (error is DioException && error.response != null && Tools.isGoodStatusCode(error.response!.statusCode) == false) { if (manualReloadTapped && (error.response!.statusCode == 403 || error.response!.statusCode == 503)) { - await Tools.checkForCaptcha(error.response!, error.requestOptions.uri); + await Tools.checkForCaptcha(error.response, error.requestOptions.uri); manualReloadTapped = false; } errorCode = error.response!.statusCode.toString(); } isFailed = true; - Future.delayed(const Duration(milliseconds: 300), () { - updateState(); - }); + Future.delayed(const Duration(milliseconds: 300), updateState); } } @@ -97,7 +94,7 @@ class _FaviconState extends State { if (mounted) setState(() {}); } - void restartLoading() async { + Future restartLoading() async { if (mounted) { await mainProvider?.evict(); } @@ -111,7 +108,7 @@ class _FaviconState extends State { mainProvider ??= await getImageProvider(); imageStream?.removeListener(imageListener!); - imageStream = mainProvider!.resolve(const ImageConfiguration()); + imageStream = mainProvider!.resolve(ImageConfiguration.empty); imageListener = ImageStreamListener( (imageInfo, syncCall) { isLoaded = true; @@ -120,7 +117,7 @@ class _FaviconState extends State { } }, onError: (e, stack) { - Logger.Inst().log("Failed to load favicon: ${widget.booru.faviconURL}", "Favicon", "build", null); // LogTypes.imageLoadingError); + Logger.Inst().log('Failed to load favicon: ${widget.booru.faviconURL}', 'Favicon', 'build', LogTypes.imageLoadingError); onError(e); }, ); diff --git a/lib/src/widgets/image/image_viewer.dart b/lib/src/widgets/image/image_viewer.dart index 5056a77f..ac310630 100644 --- a/lib/src/widgets/image/image_viewer.dart +++ b/lib/src/widgets/image/image_viewer.dart @@ -138,8 +138,8 @@ class ImageViewerState extends State { initViewer(false); } - void initViewer(bool ignoreTagsCheck) async { - if ((settingsHandler.galleryMode == "Sample" && widget.booruItem.sampleURL.isNotEmpty && widget.booruItem.sampleURL != widget.booruItem.thumbnailURL) || + Future initViewer(bool ignoreTagsCheck) async { + if ((settingsHandler.galleryMode == 'Sample' && widget.booruItem.sampleURL.isNotEmpty && widget.booruItem.sampleURL != widget.booruItem.thumbnailURL) || widget.booruItem.sampleURL == widget.booruItem.fileURL) { // use sample file if (sample gallery quality && sampleUrl exists && sampleUrl is not the same as thumbnailUrl) OR sampleUrl is the same as full res fileUrl imageFolder = 'samples'; @@ -153,7 +153,7 @@ class ImageViewerState extends State { }); if (widget.booruItem.isHated.value && !ignoreTagsCheck) { - List> hatedAndLovedTags = settingsHandler.parseTagsList(widget.booruItem.tagsList, isCapped: true); + final List> hatedAndLovedTags = settingsHandler.parseTagsList(widget.booruItem.tagsList, isCapped: true); if (hatedAndLovedTags[0].isNotEmpty) { killLoading(['Contains Hated tags:', ...hatedAndLovedTags[0]]); return; @@ -170,7 +170,7 @@ class ImageViewerState extends State { mainProvider ??= await getImageProvider(); imageStream?.removeListener(imageListener!); - imageStream = mainProvider!.resolve(const ImageConfiguration()); + imageStream = mainProvider!.resolve(ImageConfiguration.empty); imageListener = ImageStreamListener( (imageInfo, syncCall) { isLoaded = true; @@ -195,7 +195,7 @@ class ImageViewerState extends State { ImageProvider provider; cancelToken = CancelToken(); provider = CustomNetworkImage( - (settingsHandler.galleryMode == "Sample") ? widget.booruItem.sampleURL : widget.booruItem.fileURL, + (settingsHandler.galleryMode == 'Sample') ? widget.booruItem.sampleURL : widget.booruItem.fileURL, cancelToken: cancelToken, headers: await Tools.getFileCustomHeaders( searchHandler.currentBooru, diff --git a/lib/src/widgets/preview/grid_builder.dart b/lib/src/widgets/preview/grid_builder.dart index be096298..eb96bf89 100644 --- a/lib/src/widgets/preview/grid_builder.dart +++ b/lib/src/widgets/preview/grid_builder.dart @@ -14,8 +14,8 @@ class GridBuilder extends StatelessWidget { this.onDoubleTap, this.onLongPress, this.onSecondaryTap, - Key? key, - }) : super(key: key); + super.key, + }); final void Function(int, BooruItem)? onTap; final void Function(int, BooruItem)? onDoubleTap; @@ -28,10 +28,9 @@ class GridBuilder extends StatelessWidget { final SearchHandler searchHandler = SearchHandler.instance; return Obx(() { - int columnCount = - (MediaQuery.of(context).orientation == Orientation.portrait) ? settingsHandler.portraitColumns : settingsHandler.landscapeColumns; + final int columnCount = (MediaQuery.of(context).orientation == Orientation.portrait) ? settingsHandler.portraitColumns : settingsHandler.landscapeColumns; - bool isDesktop = settingsHandler.appMode.value.isDesktop; + final bool isDesktop = settingsHandler.appMode.value.isDesktop; return GridView.builder( controller: searchHandler.gridScrollController, @@ -42,7 +41,9 @@ class GridBuilder extends StatelessWidget { itemCount: searchHandler.currentFetched.length, padding: EdgeInsets.fromLTRB(2, 2 + (isDesktop ? 0 : (kToolbarHeight + MediaQuery.of(context).padding.top)), 2, 80), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: columnCount, childAspectRatio: settingsHandler.previewDisplay == 'Square' ? 1 : 9 / 16), + crossAxisCount: columnCount, + childAspectRatio: settingsHandler.previewDisplay == 'Square' ? 1 : 9 / 16, + ), itemBuilder: (BuildContext context, int index) { final BooruItem item = searchHandler.currentFetched[index]; diff --git a/lib/src/widgets/preview/media_previews.dart b/lib/src/widgets/preview/media_previews.dart index e4bee265..e0d4bd43 100644 --- a/lib/src/widgets/preview/media_previews.dart +++ b/lib/src/widgets/preview/media_previews.dart @@ -13,7 +13,7 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; import 'package:lolisnatcher/src/widgets/preview/waterfall_view.dart'; class MediaPreviews extends StatefulWidget { - const MediaPreviews({Key? key}) : super(key: key); + const MediaPreviews({super.key}); @override State createState() => _MediaPreviewsState(); @@ -73,13 +73,13 @@ class _MediaPreviewsState extends State { SettingsButton( name: 'Add New Booru', icon: const Icon(Icons.settings), - page: () => BooruEdit(Booru("New", null, "", "", "")), + page: () => BooruEdit(Booru('New', null, '', '', '')), ), SettingsButton( name: 'Help', icon: const Icon(Icons.help_center_outlined), action: () { - ServiceHandler.launchURL("https://github.com/NO-ob/LoliSnatcher_Droid/wiki"); + ServiceHandler.launchURL('https://github.com/NO-ob/LoliSnatcher_Droid/wiki'); }, trailingIcon: const Icon(Icons.exit_to_app), ), diff --git a/lib/src/widgets/preview/shimmer_builder.dart b/lib/src/widgets/preview/shimmer_builder.dart index 401ac32d..57eb801f 100644 --- a/lib/src/widgets/preview/shimmer_builder.dart +++ b/lib/src/widgets/preview/shimmer_builder.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; class ShimmerWrap extends StatelessWidget { - const ShimmerWrap({Key? key, required this.child}) : super(key: key); + const ShimmerWrap({required this.child, super.key}); final Widget child; @override @@ -26,7 +26,7 @@ class ShimmerList extends StatelessWidget { Widget build(BuildContext context) { return ShimmerWrap( child: LayoutBuilder( - builder: ((BuildContext layoutContext, BoxConstraints constraints) { + builder: (BuildContext layoutContext, BoxConstraints constraints) { final SettingsHandler settingsHandler = SettingsHandler.instance; final String displayType = settingsHandler.previewDisplay; final bool isDesktop = settingsHandler.appMode.value.isDesktop; @@ -41,8 +41,7 @@ class ShimmerList extends StatelessWidget { cacheExtent: 200, shrinkWrap: false, padding: EdgeInsets.fromLTRB(2, 2 + (isDesktop ? 0 : (kToolbarHeight + MediaQuery.of(context).padding.top)), 2, 80), - gridDelegate: - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: columnCount, childAspectRatio: displayType == 'Square' ? 1 : 9 / 16), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: columnCount, childAspectRatio: displayType == 'Square' ? 1 : 9 / 16), children: List.generate( previewCount, (int index) { @@ -58,7 +57,7 @@ class ShimmerList extends StatelessWidget { }, ), ); - }), + }, ), ); } @@ -66,10 +65,11 @@ class ShimmerList extends StatelessWidget { class ShimmerCard extends StatelessWidget { const ShimmerCard({ - Key? key, this.isLoading = true, this.child, - }) : super(key: key); + super.key, + }); + final bool isLoading; final Widget? child; @@ -94,24 +94,24 @@ LinearGradient _shimmerGradient(Color c1, Color c2, Color c3) => LinearGradient( 0.3, 0.4, ], - begin: const Alignment(-1.0, -0.3), - end: const Alignment(1.0, 0.3), + begin: const Alignment(-1, -0.3), + end: const Alignment(1, 0.3), tileMode: TileMode.clamp, ); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> class Shimmer extends StatefulWidget { - static ShimmerState? of(BuildContext context) { - return context.findAncestorStateOfType(); - } - const Shimmer({ - super.key, required this.linearGradient, this.child, + super.key, }); + static ShimmerState? of(BuildContext context) { + return context.findAncestorStateOfType(); + } + final LinearGradient linearGradient; final Widget? child; @@ -144,22 +144,22 @@ class ShimmerState extends State with SingleTickerProviderStateMixin { ); bool get isSized { - RenderObject? renderObj = context.findRenderObject(); - RenderBox? box = renderObj != null ? renderObj as RenderBox : null; + final RenderObject? renderObj = context.findRenderObject(); + final RenderBox? box = renderObj != null ? renderObj as RenderBox : null; return box?.hasSize ?? false; } Size get size { - RenderObject? renderObj = context.findRenderObject(); - RenderBox? box = renderObj != null ? renderObj as RenderBox : null; - return box?.size ?? const Size(0, 0); + final RenderObject? renderObj = context.findRenderObject(); + final RenderBox? box = renderObj != null ? renderObj as RenderBox : null; + return box?.size ?? Size.zero; } Offset getDescendantOffset({ required RenderBox descendant, Offset offset = Offset.zero, }) { - final shimmerBox = context.findRenderObject() as RenderBox; + final shimmerBox = context.findRenderObject() as RenderBox?; return descendant.localToGlobal(offset, ancestor: shimmerBox); } @@ -180,7 +180,7 @@ class _SlidingGradientTransform extends GradientTransform { @override Matrix4? transform(Rect bounds, {TextDirection? textDirection}) { - return Matrix4.translationValues(bounds.width * slidePercent, 0.0, 0.0); + return Matrix4.translationValues(bounds.width * slidePercent, 0, 0); } } @@ -188,9 +188,9 @@ class _SlidingGradientTransform extends GradientTransform { class ShimmerLoading extends StatefulWidget { const ShimmerLoading({ - super.key, required this.isLoading, required this.child, + super.key, }); final bool isLoading; diff --git a/lib/src/widgets/preview/staggered_builder.dart b/lib/src/widgets/preview/staggered_builder.dart index 37be4f00..17664ab9 100644 --- a/lib/src/widgets/preview/staggered_builder.dart +++ b/lib/src/widgets/preview/staggered_builder.dart @@ -11,15 +11,14 @@ import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/widgets/desktop/desktop_scroll_wrap.dart'; import 'package:lolisnatcher/src/widgets/thumbnail/thumbnail_card_build.dart'; - class StaggeredBuilder extends StatelessWidget { const StaggeredBuilder({ - Key? key, this.onTap, this.onDoubleTap, this.onLongPress, this.onSecondaryTap, - }) : super(key: key); + super.key, + }); final void Function(int, BooruItem)? onTap; final void Function(int, BooruItem)? onDoubleTap; @@ -31,62 +30,63 @@ class StaggeredBuilder extends StatelessWidget { final SettingsHandler settingsHandler = SettingsHandler.instance; final SearchHandler searchHandler = SearchHandler.instance; - int columnCount = - (MediaQuery.of(context).orientation == Orientation.portrait) ? settingsHandler.portraitColumns : settingsHandler.landscapeColumns; + final int columnCount = (MediaQuery.of(context).orientation == Orientation.portrait) ? settingsHandler.portraitColumns : settingsHandler.landscapeColumns; - bool isDesktop = settingsHandler.appMode.value.isDesktop; + final bool isDesktop = settingsHandler.appMode.value.isDesktop; - return LayoutBuilder(builder: (ctx, constraints) { - double itemMaxWidth = constraints.maxWidth / columnCount; //MediaQuery.of(context).size.width / columnCount; - double itemMaxHeight = itemMaxWidth * (16 / 9); //MediaQuery.of(context).size.height * 0.6; - return Obx(() { - return WaterfallFlow.builder( - controller: searchHandler.gridScrollController, - physics: getListPhysics(), // const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - shrinkWrap: false, - addAutomaticKeepAlives: false, - cacheExtent: 200, - itemCount: searchHandler.currentFetched.length, - padding: EdgeInsets.fromLTRB(2, 2 + (isDesktop ? 0 : (kToolbarHeight + MediaQuery.of(context).padding.top)), 2, 80), - gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount( - crossAxisCount: columnCount, - mainAxisSpacing: 4.0, - crossAxisSpacing: 4.0, - ), - itemBuilder: (BuildContext context, int index) { - double? widthData = searchHandler.currentFetched[index].fileWidth; - double? heightData = searchHandler.currentFetched[index].fileHeight; + return LayoutBuilder( + builder: (ctx, constraints) { + final double itemMaxWidth = constraints.maxWidth / columnCount; //MediaQuery.of(context).size.width / columnCount; + final double itemMaxHeight = itemMaxWidth * (16 / 9); //MediaQuery.of(context).size.height * 0.6; + return Obx(() { + return WaterfallFlow.builder( + controller: searchHandler.gridScrollController, + physics: getListPhysics(), // const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), + shrinkWrap: false, + addAutomaticKeepAlives: false, + cacheExtent: 200, + itemCount: searchHandler.currentFetched.length, + padding: EdgeInsets.fromLTRB(2, 2 + (isDesktop ? 0 : (kToolbarHeight + MediaQuery.of(context).padding.top)), 2, 80), + gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount( + crossAxisCount: columnCount, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + ), + itemBuilder: (BuildContext context, int index) { + final double? widthData = searchHandler.currentFetched[index].fileWidth; + final double? heightData = searchHandler.currentFetched[index].fileHeight; - double possibleWidth = itemMaxWidth; - double possibleHeight = itemMaxWidth; - bool hasSizeData = heightData != null && widthData != null; - if (hasSizeData) { - double aspectRatio = widthData / heightData; - possibleHeight = possibleWidth / aspectRatio; - } - // force to use minimum 100 px and max 60% of screen height - possibleHeight = max(min(itemMaxHeight, possibleHeight), 100); + final double possibleWidth = itemMaxWidth; + double possibleHeight = itemMaxWidth; + final bool hasSizeData = heightData != null && widthData != null; + if (hasSizeData) { + final double aspectRatio = widthData / heightData; + possibleHeight = possibleWidth / aspectRatio; + } + // force to use minimum 100 px and max 60% of screen height + possibleHeight = max(min(itemMaxHeight, possibleHeight), 100); - final BooruItem item = searchHandler.currentFetched[index]; + final BooruItem item = searchHandler.currentFetched[index]; - return SizedBox( - height: possibleHeight, - width: possibleWidth, - // constraints: hasSizeData - // ? BoxConstraints(minHeight: possibleHeight, maxHeight: possibleHeight, minWidth: possibleWidth, maxWidth: possibleWidth) - // : BoxConstraints(minHeight: possibleWidth, maxHeight: double.infinity, minWidth: possibleWidth, maxWidth: possibleWidth), - child: ThumbnailCardBuild( - index: index, - item: item, - onTap: onTap, - onDoubleTap: onDoubleTap, - onLongPress: onLongPress, - onSecondaryTap: onSecondaryTap, - ), - ); - }, - ); - }); - }); + return SizedBox( + height: possibleHeight, + width: possibleWidth, + // constraints: hasSizeData + // ? BoxConstraints(minHeight: possibleHeight, maxHeight: possibleHeight, minWidth: possibleWidth, maxWidth: possibleWidth) + // : BoxConstraints(minHeight: possibleWidth, maxHeight: double.infinity, minWidth: possibleWidth, maxWidth: possibleWidth), + child: ThumbnailCardBuild( + index: index, + item: item, + onTap: onTap, + onDoubleTap: onDoubleTap, + onLongPress: onLongPress, + onSecondaryTap: onSecondaryTap, + ), + ); + }, + ); + }); + }, + ); } } diff --git a/lib/src/widgets/preview/waterfall_error_buttons.dart b/lib/src/widgets/preview/waterfall_error_buttons.dart index 18c97b54..2217e6f5 100644 --- a/lib/src/widgets/preview/waterfall_error_buttons.dart +++ b/lib/src/widgets/preview/waterfall_error_buttons.dart @@ -10,7 +10,7 @@ import 'package:lolisnatcher/src/utils/tools.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class WaterfallErrorButtons extends StatefulWidget { - const WaterfallErrorButtons({Key? key}) : super(key: key); + const WaterfallErrorButtons({super.key}); @override State createState() => _WaterfallErrorButtonsState(); @@ -78,50 +78,52 @@ class _WaterfallErrorButtonsState extends State { @override Widget build(BuildContext context) { final String clickName = (Platform.isWindows || Platform.isLinux) ? 'Click' : 'Tap'; - int nowMils = DateTime.now().millisecondsSinceEpoch; - int sinceStart = _startedAt == 0 ? 0 : Duration(milliseconds: nowMils - _startedAt).inSeconds; - String sinceStartText = sinceStart > 0 ? 'Started ${sinceStart.toString()} ${Tools.pluralize('second', sinceStart)} ago' : ''; + final int nowMils = DateTime.now().millisecondsSinceEpoch; + final int sinceStart = _startedAt == 0 ? 0 : Duration(milliseconds: nowMils - _startedAt).inSeconds; + final String sinceStartText = sinceStart > 0 ? 'Started $sinceStart ${Tools.pluralize('second', sinceStart)} ago' : ''; return Obx(() { if (searchHandler.isLastPage.value) { // if last page... if (searchHandler.currentFetched.isEmpty) { // ... and no items loaded - return wrapButton(SettingsButton( - name: 'No Data Loaded', - subtitle: Text('$clickName Here to Reload'), - icon: const Icon(Icons.refresh), - dense: true, - action: () { - searchHandler.retrySearch(); - }, - drawBottomBorder: false, - )); + return wrapButton( + SettingsButton( + name: 'No Data Loaded', + subtitle: Text('$clickName Here to Reload'), + icon: const Icon(Icons.refresh), + dense: true, + action: searchHandler.retrySearch, + drawBottomBorder: false, + ), + ); } else { // .. has items loaded if (isVisible) { final int pageNum = searchHandler.pageNum.value; - return wrapButton(SettingsButton( - name: 'You Reached the End ($pageNum ${Tools.pluralize('page', pageNum)})', - subtitle: Text('$clickName Here to Reload Last Page'), - icon: const Icon(Icons.refresh), - dense: true, - action: () { - searchHandler.retrySearch(); - if (!isVisible) { - isVisible = !isVisible; - updateState(); - } - }, - trailingIcon: IconButton( - onPressed: () { - isVisible = !isVisible; - updateState(); + return wrapButton( + SettingsButton( + name: 'You Reached the End ($pageNum ${Tools.pluralize('page', pageNum)})', + subtitle: Text('$clickName Here to Reload Last Page'), + icon: const Icon(Icons.refresh), + dense: true, + action: () { + searchHandler.retrySearch(); + if (!isVisible) { + isVisible = !isVisible; + updateState(); + } }, - icon: const Icon(Icons.arrow_drop_down), + trailingIcon: IconButton( + onPressed: () { + isVisible = !isVisible; + updateState(); + }, + icon: const Icon(Icons.arrow_drop_down), + ), + drawBottomBorder: false, ), - drawBottomBorder: false, - )); + ); } else { return Row( mainAxisAlignment: MainAxisAlignment.end, @@ -167,53 +169,53 @@ class _WaterfallErrorButtonsState extends State { // if not last page... if (searchHandler.isLoading.value) { // ... and is currently loading - return wrapButton(SettingsButton( - name: 'Loading Page #${searchHandler.pageNum}', - subtitle: AnimatedOpacity( - opacity: sinceStartText.isNotEmpty ? 1.0 : 0.0, - duration: const Duration(milliseconds: 200), - child: Text(sinceStartText), - ), - icon: const SizedBox( - width: 30, - height: 30, - child: CircularProgressIndicator(), + return wrapButton( + SettingsButton( + name: 'Loading Page #${searchHandler.pageNum}', + subtitle: AnimatedOpacity( + opacity: sinceStartText.isNotEmpty ? 1.0 : 0.0, + duration: const Duration(milliseconds: 200), + child: Text(sinceStartText), + ), + icon: const SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator(), + ), + dense: true, + action: searchHandler.retrySearch, + drawBottomBorder: false, ), - dense: true, - action: () { - searchHandler.retrySearch(); - }, - drawBottomBorder: false, - )); + ); } else { if (searchHandler.errorString.isNotEmpty) { final String errorFormatted = searchHandler.errorString.isNotEmpty ? '\n${searchHandler.errorString}' : ''; // ... if error happened - return wrapButton(SettingsButton( - name: 'Error happened when Loading Page #${searchHandler.pageNum}: $errorFormatted', - subtitle: Text('$clickName Here to Retry'), - icon: const Icon(Icons.refresh), - dense: true, - action: () { - searchHandler.retrySearch(); - }, - drawBottomBorder: false, - )); + return wrapButton( + SettingsButton( + name: 'Error happened when Loading Page #${searchHandler.pageNum}: $errorFormatted', + subtitle: Text('$clickName Here to Retry'), + icon: const Icon(Icons.refresh), + dense: true, + action: searchHandler.retrySearch, + drawBottomBorder: false, + ), + ); } else if (searchHandler.currentFetched.isEmpty) { // ... no items loaded - return wrapButton(SettingsButton( - name: 'Error, no data loaded:', - subtitle: Text('$clickName Here to Retry'), - icon: const Icon(Icons.refresh), - dense: true, - action: () { - searchHandler.retrySearch(); - }, - drawBottomBorder: false, - )); + return wrapButton( + SettingsButton( + name: 'Error, no data loaded:', + subtitle: Text('$clickName Here to Retry'), + icon: const Icon(Icons.refresh), + dense: true, + action: searchHandler.retrySearch, + drawBottomBorder: false, + ), + ); } else { // return const SizedBox.shrink(); - + // add a small container to avoid scrolling when swiping from the bottom of the screen (navigation gestures) return Container(height: 10, color: Colors.transparent); } diff --git a/lib/src/widgets/preview/waterfall_view.dart b/lib/src/widgets/preview/waterfall_view.dart index 3cf752f1..1a77d7c8 100644 --- a/lib/src/widgets/preview/waterfall_view.dart +++ b/lib/src/widgets/preview/waterfall_view.dart @@ -21,7 +21,7 @@ import 'package:lolisnatcher/src/widgets/preview/staggered_builder.dart'; import 'package:lolisnatcher/src/widgets/preview/waterfall_error_buttons.dart'; class WaterfallView extends StatefulWidget { - const WaterfallView({Key? key}) : super(key: key); + const WaterfallView({super.key}); @override State createState() => _WaterfallViewState(); @@ -90,7 +90,8 @@ class _WaterfallViewState extends State { // restore scroll position on tab change if (searchHandler.gridScrollController.hasClients) { searchHandler.gridScrollController.jumpTo(searchHandler.currentTab.scrollPosition); - } else { // if (searchHandler.currentTab.scrollPosition != 0) { + } else { + // if (searchHandler.currentTab.scrollPosition != 0) { // TODO reset the controller when appMode changes searchHandler.gridScrollController = AutoScrollController( initialScrollOffset: searchHandler.currentTab.scrollPosition, @@ -100,10 +101,10 @@ class _WaterfallViewState extends State { }); // check if grid type changed when changing tab - bool newIsStaggered = settingsHandler.previewDisplay == 'Staggered' && searchHandler.currentBooruHandler.hasSizeData; + final bool newIsStaggered = settingsHandler.previewDisplay == 'Staggered' && searchHandler.currentBooruHandler.hasSizeData; if (isStaggered != newIsStaggered) { isStaggered = newIsStaggered; - setState(() { }); + setState(() {}); } } @@ -111,8 +112,9 @@ class _WaterfallViewState extends State { volumeListener?.cancel(); volumeListener = searchHandler.volumeStream?.listen(volumeCallback); } - void volumeCallback(String event) async { - if(!viewerHandler.inViewer.value) { + + Future volumeCallback(String event) async { + if (!viewerHandler.inViewer.value) { int dir = 0; if (event == 'up') { dir = -1; @@ -121,7 +123,7 @@ class _WaterfallViewState extends State { } // TODO disable when not in focus (i.e. opened settings/drawer), right now if focus is lost, this widget can't regain it - if(dir != 0 && scrollDone == true) { + if (dir != 0 && scrollDone == true) { scrollDone = false; final double offset = max(searchHandler.gridScrollController.offset + (settingsHandler.volumeButtonsScrollSpeed * dir), -20); await searchHandler.gridScrollController.animateTo(offset, duration: const Duration(milliseconds: 200), curve: Curves.linear); @@ -141,20 +143,20 @@ class _WaterfallViewState extends State { super.dispose(); } - void jumpTo(int newIndex) async { + Future jumpTo(int newIndex) async { if (!searchHandler.gridScrollController.hasClients) { return; } - if(newIndex == -1) { + if (newIndex == -1) { return; } - if(!viewerHandler.inViewer.value && isMobile) { + if (!viewerHandler.inViewer.value && isMobile) { return; // await Future.delayed(Duration(milliseconds: 500)); } - if(newIndex == 0) { + if (newIndex == 0) { // viewedIndex == 0 when tab is first created, so we should scroll to top on 0th item (not to the item itself, because there is padding on top of it) to avoid bugs with appbar searchHandler.gridScrollController.jumpTo(0); } else { @@ -162,7 +164,7 @@ class _WaterfallViewState extends State { await searchHandler.gridScrollController.scrollToIndex( newIndex, duration: Duration(milliseconds: isMobile ? 10 : 100), - preferPosition: AutoScrollPosition.begin + preferPosition: AutoScrollPosition.begin, ); } } @@ -172,7 +174,7 @@ class _WaterfallViewState extends State { if ((searchHandler.currentFetched.isNotEmpty && searchHandler.currentFetched.length < (settingsHandler.limit + 1)) && !isMobile) { if (searchHandler.viewedItem.value.fileURL.isEmpty) { // print("setting booruItem value"); - BooruItem item = searchHandler.setViewedItem(0); + final BooruItem item = searchHandler.setViewedItem(0); viewerHandler.setCurrent(item.key); } } @@ -183,10 +185,10 @@ class _WaterfallViewState extends State { viewerHandler.dropCurrent(); } - void onTap(int index, BooruItem item) async { + Future onTap(int index, BooruItem item) async { // Load the image viewer - BooruItem viewedItem = searchHandler.setViewedItem(index); + final BooruItem viewedItem = searchHandler.setViewedItem(index); viewerHandler.setCurrent(viewedItem.key); if (isMobile) { @@ -196,9 +198,9 @@ class _WaterfallViewState extends State { await Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, anim1, anim2) => - // Opacity(opacity: 0.5, child: GalleryViewPage(index)), - GalleryViewPage(index), + pageBuilder: (context, anim1, anim2) => + // Opacity(opacity: 0.5, child: GalleryViewPage(index)), + GalleryViewPage(index), opaque: false, transitionDuration: const Duration(milliseconds: 300), barrierColor: Colors.black26, @@ -207,15 +209,15 @@ class _WaterfallViewState extends State { viewerCallback(); } else { - // + // } } - void onDoubleTap(int index, BooruItem item) async { + Future onDoubleTap(int index, BooruItem item) async { await searchHandler.toggleItemFavourite(index); } - void onLongPress(int index, BooruItem item) async { + Future onLongPress(int index, BooruItem item) async { ServiceHandler.vibrate(duration: 5); if (searchHandler.currentTab.selected.contains(index)) { @@ -229,7 +231,7 @@ class _WaterfallViewState extends State { Clipboard.setData(ClipboardData(text: Uri.encodeFull(item.fileURL))); FlashElements.showSnackbar( duration: const Duration(seconds: 2), - title: const Text("Copied File URL to clipboard!", style: TextStyle(fontSize: 20)), + title: const Text('Copied File URL to clipboard!', style: TextStyle(fontSize: 20)), content: Text(Uri.encodeFull(item.fileURL), style: const TextStyle(fontSize: 16)), leadingIcon: Icons.copy, sideColor: Colors.green, @@ -241,11 +243,11 @@ class _WaterfallViewState extends State { // print('!!! WATERFALL BUILD: ${searchHandler.currentFetched.length}'); // check if grid type changed when rebuilding the widget (must happen only on start and when saving settings) - bool newIsStaggered = settingsHandler.previewDisplay == 'Staggered' && searchHandler.currentBooruHandler.hasSizeData; + final bool newIsStaggered = settingsHandler.previewDisplay == 'Staggered' && searchHandler.currentBooruHandler.hasSizeData; if (isStaggered != newIsStaggered) { WidgetsBinding.instance.addPostFrameCallback((_) { isStaggered = newIsStaggered; - setState(() { }); + setState(() {}); }); } @@ -265,51 +267,43 @@ class _WaterfallViewState extends State { // detect only key DOWN events // physicalKey guarantees detection on non-english keys/languages if (event.runtimeType == RawKeyDownEvent) { - if(event.physicalKey == PhysicalKeyboardKey.keyK || event.physicalKey == PhysicalKeyboardKey.keyS) { + if (event.physicalKey == PhysicalKeyboardKey.keyK || event.physicalKey == PhysicalKeyboardKey.keyS) { // searchHandler.gridScrollController.animateTo(searchHandler.gridScrollController.offset + 50, duration: Duration(milliseconds: 50), curve: Curves.linear); - columnCount = (MediaQuery.of(context).orientation == Orientation.portrait) - ? settingsHandler.portraitColumns - : settingsHandler.landscapeColumns; + columnCount = (MediaQuery.of(context).orientation == Orientation.portrait) ? settingsHandler.portraitColumns : settingsHandler.landscapeColumns; oldIndex = searchHandler.viewedIndex.value; newIndex = oldIndex + columnCount; - if(newIndex < searchHandler.currentFetched.length) { + if (newIndex < searchHandler.currentFetched.length) { item = searchHandler.setViewedItem(newIndex); viewerHandler.setCurrent(item.key); jumpTo(newIndex); } - - } else if(event.physicalKey == PhysicalKeyboardKey.keyJ || event.physicalKey == PhysicalKeyboardKey.keyW) { + } else if (event.physicalKey == PhysicalKeyboardKey.keyJ || event.physicalKey == PhysicalKeyboardKey.keyW) { // searchHandler.gridScrollController.animateTo(searchHandler.gridScrollController.offset - 50, duration: Duration(milliseconds: 50), curve: Curves.linear); - columnCount = (MediaQuery.of(context).orientation == Orientation.portrait) - ? settingsHandler.portraitColumns - : settingsHandler.landscapeColumns; + columnCount = (MediaQuery.of(context).orientation == Orientation.portrait) ? settingsHandler.portraitColumns : settingsHandler.landscapeColumns; oldIndex = searchHandler.viewedIndex.value; newIndex = oldIndex - columnCount; - if(newIndex > -1) { + if (newIndex > -1) { item = searchHandler.setViewedItem(newIndex); viewerHandler.setCurrent(item.key); jumpTo(newIndex); } - - } else if(event.physicalKey == PhysicalKeyboardKey.keyD) { + } else if (event.physicalKey == PhysicalKeyboardKey.keyD) { oldIndex = searchHandler.viewedIndex.value; newIndex = oldIndex + 1; - if(newIndex < searchHandler.currentFetched.length) { + if (newIndex < searchHandler.currentFetched.length) { item = searchHandler.setViewedItem(newIndex); viewerHandler.setCurrent(item.key); jumpTo(newIndex); } - - } else if(event.physicalKey == PhysicalKeyboardKey.keyA) { + } else if (event.physicalKey == PhysicalKeyboardKey.keyA) { oldIndex = searchHandler.viewedIndex.value; newIndex = oldIndex - 1; - if(newIndex > -1) { + if (newIndex > -1) { item = searchHandler.setViewedItem(newIndex); viewerHandler.setCurrent(item.key); jumpTo(newIndex); } - - } else if(event.physicalKey == PhysicalKeyboardKey.escape) { + } else if (event.physicalKey == PhysicalKeyboardKey.escape) { searchHandler.setViewedItem(-1); viewerHandler.dropCurrent(); } @@ -336,10 +330,10 @@ class _WaterfallViewState extends State { return Stack( children: [ DesktopScrollWrap( - controller: searchHandler.gridScrollController, - // if staggered - fallback to grid if booru doesn't give image sizes in api, otherwise layout will lag and jump around uncontrollably - child: ShimmerWrap( - child: isStaggered + controller: searchHandler.gridScrollController, + // if staggered - fallback to grid if booru doesn't give image sizes in api, otherwise layout will lag and jump around uncontrollably + child: ShimmerWrap( + child: isStaggered ? StaggeredBuilder( onTap: onTap, onDoubleTap: onDoubleTap, @@ -352,14 +346,12 @@ class _WaterfallViewState extends State { onLongPress: onLongPress, onSecondaryTap: onSecondaryTap, ), - ), - ), - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: isLoadingOrNoItems - ? const ShimmerList() - : const SizedBox.shrink(), ), + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: isLoadingOrNoItems ? const ShimmerList() : const SizedBox.shrink(), + ), ], ); }), @@ -372,11 +364,15 @@ class _WaterfallViewState extends State { //print(searchHandler.gridScrollController.position.maxScrollExtent); //print(notif.metrics); // pixels before viewport, in viewport, after viewport - bool isNotAtStart = notif.metrics.pixels > 0; - bool isAtOrNearEdge = notif.metrics.atEdge || notif.metrics.pixels > (notif.metrics.maxScrollExtent - (notif.metrics.extentInside * 2)); // trigger new page when at edge or scroll position is less than 2 viewports - bool isScreenFilled = notif.metrics.extentBefore != 0 || notif.metrics.extentAfter != 0; // for cases when first page doesn't fill the screen + final bool isNotAtStart = notif.metrics.pixels > 0; + final bool isAtOrNearEdge = notif.metrics.atEdge || + notif.metrics.pixels > + (notif.metrics.maxScrollExtent - + (notif.metrics.extentInside * 2)); // trigger new page when at edge or scroll position is less than 2 viewports + final bool isScreenFilled = + notif.metrics.extentBefore != 0 || notif.metrics.extentAfter != 0; // for cases when first page doesn't fill the screen - if(!searchHandler.isLoading.value) { + if (!searchHandler.isLoading.value) { if (!isScreenFilled || (isNotAtStart && isAtOrNearEdge)) { // print('LOADING MORE'); // print('isScreenFilled: $isScreenFilled'); @@ -404,7 +400,7 @@ class _WaterfallViewState extends State { const WaterfallErrorButtons(), ], - ) + ), ); } } diff --git a/lib/src/widgets/root/active_title.dart b/lib/src/widgets/root/active_title.dart index 71fb2da3..397977f2 100644 --- a/lib/src/widgets/root/active_title.dart +++ b/lib/src/widgets/root/active_title.dart @@ -7,7 +7,7 @@ import 'package:lolisnatcher/src/handlers/snatch_handler.dart'; import 'package:lolisnatcher/src/widgets/tabs/tab_selector.dart'; class ActiveTitle extends StatelessWidget { - const ActiveTitle({Key? key}) : super(key: key); + const ActiveTitle({super.key}); @override Widget build(BuildContext context) { @@ -22,7 +22,7 @@ class ActiveTitle extends StatelessWidget { return FittedBox( fit: BoxFit.fitWidth, - child: Text("Snatching: ${snatchHandler.status} $progressText".trim()), + child: Text('Snatching: ${snatchHandler.status} $progressText'.trim()), ); } else { if (searchHandler.list.isEmpty) { diff --git a/lib/src/widgets/root/image_stats.dart b/lib/src/widgets/root/image_stats.dart index 01071175..d4c1f23f 100644 --- a/lib/src/widgets/root/image_stats.dart +++ b/lib/src/widgets/root/image_stats.dart @@ -7,13 +7,13 @@ import 'package:lolisnatcher/src/utils/tools.dart'; class ImageStats extends StatefulWidget { const ImageStats({ - Key? key, - required this.child, - this.width = 120, - this.height = 40, - this.isEnabled = true, - this.align - }) : super(key: key); + required this.child, + this.width = 120, + this.height = 40, + this.isEnabled = true, + this.align, + super.key, + }); /// Toggle the stats on/off, there should be no performance cost when the widget is off. final bool isEnabled; @@ -37,13 +37,13 @@ class ImageStats extends StatefulWidget { class _ImageStatsState extends State { int _lastCalcTime = 0; late Ticker _ticker; - double _ticks = 0; + // double _ticks = 0; final RxInt _totalLive = 0.obs; final RxInt _totalPending = 0.obs; final RxInt _totalAll = 0.obs; final RxInt _cacheSize = 0.obs; final RxInt _cacheMax = 0.obs; - final bool _shouldRepaint = false; + // final bool _shouldRepaint = false; int sampleTimeMs = 500; int get nowMs => DateTime.now().millisecondsSinceEpoch; @@ -78,7 +78,7 @@ class _ImageStatsState extends State { _totalLive.value = PaintingBinding.instance.imageCache.liveImageCount; _totalPending.value = PaintingBinding.instance.imageCache.pendingImageCount; _totalAll.value = PaintingBinding.instance.imageCache.currentSize; - _cacheSize.value = PaintingBinding.instance.imageCache.currentSizeBytes ; + _cacheSize.value = PaintingBinding.instance.imageCache.currentSizeBytes; _cacheMax.value = PaintingBinding.instance.imageCache.maximumSizeBytes; } @@ -88,12 +88,12 @@ class _ImageStatsState extends State { return; } // Tick - _ticks++; + // _ticks++; // Calculate if (nowMs - _lastCalcTime > sampleTimeMs) { - int remainder = (nowMs - _lastCalcTime - sampleTimeMs).round(); + final int remainder = nowMs - _lastCalcTime - sampleTimeMs; _lastCalcTime = nowMs - remainder; - _ticks = 0; + // _ticks = 0; updateValues(); } } @@ -115,15 +115,17 @@ class _ImageStatsState extends State { height: widget.height, color: Colors.white.withOpacity(0.8), child: RepaintBoundary( - child: Obx(() => Column( - children: [ - Text('Live: ${_totalLive.value}'), - Text('Pending: ${_totalPending.value}'), - Text('Total: ${_totalAll.value}'), - Text('Size: ${Tools.formatBytes(_cacheSize.value, 0)}'), - Text('Max: ${Tools.formatBytes(_cacheMax.value, 0)}'), - ] - )), + child: Obx( + () => Column( + children: [ + Text('Live: ${_totalLive.value}'), + Text('Pending: ${_totalPending.value}'), + Text('Total: ${_totalAll.value}'), + Text('Size: ${Tools.formatBytes(_cacheSize.value, 0)}'), + Text('Max: ${Tools.formatBytes(_cacheMax.value, 0)}'), + ], + ), + ), ), ), ), diff --git a/lib/src/widgets/root/main_appbar.dart b/lib/src/widgets/root/main_appbar.dart index 8d46b37e..299e5779 100644 --- a/lib/src/widgets/root/main_appbar.dart +++ b/lib/src/widgets/root/main_appbar.dart @@ -16,15 +16,15 @@ import 'package:lolisnatcher/src/widgets/root/active_title.dart'; class MainAppBar extends StatefulWidget implements PreferredSizeWidget { const MainAppBar({ - Key? key, this.leading, this.trailing, - }) : super(key: key); + super.key, + }); final Widget? leading; final Widget? trailing; - final double defaultHeight = kToolbarHeight; //56.0 + double get defaultHeight => kToolbarHeight; //56.0 @override Size get preferredSize => Size.fromHeight(defaultHeight); @@ -39,8 +39,8 @@ class _MainAppBarState extends State { final SnatchHandler snatchHandler = SnatchHandler.instance; final ViewerHandler viewerHandler = ViewerHandler.instance; - double _scrollOffset = 1.0; - double _lastScrollPosition = 0.0; + double _scrollOffset = 1; + double _lastScrollPosition = 0; StreamSubscription? scrollListener; // StreamSubscription? indexListener; @@ -62,7 +62,7 @@ class _MainAppBarState extends State { void updatePosition(double newOffset) { final double prevOffset = _scrollOffset; final double viewportDimension = searchHandler.gridScrollController.position.viewportDimension; - double scrollChange = ((_lastScrollPosition - newOffset) / viewportDimension) * 10; + final double scrollChange = ((_lastScrollPosition - newOffset) / viewportDimension) * 10; if (newOffset < 0 || (newOffset + 100) > searchHandler.gridScrollController.position.maxScrollExtent) { // do nothing when oversrolling @@ -125,52 +125,55 @@ class _MainAppBarState extends State { Widget saveButton() { return Obx(() { if (searchHandler.list.isNotEmpty) { - return Stack(alignment: Alignment.center, children: [ - IconButton( - icon: Icon(Icons.save, color: Theme.of(context).appBarTheme.iconTheme?.color), - onPressed: () { - getPerms(); - // call a function to save the currently viewed image when the save button is pressed - if (searchHandler.currentTab.selected.isNotEmpty) { - snatchHandler.queue( - searchHandler.currentTab.getSelected(), - searchHandler.currentBooru, - settingsHandler.snatchCooldown, - false, - ); - searchHandler.currentTab.selected.value = []; - } else { - FlashElements.showSnackbar( - context: context, - title: const Text("No items selected", style: TextStyle(fontSize: 20)), - overrideLeadingIconWidget: const Text(" (」°ロ°)」 ", style: TextStyle(fontSize: 18)), - ); - } - }, - ), - if (searchHandler.currentTab.selected.isNotEmpty) - Positioned( - right: 2, - bottom: 5, - child: Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.secondary, - border: Border.all(color: Theme.of(context).colorScheme.secondary, width: 1), - borderRadius: BorderRadius.circular(15), - ), - child: Center( - child: FittedBox( - child: Text( - '${searchHandler.currentTab.selected.length}', - style: TextStyle(color: Theme.of(context).colorScheme.onSecondary), + return Stack( + alignment: Alignment.center, + children: [ + IconButton( + icon: Icon(Icons.save, color: Theme.of(context).appBarTheme.iconTheme?.color), + onPressed: () { + getPerms(); + // call a function to save the currently viewed image when the save button is pressed + if (searchHandler.currentTab.selected.isNotEmpty) { + snatchHandler.queue( + searchHandler.currentTab.getSelected(), + searchHandler.currentBooru, + settingsHandler.snatchCooldown, + false, + ); + searchHandler.currentTab.selected.value = []; + } else { + FlashElements.showSnackbar( + context: context, + title: const Text('No items selected', style: TextStyle(fontSize: 20)), + overrideLeadingIconWidget: const Text(' (」°ロ°)」 ', style: TextStyle(fontSize: 18)), + ); + } + }, + ), + if (searchHandler.currentTab.selected.isNotEmpty) + Positioned( + right: 2, + bottom: 5, + child: Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.secondary, + border: Border.all(color: Theme.of(context).colorScheme.secondary, width: 1), + borderRadius: BorderRadius.circular(15), + ), + child: Center( + child: FittedBox( + child: Text( + '${searchHandler.currentTab.selected.length}', + style: TextStyle(color: Theme.of(context).colorScheme.onSecondary), + ), ), ), ), ), - ), - ]); + ], + ); } else { return const SizedBox.shrink(); } diff --git a/lib/src/widgets/root/theme_builder.dart b/lib/src/widgets/root/theme_builder.dart index 327daf1d..aceceec3 100644 --- a/lib/src/widgets/root/theme_builder.dart +++ b/lib/src/widgets/root/theme_builder.dart @@ -7,7 +7,7 @@ import 'package:lolisnatcher/src/handlers/settings_handler.dart'; import 'package:lolisnatcher/src/handlers/theme_handler.dart'; class ThemeBuilder extends StatelessWidget { - const ThemeBuilder({Key? key, required this.child}) : super(key: key); + const ThemeBuilder({required this.child, super.key}); final Widget child; @@ -16,7 +16,7 @@ class ThemeBuilder extends StatelessWidget { final SettingsHandler settingsHandler = SettingsHandler.instance; return Obx(() { - ThemeItem theme = settingsHandler.theme.value.name == 'Custom' + final ThemeItem theme = settingsHandler.theme.value.name == 'Custom' ? ThemeItem( name: 'Custom', primary: settingsHandler.customPrimaryColor.value, diff --git a/lib/src/widgets/search/tag_chip.dart b/lib/src/widgets/search/tag_chip.dart index 2e61e633..716f0a4d 100644 --- a/lib/src/widgets/search/tag_chip.dart +++ b/lib/src/widgets/search/tag_chip.dart @@ -4,7 +4,7 @@ import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/handlers/tag_handler.dart'; class TagChip extends StatelessWidget { - TagChip({Key? key, this.tagString = "", required this.gestureDetector}) : super(key: key); + TagChip({required this.gestureDetector, super.key, this.tagString = ''}); final String tagString; final GestureDetector gestureDetector; @@ -15,7 +15,7 @@ class TagChip extends StatelessWidget { @override Widget build(BuildContext context) { String stringContent = tagString; - List tagPins = []; + final List tagPins = []; // exclude (-), or(~) final bool isExclude = stringContent.startsWith('-'); @@ -26,17 +26,17 @@ class TagChip extends StatelessWidget { } // numbered tags for multibooru - if (stringContent.startsWith(RegExp(r"\d+#"))) { - String multiIndex = stringContent.split("#")[0]; - stringContent = stringContent.split("#")[1]; + if (stringContent.startsWith(RegExp(r'\d+#'))) { + final String multiIndex = stringContent.split('#')[0]; + stringContent = stringContent.split('#')[1]; tagPins.add(TagPin(content: multiIndex, color: Colors.purple)); } // shorten stuff like order, sort, rating, ... - Map modifierMap = searchHandler.currentBooruHandler.tagModifierMap; + final Map modifierMap = searchHandler.currentBooruHandler.tagModifierMap; modifierMap.forEach((modifier, displayValue) { if (stringContent.toLowerCase().startsWith(modifier)) { - stringContent = stringContent.toLowerCase().replaceFirst(modifier, ""); + stringContent = stringContent.toLowerCase().replaceFirst(modifier, ''); tagPins.add(TagPin(content: displayValue, color: Colors.purple)); } }); @@ -47,7 +47,7 @@ class TagChip extends StatelessWidget { chipColour = chipColour == Colors.transparent ? Colors.blue : chipColour; // replace all _ with spaces and trim - stringContent = stringContent.replaceAll(RegExp(r"_"), " ").trim(); + stringContent = stringContent.replaceAll(RegExp('_'), ' ').trim(); return Container( decoration: BoxDecoration( @@ -82,10 +82,10 @@ class TagChip extends StatelessWidget { class TagPin extends StatelessWidget { const TagPin({ - Key? key, required this.content, required this.color, - }) : super(key: key); + super.key, + }); final String content; final Color color; diff --git a/lib/src/widgets/search/tag_search_box.dart b/lib/src/widgets/search/tag_search_box.dart index d108be82..2e49d992 100644 --- a/lib/src/widgets/search/tag_search_box.dart +++ b/lib/src/widgets/search/tag_search_box.dart @@ -6,8 +6,8 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/boorus/mergebooru_handler.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/handlers/service_handler.dart'; @@ -23,7 +23,7 @@ import 'package:lolisnatcher/src/widgets/search/tag_chip.dart'; // - parse tag type from search if possible class TagSearchBox extends StatefulWidget { - const TagSearchBox({Key? key}) : super(key: key); + const TagSearchBox({super.key}); @override State createState() => _TagSearchBoxState(); @@ -41,9 +41,9 @@ class _TagSearchBoxState extends State { OverlayEntry? _overlayEntry; bool isFocused = false; - String input = ""; - String lastTag = ""; - String replaceString = ""; + String input = ''; + String lastTag = ''; + String replaceString = ''; int startIndex = 0; int multiIndex = -1; int cursorPos = 0; @@ -195,8 +195,8 @@ class _TagSearchBoxState extends State { color: Theme.of(context).colorScheme.secondary, child: TextButton( onPressed: () async { - ClipboardData? cdata = await Clipboard.getData(Clipboard.kTextPlain); - String copied = cdata?.text ?? ''; + final ClipboardData? cdata = await Clipboard.getData(Clipboard.kTextPlain); + final String copied = cdata?.text ?? ''; if (copied.isNotEmpty) { searchHandler.searchTextController.text += ' $copied '; searchHandler.searchTextController.selection = @@ -263,10 +263,10 @@ class _TagSearchBoxState extends State { List getTagsChips() { // TODO on desktop - set cursor to where user clicked? // based on https://github.com/eyoeldefare/textfield_tags - List tags = []; + final List tags = []; for (var i = 0; i < splitInput.length; i++) { - String stringContent = splitInput.elementAt(i); + final String stringContent = splitInput.elementAt(i); if (stringContent.isEmpty) { // skip creating chip element for empty tags (i.e double spaces...) continue; @@ -298,19 +298,19 @@ class _TagSearchBoxState extends State { void tagStuff() { input = searchHandler.searchTextController.text; if (searchHandler.currentBooru.type == BooruType.Hydrus) { - splitInput = input.trim().split(","); + splitInput = input.trim().split(','); } else { - splitInput = input.trim().split(" "); + splitInput = input.trim().split(' '); } startIndex = 0; setSelectedTag(input); multiIndex = -1; - if (lastTag.startsWith(RegExp(r"\d+#"))) { - int tmpIndex = int.parse(lastTag.split("#")[0]) - 1; - String tag = lastTag.split("#")[1]; + if (lastTag.startsWith(RegExp(r'\d+#'))) { + final int tmpIndex = int.parse(lastTag.split('#')[0]) - 1; + final String tag = lastTag.split('#')[1]; if (searchHandler.currentBooruHandler is MergebooruHandler) { - MergebooruHandler handler = searchHandler.currentBooruHandler as MergebooruHandler; - if ((tmpIndex) >= 0 && tmpIndex < handler.booruHandlers.length) { + final MergebooruHandler handler = searchHandler.currentBooruHandler as MergebooruHandler; + if (tmpIndex >= 0 && tmpIndex < handler.booruHandlers.length) { multiIndex = tmpIndex; lastTag = tag; } @@ -318,7 +318,7 @@ class _TagSearchBoxState extends State { } //remove minus (exclude symbol) or tilde (or symbol) - lastTag = lastTag.replaceAll(RegExp(r'^-'), '').replaceAll(RegExp(r'^~'), ''); + lastTag = lastTag.replaceAll(RegExp('^-'), '').replaceAll(RegExp('^~'), ''); // print("LASTTAG: $lastTag"); setState(() {}); } @@ -327,7 +327,7 @@ class _TagSearchBoxState extends State { cursorPos = searchHandler.searchTextController.selection.baseOffset; if (cursorPos < 0) cursorPos = 0; int tmpStartIndex = cursorPos - 1; - while (tmpStartIndex > 0 && (searchHandler.currentBooru.type == BooruType.Hydrus ? input[tmpStartIndex] != "," : input[tmpStartIndex] != " ")) { + while (tmpStartIndex > 0 && (searchHandler.currentBooru.type == BooruType.Hydrus ? input[tmpStartIndex] != ',' : input[tmpStartIndex] != ' ')) { tmpStartIndex--; } @@ -335,12 +335,12 @@ class _TagSearchBoxState extends State { lastTag = splitInput.last; replaceString = lastTag; } else { - int endIndex = input.indexOf(" ", cursorPos); + int endIndex = input.indexOf(' ', cursorPos); if (searchHandler.currentBooru.type == BooruType.Hydrus) { if (tmpStartIndex == -1) { endIndex = input.length; } else { - endIndex = input.indexOf(",", tmpStartIndex); + endIndex = input.indexOf(',', tmpStartIndex); } } if (endIndex == -1) endIndex = cursorPos; @@ -350,14 +350,14 @@ class _TagSearchBoxState extends State { } } - void searchBooru() async { + Future searchBooru() async { booruResults.value = [ [' ', 'loading'] ]; // TODO cancel previous search when new starts List getFromBooru = []; if (multiIndex != -1) { - MergebooruHandler handler = searchHandler.currentBooruHandler as MergebooruHandler; + final MergebooruHandler handler = searchHandler.currentBooruHandler as MergebooruHandler; getFromBooru = await handler.booruHandlers[multiIndex].tagSearch(lastTag); } else { getFromBooru = await searchHandler.currentBooruHandler.tagSearch(lastTag); @@ -369,7 +369,7 @@ class _TagSearchBoxState extends State { }).toList(); } - void searchHistory() async { + Future searchHistory() async { historyResults.value = [ [' ', 'loading'] ]; @@ -382,7 +382,7 @@ class _TagSearchBoxState extends State { historyResults.where((tag) => booruResults.indexWhere((btag) => btag[0].toLowerCase() == tag[0].toLowerCase()) == -1).toList(); // filter out duplicates } - void searchDatabase() async { + Future searchDatabase() async { databaseResults.value = [ [' ', 'loading'] ]; @@ -420,7 +420,7 @@ class _TagSearchBoxState extends State { } OverlayEntry? _createOverlayEntry() { - RenderBox renderBox = context.findRenderObject()! as RenderBox; + final RenderBox renderBox = context.findRenderObject()! as RenderBox; final size = renderBox.size; final offset = renderBox.localToGlobal(Offset.zero); @@ -431,10 +431,10 @@ class _TagSearchBoxState extends State { width: size.width * 1.2, height: 300, child: Material( - elevation: 4.0, + elevation: 4, color: Theme.of(context).colorScheme.surface, child: Obx(() { - List> items = [ + final List> items = [ ...historyResults.where((tag) => booruResults.indexWhere((btag) => btag[0].toLowerCase() == tag[0].toLowerCase()) == -1), ...databaseResults.where( (tag) => @@ -474,7 +474,7 @@ class _TagSearchBoxState extends State { final String tag = item[0]; final String type = item[1]; - Color tagColor = tagHandler.getTag(tag).getColour(); + final Color tagColor = tagHandler.getTag(tag).getColour(); if (tag.isNotEmpty) { Widget itemIcon = const SizedBox(); @@ -523,24 +523,24 @@ class _TagSearchBoxState extends State { // widget.searchBoxFocus.unfocus(); - String multiIndex = replaceString.startsWith(RegExp(r"\d+#")) ? "${replaceString.split("#")[0]}#" : ""; + final String multiIndex = replaceString.startsWith(RegExp(r'\d+#')) ? "${replaceString.split("#")[0]}#" : ''; // Keep minus if its in the beggining of current (last) tag - bool isExclude = RegExp(r'^-').hasMatch(replaceString.replaceAll(RegExp(r"\d+#"), "")); - bool isOr = RegExp(r'^~').hasMatch(replaceString.replaceAll(RegExp(r"\d+#"), "")); + final bool isExclude = RegExp('^-').hasMatch(replaceString.replaceAll(RegExp(r'\d+#'), '')); + final bool isOr = RegExp('^~').hasMatch(replaceString.replaceAll(RegExp(r'\d+#'), '')); String newTag = multiIndex + (isExclude ? '-' : '') + (isOr ? '~' : '') + tag; if (searchHandler.currentBooru.type == BooruType.Hydrus) { - final String tagWithSpaces = newTag.replaceAll(RegExp(r'_'), ' '); - newTag = "$tagWithSpaces,"; + final String tagWithSpaces = newTag.replaceAll(RegExp('_'), ' '); + newTag = '$tagWithSpaces,'; } else { - newTag = "$newTag "; + newTag = '$newTag '; } - String newInput = ""; + String newInput = ''; if (startIndex >= 0 && replaceString.isNotEmpty) { //newInput = searchHandler.searchTextController.text.replaceRange(start, end, replacement) newInput = searchHandler.searchTextController.text.replaceFirst(replaceString, newTag, cursorPos - replaceString.length); } else if (startIndex == -1) { - newInput = newTag + (searchHandler.currentBooru.type == BooruType.Hydrus ? "," : " ") + searchHandler.searchTextController.text; + newInput = newTag + (searchHandler.currentBooru.type == BooruType.Hydrus ? ',' : ' ') + searchHandler.searchTextController.text; } else { newInput = searchHandler.searchTextController.text + newTag; } @@ -617,7 +617,7 @@ class _TagSearchBoxState extends State { } }, decoration: InputDecoration( - hintText: searchHandler.searchTextController.text.isEmpty ? "Enter Tags" : '', + hintText: searchHandler.searchTextController.text.isEmpty ? 'Enter Tags' : '', prefixIcon: isFocused //searchHandler.searchTextController.text.length > 0 ? IconButton( padding: const EdgeInsets.all(5), diff --git a/lib/src/widgets/search/tag_search_button.dart b/lib/src/widgets/search/tag_search_button.dart index 0f37025f..b556d6e6 100644 --- a/lib/src/widgets/search/tag_search_button.dart +++ b/lib/src/widgets/search/tag_search_button.dart @@ -4,7 +4,7 @@ import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/handlers/service_handler.dart'; class TagSearchButton extends StatelessWidget { - const TagSearchButton({Key? key}) : super(key: key); + const TagSearchButton({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/tabs/tab_booru_selector.dart b/lib/src/widgets/tabs/tab_booru_selector.dart index 8c165be1..13952527 100644 --- a/lib/src/widgets/tabs/tab_booru_selector.dart +++ b/lib/src/widgets/tabs/tab_booru_selector.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:get/get.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/handlers/settings_handler.dart'; @@ -11,7 +11,7 @@ import 'package:lolisnatcher/src/widgets/common/marquee_text.dart'; import 'package:lolisnatcher/src/widgets/image/favicon.dart'; class TabBooruSelector extends StatelessWidget { - const TabBooruSelector(this.isPrimary, {Key? key}) : super(key: key); + const TabBooruSelector(this.isPrimary, {super.key}); final bool isPrimary; @override @@ -71,8 +71,8 @@ class TabBooruSelector extends StatelessWidget { ), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( - labelText: "Secondary Boorus", - hintText: "Secondary Boorus", + labelText: 'Secondary Boorus', + hintText: 'Secondary Boorus', contentPadding: settingsHandler.appMode.value.isDesktop ? const EdgeInsets.symmetric(horizontal: 12, vertical: 2) : const EdgeInsets.symmetric(horizontal: 12, vertical: 8), @@ -81,9 +81,9 @@ class TabBooruSelector extends StatelessWidget { dropdownBuilder: (BuildContext context, List selectedItems) { if (selectedItems.isEmpty) { return const ListTile( - contentPadding: EdgeInsets.all(0), + contentPadding: EdgeInsets.zero, leading: Icon(null), - title: Text("No boorus selected"), + title: Text('No boorus selected'), ); } @@ -129,7 +129,7 @@ class TabBooruSelector extends StatelessWidget { onChanged: (Booru? newValue) { if (searchHandler.currentBooru != newValue) { // if not already selected - searchHandler.searchAction(searchHandler.searchTextController.text, newValue!); + searchHandler.searchAction(searchHandler.searchTextController.text, newValue); } }, selectedItemBuilder: (BuildContext context) { @@ -172,17 +172,17 @@ class TabBooruSelector extends StatelessWidget { class TabBooruSelectorItem extends StatelessWidget { const TabBooruSelectorItem({ - Key? key, required this.booru, this.withFavicon = true, - }) : super(key: key); + super.key, + }); final Booru booru; final bool withFavicon; @override Widget build(BuildContext context) { - String name = " ${booru.name}"; + final String name = ' ${booru.name}'; return Row( children: [ diff --git a/lib/src/widgets/tabs/tab_buttons.dart b/lib/src/widgets/tabs/tab_buttons.dart index 97407473..a6a81448 100644 --- a/lib/src/widgets/tabs/tab_buttons.dart +++ b/lib/src/widgets/tabs/tab_buttons.dart @@ -9,12 +9,12 @@ import 'package:lolisnatcher/src/widgets/dialogs/page_number_dialog.dart'; import 'package:lolisnatcher/src/widgets/history/history.dart'; class TabButtons extends StatelessWidget { - const TabButtons(this.withArrows, this.alignment, {Key? key}) : super(key: key); + const TabButtons(this.withArrows, this.alignment, {super.key}); final bool withArrows; final WrapAlignment? alignment; - Future showHistory(BuildContext context) async { - return await SettingsPageOpen( + Future showHistory(BuildContext context) { + return SettingsPageOpen( context: context, page: () => const HistoryList(), ).open(); @@ -64,10 +64,8 @@ class TabButtons extends StatelessWidget { final Widget removeButton = IconButton( icon: const Icon(Icons.remove_circle_outline), color: iconColor, - onPressed: () { - // Remove selected searchtab from list and apply nearest to search bar - searchHandler.removeTabAt(); - }, + // Remove selected searchtab from list and apply nearest to search bar + onPressed: searchHandler.removeTabAt, ); // Add new tab @@ -75,9 +73,7 @@ class TabButtons extends StatelessWidget { icon: const Icon(Icons.add_circle_outline), color: iconColor, onPressed: () { - final String defaultText = searchHandler.currentBooru.defTags?.isNotEmpty == true - ? searchHandler.currentBooru.defTags! - : settingsHandler.defTags; + final String defaultText = searchHandler.currentBooru.defTags?.isNotEmpty == true ? searchHandler.currentBooru.defTags! : settingsHandler.defTags; // add new tab and switch to it searchHandler.searchTextController.text = defaultText; searchHandler.addTabByString(defaultText, switchToNew: true); diff --git a/lib/src/widgets/tabs/tab_manager_dialog.dart b/lib/src/widgets/tabs/tab_manager_dialog.dart index cef6c94b..505659e9 100644 --- a/lib/src/widgets/tabs/tab_manager_dialog.dart +++ b/lib/src/widgets/tabs/tab_manager_dialog.dart @@ -3,10 +3,10 @@ import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; +import 'package:lolisnatcher/src/boorus/booru_type.dart'; import 'package:lolisnatcher/src/data/booru.dart'; import 'package:lolisnatcher/src/handlers/search_handler.dart'; import 'package:lolisnatcher/src/utils/tools.dart'; @@ -19,7 +19,7 @@ import 'package:lolisnatcher/src/widgets/image/favicon.dart'; import 'package:lolisnatcher/src/widgets/tabs/tab_move_dialog.dart'; class TabManagerDialog extends StatefulWidget { - const TabManagerDialog({Key? key}) : super(key: key); + const TabManagerDialog({super.key}); @override State createState() => _TabManagerDialogState(); @@ -102,7 +102,7 @@ class _TabManagerDialogState extends State { if (filterController.text.isNotEmpty) { filteredTabs = filteredTabs.where((t) { - String filterText = filterController.text.toLowerCase().trim(); + final String filterText = filterController.text.toLowerCase().trim(); return t.tags.toLowerCase().contains(filterText); }).toList(); } @@ -154,7 +154,7 @@ class _TabManagerDialogState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Copied to clipboard!", style: TextStyle(fontSize: 20)), + title: const Text('Copied to clipboard!', style: TextStyle(fontSize: 20)), content: Text(data.tags, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.copy, sideColor: Colors.green, @@ -265,13 +265,13 @@ class _TabManagerDialogState extends State { // print(value.tags); final int totalCount = tab.booruHandler.totalCount.value; - final String totalCountText = (totalCount > 0) ? " ($totalCount)" : ""; + final String totalCountText = (totalCount > 0) ? ' ($totalCount)' : ''; final String tagText = "${tab.tags == "" ? "[No Tags]" : tab.tags}$totalCountText"; final bool hasItems = tab.booruHandler.filteredFetched.isNotEmpty; - final String? givenIndexText = isFilterActive ? "${index + 1}" : null; - final String tabIndexText = "${searchHandler.list.indexOf(tab) + 1}"; + final String? givenIndexText = isFilterActive ? '${index + 1}' : null; + final String tabIndexText = '${searchHandler.list.indexOf(tab) + 1}'; final Widget checkbox = Checkbox( value: isSelected, @@ -322,10 +322,10 @@ class _TabManagerDialogState extends State { text: tagText, fontSize: 16, fontStyle: hasItems ? FontStyle.normal : FontStyle.italic, - color: tab.tags == "" ? Colors.grey : null, + color: tab.tags == '' ? Colors.grey : null, isExpanded: false, ), - subtitle: Text(isNotEmptyBooru ? tab.selectedBooru.value.name! : ""), + subtitle: Text(isNotEmptyBooru ? tab.selectedBooru.value.name! : ''), ), ); } @@ -344,7 +344,7 @@ class _TabManagerDialogState extends State { return count; } - void openFiltersDialog() async { + Future openFiltersDialog() async { final String? result = await SettingsPageOpen( context: context, asBottomSheet: true, @@ -574,7 +574,7 @@ class _TabManagerDialogState extends State { margin: const EdgeInsets.all(10), child: ElevatedButton.icon( icon: const Icon(Icons.select_all), - label: const Text("Select all"), + label: const Text('Select all'), onPressed: () { selectedTabs = filteredTabs.where((element) => element != searchHandler.currentTab).toList(); setState(() {}); @@ -610,7 +610,7 @@ class _TabManagerDialogState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("Delete Tabs"), + const Text('Delete Tabs'), Text( 'Are you sure you want to delete ${selectedTabs.length} ${Tools.pluralize('tab', selectedTabs.length)}?', style: const TextStyle(fontSize: 16), @@ -639,7 +639,7 @@ class _TabManagerDialogState extends State { actionButtons: [ const CancelButton(), ElevatedButton.icon( - label: const Text("Delete"), + label: const Text('Delete'), icon: const Icon(Icons.delete_forever), onPressed: () { for (int i = 0; i < selectedTabs.length; i++) { @@ -666,7 +666,7 @@ class _TabManagerDialogState extends State { Expanded( child: ElevatedButton.icon( icon: const Icon(Icons.border_clear), - label: const Text("Clear selection"), + label: const Text('Clear selection'), onPressed: () { selectedTabs.clear(); setState(() {}); @@ -681,7 +681,7 @@ class _TabManagerDialogState extends State { @override Widget build(BuildContext context) { return SettingsPageDialog( - title: const Text("Tabs"), + title: const Text('Tabs'), content: Column( children: [ filterBuild(), @@ -733,11 +733,11 @@ class _TabManagerFiltersDialogState extends State { @override Widget build(BuildContext context) { return SettingsBottomSheet( - title: const Text("Tab Filters"), + title: const Text('Tab Filters'), contentPadding: const EdgeInsets.all(16), contentItems: [ SettingsBooruDropdown( - title: "Booru", + title: 'Booru', value: booruFilter, drawBottomBorder: false, onChanged: (Booru? newValue) { @@ -746,7 +746,7 @@ class _TabManagerFiltersDialogState extends State { }, ), SettingsDropdown( - title: "Loaded", + title: 'Loaded', value: loadedFilter, drawBottomBorder: false, onChanged: (bool? newValue) { @@ -758,15 +758,15 @@ class _TabManagerFiltersDialogState extends State { true, false, ], - itemBuilder: (item) => item == null ? const Text("All") : Text(item ? "Loaded" : "Unloaded"), + itemBuilder: (item) => item == null ? const Text('All') : Text(item ? 'Loaded' : 'Unloaded'), itemTitleBuilder: (item) => item == null - ? "All" + ? 'All' : item - ? "Loaded" - : "Unloaded", + ? 'Loaded' + : 'Unloaded', ), SettingsToggle( - title: "Duplicates", + title: 'Duplicates', value: duplicateFilter, onChanged: (bool newValue) { duplicateFilter = newValue; @@ -776,7 +776,7 @@ class _TabManagerFiltersDialogState extends State { ], actionButtons: [ ElevatedButton.icon( - label: const Text("Clear"), + label: const Text('Clear'), icon: const Icon(Icons.delete), onPressed: () { Navigator.of(context).pop('clear'); @@ -784,7 +784,7 @@ class _TabManagerFiltersDialogState extends State { ), const SizedBox(width: 10), ElevatedButton.icon( - label: const Text("Apply"), + label: const Text('Apply'), icon: const Icon(Icons.check), onPressed: () { widget.loadedFilterChanged(loadedFilter); diff --git a/lib/src/widgets/tabs/tab_move_dialog.dart b/lib/src/widgets/tabs/tab_move_dialog.dart index b3edcc2d..2eb7b3eb 100644 --- a/lib/src/widgets/tabs/tab_move_dialog.dart +++ b/lib/src/widgets/tabs/tab_move_dialog.dart @@ -5,7 +5,12 @@ import 'package:lolisnatcher/src/widgets/common/flash_elements.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class TabMoveDialog extends StatefulWidget { - const TabMoveDialog({Key? key, required this.row, required this.index}) : super(key: key); + const TabMoveDialog({ + required this.row, + required this.index, + super.key, + }); + final Widget row; final int index; @@ -30,7 +35,7 @@ class _TabMoveDialogState extends State { return SettingsDialog( contentItems: [ SizedBox(width: double.maxFinite, child: widget.row), - // + // const SizedBox(height: 10), ListTile( shape: RoundedRectangleBorder( @@ -45,7 +50,7 @@ class _TabMoveDialogState extends State { leading: const Icon(Icons.vertical_align_top), title: const Text('To Top'), ), - // + // const SizedBox(height: 10), ListTile( shape: RoundedRectangleBorder( @@ -60,21 +65,21 @@ class _TabMoveDialogState extends State { leading: const Icon(Icons.vertical_align_bottom), title: const Text('To Bottom'), ), - // + // const SizedBox(height: 30), SettingsTextInput( - title: "Tab Number", - hintText: "Tab Number", + title: 'Tab Number', + hintText: 'Tab Number', onlyInput: true, controller: indexController, - inputType: const TextInputType.numberWithOptions(signed: false, decimal: false), + inputType: TextInputType.number, numberButtons: true, resetText: () => (widget.index + 1).toString(), numberStep: 1, numberMin: 1, numberMax: searchHandler.total.toDouble(), onChanged: (String value) { - setState(() { }); + setState(() {}); }, ), ListTile( @@ -84,28 +89,28 @@ class _TabMoveDialogState extends State { ), onTap: () async { final int? enteredIndex = int.tryParse(indexController.text); - if(enteredIndex == null) { + if (enteredIndex == null) { return await FlashElements.showSnackbar( - title: const Text("Invalid Tab Number"), - content: Column( + title: const Text('Invalid Tab Number'), + content: const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text("Invalid Input"), + children: [ + Text('Invalid Input'), SizedBox(height: 10), - Text("Please enter a valid tab number"), + Text('Please enter a valid tab number'), ], ), ); } else { - if(enteredIndex < 0 || enteredIndex > searchHandler.total) { + if (enteredIndex < 0 || enteredIndex > searchHandler.total) { return await FlashElements.showSnackbar( - title: const Text("Invalid Tab Number"), - content: Column( + title: const Text('Invalid Tab Number'), + content: const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text("Out of range"), + children: [ + Text('Out of range'), SizedBox(height: 10), - Text("Please enter a valid tab number"), + Text('Please enter a valid tab number'), ], ), ); @@ -122,7 +127,7 @@ class _TabMoveDialogState extends State { leading: const Icon(Icons.vertical_align_center), title: const Text('To Set Number'), ), - // + // const SizedBox(height: 10), const Text('Preview:'), const SizedBox(height: 10), @@ -137,10 +142,10 @@ class _TabMoveDialogState extends State { class TabMovePreview extends StatelessWidget { const TabMovePreview({ - Key? key, required this.index, required this.indexController, - }) : super(key: key); + super.key, + }); final int index; final TextEditingController indexController; @@ -151,9 +156,9 @@ class TabMovePreview extends StatelessWidget { int enteredIndex = int.tryParse(indexController.text) ?? index; - if(enteredIndex < 1) { + if (enteredIndex < 1) { enteredIndex = index; - } else if(enteredIndex > searchHandler.total) { + } else if (enteredIndex > searchHandler.total) { enteredIndex = index; } @@ -168,30 +173,35 @@ class TabMovePreview extends StatelessWidget { final SearchTab lastTab = searchHandler.getTabByIndex(searchHandler.total - 1)!; final bool showFirst = enteredIndex > 2; - final bool showFirstDots = showFirst && (enteredIndex > 1) && (enteredIndex - 1 > 2); // is first tab shown and entered number is bigger than 2 and possible prev tab number is bigger than 2 + final bool showFirstDots = showFirst && + (enteredIndex > 1) && + (enteredIndex - 1 > 2); // is first tab shown and entered number is bigger than 2 and possible prev tab number is bigger than 2 final bool showLast = enteredIndex < searchHandler.total - 1; - final bool showLastDots = showLast && (enteredIndex < searchHandler.total) && (enteredIndex + 1 < searchHandler.total - 1); // is last tab shown and entered number is smaller than total and possible next tab number is smaller than total - 1 + final bool showLastDots = showLast && + (enteredIndex < searchHandler.total) && + (enteredIndex + 1 < + searchHandler.total - 1); // is last tab shown and entered number is smaller than total and possible next tab number is smaller than total - 1 return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if(showFirst) + if (showFirst) Container( margin: const EdgeInsets.only(left: 10, bottom: 10), child: ElevatedButton( child: Text('#1 - ${firstTab.tags}'), onPressed: () { - indexController.text = "1"; + indexController.text = '1'; }, ), ), - if(showFirstDots) + if (showFirstDots) Container( margin: const EdgeInsets.only(left: 10, bottom: 5), - child: const Text("..."), + child: const Text('...'), ), - // - if(prevTab != null) + // + if (prevTab != null) Container( margin: const EdgeInsets.only(left: 10, bottom: 10), child: ElevatedButton( @@ -201,14 +211,14 @@ class TabMovePreview extends StatelessWidget { }, ), ), - // + // Container( margin: const EdgeInsets.only(left: 10, bottom: 10), child: ElevatedButton( style: Theme.of(context).elevatedButtonTheme.style?.copyWith( - backgroundColor: MaterialStateProperty.all(Colors.transparent), - side: MaterialStateProperty.all(BorderSide(color: Theme.of(context).colorScheme.secondary, width: 2)), - ), + backgroundColor: MaterialStateProperty.all(Colors.transparent), + side: MaterialStateProperty.all(BorderSide(color: Theme.of(context).colorScheme.secondary, width: 2)), + ), child: Text( '#$enteredIndex - ${currentTab.tags}', ), @@ -217,8 +227,8 @@ class TabMovePreview extends StatelessWidget { }, ), ), - // - if(nextTab != null) + // + if (nextTab != null) Container( margin: const EdgeInsets.only(left: 10, bottom: 10), child: ElevatedButton( @@ -228,13 +238,13 @@ class TabMovePreview extends StatelessWidget { }, ), ), - // - if(showLastDots) + // + if (showLastDots) Container( margin: const EdgeInsets.only(left: 10, bottom: 5), - child: const Text("..."), + child: const Text('...'), ), - if(showLast) + if (showLast) Container( margin: const EdgeInsets.only(left: 10), child: ElevatedButton( @@ -244,8 +254,7 @@ class TabMovePreview extends StatelessWidget { }, ), ), - ], ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/tabs/tab_row.dart b/lib/src/widgets/tabs/tab_row.dart index f6a7df5c..c99fce14 100644 --- a/lib/src/widgets/tabs/tab_row.dart +++ b/lib/src/widgets/tabs/tab_row.dart @@ -10,12 +10,12 @@ import 'package:lolisnatcher/src/widgets/image/favicon.dart'; class TabRow extends StatelessWidget { const TabRow({ - Key? key, required this.tab, this.color, this.fontWeight, this.withFavicon = true, - }) : super(key: key); + super.key, + }); final SearchTab tab; final Color? color; @@ -27,8 +27,8 @@ class TabRow extends StatelessWidget { return Obx(() { // print(tab.tags); final int totalCount = tab.booruHandler.totalCount.value; - final String totalCountText = (totalCount > 0) ? " ($totalCount)" : ""; - final String multiText = (tab.secondaryBoorus?.isNotEmpty ?? false) ? " [M]" : ""; + final String totalCountText = (totalCount > 0) ? ' ($totalCount)' : ''; + final String multiText = (tab.secondaryBoorus?.isNotEmpty ?? false) ? ' [M]' : ''; final String tagText = "${tab.tags == "" ? "[No Tags]" : tab.tags}$totalCountText$multiText".trim(); final bool hasItems = tab.booruHandler.filteredFetched.isNotEmpty; @@ -51,7 +51,7 @@ class TabRow extends StatelessWidget { fontSize: 16, fontStyle: hasItems ? FontStyle.normal : FontStyle.italic, fontWeight: fontWeight ?? FontWeight.normal, - color: color ?? (tab.tags == "" ? Colors.grey : null), + color: color ?? (tab.tags == '' ? Colors.grey : null), ), ], ), diff --git a/lib/src/widgets/tabs/tab_selector.dart b/lib/src/widgets/tabs/tab_selector.dart index 27a8f947..851cc959 100644 --- a/lib/src/widgets/tabs/tab_selector.dart +++ b/lib/src/widgets/tabs/tab_selector.dart @@ -9,7 +9,7 @@ import 'package:lolisnatcher/src/widgets/tabs/tab_manager_dialog.dart'; import 'package:lolisnatcher/src/widgets/tabs/tab_row.dart'; class TabSelector extends StatelessWidget { - const TabSelector({Key? key}) : super(key: key); + const TabSelector({super.key}); @override Widget build(BuildContext context) { @@ -28,7 +28,7 @@ class TabSelector extends StatelessWidget { } class TabSelectorHeader extends StatelessWidget { - const TabSelectorHeader({Key? key}) : super(key: key); + const TabSelectorHeader({super.key}); @override Widget build(BuildContext context) { @@ -52,13 +52,13 @@ class TabSelectorHeader extends StatelessWidget { class TabSelectorRender extends StatelessWidget { const TabSelectorRender({ - Key? key, required this.isDesktop, required this.padding, required this.contentPadding, this.borderColor, this.textColor, - }) : super(key: key); + super.key, + }); final bool isDesktop; final EdgeInsetsGeometry padding; @@ -66,8 +66,8 @@ class TabSelectorRender extends StatelessWidget { final Color? borderColor; final Color? textColor; - Future openTabsDialog(context) async { - return await SettingsPageOpen( + Future openTabsDialog(BuildContext context) { + return SettingsPageOpen( context: context, page: () => const TabManagerDialog(), ).open(); @@ -82,8 +82,8 @@ class TabSelectorRender extends StatelessWidget { return Container( padding: padding, child: Obx(() { - List list = searchHandler.list; - int index = searchHandler.currentIndex; + final List list = searchHandler.list; + final int index = searchHandler.currentIndex; if (list.isEmpty) { return const SizedBox(); @@ -130,7 +130,7 @@ class TabSelectorRender extends StatelessWidget { }, selectedItemBuilder: (BuildContext context) { return list.map>((SearchTab value) { - bool isCurrent = list.indexOf(value) == index; + final bool isCurrent = list.indexOf(value) == index; return DropdownMenuItem( value: value, @@ -139,7 +139,7 @@ class TabSelectorRender extends StatelessWidget { }).toList(); }, items: list.map>((SearchTab value) { - bool isCurrent = list.indexOf(value) == index; + final bool isCurrent = list.indexOf(value) == index; return DropdownMenuItem( value: value, diff --git a/lib/src/widgets/tags_filters/tf_add_dialog.dart b/lib/src/widgets/tags_filters/tf_add_dialog.dart index 0b113cb7..4fa6fa89 100644 --- a/lib/src/widgets/tags_filters/tf_add_dialog.dart +++ b/lib/src/widgets/tags_filters/tf_add_dialog.dart @@ -7,10 +7,10 @@ import 'package:lolisnatcher/src/widgets/tags_filters/tf_list_item.dart'; class TagsFiltersAddDialog extends StatefulWidget { const TagsFiltersAddDialog({ - Key? key, required this.onAdd, required this.tagFilterType, - }) : super(key: key); + super.key, + }); final Function(String) onAdd; final String tagFilterType; @@ -29,7 +29,7 @@ class _TagsFiltersAddDialogState extends State { } else { FlashElements.showSnackbar( context: context, - title: const Text("Empty input!", style: TextStyle(fontSize: 20)), + title: const Text('Empty input!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -55,8 +55,8 @@ class _TagsFiltersAddDialogState extends State { Container( margin: const EdgeInsets.symmetric(vertical: 20), child: SettingsTextInput( - title: "New ${widget.tagFilterType} Tag Filter", - hintText: "New Filter", + title: 'New ${widget.tagFilterType} Tag Filter', + hintText: 'New Filter', onlyInput: true, controller: _controller, autofocus: true, diff --git a/lib/src/widgets/tags_filters/tf_edit_dialog.dart b/lib/src/widgets/tags_filters/tf_edit_dialog.dart index d8075563..0d385d0c 100644 --- a/lib/src/widgets/tags_filters/tf_edit_dialog.dart +++ b/lib/src/widgets/tags_filters/tf_edit_dialog.dart @@ -7,11 +7,11 @@ import 'package:lolisnatcher/src/widgets/tags_filters/tf_list_item.dart'; class TagsFiltersEditDialog extends StatefulWidget { const TagsFiltersEditDialog({ - Key? key, required this.tag, required this.onEdit, required this.onDelete, - }) : super(key: key); + super.key, + }); final String tag; final Function(String) onEdit; @@ -37,7 +37,7 @@ class _TagsFiltersEditDialogState extends State { } else { FlashElements.showSnackbar( context: context, - title: const Text("Empty input!", style: TextStyle(fontSize: 20)), + title: const Text('Empty input!', style: TextStyle(fontSize: 20)), leadingIcon: Icons.warning_amber, leadingIconColor: Colors.red, sideColor: Colors.red, @@ -67,8 +67,8 @@ class _TagsFiltersEditDialogState extends State { Container( margin: const EdgeInsets.symmetric(vertical: 20), child: SettingsTextInput( - title: "Edit Filter", - hintText: "Edit Filter", + title: 'Edit Filter', + hintText: 'Edit Filter', onlyInput: true, controller: _controller, autofocus: false, diff --git a/lib/src/widgets/tags_filters/tf_list.dart b/lib/src/widgets/tags_filters/tf_list.dart index 8d012cb3..ccd056d4 100644 --- a/lib/src/widgets/tags_filters/tf_list.dart +++ b/lib/src/widgets/tags_filters/tf_list.dart @@ -6,7 +6,6 @@ import 'package:lolisnatcher/src/widgets/tags_filters/tf_list_item.dart'; class TagsFiltersList extends StatelessWidget { const TagsFiltersList({ - Key? key, required this.tagsList, required this.filterTagsType, required this.onTagSelected, @@ -14,7 +13,8 @@ class TagsFiltersList extends StatelessWidget { required this.tagSearchController, required this.onSearchTextChanged, required this.openAddDialog, - }) : super(key: key); + super.key, + }); final List tagsList; final String filterTagsType; @@ -29,7 +29,7 @@ class TagsFiltersList extends StatelessWidget { @override Widget build(BuildContext context) { final int originalCount = tagsList.length; - List filteredTagsList = tagsList.where((String tag) { + final List filteredTagsList = tagsList.where((String tag) { if (!isSearchActive) { return true; } @@ -68,11 +68,11 @@ class TagsFiltersList extends StatelessWidget { scrollDirection: Axis.vertical, physics: getListPhysics(), // const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), itemBuilder: (BuildContext context, int index) { - String currentEntry = filteredTagsList[index]; + final String currentEntry = filteredTagsList[index]; return TagsFiltersListItem( tag: currentEntry, - onTap: (String tag) => onTagSelected(tag), + onTap: onTagSelected, ); }, ), diff --git a/lib/src/widgets/tags_filters/tf_list_item.dart b/lib/src/widgets/tags_filters/tf_list_item.dart index 4dc89235..06f773a3 100644 --- a/lib/src/widgets/tags_filters/tf_list_item.dart +++ b/lib/src/widgets/tags_filters/tf_list_item.dart @@ -5,11 +5,11 @@ import 'package:lolisnatcher/src/widgets/common/marquee_text.dart'; class TagsFiltersListItem extends StatelessWidget { const TagsFiltersListItem({ - Key? key, required this.tag, this.onTap, this.overrideIcon, - }) : super(key: key); + super.key, + }); final String tag; final Function(String)? onTap; diff --git a/lib/src/widgets/tags_filters/tf_settings_list.dart b/lib/src/widgets/tags_filters/tf_settings_list.dart index f36687e8..4a9bc7ff 100644 --- a/lib/src/widgets/tags_filters/tf_settings_list.dart +++ b/lib/src/widgets/tags_filters/tf_settings_list.dart @@ -5,13 +5,13 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class TagsFiltersSettingsList extends StatelessWidget { const TagsFiltersSettingsList({ - Key? key, required this.scrollController, required this.filterHated, required this.onFilterHatedChanged, required this.filterFavourites, required this.onFilterFavouritesChanged, - }) : super(key: key); + super.key, + }); final ScrollController scrollController; final bool filterHated; @@ -27,13 +27,13 @@ class TagsFiltersSettingsList extends StatelessWidget { const SettingsButton(name: '', enabled: false), // SettingsToggle( - title: "Remove Items with Hated Tags", + title: 'Remove Items with Hated Tags', value: filterHated, onChanged: onFilterHatedChanged, trailingIcon: const Icon(CupertinoIcons.eye_slash), ), SettingsToggle( - title: "Remove Favourited Items", + title: 'Remove Favourited Items', value: filterFavourites, onChanged: onFilterFavouritesChanged, trailingIcon: const Icon(Icons.favorite, color: Colors.red), diff --git a/lib/src/widgets/tags_manager/tm_add_dialog.dart b/lib/src/widgets/tags_manager/tm_add_dialog.dart index 08926413..d95ba60b 100644 --- a/lib/src/widgets/tags_manager/tm_add_dialog.dart +++ b/lib/src/widgets/tags_manager/tm_add_dialog.dart @@ -6,7 +6,7 @@ import 'package:lolisnatcher/src/widgets/common/cancel_button.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; class TagsManagerAddDialog extends StatefulWidget { - const TagsManagerAddDialog({Key? key}) : super(key: key); + const TagsManagerAddDialog({super.key}); @override State createState() => _TagsManagerAddDialogState(); @@ -19,11 +19,11 @@ class _TagsManagerAddDialogState extends State { @override Widget build(BuildContext context) { return SettingsDialog( - title: const Text("Add Tag"), + title: const Text('Add Tag'), contentItems: [ SettingsTextInput( controller: _controller, - title: "Name", + title: 'Name', drawBottomBorder: false, ), SettingsDropdown( @@ -41,15 +41,17 @@ class _TagsManagerAddDialogState extends State { actionButtons: [ const CancelButton(returnData: null), ElevatedButton.icon( - label: const Text("Add"), + label: const Text('Add'), icon: const Icon(Icons.add), onPressed: () { final String tagName = _controller.text.trim(); if (tagName.isNotEmpty) { - Navigator.of(context).pop(Tag( - tagName, - tagType: _type, - )); + Navigator.of(context).pop( + Tag( + tagName, + tagType: _type, + ), + ); } else { Navigator.of(context).pop(null); } diff --git a/lib/src/widgets/tags_manager/tm_dialog.dart b/lib/src/widgets/tags_manager/tm_dialog.dart index 623260ca..91e482ff 100644 --- a/lib/src/widgets/tags_manager/tm_dialog.dart +++ b/lib/src/widgets/tags_manager/tm_dialog.dart @@ -12,7 +12,7 @@ import 'package:lolisnatcher/src/widgets/tags_manager/tm_list_item.dart'; import 'package:lolisnatcher/src/widgets/tags_manager/tm_list_item_dialog.dart'; class TagsManagerDialog extends StatefulWidget { - const TagsManagerDialog({Key? key}) : super(key: key); + const TagsManagerDialog({super.key}); @override State createState() => _TagsManagerDialogState(); @@ -115,7 +115,7 @@ class _TagsManagerDialogState extends State { ); } - void showAddDialog() async { + Future showAddDialog() async { final Tag? tag = await showDialog( context: context, builder: (context) { @@ -135,7 +135,7 @@ class _TagsManagerDialogState extends State { return SettingsPageDialog( title: const Text('Tags'), fab: Padding( - padding: const EdgeInsets.only(bottom: 40.0), + padding: const EdgeInsets.only(bottom: 40), child: FloatingActionButton( onPressed: showAddDialog, child: const Icon(Icons.add), diff --git a/lib/src/widgets/tags_manager/tm_list.dart b/lib/src/widgets/tags_manager/tm_list.dart index 1582af8b..cbffaec9 100644 --- a/lib/src/widgets/tags_manager/tm_list.dart +++ b/lib/src/widgets/tags_manager/tm_list.dart @@ -7,18 +7,17 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:lolisnatcher/src/data/tag.dart'; import 'package:lolisnatcher/src/widgets/common/custom_scroll_bar_thumb.dart'; -import 'package:lolisnatcher/src/widgets/desktop/desktop_scroll_wrap.dart'; import 'package:lolisnatcher/src/widgets/tags_manager/tm_list_item.dart'; class TagsManagerList extends StatelessWidget { const TagsManagerList({ - Key? key, required this.tags, required this.selected, required this.onRefresh, required this.onTap, required this.onSelect, - }) : super(key: key); + super.key, + }); final List tags; final List selected; @@ -26,7 +25,7 @@ class TagsManagerList extends StatelessWidget { final void Function(Tag) onTap; final void Function(bool?, Tag) onSelect; - static const int PAGE_SIZE = 12; + static const int defaultPageSize = 12; Future> _loadPage(int page, int pageSize) async { final int start = page * pageSize; @@ -75,15 +74,16 @@ class TagsManagerList extends StatelessWidget { // child: Text('Error loading tags'), // ); // }, - pageFuture: (page) => _loadPage(page, TagsManagerList.PAGE_SIZE), - pageSize: TagsManagerList.PAGE_SIZE, - thumbBuilder: (Color backgroundColor, Color drawColor, double height, int index, bool alwaysVisibleScrollThumb, Animation thumbAnimation) { - Tag tag = tags[index]; + pageFuture: (page) => _loadPage(page, TagsManagerList.defaultPageSize), + pageSize: TagsManagerList.defaultPageSize, + thumbBuilder: + (Color backgroundColor, Color drawColor, double height, int index, bool alwaysVisibleScrollThumb, Animation thumbAnimation) { + final Tag tag = tags[index]; return CustomScrollBarThumb( backgroundColor: backgroundColor, drawColor: drawColor, height: height * 1.2, // 48 - title: '${tag.tagType.toString()} [${tag.fullString[0]}]', + title: '${tag.tagType} [${tag.fullString[0]}]', ); }, thumbBackgroundColor: Theme.of(context).colorScheme.surface, @@ -95,53 +95,5 @@ class TagsManagerList extends StatelessWidget { ), ), ); - - final ScrollController scrollController = ScrollController(); - return Expanded( - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Material( - child: SizedBox( - width: double.maxFinite, - child: Scrollbar( - controller: scrollController, - child: RefreshIndicator( - triggerMode: RefreshIndicatorTriggerMode.anywhere, - displacement: 80, - strokeWidth: 4, - color: Theme.of(context).colorScheme.secondary, - onRefresh: onRefresh, - child: DesktopScrollWrap( - controller: scrollController, - child: ListView.builder( - padding: const EdgeInsets.fromLTRB(10, 5, 10, 5), - controller: scrollController, - physics: getListPhysics(), // const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - shrinkWrap: false, - itemCount: tags.length, - scrollDirection: Axis.vertical, - itemBuilder: (BuildContext context, int index) => Row( - key: Key(index.toString()), - children: [ - Expanded( - child: TagsManagerListItem( - tag: tags[index], - isSelected: selected.contains(tags[index]), - onSelect: (bool? value) => onSelect(value, tags[index]), - onTap: () { - onTap(tags[index]); - }, - ), - ), - ], - ), - ), - ), - ), - ), - ), - ), - ), - ); } } diff --git a/lib/src/widgets/tags_manager/tm_list_bottom.dart b/lib/src/widgets/tags_manager/tm_list_bottom.dart index 5a1175f0..ef97211c 100644 --- a/lib/src/widgets/tags_manager/tm_list_bottom.dart +++ b/lib/src/widgets/tags_manager/tm_list_bottom.dart @@ -6,16 +6,15 @@ import 'package:lolisnatcher/src/widgets/common/cancel_button.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; import 'package:lolisnatcher/src/widgets/tags_manager/tm_list_item.dart'; - class TagsManagerListBottom extends StatelessWidget { const TagsManagerListBottom({ - Key? key, required this.selected, required this.isFilterActive, required this.onSelectAll, required this.onDeselectAll, required this.onDelete, - }) : super(key: key); + super.key, + }); final List selected; final bool isFilterActive; @@ -34,7 +33,7 @@ class TagsManagerListBottom extends StatelessWidget { margin: const EdgeInsets.all(10), child: ElevatedButton.icon( icon: const Icon(Icons.select_all), - label: const Text("Select all"), + label: const Text('Select all'), onPressed: onSelectAll, ), ), @@ -60,7 +59,7 @@ class TagsManagerListBottom extends StatelessWidget { } final Widget deleteDialog = SettingsDialog( - title: const Text("Delete Tags"), + title: const Text('Delete Tags'), scrollable: false, content: SizedBox( width: double.maxFinite, @@ -71,14 +70,14 @@ class TagsManagerListBottom extends StatelessWidget { const SizedBox(height: 10), ...selected.map((Tag entry) { return TagsManagerListItem(tag: entry); - }).toList(), + }), ], ), ), actionButtons: [ const CancelButton(), ElevatedButton.icon( - label: const Text("Delete"), + label: const Text('Delete'), icon: const Icon(Icons.delete_forever), onPressed: onDelete, ), @@ -97,7 +96,7 @@ class TagsManagerListBottom extends StatelessWidget { Expanded( child: ElevatedButton.icon( icon: const Icon(Icons.border_clear), - label: const Text("Clear selection"), + label: const Text('Clear selection'), onPressed: onDeselectAll, ), ), diff --git a/lib/src/widgets/tags_manager/tm_list_filter.dart b/lib/src/widgets/tags_manager/tm_list_filter.dart index 3ade715b..bd848b7c 100644 --- a/lib/src/widgets/tags_manager/tm_list_filter.dart +++ b/lib/src/widgets/tags_manager/tm_list_filter.dart @@ -2,14 +2,13 @@ import 'package:flutter/material.dart'; import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; - class TagsManagerListFilter extends StatelessWidget { const TagsManagerListFilter({ - Key? key, required this.title, required this.controller, required this.onChanged, - }) : super(key: key); + super.key, + }); final String title; final TextEditingController controller; diff --git a/lib/src/widgets/tags_manager/tm_list_item.dart b/lib/src/widgets/tags_manager/tm_list_item.dart index 9c0d7a02..e85c6b69 100644 --- a/lib/src/widgets/tags_manager/tm_list_item.dart +++ b/lib/src/widgets/tags_manager/tm_list_item.dart @@ -5,12 +5,12 @@ import 'package:lolisnatcher/src/widgets/common/marquee_text.dart'; class TagsManagerListItem extends StatelessWidget { const TagsManagerListItem({ - Key? key, required this.tag, this.isSelected = false, this.onSelect, this.onTap, - }) : super(key: key); + super.key, + }); final Tag tag; final bool isSelected; @@ -19,8 +19,8 @@ class TagsManagerListItem extends StatelessWidget { @override Widget build(BuildContext context) { - bool isStale = tag.updatedAt < DateTime.now().subtract(const Duration(days: 3)).millisecondsSinceEpoch; - String staleText = isStale ? " (stale)" : ""; + final bool isStale = tag.updatedAt < DateTime.now().subtract(const Duration(days: 3)).millisecondsSinceEpoch; + final String staleText = isStale ? ' (stale)' : ''; return Container( margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), @@ -56,7 +56,7 @@ class TagsManagerListItem extends StatelessWidget { fontWeight: FontWeight.bold, isExpanded: false, ), - subtitle: Text('${tag.tagType.toString()} $staleText'.trim()), + subtitle: Text('${tag.tagType} $staleText'.trim()), ), ); } diff --git a/lib/src/widgets/tags_manager/tm_list_item_dialog.dart b/lib/src/widgets/tags_manager/tm_list_item_dialog.dart index 37a9d743..cf5ae0be 100644 --- a/lib/src/widgets/tags_manager/tm_list_item_dialog.dart +++ b/lib/src/widgets/tags_manager/tm_list_item_dialog.dart @@ -12,14 +12,14 @@ import 'package:lolisnatcher/src/widgets/tags_manager/tm_list_item.dart'; class TagsManagerListItemDialog extends StatefulWidget { const TagsManagerListItemDialog({ - Key? key, required this.tag, required this.onDelete, required this.onChangedType, required this.onSetStale, required this.onResetStale, required this.onSetUnstaleable, - }) : super(key: key); + super.key, + }); final Tag tag; final void Function() onDelete; @@ -40,7 +40,7 @@ class _TagsManagerListItemDialogState extends State { @override Widget build(BuildContext context) { - String staleText = DateTime.fromMillisecondsSinceEpoch(widget.tag.updatedAt).add(const Duration(milliseconds: Constants.tagStaleTime)).toString(); + final String staleText = DateTime.fromMillisecondsSinceEpoch(widget.tag.updatedAt).add(const Duration(milliseconds: Constants.tagStaleTime)).toString(); return SettingsDialog( contentItems: [ @@ -74,7 +74,7 @@ class _TagsManagerListItemDialogState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Added a tab!", style: TextStyle(fontSize: 20)), + title: const Text('Added a tab!', style: TextStyle(fontSize: 20)), content: Text(widget.tag.fullString, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.copy, sideColor: Colors.green, @@ -97,7 +97,7 @@ class _TagsManagerListItemDialogState extends State { FlashElements.showSnackbar( context: context, duration: const Duration(seconds: 2), - title: const Text("Copied to clipboard!", style: TextStyle(fontSize: 20)), + title: const Text('Copied to clipboard!', style: TextStyle(fontSize: 20)), content: Text(widget.tag.fullString, style: const TextStyle(fontSize: 16)), leadingIcon: Icons.copy, sideColor: Colors.green, @@ -119,7 +119,7 @@ class _TagsManagerListItemDialogState extends State { // leading: Icon(Icons.delete_forever, color: Theme.of(context).errorColor), // title: const Text('Delete'), // ), - // + // const SizedBox(height: 10), ListTile( shape: RoundedRectangleBorder( @@ -130,7 +130,7 @@ class _TagsManagerListItemDialogState extends State { leading: const Icon(Icons.timer_off), title: const Text('Set Stale'), ), - // + // const SizedBox(height: 10), ListTile( shape: RoundedRectangleBorder( @@ -141,7 +141,7 @@ class _TagsManagerListItemDialogState extends State { leading: const Icon(Icons.restore), title: const Text('Reset Stale'), ), - // + // const SizedBox(height: 10), ListTile( shape: RoundedRectangleBorder( diff --git a/lib/src/widgets/thumbnail/thumbnail.dart b/lib/src/widgets/thumbnail/thumbnail.dart index c013646b..32fbc848 100644 --- a/lib/src/widgets/thumbnail/thumbnail.dart +++ b/lib/src/widgets/thumbnail/thumbnail.dart @@ -20,11 +20,11 @@ import 'package:lolisnatcher/src/widgets/preview/shimmer_builder.dart'; class Thumbnail extends StatefulWidget { const Thumbnail({ - Key? key, required this.item, required this.isStandalone, this.ignoreColumnsCount = false, - }) : super(key: key); + super.key, + }); final BooruItem item; @@ -86,7 +86,7 @@ class _ThumbnailState extends State { // } cancelToken ??= CancelToken(); - ImageProvider provider = CustomNetworkImage( + final ImageProvider provider = CustomNetworkImage( isMain ? thumbURL : widget.item.thumbnailURL, cancelToken: cancelToken, headers: await Tools.getFileCustomHeaders(searchHandler.currentBooru, checkForReferer: true), @@ -111,7 +111,7 @@ class _ThumbnailState extends State { final MediaQueryData mQuery = NavigationHandler.instance.navigatorKey.currentContext!.mediaQuery; final double widthLimit = (mQuery.size.width / columnsCount()) * mQuery.devicePixelRatio * 1; double thumbRatio = 1; - bool hasSizeData = widget.item.fileHeight != null && widget.item.fileWidth != null; + final bool hasSizeData = widget.item.fileHeight != null && widget.item.fileWidth != null; if (widget.isStandalone) { thumbWidth = widthLimit; @@ -172,7 +172,7 @@ class _ThumbnailState extends State { if (restartedCount < 5) { // attempt to reload 5 times with a second delay Debounce.debounce( - tag: 'thumbnail_reload_${searchHandler.currentTab.id.toString()}#${searchHandler.getItemIndex(widget.item)}', + tag: 'thumbnail_reload_${searchHandler.currentTab.id}#${searchHandler.getItemIndex(widget.item)}', callback: () { restartLoading(); restartedCount++; @@ -211,10 +211,10 @@ class _ThumbnailState extends State { startedAt.value = DateTime.now().millisecondsSinceEpoch; // if scaling is disabled - allow gifs as thumbnails, but only if they are not hated (resize image doesnt work with gifs) - final bool isGifSampleNotAllowed = - widget.item.mediaType.value.isAnimation && ((settingsHandler.disableImageScaling && settingsHandler.gifsAsThumbnails) ? widget.item.isHated.value : true); + final bool isGifSampleNotAllowed = widget.item.mediaType.value.isAnimation && + ((settingsHandler.disableImageScaling && settingsHandler.gifsAsThumbnails) ? widget.item.isHated.value : true); - isThumbQuality = settingsHandler.previewMode == "Thumbnail" || + isThumbQuality = settingsHandler.previewMode == 'Thumbnail' || (isGifSampleNotAllowed || widget.item.mediaType.value.isVideo || widget.item.mediaType.value.isNeedsExtraRequest) || (!widget.isStandalone && widget.item.fileURL == widget.item.sampleURL); thumbURL = isThumbQuality == true ? widget.item.thumbnailURL : widget.item.sampleURL; @@ -231,21 +231,19 @@ class _ThumbnailState extends State { // delay loading a little to improve performance when scrolling fast, ignore delay if it's a standalone widget (i.e. not in a list) Debounce.debounce( - tag: 'thumbnail_start_${searchHandler.currentTab.id.toString()}#${searchHandler.getItemIndex(widget.item)}', - callback: () { - startDownloading(); - }, + tag: 'thumbnail_start_${searchHandler.currentTab.id}#${searchHandler.getItemIndex(widget.item)}', + callback: startDownloading, duration: Duration(milliseconds: widget.isStandalone ? 200 : 0), ); return; } - void startDownloading() async { + Future startDownloading() async { final bool useExtra = isThumbQuality == false && !widget.item.isHated.value; mainProvider = await getImageProvider(true); mainImageStream?.removeListener(mainImageListener!); - mainImageStream = mainProvider!.resolve(const ImageConfiguration()); + mainImageStream = mainProvider!.resolve(ImageConfiguration.empty); mainImageListener = ImageStreamListener( (imageInfo, syncCall) { isLoaded = true; @@ -269,7 +267,7 @@ class _ThumbnailState extends State { if (useExtra) { extraProvider = await getImageProvider(false); extraImageStream?.removeListener(extraImageListener!); - extraImageStream = extraProvider!.resolve(const ImageConfiguration()); + extraImageStream = extraProvider!.resolve(ImageConfiguration.empty); extraImageListener = ImageStreamListener( (imageInfo, syncCall) { isLoadedExtra = true; @@ -315,15 +313,15 @@ class _ThumbnailState extends State { selectThumbProvider(); } - void cleanProviderCache() async { + Future cleanProviderCache() async { if (mainProvider != null) { final CustomNetworkImage usedMainProvider = - (mainProvider is ResizeImage ? (mainProvider as ResizeImage).imageProvider : mainProvider) as CustomNetworkImage; + (mainProvider is ResizeImage ? (mainProvider! as ResizeImage).imageProvider : mainProvider!) as CustomNetworkImage; await usedMainProvider.deleteCacheFile(); } if (extraProvider != null) { final CustomNetworkImage usedExtraProvider = - (extraProvider is ResizeImage ? (extraProvider as ResizeImage).imageProvider : extraProvider) as CustomNetworkImage; + (extraProvider is ResizeImage ? (extraProvider! as ResizeImage).imageProvider : extraProvider!) as CustomNetworkImage; await usedExtraProvider.deleteCacheFile(); } } @@ -359,8 +357,8 @@ class _ThumbnailState extends State { extraProvider = null; } - Debounce.cancel('thumbnail_start_${searchHandler.currentTab.id.toString()}#${searchHandler.getItemIndex(widget.item)}'); - Debounce.cancel('thumbnail_reload_${searchHandler.currentTab.id.toString()}#${searchHandler.getItemIndex(widget.item)}'); + Debounce.cancel('thumbnail_start_${searchHandler.currentTab.id}#${searchHandler.getItemIndex(widget.item)}'); + Debounce.cancel('thumbnail_reload_${searchHandler.currentTab.id}#${searchHandler.getItemIndex(widget.item)}'); } Widget renderImages(BuildContext context) { @@ -405,10 +403,7 @@ class _ThumbnailState extends State { }, ), ) - : const SizedBox( - width: double.infinity, - height: double.infinity, - ), + : const SizedBox.expand(), ), ), AnimatedOpacity( @@ -441,10 +436,7 @@ class _ThumbnailState extends State { }, ), ) - : const SizedBox( - width: double.infinity, - height: double.infinity, - ), + : const SizedBox.expand(), ), ), AnimatedSwitcher( @@ -474,16 +466,17 @@ class _ThumbnailState extends State { ), if (widget.item.isHated.value) Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), - borderRadius: BorderRadius.circular(iconSize * 0.1), - ), - width: iconSize, - height: iconSize, - child: const Icon(CupertinoIcons.eye_slash, color: Colors.white)), + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: BorderRadius.circular(iconSize * 0.1), + ), + width: iconSize, + height: iconSize, + child: const Icon(CupertinoIcons.eye_slash, color: Colors.white), + ), if (settingsHandler.showURLOnThumb) - Container( + ColoredBox( color: Colors.black, child: Text(thumbURL), ), @@ -505,7 +498,7 @@ class _ThumbnailState extends State { ); } else { // print('building thumb ${searchHandler.getItemIndex(widget.item)}'); - return Container(color: Colors.black, child: renderImages(context)); + return ColoredBox(color: Colors.black, child: renderImages(context)); } } } diff --git a/lib/src/widgets/thumbnail/thumbnail_build.dart b/lib/src/widgets/thumbnail/thumbnail_build.dart index 4b9976c1..9f08326c 100644 --- a/lib/src/widgets/thumbnail/thumbnail_build.dart +++ b/lib/src/widgets/thumbnail/thumbnail_build.dart @@ -42,7 +42,7 @@ class ThumbnailBuild extends StatelessWidget { return ClipRRect( borderRadius: BorderRadius.circular(4), child: Stack( - alignment: settingsHandler.previewDisplay == "Square" ? Alignment.center : Alignment.bottomCenter, + alignment: settingsHandler.previewDisplay == 'Square' ? Alignment.center : Alignment.bottomCenter, children: [ Thumbnail( item: item, diff --git a/lib/src/widgets/thumbnail/thumbnail_card_build.dart b/lib/src/widgets/thumbnail/thumbnail_card_build.dart index 2c143257..064e99f8 100644 --- a/lib/src/widgets/thumbnail/thumbnail_card_build.dart +++ b/lib/src/widgets/thumbnail/thumbnail_card_build.dart @@ -12,15 +12,14 @@ import 'package:lolisnatcher/src/widgets/thumbnail/thumbnail_build.dart'; class ThumbnailCardBuild extends StatelessWidget { const ThumbnailCardBuild({ - Key? key, required this.index, required this.item, this.onTap, this.onDoubleTap, this.onLongPress, this.onSecondaryTap, - - }) : super(key: key); + super.key, + }); final int index; final BooruItem item; @@ -36,8 +35,8 @@ class ThumbnailCardBuild extends StatelessWidget { // print('ThumbnailCardBuild: $index'); return Obx(() { - bool isSelected = searchHandler.currentTab.selected.contains(index); - bool isCurrent = settingsHandler.appMode.value.isDesktop && (searchHandler.viewedIndex.value == index); + final bool isSelected = searchHandler.currentTab.selected.contains(index); + final bool isCurrent = settingsHandler.appMode.value.isDesktop && (searchHandler.viewedIndex.value == index); // print('ThumbnailCardBuild obx: $index'); diff --git a/lib/src/widgets/video/guess_extension_viewer.dart b/lib/src/widgets/video/guess_extension_viewer.dart index 44ad8144..0f50bd6e 100644 --- a/lib/src/widgets/video/guess_extension_viewer.dart +++ b/lib/src/widgets/video/guess_extension_viewer.dart @@ -60,7 +60,7 @@ class _GuessExtensionViewerState extends State { possibleExtensions = [...videoExtensions, ...imageExtensions, ...gifExtensions]; } - for (String extension in possibleExtensions) { + for (final String extension in possibleExtensions) { try { currentExtension = extension; setState(() {}); diff --git a/lib/src/widgets/video/loli_controls.dart b/lib/src/widgets/video/loli_controls.dart index bf76fffd..f65e643e 100644 --- a/lib/src/widgets/video/loli_controls.dart +++ b/lib/src/widgets/video/loli_controls.dart @@ -1,9 +1,11 @@ +// ignore_for_file: implementation_imports + import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:chewie/src/chewie_player.dart'; +import 'package:chewie/src/chewie_player.dart' show ChewieController; import 'package:chewie/src/chewie_progress_colors.dart'; import 'package:chewie/src/helpers/utils.dart'; import 'package:chewie/src/progress_bar.dart'; @@ -134,8 +136,8 @@ class _LoliControlsState extends State with SingleTickerProviderSt final iconColor = Theme.of(context).textTheme.labelLarge?.color; // Don't draw progress bar if shorter than 2 seconds, moves too fast on short durations - bool isTooShort = controller.value.duration.inSeconds <= 2; - bool drawProgressBar = !(chewieController.isLive || isTooShort); + final bool isTooShort = controller.value.duration.inSeconds <= 2; + final bool drawProgressBar = !(chewieController.isLive || isTooShort); return AnimatedOpacity( opacity: _hideStuff ? 0.0 : 1.0, @@ -199,7 +201,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt AnimatedOpacity _buildBottomProgress() { // Don't draw if shorter than 2 seconds, moves too fast on short durations - bool isTooShort = controller.value.duration.inSeconds <= 2; + final bool isTooShort = controller.value.duration.inSeconds <= 2; return AnimatedOpacity( opacity: (_hideStuff && !isTooShort) ? 1.0 : 0.0, @@ -219,31 +221,29 @@ class _LoliControlsState extends State with SingleTickerProviderSt } AnimatedOpacity _buildDoubleTapMessage() { - String msgText = _doubleTapExtraMessage != '' - ? _doubleTapExtraMessage - : "${_lastDoubleTapAmount}s"; + final String msgText = _doubleTapExtraMessage != '' ? _doubleTapExtraMessage : '${_lastDoubleTapAmount}s'; - IconData iconData = _lastDoubleTapSide > 0 ? Icons.fast_forward : Icons.fast_rewind; - Widget msgIcon = Icon( + final IconData iconData = _lastDoubleTapSide > 0 ? Icons.fast_forward : Icons.fast_rewind; + final Widget msgIcon = Icon( iconData, color: Colors.white, - size: 32.0, + size: 32, ); - Widget msgWidget = ClipRRect( - borderRadius: BorderRadius.circular(10.0), + final Widget msgWidget = ClipRRect( + borderRadius: BorderRadius.circular(10), child: Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), color: Colors.black38, child: Row( children: [ - if(_lastDoubleTapSide < 0) ...[ + if (_lastDoubleTapSide < 0) ...[ msgIcon, const SizedBox(width: 8), ], Text(msgText, style: const TextStyle(fontSize: 24, color: Colors.white)), - if(_lastDoubleTapSide > 0) ...[ + if (_lastDoubleTapSide > 0) ...[ const SizedBox(width: 8), msgIcon, ], @@ -252,7 +252,6 @@ class _LoliControlsState extends State with SingleTickerProviderSt ), ); - return AnimatedOpacity( opacity: _doubleTapped ? 1.0 : 0.0, onEnd: () { @@ -260,7 +259,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt setState(() { _lastDoubleTapAmount = 0; _lastDoubleTapSide = 0; - _doubleTapExtraMessage = ""; + _doubleTapExtraMessage = ''; }); } }, @@ -281,17 +280,11 @@ class _LoliControlsState extends State with SingleTickerProviderSt ), child: Row( children: [ - if (_lastDoubleTapSide < 0) - msgWidget - else - const SizedBox(), + if (_lastDoubleTapSide < 0) msgWidget else const SizedBox(), // const Spacer(), // - if (_lastDoubleTapSide > 0) - msgWidget - else - const SizedBox(), + if (_lastDoubleTapSide > 0) msgWidget else const SizedBox(), ], ), ), @@ -310,10 +303,10 @@ class _LoliControlsState extends State with SingleTickerProviderSt duration: const Duration(milliseconds: 300), child: Container( height: barHeight, - margin: const EdgeInsets.only(right: 12.0), + margin: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only( - left: 8.0, - right: 8.0, + left: 8, + right: 8, ), child: Center( child: Icon( @@ -351,7 +344,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt toggleToolbar(); }, - child: Container( + child: ColoredBox( // color: Colors.yellow.withOpacity(0.66), color: Colors.transparent, child: Center( @@ -365,22 +358,24 @@ class _LoliControlsState extends State with SingleTickerProviderSt child: Container( decoration: BoxDecoration( color: Colors.black87, //Theme.of(context).dialogBackgroundColor.withOpacity(0.75), - borderRadius: BorderRadius.circular(48.0), + borderRadius: BorderRadius.circular(48), ), child: Padding( - padding: const EdgeInsets.all(12.0), + padding: const EdgeInsets.all(12), child: IconButton( icon: isFinished - ? const Icon(Icons.replay, size: 32.0, color: Colors.white) + ? const Icon( + Icons.replay, + size: 32, + color: Colors.white, + ) : AnimatedIcon( icon: AnimatedIcons.play_pause, progress: playPauseIconAnimationController, color: Colors.white, - size: 32.0, + size: 32, ), - onPressed: () { - _playPause(); - }, + onPressed: _playPause, ), ), ), @@ -435,8 +430,8 @@ class _LoliControlsState extends State with SingleTickerProviderSt child: Container( height: barHeight, padding: const EdgeInsets.only( - left: 8.0, - right: 8.0, + left: 8, + right: 8, ), child: Icon( Icons.speed, @@ -449,7 +444,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt } GestureDetector _buildMuteButton(VideoPlayerController controller) { - bool isGlobalMute = viewerHandler.videoAutoMute; + final bool isGlobalMute = viewerHandler.videoAutoMute; return GestureDetector( onTap: () { @@ -477,8 +472,8 @@ class _LoliControlsState extends State with SingleTickerProviderSt child: Container( height: barHeight, padding: const EdgeInsets.only( - left: 8.0, - right: 8.0, + left: 8, + right: 8, ), child: Icon( _latestValue.volume > 0 ? Icons.volume_up : (isGlobalMute ? Icons.volume_off : Icons.volume_mute), @@ -496,10 +491,10 @@ class _LoliControlsState extends State with SingleTickerProviderSt child: Container( height: barHeight, color: Colors.transparent, - margin: const EdgeInsets.only(left: 8.0, right: 4.0), + margin: const EdgeInsets.only(left: 8, right: 4), padding: const EdgeInsets.only( - left: 12.0, - right: 12.0, + left: 12, + right: 12, ), child: Icon( controller.value.isPlaying ? Icons.pause : Icons.play_arrow, @@ -517,7 +512,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt '${formatDuration(position)} / ${formatDuration(duration)}', //Text('${formatDurationShorter(position)} / ${formatDurationShorter(duration)}', style: const TextStyle( - fontSize: 14.0, + fontSize: 14, color: Colors.white, ), ); @@ -599,9 +594,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt _hideStuff = true; chewieController.toggleFullScreen(); _showAfterExpandCollapseTimer = Timer(const Duration(milliseconds: 300), () { - setState(() { - _cancelAndRestartTimer(); - }); + setState(_cancelAndRestartTimer); }); }); } @@ -666,10 +659,10 @@ class _LoliControlsState extends State with SingleTickerProviderSt if (_doubleTapInfo == null || !controller.value.isInitialized) return; // Detect on which side we tapped - double screenWidth = MediaQuery.of(context).size.width; - double screenMiddle = screenWidth / 2; - double sidesLimit = screenWidth / 6; - double tapPositionWidth = _doubleTapInfo!.localPosition.dx; + final double screenWidth = MediaQuery.of(context).size.width; + final double screenMiddle = screenWidth / 2; + final double sidesLimit = screenWidth / 6; + final double tapPositionWidth = _doubleTapInfo!.localPosition.dx; int tapSide; bool isAtVideoEdge = false; if (tapPositionWidth > (screenMiddle + sidesLimit)) { @@ -681,7 +674,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt } // Decide how much we will skip depending on video length - int videoDuration = controller.value.duration.inSeconds; + final int videoDuration = controller.value.duration.inSeconds; int skipSeconds; if (videoDuration <= 5) { skipSeconds = 0; @@ -696,10 +689,10 @@ class _LoliControlsState extends State with SingleTickerProviderSt } if (tapSide != 0 && skipSeconds != 0) { - int videoPositionMillisecs = controller.value.position.inMilliseconds; - int videoDurationMillisecs = controller.value.duration.inMilliseconds; + final int videoPositionMillisecs = controller.value.position.inMilliseconds; + final int videoDurationMillisecs = controller.value.duration.inMilliseconds; // Calculate new time with skip and limit it to range (0 to duration of video) (in milliseconds for accuracy) - int newTime = min( + final int newTime = min( max(0, videoPositionMillisecs + (skipSeconds * 1000 * tapSide)), videoDurationMillisecs, ); @@ -717,7 +710,7 @@ class _LoliControlsState extends State with SingleTickerProviderSt isAtVideoEdge = true; _doubleTapExtraMessage = 'Start'; } else { - _doubleTapExtraMessage = ""; + _doubleTapExtraMessage = ''; } // Add to last skip amount if it's still visible @@ -787,7 +780,7 @@ class _PlaybackSpeedDialog extends StatelessWidget { height: 58, child: Row( children: [ - SizedBox(width: 16.0), + SizedBox(width: 16), Text('Select Video Speed:', style: TextStyle(color: Colors.white)), ], ), @@ -804,12 +797,12 @@ class _PlaybackSpeedDialog extends StatelessWidget { if (speed == _selected) Icon( Icons.check, - size: 20.0, + size: 20, color: selectedColor, ) else - Container(width: 20.0), - const SizedBox(width: 16.0), + Container(width: 20), + const SizedBox(width: 16), Text( speed.toString(), style: const TextStyle(color: Colors.white), diff --git a/lib/src/widgets/video/unknown_viewer_placeholder.dart b/lib/src/widgets/video/unknown_viewer_placeholder.dart index 04e652eb..0414ecd4 100644 --- a/lib/src/widgets/video/unknown_viewer_placeholder.dart +++ b/lib/src/widgets/video/unknown_viewer_placeholder.dart @@ -7,14 +7,17 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; import 'package:lolisnatcher/src/widgets/thumbnail/thumbnail.dart'; class UnknownViewerPlaceholder extends StatelessWidget { - const UnknownViewerPlaceholder({required this.item, super.key,}); + const UnknownViewerPlaceholder({ + required this.item, + super.key, + }); final BooruItem item; @override Widget build(BuildContext context) { return Center( - child: Container( + child: ColoredBox( color: Colors.black, child: Stack( alignment: Alignment.center, diff --git a/lib/src/widgets/video/video_viewer.dart b/lib/src/widgets/video/video_viewer.dart index f7c149b3..6472e472 100644 --- a/lib/src/widgets/video/video_viewer.dart +++ b/lib/src/widgets/video/video_viewer.dart @@ -25,7 +25,11 @@ import 'package:lolisnatcher/src/widgets/thumbnail/thumbnail.dart'; import 'package:lolisnatcher/src/widgets/video/loli_controls.dart'; class VideoViewer extends StatefulWidget { - const VideoViewer(this.booruItem, {this.enableFullscreen = true, super.key,}); + const VideoViewer( + this.booruItem, { + this.enableFullscreen = true, + super.key, + }); final BooruItem booruItem; final bool enableFullscreen; @@ -127,11 +131,13 @@ class VideoViewerState extends State { Future getSize() async { sizeCancelToken = CancelToken(); - sizeClient = DioDownloader(widget.booruItem.fileURL, - headers: await Tools.getFileCustomHeaders(searchHandler.currentBooru, checkForReferer: true), - cancelToken: sizeCancelToken, - onEvent: onEvent, - fileNameExtras: widget.booruItem.fileNameExtras); + sizeClient = DioDownloader( + widget.booruItem.fileURL, + headers: await Tools.getFileCustomHeaders(searchHandler.currentBooru, checkForReferer: true), + cancelToken: sizeCancelToken, + onEvent: onEvent, + fileNameExtras: widget.booruItem.fileNameExtras, + ); unawaited(sizeClient!.runRequestSize()); return; } @@ -236,7 +242,7 @@ class VideoViewerState extends State { void initVideo(bool ignoreTagsCheck) { if (widget.booruItem.isHated.value && !ignoreTagsCheck) { - List> hatedAndLovedTags = settingsHandler.parseTagsList(widget.booruItem.tagsList, isCapped: true); + final List> hatedAndLovedTags = settingsHandler.parseTagsList(widget.booruItem.tagsList, isCapped: true); killLoading(['Contains Hated tags:', ...hatedAndLovedTags[0]]); } else { downloadVideo(); @@ -401,8 +407,8 @@ class VideoViewerState extends State { await Future.wait([videoController!.initialize()]); videoController!.addListener(updateVideoState); - Color accentColor = Theme.of(context).colorScheme.secondary; - Color darkenedAceentColor = Color.lerp(accentColor, Colors.black, 0.5)!; + final Color accentColor = Theme.of(context).colorScheme.secondary; + final Color darkenedAceentColor = Color.lerp(accentColor, Colors.black, 0.5)!; // Player wrapper to allow controls, looping... chewieController = ChewieController( @@ -492,8 +498,8 @@ class VideoViewerState extends State { // print('!!! Build video mobile ${widget.index}!!!'); // protects from video restart when something forces restate here while video is active (example: favoriting from appbar) - int viewedIndex = searchHandler.viewedIndex.value; - bool needsRestart = lastViewedIndex != viewedIndex; + final int viewedIndex = searchHandler.viewedIndex.value; + final bool needsRestart = lastViewedIndex != viewedIndex; if (isVideoInited) { if (isViewed) { @@ -517,9 +523,9 @@ class VideoViewerState extends State { lastViewedIndex = viewedIndex; } - double aspectRatio = videoController?.value.aspectRatio ?? 16 / 9; - double screenRatio = MediaQuery.of(context).size.width / MediaQuery.of(context).size.height; - Size childSize = Size( + final double aspectRatio = videoController?.value.aspectRatio ?? 16 / 9; + final double screenRatio = MediaQuery.of(context).size.width / MediaQuery.of(context).size.height; + final Size childSize = Size( aspectRatio > screenRatio ? MediaQuery.of(context).size.width : MediaQuery.of(context).size.height * aspectRatio, aspectRatio < screenRatio ? MediaQuery.of(context).size.height : MediaQuery.of(context).size.width / aspectRatio, ); diff --git a/lib/src/widgets/video/video_viewer_desktop.dart b/lib/src/widgets/video/video_viewer_desktop.dart index 42ab131c..6592ab0c 100644 --- a/lib/src/widgets/video/video_viewer_desktop.dart +++ b/lib/src/widgets/video/video_viewer_desktop.dart @@ -249,7 +249,7 @@ class VideoViewerDesktopState extends State { void initVideo(bool ignoreTagsCheck) { if (widget.booruItem.isHated.value && !ignoreTagsCheck) { - List> hatedAndLovedTags = settingsHandler.parseTagsList(widget.booruItem.tagsList, isCapped: true); + final List> hatedAndLovedTags = settingsHandler.parseTagsList(widget.booruItem.tagsList, isCapped: true); killLoading(['Contains Hated tags:', ...hatedAndLovedTags[0]]); } else { _downloadVideo(); @@ -431,11 +431,11 @@ class VideoViewerDesktopState extends State { Widget build(BuildContext context) { // print('!!! Build video desktop ${widget.index}!!!'); - bool initialized = isLoaded; // videoController != null; + final bool initialized = isLoaded; // videoController != null; // protects from video restart when something forces restate here while video is active (example: favoriting from appbar) - int viewedIndex = searchHandler.viewedIndex.value; - bool needsRestart = _lastViewedIndex != viewedIndex; + final int viewedIndex = searchHandler.viewedIndex.value; + final bool needsRestart = _lastViewedIndex != viewedIndex; if (initialized) { if (isViewed) { @@ -525,7 +525,7 @@ class VideoViewerDesktopState extends State { if (isViewed && initialized) Video( player: videoController, - scale: 1.0, + scale: 1, progressBarInactiveColor: Colors.grey, progressBarActiveColor: accentColor, progressBarThumbColor: accentColor, diff --git a/lib/src/widgets/video/video_viewer_placeholder.dart b/lib/src/widgets/video/video_viewer_placeholder.dart index ba80a24f..943fea6a 100644 --- a/lib/src/widgets/video/video_viewer_placeholder.dart +++ b/lib/src/widgets/video/video_viewer_placeholder.dart @@ -8,7 +8,10 @@ import 'package:lolisnatcher/src/widgets/common/settings_widgets.dart'; import 'package:lolisnatcher/src/widgets/thumbnail/thumbnail.dart'; class VideoViewerPlaceholder extends StatelessWidget { - const VideoViewerPlaceholder({required this.item, super.key,}); + const VideoViewerPlaceholder({ + required this.item, + super.key, + }); final BooruItem item; @@ -30,7 +33,7 @@ class VideoViewerPlaceholder extends StatelessWidget { name: Platform.isLinux ? 'Open Video in External Player' : 'Open Video in Browser', action: () { if (Platform.isLinux) { - Process.run('mpv', ["--loop", item.fileURL]); + Process.run('mpv', ['--loop', item.fileURL]); } else { ServiceHandler.launchURL(item.fileURL); } diff --git a/lib/src/widgets/webview/webview_navigation_controls.dart b/lib/src/widgets/webview/webview_navigation_controls.dart index 925afc96..d6cd98af 100644 --- a/lib/src/widgets/webview/webview_navigation_controls.dart +++ b/lib/src/widgets/webview/webview_navigation_controls.dart @@ -68,9 +68,7 @@ class WebviewNavigationControls extends StatelessWidget { ), IconButton( icon: const Icon(Icons.replay), - onPressed: () { - controller.reload(); - }, + onPressed: controller.reload, ), ], ); @@ -80,7 +78,11 @@ class WebviewNavigationControls extends StatelessWidget { } class WebviewHistoryDialog extends StatefulWidget { - const WebviewHistoryDialog({required this.controller, required this.onSelect, super.key}); + const WebviewHistoryDialog({ + required this.controller, + required this.onSelect, + super.key, + }); final InAppWebViewController controller; final Function(String) onSelect; @@ -91,8 +93,8 @@ class WebviewHistoryDialog extends StatefulWidget { class _WebviewHistoryDialogState extends State { WebHistory? _history; - int? _currentIndex; - WebHistoryItem? _currentItem; + // int? _currentIndex; + // WebHistoryItem? _currentItem; List _historyItems = []; @override @@ -103,8 +105,8 @@ class _WebviewHistoryDialogState extends State { setState(() { _history = history; _historyItems = _history?.list ?? []; - _currentIndex = _history?.currentIndex; - _currentItem = _history?.list?[_currentIndex ?? 0]; + // _currentIndex = _history?.currentIndex; + // _currentItem = _history?.list?[_currentIndex ?? 0]; }); }); } diff --git a/lib/src/widgets/webview/webview_navigation_menu.dart b/lib/src/widgets/webview/webview_navigation_menu.dart index 06a1fc3d..e79e3f33 100644 --- a/lib/src/widgets/webview/webview_navigation_menu.dart +++ b/lib/src/widgets/webview/webview_navigation_menu.dart @@ -49,10 +49,11 @@ class _WebviewNavigationMenuState extends State { labelText: 'Enter a URL', border: const OutlineInputBorder(), suffixIcon: GestureDetector( - onTap: () { - _urlController.clear(); - }, - child: Icon(Icons.clear, color: Theme.of(context).colorScheme.onSurface), + onTap: _urlController.clear, + child: Icon( + Icons.clear, + color: Theme.of(context).colorScheme.onSurface, + ), ), ), keyboardType: TextInputType.url, @@ -127,7 +128,10 @@ class _WebviewNavigationMenuState extends State { String? favicon; if (html != null) { - final RegExp faviconRegex = RegExp(r' main() async { // prepare/init handlers and stuff final SettingsHandler settingsHandler = Get.put(SettingsHandler()); await settingsHandler.initialize(); - TagHandler tagHandler = Get.put(TagHandler()); + final TagHandler tagHandler = Get.put(TagHandler()); await tagHandler.initialize(); SettingsHandler.instance.tagTypeFetchEnabled = false; SettingsHandler.instance.limit = itemLimit; - group("booru tests", () { + group('booru tests', () { test('BooruOnRailsHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("twibooru", BooruType.BooruOnRails, "", "https://twibooru.org", "")); + final BooruHandler booruHandler = await testBooru(Booru('twibooru', BooruType.BooruOnRails, '', 'https://twibooru.org', '')); expect(booruHandler, isA()); }); test('DanbooruHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("danbooru", BooruType.Danbooru, "", "https://danbooru.donmai.us/", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('danbooru', BooruType.Danbooru, '', 'https://danbooru.donmai.us/', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); test('e621Handler', () async { - BooruHandler booruHandler = await testBooru(Booru("e621", BooruType.e621, "", "https://e621.net/", "")); + final BooruHandler booruHandler = await testBooru(Booru('e621', BooruType.e621, '', 'https://e621.net/', '')); expect(booruHandler, isA()); }); group('GelbooruAlikesHandler(s)', () { test('Rule34xxx', () async { - BooruHandler booruHandler = await testBooru(Booru("rule34", BooruType.Gelbooru, "", "https://rule34.xxx", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('rule34', BooruType.Gelbooru, '', 'https://rule34.xxx', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); test('Safebooru', () async { - BooruHandler booruHandler = await testBooru(Booru("safebooru", BooruType.Gelbooru, "", "https://safebooru.org", "")); + final BooruHandler booruHandler = await testBooru(Booru('safebooru', BooruType.Gelbooru, '', 'https://safebooru.org', '')); expect(booruHandler, isA()); }); test('Realbooru', () async { // TODO first page doesn't give correct amount of items? api side problem? - BooruHandler booruHandler = await testBooru(Booru("realbooru", BooruType.Gelbooru, "", "https://realbooru.com", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('realbooru', BooruType.Gelbooru, '', 'https://realbooru.com', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); }); test('GelbooruHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("gelbooru", BooruType.Gelbooru, "", "https://gelbooru.com", "")); + final BooruHandler booruHandler = await testBooru(Booru('gelbooru', BooruType.Gelbooru, '', 'https://gelbooru.com', '')); expect(booruHandler, isA()); }); test('GelbooruV1Handler', () async { - BooruHandler booruHandler = await testBooru(Booru("os tan", BooruType.GelbooruV1, "", "https://os-tan.booru.org", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('os tan', BooruType.GelbooruV1, '', 'https://os-tan.booru.org', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); test('InkBunnyHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("inkbunny", BooruType.InkBunny, "", "https://inkbunny.net", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('inkbunny', BooruType.InkBunny, '', 'https://inkbunny.net', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); test('MoebooruHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("lolibooru", BooruType.Moebooru, "", "https://lolibooru.moe", "")); + final BooruHandler booruHandler = await testBooru(Booru('lolibooru', BooruType.Moebooru, '', 'https://lolibooru.moe', '')); expect(booruHandler, isA()); }); test('NyanPalsHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("nyanpals", BooruType.NyanPals, "", "https://nyanpals.com", "")); + final BooruHandler booruHandler = await testBooru(Booru('nyanpals', BooruType.NyanPals, '', 'https://nyanpals.com', '')); expect(booruHandler, isA()); }); test('PhilomenaHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("derpibooru", BooruType.Philomena, "", "https://derpibooru.org", "")); + final BooruHandler booruHandler = await testBooru(Booru('derpibooru', BooruType.Philomena, '', 'https://derpibooru.org', '')); expect(booruHandler, isA()); }); test('R34HentaiHandler', () async { // requires USA proxy - BooruHandler booruHandler = await testBooru(Booru("r34hentai", BooruType.R34Hentai, "", "https://r34hentai.com", "")); + final BooruHandler booruHandler = await testBooru(Booru('r34hentai', BooruType.R34Hentai, '', 'https://r34hentai.com', '')); expect(booruHandler, isA()); }); //Not in the factory? @@ -113,46 +113,46 @@ Future main() async { });*/ test('SankakuHandler', () async { // TODO doesn't parse all items correctly? - BooruHandler booruHandler = await testBooru(Booru("sankaku", BooruType.Sankaku, "", "https://capi-v2.sankakucomplex.com", "")); + final BooruHandler booruHandler = await testBooru(Booru('sankaku', BooruType.Sankaku, '', 'https://capi-v2.sankakucomplex.com', '')); expect(booruHandler, isA()); }); test('IdolSankakuHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("idolsankaku", BooruType.IdolSankaku, "", "https://iapi.sankakucomplex.com", "")); + final BooruHandler booruHandler = await testBooru(Booru('idolsankaku', BooruType.IdolSankaku, '', 'https://iapi.sankakucomplex.com', '')); expect(booruHandler, isA()); }); test('ShimmieHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("r34 paheal", BooruType.Shimmie, "", "https://rule34.paheal.net", "")); + final BooruHandler booruHandler = await testBooru(Booru('r34 paheal', BooruType.Shimmie, '', 'https://rule34.paheal.net', '')); expect(booruHandler, isA()); }); test('SzurubooruHandler', () async { - BooruHandler booruHandler = - await testBooru(Booru("xivbooru", BooruType.Szurubooru, "", "http://xivbooru.com", ""), customSuggestionsInput: 'tat'); // tat[too] + final BooruHandler booruHandler = + await testBooru(Booru('xivbooru', BooruType.Szurubooru, '', 'http://xivbooru.com', ''), customSuggestionsInput: 'tat'); // tat[too] expect(booruHandler, isA()); }); group('WorldXyzHandler', () { test('World', () async { - BooruHandler booruHandler = await testBooru(Booru("r34world", BooruType.World, "", "https://rule34.world", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('r34world', BooruType.World, '', 'https://rule34.world', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); test('Xyz', () async { - BooruHandler booruHandler = await testBooru(Booru("r34xyz", BooruType.World, "", "https://rule34.xyz", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('r34xyz', BooruType.World, '', 'https://rule34.xyz', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); }); }); // Sometimes this fails maybe its giving the thumb urls but they aren't generated yet - group("slow booru tests", () { + group('slow booru tests', () { test('AGNPHHandler', () async { - BooruHandler booruHandler = await testBooru( - Booru("agn.ph", BooruType.AGNPH, "", "https://agn.ph", ""), + final BooruHandler booruHandler = await testBooru( + Booru('agn.ph', BooruType.AGNPH, '', 'https://agn.ph', ''), hardFetchedLength: false, timeoutBeforeTagCheck: true, ); expect(booruHandler, isA()); }); test('RainbooruHandler', () async { - BooruHandler booruHandler = await testBooru(Booru("rainbooru", BooruType.Rainbooru, "", "https://www.rainbooru.org", ""), hardFetchedLength: false); + final BooruHandler booruHandler = await testBooru(Booru('rainbooru', BooruType.Rainbooru, '', 'https://www.rainbooru.org', ''), hardFetchedLength: false); expect(booruHandler, isA()); }); }); @@ -170,9 +170,9 @@ Future testBooru( // +1 because starting page number is out of range booruHandler.pageNum = (temp[1] as int) + 1; - List fetched = await booruHandler.search(defaultInput, booruHandler.pageNum); + final List fetched = await booruHandler.search(defaultInput, booruHandler.pageNum); if (booruHandler.errorString.isNotEmpty) { - print("Error: ${booruHandler.errorString}"); + print('Error: ${booruHandler.errorString}'); } print('fetched length: ${fetched.length}'); if (hardFetchedLength) { @@ -188,7 +188,7 @@ Future testBooru( // const int imageTestLimit = imageLimit; // hardFetchedLength ? itemLimit : fetched.length for (int i = 0; i < imageTestLimit; i++) { - BooruItem item = fetched[i]; + final BooruItem item = fetched[i]; print('Fetching images for ${item.postURL}'); print('${item.fileURL} ${item.sampleURL} ${item.thumbnailURL}'); // file @@ -216,9 +216,9 @@ Future testBooru( } Future testSuggestions(BooruHandler booruHandler, {String? customSuggestionsInput}) async { - print("Testing suggestions for ${booruHandler.booru.name}"); - String input = customSuggestionsInput ?? "ani"; // ani[mated] - List suggestions = await booruHandler.tagSearch(input); + print('Testing suggestions for ${booruHandler.booru.name}'); + final String input = customSuggestionsInput ?? 'ani'; // ani[mated] + final List suggestions = await booruHandler.tagSearch(input); expect(suggestions.isNotEmpty, equals(true)); expect(suggestions.length, equals(10)); }