Fladder/lib/util/update_checker.dart
PartyDonut 2c71dde228
feature: Added update notification and download links per platform (#362)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
2025-06-01 21:36:50 +02:00

150 lines
4.9 KiB
Dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
class ReleaseInfo {
final String version;
final String changelog;
final String url;
final bool isNewerThanCurrent;
final Map<String, String> downloads;
ReleaseInfo({
required this.version,
required this.changelog,
required this.url,
required this.isNewerThanCurrent,
required this.downloads,
});
String? downloadUrlFor(String platform) => downloads[platform];
Map<String, String> get preferredDownloads {
final group = _platformGroup();
final entries = downloads.entries.where((e) => e.key.contains(group));
return Map.fromEntries(entries);
}
Map<String, String> get otherDownloads {
final group = _platformGroup();
final entries = downloads.entries.where((e) => !e.key.contains(group));
return Map.fromEntries(entries);
}
String _platformGroup() {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return 'android';
case TargetPlatform.iOS:
return 'ios';
case TargetPlatform.windows:
return 'windows';
case TargetPlatform.macOS:
return 'macos';
case TargetPlatform.linux:
return 'linux';
default:
return '';
}
}
}
extension DownloadLabelFormatter on String {
String prettifyKey() {
final parts = split('_');
if (parts.isEmpty) return this;
final base = parts.first.capitalize();
if (parts.length == 1) return base;
final variant = parts.sublist(1).join(' ').capitalize();
return '$base ($variant)';
}
String capitalize() => isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}';
}
class UpdateChecker {
final String owner = 'DonutWare';
final String repo = 'Fladder';
Future<List<ReleaseInfo>> fetchRecentReleases({int count = 5}) async {
final info = await PackageInfo.fromPlatform();
final currentVersion = info.version;
final url = Uri.parse('https://api.github.com/repos/$owner/$repo/releases?per_page=$count');
final response = await http.get(url);
if (response.statusCode != 200) {
print('Failed to fetch releases: ${response.statusCode}');
return [];
}
final List<dynamic> releases = jsonDecode(response.body);
return releases.map((json) {
final tag = (json['tag_name'] as String?)?.replaceFirst(RegExp(r'^v'), '');
final changelog = json['body'] as String? ?? '';
final htmlUrl = json['html_url'] as String? ?? '';
final assets = json['assets'] as List<dynamic>? ?? [];
final Map<String, String> downloads = {};
for (final asset in assets) {
final name = asset['name'] as String? ?? '';
final downloadUrl = asset['browser_download_url'] as String? ?? '';
if (name.contains('Android') && name.endsWith('.apk')) {
downloads['android'] = downloadUrl;
} else if (name.contains('iOS') && name.endsWith('.ipa')) {
downloads['ios'] = downloadUrl;
} else if (name.contains('Windows') && name.endsWith('Setup.exe')) {
downloads['windows_installer'] = downloadUrl;
} else if (name.contains('Windows') && name.endsWith('.zip')) {
downloads['windows_portable'] = downloadUrl;
} else if (name.contains('macOS') && name.endsWith('.dmg')) {
downloads['macos'] = downloadUrl;
} else if (name.contains('Linux') && name.endsWith('.AppImage')) {
downloads['linux_appimage'] = downloadUrl;
} else if (name.contains('Linux') && name.endsWith('.flatpak')) {
downloads['linux_flatpak'] = downloadUrl;
} else if (name.contains('Linux') && name.endsWith('.zip')) {
downloads['linux_zip'] = downloadUrl;
} else if (name.contains('Linux') && name.endsWith('.zsync')) {
downloads['linux_zsync'] = downloadUrl;
} else if (name.contains('Web') && name.endsWith('.zip')) {
downloads['web'] = downloadUrl;
}
}
bool isNewer = tag != null && _compareVersions(tag, currentVersion) > 0;
return ReleaseInfo(
version: tag ?? 'unknown',
changelog: changelog.trim(),
url: htmlUrl,
isNewerThanCurrent: isNewer,
downloads: downloads,
);
}).toList();
}
Future<bool> isUpToDate() async {
final releases = await fetchRecentReleases(count: 1);
if (releases.isEmpty) return true;
return !releases.first.isNewerThanCurrent;
}
static int _compareVersions(String a, String b) {
final aParts = a.split('.').map(int.tryParse).toList();
final bParts = b.split('.').map(int.tryParse).toList();
for (var i = 0; i < aParts.length || i < bParts.length; i++) {
final aVal = i < aParts.length ? (aParts[i] ?? 0) : 0;
final bVal = i < bParts.length ? (bParts[i] ?? 0) : 0;
if (aVal != bVal) return aVal.compareTo(bVal);
}
return 0;
}
}