diff --git a/.metadata b/.metadata index fe11f507..c37580fd 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + revision: 4b12645012342076800eb701bcdfe18f87da21cf channel: stable project_type: app @@ -13,26 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - platform: android - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - platform: ios - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - platform: linux - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - platform: macos - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - platform: web - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf - platform: windows - create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da - base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf # User provided section diff --git a/android/build.gradle b/android/build.gradle index 8ac0cb38..8ac4d8ed 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.8.10' + ext.kotlin_version = '1.8.20' repositories { google() mavenCentral() diff --git a/lib/src/constants/db_keys.dart b/lib/src/constants/db_keys.dart index c1133531..d2f327d2 100644 --- a/lib/src/constants/db_keys.dart +++ b/lib/src/constants/db_keys.dart @@ -9,7 +9,9 @@ import 'package:flutter/material.dart'; import 'enum.dart'; enum DBKeys { - serverUrl('http://127.0.0.1:4567'), + serverUrl('http://127.0.0.1'), + serverPort(4567), + serverPortToggle(true), sourceLanguageFilter(["all", "lastUsed", "en", "localsourcelang"]), extensionLanguageFilter(["installed", "update", "en", "all"]), sourceLastUsed(null), diff --git a/lib/src/constants/endpoints.dart b/lib/src/constants/endpoints.dart index 3492b6f8..a623c2b6 100644 --- a/lib/src/constants/endpoints.dart +++ b/lib/src/constants/endpoints.dart @@ -8,8 +8,14 @@ import 'db_keys.dart'; abstract class Endpoints { // base url - static String baseApi({String? baseUrl, bool appendApiToUrl = true}) => + static String baseApi({ + String? baseUrl, + int? port, + bool addPort = true, + bool appendApiToUrl = true, + }) => "${baseUrl ?? DBKeys.serverUrl.initial}" + "${port != null && addPort ? ":$port" : ''}" "${appendApiToUrl ? '/api/v1' : ''}"; // receiveTimeout diff --git a/lib/src/features/settings/presentation/backup/backup_screen.dart b/lib/src/features/settings/presentation/backup/backup_screen.dart index 29107083..ab731f9d 100644 --- a/lib/src/features/settings/presentation/backup/backup_screen.dart +++ b/lib/src/features/settings/presentation/backup/backup_screen.dart @@ -14,6 +14,7 @@ import '../../../../utils/extensions/custom_extensions.dart'; import '../../../../utils/launch_url_in_web.dart'; import '../../../../utils/misc/toast/toast.dart'; import '../../data/backup/backup_repository.dart'; +import '../../widgets/server_port_tile/server_port_tile.dart'; import '../../widgets/server_url_tile/server_url_tile.dart'; import 'widgets/backup_missing_dialog.dart'; @@ -67,7 +68,11 @@ class BackupScreen extends ConsumerWidget { final toast = ref.read(toastProvider(context)); launchUrlInWeb( context, - Endpoints.baseApi(baseUrl: ref.read(serverUrlProvider)) + + Endpoints.baseApi( + baseUrl: ref.read(serverUrlProvider), + port: ref.read(serverPortProvider), + addPort: ref.watch(serverPortToggleProvider).ifNull(), + ) + BackupUrl.export, toast, ); diff --git a/lib/src/features/settings/presentation/server/server_screen.dart b/lib/src/features/settings/presentation/server/server_screen.dart index 4f7326ad..c9d14d64 100644 --- a/lib/src/features/settings/presentation/server/server_screen.dart +++ b/lib/src/features/settings/presentation/server/server_screen.dart @@ -8,12 +8,14 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../../../../constants/endpoints.dart'; import '../../../../constants/enum.dart'; import '../../../../global_providers/global_providers.dart'; import '../../../../utils/extensions/custom_extensions.dart'; import '../../../../utils/launch_url_in_web.dart'; import '../../../../utils/misc/toast/toast.dart'; +import '../../widgets/server_port_tile/server_port_tile.dart'; import '../../widgets/server_url_tile/server_url_tile.dart'; import 'widget/auth_type_tile.dart'; import 'widget/credential_popup/credentials_popup.dart'; @@ -30,6 +32,7 @@ class ServerScreen extends ConsumerWidget { body: ListView( children: [ const ServerUrlTile(), + const ServerPortTile(), const AuthTypeTile(), if (authType != null && authType != AuthType.none) ListTile( @@ -47,11 +50,15 @@ class ServerScreen extends ConsumerWidget { leading: const Icon(Icons.web_rounded), title: Text(context.l10n!.webUI), onTap: () { - final url = ref.read(serverUrlProvider); + final url = Endpoints.baseApi( + baseUrl: ref.read(serverUrlProvider), + port: ref.read(serverPortProvider), + addPort: ref.watch(serverPortToggleProvider).ifNull(), + ); if (url.isNotBlank) { launchUrlInWeb( context, - url!, + url, ref.read(toastProvider(context)), ); } diff --git a/lib/src/features/settings/widgets/server_port_tile/server_port_tile.dart b/lib/src/features/settings/widgets/server_port_tile/server_port_tile.dart new file mode 100644 index 00000000..c04a6f00 --- /dev/null +++ b/lib/src/features/settings/widgets/server_port_tile/server_port_tile.dart @@ -0,0 +1,118 @@ +// Copyright (c) 2022 Contributors to the Suwayomi project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../../../constants/db_keys.dart'; + +import '../../../../utils/extensions/custom_extensions.dart'; +import '../../../../utils/mixin/shared_preferences_client_mixin.dart'; +import '../../../../widgets/pop_button.dart'; + +part 'server_port_tile.g.dart'; + +@riverpod +class ServerPort extends _$ServerPort with SharedPreferenceClientMixin { + @override + int? build() => initialize( + ref, + key: DBKeys.serverPort.name, + initial: DBKeys.serverPort.initial, + ); +} + +@riverpod +class ServerPortToggle extends _$ServerPortToggle + with SharedPreferenceClientMixin { + @override + bool? build() => initialize( + ref, + key: DBKeys.serverPortToggle.name, + initial: DBKeys.serverPortToggle.initial, + ); +} + +class ServerPortTile extends ConsumerWidget { + const ServerPortTile({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final serverPort = ref.watch(serverPortProvider); + final serverToggle = ref.watch(serverPortToggleProvider).ifNull(); + return ListTile( + leading: const Icon(Icons.dns_rounded), + title: Text(context.l10n!.serverPort), + subtitle: serverToggle && serverPort != null + ? Text(serverPort.toString()) + : null, + trailing: Switch( + value: serverToggle, + onChanged: (value) { + ref.read(serverPortToggleProvider.notifier).update(value); + }, + ), + onTap: serverToggle + ? () => showDialog( + context: context, + builder: (context) => ServerPortField(initialPort: serverPort), + ) + : null, + ); + } +} + +class ServerPortField extends HookConsumerWidget { + const ServerPortField({ + this.initialPort, + super.key, + }); + final int? initialPort; + + void _update(int? port, WidgetRef ref) { + ref.read(serverPortProvider.notifier).update(port); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final controller = useTextEditingController( + text: (initialPort ?? '').toString(), + ); + return AlertDialog( + title: Text(context.l10n!.serverPort), + content: TextField( + autofocus: true, + controller: controller, + onSubmitted: (value) { + _update(int.tryParse(controller.text), ref); + context.pop(); + }, + keyboardType: TextInputType.number, + maxLength: 5, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: InputDecoration( + counter: const SizedBox.shrink(), + border: const OutlineInputBorder(), + hintText: (context.l10n!.serverPortHintText), + ), + ), + actions: [ + const PopButton(), + ElevatedButton( + onPressed: () { + _update(int.tryParse(controller.text), ref); + context.pop(); + }, + child: Text(context.l10n!.save), + ), + ], + ); + } +} diff --git a/lib/src/features/settings/widgets/server_port_tile/server_port_tile.g.dart b/lib/src/features/settings/widgets/server_port_tile/server_port_tile.g.dart new file mode 100644 index 00000000..99545e6a --- /dev/null +++ b/lib/src/features/settings/widgets/server_port_tile/server_port_tile.g.dart @@ -0,0 +1,40 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_port_tile.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$serverPortHash() => r'3cc6aeb1ff31120ea0f590de35389013914693bc'; + +/// See also [ServerPort]. +@ProviderFor(ServerPort) +final serverPortProvider = + AutoDisposeNotifierProvider.internal( + ServerPort.new, + name: r'serverPortProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$serverPortHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ServerPort = AutoDisposeNotifier; +String _$serverPortToggleHash() => r'ee1ab5dd40f037ecb6958ebc0bab7268422c0040'; + +/// See also [ServerPortToggle]. +@ProviderFor(ServerPortToggle) +final serverPortToggleProvider = + AutoDisposeNotifierProvider.internal( + ServerPortToggle.new, + name: r'serverPortToggleProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$serverPortToggleHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ServerPortToggle = AutoDisposeNotifier; +// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions diff --git a/lib/src/features/settings/widgets/server_url_tile/server_search_button.dart b/lib/src/features/settings/widgets/server_url_tile/server_search_button.dart new file mode 100644 index 00000000..4554c217 --- /dev/null +++ b/lib/src/features/settings/widgets/server_url_tile/server_search_button.dart @@ -0,0 +1,89 @@ +// Copyright (c) 2023 Contributors to the Suwayomi project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:network_info_plus/network_info_plus.dart'; + +import '../../../../constants/db_keys.dart'; +import '../../../../utils/extensions/custom_extensions.dart'; +import '../../../../utils/misc/toast/toast.dart'; +import '../../../../widgets/async_buttons/async_text_button.dart'; +import '../server_port_tile/server_port_tile.dart'; +import 'server_url_tile.dart'; + +class ServerSearchButton extends ConsumerWidget { + const ServerSearchButton({ + Key? key, + this.text, + }) : super(key: key); + final String? text; + void _update(String url, WidgetRef ref) { + final tempUrl = url.endsWith('/') ? url.substring(0, url.length - 1) : url; + ref.read(serverUrlProvider.notifier).update(tempUrl); + } + + Future getServerAddress(int? port) async { + final ip = await NetworkInfo().getWifiIP(); + if (ip.isBlank) return null; + + port ??= 4567; + String serverIp = ip!; + + if (await pingIp(serverIp, port)) return DBKeys.serverUrl.initial; + + final String subnet = ip.substring(0, ip.lastIndexOf('.')); + + for (var i = 0; i < 254; i++) { + serverIp = '$subnet.$i'; + if (await pingIp(serverIp, port)) return "http://$serverIp"; + } + return null; + } + + Future pingIp(String ip, int port) async { + bool isValidIp = false; + await Socket.connect(ip, port, timeout: const Duration(milliseconds: 50)) + .then( + (socket) async { + await InternetAddress(socket.address.address).reverse().then( + (value) => isValidIp = true, + onError: (_) => isValidIp = true, + ); + socket.destroy(); + }, + onError: (_) => null, + ); + return isValidIp; + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final port = ref.watch(serverPortProvider); + final addPort = ref.watch(serverPortToggleProvider).ifNull(); + if (!addPort) return const SizedBox.shrink(); + return AsyncTextButton( + icon: const Icon(Icons.search), + onPressed: port != null + ? () async { + final value = await getServerAddress(port); + if (value != null) { + _update(value, ref); + } else { + if (context.mounted) { + ref + .watch(toastProvider(context)) + .showError(context.l10n!.noServerFound); + } + } + } + : null, + child: Text(context.l10n!.findServer), + ); + } +} diff --git a/lib/src/features/settings/widgets/server_url_tile/server_url_tile.dart b/lib/src/features/settings/widgets/server_url_tile/server_url_tile.dart index 03b2a6bf..aaac292e 100644 --- a/lib/src/features/settings/widgets/server_url_tile/server_url_tile.dart +++ b/lib/src/features/settings/widgets/server_url_tile/server_url_tile.dart @@ -4,6 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; @@ -15,6 +16,7 @@ import '../../../../constants/db_keys.dart'; import '../../../../utils/extensions/custom_extensions.dart'; import '../../../../utils/mixin/shared_preferences_client_mixin.dart'; import '../../../../widgets/pop_button.dart'; +import 'server_search_button.dart'; part 'server_url_tile.g.dart'; @@ -38,6 +40,7 @@ class ServerUrlTile extends ConsumerWidget { leading: const Icon(Icons.computer_rounded), title: Text(context.l10n!.serverUrl), subtitle: serverUrl.isNotBlank ? Text(serverUrl!) : null, + trailing: kIsWeb ? null : const ServerSearchButton(), onTap: () => showDialog( context: context, builder: (context) => ServerUrlField(initialUrl: serverUrl), diff --git a/lib/src/global_providers/global_providers.dart b/lib/src/global_providers/global_providers.dart index b5f29de5..72901c5b 100644 --- a/lib/src/global_providers/global_providers.dart +++ b/lib/src/global_providers/global_providers.dart @@ -14,6 +14,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../constants/db_keys.dart'; import '../constants/enum.dart'; import '../features/settings/presentation/server/widget/credential_popup/credentials_popup.dart'; +import '../features/settings/widgets/server_port_tile/server_port_tile.dart'; import '../features/settings/widgets/server_url_tile/server_url_tile.dart'; import '../utils/extensions/custom_extensions.dart'; import '../utils/mixin/shared_preferences_client_mixin.dart'; @@ -26,6 +27,8 @@ part 'global_providers.g.dart'; DioClient dioClientKey(DioClientKeyRef ref) => DioClient( dio: ref.watch(networkModuleProvider).provideDio( baseUrl: ref.watch(serverUrlProvider) ?? DBKeys.serverUrl.initial, + port: ref.watch(serverPortProvider), + addPort: ref.watch(serverPortToggleProvider).ifNull(), authType: ref.watch(authTypeKeyProvider) ?? DBKeys.authType.initial, credentials: ref.watch(credentialsProvider), hiveCacheStore: ref.watch(hiveCacheStoreProvider), diff --git a/lib/src/global_providers/global_providers.g.dart b/lib/src/global_providers/global_providers.g.dart index c97cfc3c..ec2ad77b 100644 --- a/lib/src/global_providers/global_providers.g.dart +++ b/lib/src/global_providers/global_providers.g.dart @@ -6,7 +6,7 @@ part of 'global_providers.dart'; // RiverpodGenerator // ************************************************************************** -String _$dioClientKeyHash() => r'f8ac67719cc287981dabd817e2010e3b8650659a'; +String _$dioClientKeyHash() => r'3c0bdf6561be103898cbba24855ca355c5dfba6b'; /// See also [dioClientKey]. @ProviderFor(dioClientKey) diff --git a/lib/src/l10n/app_en.arb b/lib/src/l10n/app_en.arb index 43fc2da1..8b15d92f 100644 --- a/lib/src/l10n/app_en.arb +++ b/lib/src/l10n/app_en.arb @@ -207,6 +207,9 @@ "@filter": { "description": "title of filter Tab across the app" }, + "@findServer": { + "description": "Button text of Find Server" + }, "@finished": { "description": "Text to show the Currently Finished reading chapter in Reader Screen" }, @@ -349,6 +352,9 @@ "@noResultFound": { "description": "Text to show that no results found for the given search query" }, + "@noServerFound": { + "description": "Text to show that no Server found in locale network" + }, "@noSourcesFound": { "description": "Text to show that the Source list is empty" }, @@ -505,9 +511,15 @@ "@serverUrl": { "description": "Popup title and Button text to update Server Url" }, + "@serverPort": { + "description": "Popup title and Button text to update Server Port" + }, "@serverUrlHintText": { "description": "Text Filed hint text to update Server Url" }, + "@serverPortHintText": { + "description": "Text Filed hint text to update Server Port" + }, "@serverVersion": { "description": "Text title for the current version of the Server in About screen" }, @@ -665,6 +677,7 @@ "extensions": "Extensions", "failed": "Failed", "filter": "Filter", + "findServer" : "Find", "finished": "Finished", "general": "General", "gitHub": "GitHub", @@ -707,6 +720,7 @@ "noMangaFound": "No Mangas Found", "noOfChapters": "{count} Chapters", "noResultFound": "No results found", + "noServerFound": "No Server found in your local network", "noSourcesFound": "No sources found", "noUpdatesAvailable": "You're using the latest version", "noUpdatesFound": "No updates found", @@ -755,7 +769,9 @@ "searchingForUpdates": "Searching for updates", "server": "Server", "serverUrl": "Server URL", - "serverUrlHintText": "Enter server url", + "serverUrlHintText": "Server url", + "serverPort": "Server Port", + "serverPortHintText": "Server port", "serverVersion": "Server version", "settings": "Settings", "sort": "Sort", diff --git a/lib/src/utils/extensions/custom_extensions/string_extensions.dart b/lib/src/utils/extensions/custom_extensions/string_extensions.dart index 1d94c0c9..7e24c6d2 100644 --- a/lib/src/utils/extensions/custom_extensions/string_extensions.dart +++ b/lib/src/utils/extensions/custom_extensions/string_extensions.dart @@ -13,6 +13,8 @@ extension StringExtensions on String? { bool get isNotBlank => !isBlank; + bool get isInt => isNull ? false : int.tryParse(this!) != null; + bool? get tryParseBool => isNull ? null : (this!).toLowerCase() == 'true'; double? get tryParseInt => isNull ? null : double.tryParse(this!); diff --git a/lib/src/utils/storage/dio/network_module.dart b/lib/src/utils/storage/dio/network_module.dart index eb672e1c..b5af34f8 100644 --- a/lib/src/utils/storage/dio/network_module.dart +++ b/lib/src/utils/storage/dio/network_module.dart @@ -26,6 +26,8 @@ parseJson(String text) => compute(_parseAndDecode, text); class DioNetworkModule { Dio provideDio({ required String baseUrl, + int? port, + bool addPort = true, required AuthType authType, HiveCacheStore? hiveCacheStore, String? credentials, @@ -41,7 +43,11 @@ class DioNetworkModule { (dio.transformer as BackgroundTransformer).jsonDecodeCallback = parseJson; dio - ..options.baseUrl = Endpoints.baseApi(baseUrl: baseUrl) + ..options.baseUrl = Endpoints.baseApi( + baseUrl: baseUrl, + port: port, + addPort: addPort, + ) ..options.connectTimeout = Endpoints.connectionTimeout ..options.receiveTimeout = Endpoints.receiveTimeout ..options.contentType = Headers.jsonContentType diff --git a/lib/src/utils/storage/dio_error_util.dart b/lib/src/utils/storage/dio_error_util.dart index 71769b17..26c0cd72 100644 --- a/lib/src/utils/storage/dio_error_util.dart +++ b/lib/src/utils/storage/dio_error_util.dart @@ -39,7 +39,7 @@ class DioErrorUtil { break; case DioErrorType.connectionError: errorDescription = - "Check your Internet Connection (Connection Error)"; + "Check your Internet Connection (Check server IP in settings)"; break; } } else { diff --git a/lib/src/widgets/async_buttons/async_text_button.dart b/lib/src/widgets/async_buttons/async_text_button.dart new file mode 100644 index 00000000..5e9e671b --- /dev/null +++ b/lib/src/widgets/async_buttons/async_text_button.dart @@ -0,0 +1,40 @@ +// Copyright (c) 2023 Contributors to the Suwayomi project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import '../custom_circular_progress_indicator.dart'; + +class AsyncTextButton extends HookWidget { + const AsyncTextButton({ + super.key, + required this.onPressed, + required this.child, + required this.icon, + }); + + final AsyncCallback? onPressed; + final Widget child; + final Widget icon; + + @override + Widget build(BuildContext context) { + final isLoading = useState(false); + return TextButton.icon( + onPressed: !isLoading.value + ? () async { + isLoading.value = true; + await onPressed!(); + isLoading.value = false; + } + : null, + icon: isLoading.value ? const SizedBox.shrink() : icon, + label: isLoading.value ? const MiniCircularProgressIndicator() : child, + ); + } +} diff --git a/lib/src/widgets/server_image.dart b/lib/src/widgets/server_image.dart index 7b2a9c96..4335b030 100644 --- a/lib/src/widgets/server_image.dart +++ b/lib/src/widgets/server_image.dart @@ -13,8 +13,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../constants/endpoints.dart'; import '../constants/enum.dart'; import '../features/settings/presentation/server/widget/credential_popup/credentials_popup.dart'; +import '../features/settings/widgets/server_port_tile/server_port_tile.dart'; import '../features/settings/widgets/server_url_tile/server_url_tile.dart'; import '../global_providers/global_providers.dart'; +import '../utils/extensions/custom_extensions.dart'; class ServerImage extends ConsumerWidget { const ServerImage({ @@ -36,11 +38,14 @@ class ServerImage extends ConsumerWidget { final Widget Function(Widget child)? wrapper; @override Widget build(BuildContext context, WidgetRef ref) { - final baseUrl = ref.watch(serverUrlProvider); final authType = ref.watch(authTypeKeyProvider); final basicToken = ref.watch(credentialsProvider); - final baseApi = - "${Endpoints.baseApi(baseUrl: baseUrl, appendApiToUrl: appendApiToUrl)}" + final baseApi = "${Endpoints.baseApi( + baseUrl: ref.watch(serverUrlProvider), + port: ref.watch(serverPortProvider), + addPort: ref.watch(serverPortToggleProvider).ifNull(), + appendApiToUrl: appendApiToUrl, + )}" "$imageUrl"; return CachedNetworkImage( imageUrl: baseApi, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8ca0b6dc..7fc4d02d 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import network_info_plus import package_info_plus import path_provider_foundation import share_plus @@ -13,6 +14,7 @@ import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 1b726885..19525ccc 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,6 +3,8 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - network_info_plus (0.0.1): + - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS - path_provider_foundation (0.0.1): @@ -21,6 +23,7 @@ PODS: DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) + - network_info_plus (from `Flutter/ephemeral/.symlinks/plugins/network_info_plus/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) @@ -35,6 +38,8 @@ SPEC REPOS: EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral + network_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/network_info_plus/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: @@ -51,6 +56,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + network_info_plus: f4fbc7877ab7b3294500d9441dfa53cd54972d05 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 diff --git a/pubspec.lock b/pubspec.lock index 08a4011c..51c03946 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -281,6 +281,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" dio: dependency: "direct main" description: @@ -669,6 +677,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + network_info_plus: + dependency: "direct main" + description: + name: network_info_plus + sha256: bee3cee3893ec8dbfdf11604e8fea9284a9cd9c3387e054114e95c4f289aa9b0 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + network_info_plus_platform_interface: + dependency: transitive + description: + name: network_info_plus_platform_interface + sha256: "881f5029c5edaf19c616c201d3d8b366c5b1384afd5c1da5a49e4345de82fb8b" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" octo_image: dependency: transitive description: @@ -721,10 +753,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" + sha256: da97262be945a72270513700a92b39dd2f4a54dad55d061687e2e37a6390366a url: "https://pub.dev" source: hosted - version: "2.0.24" + version: "2.0.25" path_provider_foundation: dependency: transitive description: @@ -841,10 +873,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.7.2" + version: "3.7.3" pool: dependency: transitive description: @@ -953,10 +985,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "8304d8a1f7d21a429f91dee552792249362b68a331ac5c3c1caf370f658873f6" + sha256: "7fa90471a6875d26ad78c7e4a675874b2043874586891128dc5899662c97db46" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" shared_preferences_foundation: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index da5c4f74..7761e67b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: tachidesk_sorayomi description: A new Flutter frontend for Tachidesk. publish_to: "none" -version: 0.5.1+1 +version: 0.5.2+1 environment: sdk: ">=2.18.1 <3.0.0" @@ -29,6 +29,7 @@ dependencies: infinite_scroll_pagination: ^3.2.0 intl: ^0.17.0 json_annotation: ^4.8.0 + network_info_plus: ^3.0.3 package_info_plus: ^3.0.1 path_provider: ^2.0.11 permission_handler: ^10.2.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d5013ba1..4c4fe7d4 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,11 +6,14 @@ #include "generated_plugin_registrant.h" +#include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + NetworkInfoPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("NetworkInfoPlusWindowsPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index a0d13886..3ba95773 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + network_info_plus permission_handler_windows share_plus url_launcher_windows