Compare commits

...
2 Commits
Author SHA1 Message Date
RarDog f60c767a0d feat: add update source picker (Gitea recommended vs GitHub) (v3.0.2)
Build and Release / Build Windows x64 (push) Has been cancelled
2026-09-04 16:56:19 +03:00
RarDog 40ac7cd055 chore: sync repository metadata 2026-09-04 16:45:41 +03:00
4 changed files with 259 additions and 27 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
const appDisplayVersion = '3.0.1'; const appDisplayVersion = '3.0.2';
const appBuildNumber = 52; const appBuildNumber = 53;
const appReleaseChannel = 'Stable'; const appReleaseChannel = 'Stable';
+44 -20
View File
@@ -9,14 +9,27 @@ import '../../core/utils/result.dart';
import '../models/app_update_info.dart'; import '../models/app_update_info.dart';
import 'settings_service.dart'; import 'settings_service.dart';
enum UpdateSource {
gitea,
github,
}
class UpdateService { class UpdateService {
UpdateService(this._dio, this._settingsService); 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'; 'https://api.github.com/repos/RarDog/Lunaris/releases/latest';
static const releasesUrl = static const githubReleasesUrl =
'https://api.github.com/repos/RarDog/Lunaris/releases'; 'https://api.github.com/repos/RarDog/Lunaris/releases';
static const latestReleaseUrl = githubLatestReleaseUrl;
static const releasesUrl = githubReleasesUrl;
static const _deviceChannel = MethodChannel('rulegel/device'); static const _deviceChannel = MethodChannel('rulegel/device');
final Dio _dio; final Dio _dio;
@@ -50,14 +63,17 @@ class UpdateService {
} }
} }
Future<Result<AppUpdateInfo?>> checkForUpdates({bool force = false}) async { Future<Result<AppUpdateInfo?>> checkForUpdates({
bool force = false,
UpdateSource source = UpdateSource.gitea,
}) async {
final settingsResult = await _settingsService.getSettings(); final settingsResult = await _settingsService.getSettings();
final settings = settingsResult is Success<AppSettings> final settings = settingsResult is Success<AppSettings>
? settingsResult.data ? settingsResult.data
: AppSettings.defaults; : AppSettings.defaults;
try { try {
final info = await _latestAllowedRelease(settings); final info = await _latestAllowedRelease(settings, source: source);
final nextSettings = settings.copyWith( final nextSettings = settings.copyWith(
lastUpdateCheckAt: DateTime.now().toIso8601String(), lastUpdateCheckAt: DateTime.now().toIso8601String(),
); );
@@ -81,16 +97,29 @@ class UpdateService {
} }
} }
Future<AppUpdateInfo> _latestAllowedRelease(AppSettings settings) async { Future<AppUpdateInfo> _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<dynamic>( final response = await _dio.get<dynamic>(
releasesUrl, rUrl,
queryParameters: {'per_page': 30}, queryParameters: isGitea ? {'limit': 30} : {'per_page': 30},
options: Options( options: options,
headers: {
'User-Agent': 'Lunaris-App',
'Accept': 'application/vnd.github+json',
},
),
); );
final items = (response.data as List?) ?? const []; final items = (response.data as List?) ?? const [];
final releases = items final releases = items
@@ -102,13 +131,8 @@ class UpdateService {
.toList(growable: false); .toList(growable: false);
if (releases.isEmpty) { if (releases.isEmpty) {
final latest = await _dio.get<dynamic>( final latest = await _dio.get<dynamic>(
latestReleaseUrl, latestUrl,
options: Options( options: options,
headers: {
'User-Agent': 'Lunaris-App',
'Accept': 'application/vnd.github+json',
},
),
); );
return _releaseFromJson(Map<String, dynamic>.from(latest.data as Map)); return _releaseFromJson(Map<String, dynamic>.from(latest.data as Map));
} }
@@ -958,12 +958,216 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
} }
Future<void> _checkUpdates(BuildContext context, WidgetRef ref) async { Future<void> _checkUpdates(BuildContext context, WidgetRef ref) async {
final isRu = settings.languageCode == 'ru';
final theme = Theme.of(context);
final selectedSource = await showModalBottomSheet<UpdateSource>(
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); final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar( 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 = final result = await ref.read(updateServiceProvider).checkForUpdates(
await ref.read(updateServiceProvider).checkForUpdates(force: true); force: true,
source: selectedSource,
);
if (!context.mounted) return; if (!context.mounted) return;
messenger.hideCurrentSnackBar(); messenger.hideCurrentSnackBar();
if (result is Error<AppUpdateInfo?>) { if (result is Error<AppUpdateInfo?>) {
@@ -973,7 +1177,11 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
final update = (result as Success<AppUpdateInfo?>).data; final update = (result as Success<AppUpdateInfo?>).data;
if (update == null) { if (update == null) {
messenger.showSnackBar( messenger.showSnackBar(
const SnackBar(content: Text('Lunaris is up to date')), SnackBar(
content: Text(
isRu ? 'У вас установлена последняя версия Lunaris' : 'Lunaris is up to date',
),
),
); );
return; return;
} }
+1 -1
View File
@@ -1,7 +1,7 @@
name: gel_rule_app name: gel_rule_app
description: Local backend/core layer for a Flutter booru browser app. description: Local backend/core layer for a Flutter booru browser app.
publish_to: "none" publish_to: "none"
version: 3.0.1+52 version: 3.0.2+53
environment: environment:
sdk: ">=3.3.0 <4.0.0" sdk: ">=3.3.0 <4.0.0"