diff --git a/lib/app/app_version.dart b/lib/app/app_version.dart index 28961b6..2d8a1a2 100644 --- a/lib/app/app_version.dart +++ b/lib/app/app_version.dart @@ -1,3 +1,3 @@ -const appDisplayVersion = '3.0.1'; -const appBuildNumber = 52; +const appDisplayVersion = '3.0.2'; +const appBuildNumber = 53; const appReleaseChannel = 'Stable'; diff --git a/lib/backend/services/update_service.dart b/lib/backend/services/update_service.dart index 02e62d0..f9cdcb2 100644 --- a/lib/backend/services/update_service.dart +++ b/lib/backend/services/update_service.dart @@ -9,14 +9,27 @@ import '../../core/utils/result.dart'; import '../models/app_update_info.dart'; import 'settings_service.dart'; +enum UpdateSource { + gitea, + github, +} + class UpdateService { UpdateService(this._dio, this._settingsService); - static const latestReleaseUrl = + static const giteaLatestReleaseUrl = + 'https://gitea.rardogsynapse.online/api/v1/repos/RarDog/Lunaris/releases/latest'; + static const giteaReleasesUrl = + 'https://gitea.rardogsynapse.online/api/v1/repos/RarDog/Lunaris/releases'; + + static const githubLatestReleaseUrl = 'https://api.github.com/repos/RarDog/Lunaris/releases/latest'; - static const releasesUrl = + static const githubReleasesUrl = 'https://api.github.com/repos/RarDog/Lunaris/releases'; + static const latestReleaseUrl = githubLatestReleaseUrl; + static const releasesUrl = githubReleasesUrl; + static const _deviceChannel = MethodChannel('rulegel/device'); final Dio _dio; @@ -50,14 +63,17 @@ class UpdateService { } } - Future> checkForUpdates({bool force = false}) async { + Future> checkForUpdates({ + bool force = false, + UpdateSource source = UpdateSource.gitea, + }) async { final settingsResult = await _settingsService.getSettings(); final settings = settingsResult is Success ? settingsResult.data : AppSettings.defaults; try { - final info = await _latestAllowedRelease(settings); + final info = await _latestAllowedRelease(settings, source: source); final nextSettings = settings.copyWith( lastUpdateCheckAt: DateTime.now().toIso8601String(), ); @@ -81,16 +97,29 @@ class UpdateService { } } - Future _latestAllowedRelease(AppSettings settings) async { + Future _latestAllowedRelease( + AppSettings settings, { + UpdateSource source = UpdateSource.gitea, + }) async { + final isGitea = source == UpdateSource.gitea; + final rUrl = isGitea ? giteaReleasesUrl : githubReleasesUrl; + final latestUrl = isGitea ? giteaLatestReleaseUrl : githubLatestReleaseUrl; + + final options = Options( + headers: isGitea + ? { + 'Authorization': 'token 1c744d6044d756759d7b1f693c94c80cf70d75fa', + } + : { + 'User-Agent': 'Lunaris-App', + 'Accept': 'application/vnd.github+json', + }, + ); + final response = await _dio.get( - releasesUrl, - queryParameters: {'per_page': 30}, - options: Options( - headers: { - 'User-Agent': 'Lunaris-App', - 'Accept': 'application/vnd.github+json', - }, - ), + rUrl, + queryParameters: isGitea ? {'limit': 30} : {'per_page': 30}, + options: options, ); final items = (response.data as List?) ?? const []; final releases = items @@ -102,13 +131,8 @@ class UpdateService { .toList(growable: false); if (releases.isEmpty) { final latest = await _dio.get( - latestReleaseUrl, - options: Options( - headers: { - 'User-Agent': 'Lunaris-App', - 'Accept': 'application/vnd.github+json', - }, - ), + latestUrl, + options: options, ); return _releaseFromJson(Map.from(latest.data as Map)); } diff --git a/lib/features/settings/presentation/settings_screen.dart b/lib/features/settings/presentation/settings_screen.dart index 80f28ac..54c2d78 100644 --- a/lib/features/settings/presentation/settings_screen.dart +++ b/lib/features/settings/presentation/settings_screen.dart @@ -958,12 +958,216 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> { } Future _checkUpdates(BuildContext context, WidgetRef ref) async { + final isRu = settings.languageCode == 'ru'; + final theme = Theme.of(context); + + final selectedSource = await showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (sheetContext) { + return Container( + decoration: BoxDecoration( + color: theme.colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(28)), + ), + padding: const EdgeInsets.fromLTRB(20, 12, 20, 28), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 16), + decoration: BoxDecoration( + color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(2), + ), + ), + ), + Text( + isRu ? 'Источник обновлений' : 'Update Source', + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 6), + Text( + isRu + ? 'Выберите сервер для загрузки обновлений' + : 'Choose a server to download updates from', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + + // Option 1: Gitea (Recommended for speed) + Material( + color: theme.colorScheme.primaryContainer.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(16), + child: InkWell( + borderRadius: BorderRadius.circular(16), + onTap: () => Navigator.pop(sheetContext, UpdateSource.gitea), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: theme.colorScheme.primary.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.bolt_rounded, + color: theme.colorScheme.primary, + size: 24, + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + 'Gitea', + style: theme.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 2, + ), + decoration: BoxDecoration( + color: Colors.green.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + isRu ? 'Рекомендуется для скорости' : 'Recommended for speed', + style: const TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.green, + ), + ), + ), + ], + ), + const SizedBox(height: 3), + Text( + isRu + ? 'Прямая быстрая загрузка без ограничений' + : 'Direct fast download without limits', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 14, + color: theme.colorScheme.onSurfaceVariant, + ), + ], + ), + ), + ), + ), + const SizedBox(height: 12), + + // Option 2: GitHub (Muted / neutral grey) + Material( + color: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.25), + borderRadius: BorderRadius.circular(16), + child: InkWell( + borderRadius: BorderRadius.circular(16), + onTap: () => Navigator.pop(sheetContext, UpdateSource.github), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: theme.colorScheme.onSurface.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.code_rounded, + color: theme.colorScheme.onSurfaceVariant, + size: 22, + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'GitHub', + style: theme.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + color: theme.colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 3), + Text( + isRu + ? 'Глобальное зеркало релизов' + : 'Global release mirror', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.7), + ), + ), + ], + ), + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 14, + color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.5), + ), + ], + ), + ), + ), + ), + ], + ), + ); + }, + ); + + if (selectedSource == null) return; + if (!context.mounted) return; + final messenger = ScaffoldMessenger.of(context); messenger.showSnackBar( - const SnackBar(content: Text('Checking for updates...')), + SnackBar( + content: Text( + selectedSource == UpdateSource.gitea + ? (isRu ? 'Проверка обновлений на Gitea...' : 'Checking updates on Gitea...') + : (isRu ? 'Проверка обновлений на GitHub...' : 'Checking updates on GitHub...'), + ), + ), ); - final result = - await ref.read(updateServiceProvider).checkForUpdates(force: true); + final result = await ref.read(updateServiceProvider).checkForUpdates( + force: true, + source: selectedSource, + ); if (!context.mounted) return; messenger.hideCurrentSnackBar(); if (result is Error) { @@ -973,7 +1177,11 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> { final update = (result as Success).data; if (update == null) { messenger.showSnackBar( - const SnackBar(content: Text('Lunaris is up to date')), + SnackBar( + content: Text( + isRu ? 'У вас установлена последняя версия Lunaris' : 'Lunaris is up to date', + ), + ), ); return; } diff --git a/pubspec.yaml b/pubspec.yaml index a2deb5f..33fef0e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: gel_rule_app description: Local backend/core layer for a Flutter booru browser app. publish_to: "none" -version: 3.0.1+52 +version: 3.0.2+53 environment: sdk: ">=3.3.0 <4.0.0"