No need update check for web
This commit is contained in:
parent
0bd2a94ece
commit
9267ebee2c
|
|
@ -2,13 +2,14 @@
|
|||
/// `AppUpdateService` fetches the most recent entry and compares it against the
|
||||
/// running application's build number.
|
||||
class AppVersion {
|
||||
/// Incrementing integer that matches the Android `versionCode`.
|
||||
final int versionCode;
|
||||
/// Version string, e.g. `1.2.3` or `2026.03.12`. Stored as text in
|
||||
/// the database so semantic versions are allowed.
|
||||
final String versionCode;
|
||||
|
||||
/// If the device is running a build number that is *strictly less* than this
|
||||
/// value the update is considered "forced" and the user cannot continue
|
||||
/// using the existing install.
|
||||
final int minVersionRequired;
|
||||
/// using the existing install. Also a string.
|
||||
final String minVersionRequired;
|
||||
|
||||
/// A publicly‑accessible URL pointing at an APK stored in Supabase Storage.
|
||||
final String downloadUrl;
|
||||
|
|
@ -25,8 +26,8 @@ class AppVersion {
|
|||
|
||||
factory AppVersion.fromMap(Map<String, dynamic> map) {
|
||||
return AppVersion(
|
||||
versionCode: map['version_code'] as int,
|
||||
minVersionRequired: map['min_version_required'] as int,
|
||||
versionCode: map['version_code']?.toString() ?? '',
|
||||
minVersionRequired: map['min_version_required']?.toString() ?? '',
|
||||
downloadUrl: map['download_url'] as String,
|
||||
releaseNotes: map['release_notes'] as String? ?? '',
|
||||
);
|
||||
|
|
@ -40,8 +41,9 @@ class AppUpdateInfo {
|
|||
/// table was empty (should not happen in normal operation).
|
||||
final AppVersion? latestVersion;
|
||||
|
||||
/// Current build number as reported by ``package_info_plus``.
|
||||
final int currentBuildNumber;
|
||||
/// Current build number / version string. May be empty on non-Android
|
||||
/// platforms since checkForUpdate is skipped there.
|
||||
final String currentBuildNumber;
|
||||
|
||||
/// ``true`` when ``currentBuildNumber < latestVersion.versionCode``.
|
||||
final bool isUpdateAvailable;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
|
|
@ -41,16 +42,40 @@ class _AppUpdateScreenState extends ConsumerState<AppUpdateScreen> {
|
|||
Future<void> _loadCurrent() async {
|
||||
try {
|
||||
final client = Supabase.instance.client;
|
||||
final data = await client
|
||||
.from('app_versions')
|
||||
.select()
|
||||
.order('version_code', ascending: false)
|
||||
.limit(1)
|
||||
.maybeSingle();
|
||||
if (data is Map<String, dynamic>) {
|
||||
_versionController.text = data['version_code']?.toString() ?? '';
|
||||
_minController.text = data['min_version_required']?.toString() ?? '';
|
||||
_notesController.text = data['release_notes'] ?? '';
|
||||
final rows = await client.from('app_versions').select().maybeSingle();
|
||||
// when using text versions we can't rely on server-side ordering; instead
|
||||
// parse locally and choose the greatest semantic version.
|
||||
if (rows is List) {
|
||||
final rowList = rows as List?;
|
||||
Version? best;
|
||||
Map<String, dynamic>? bestRow;
|
||||
if (rowList != null) {
|
||||
for (final r in rowList) {
|
||||
if (r is Map<String, dynamic>) {
|
||||
final v = r['version_code']?.toString() ?? '';
|
||||
Version parsed;
|
||||
try {
|
||||
parsed = Version.parse(v);
|
||||
} catch (_) {
|
||||
continue;
|
||||
}
|
||||
if (best == null || parsed > best) {
|
||||
best = parsed;
|
||||
bestRow = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestRow != null) {
|
||||
_versionController.text = bestRow['version_code']?.toString() ?? '';
|
||||
_minController.text =
|
||||
bestRow['min_version_required']?.toString() ?? '';
|
||||
_notesController.text = bestRow['release_notes'] ?? '';
|
||||
}
|
||||
} else if (rows is Map<String, dynamic>) {
|
||||
_versionController.text = rows['version_code']?.toString() ?? '';
|
||||
_minController.text = rows['min_version_required']?.toString() ?? '';
|
||||
_notesController.text = rows['release_notes'] ?? '';
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
|
@ -169,10 +194,11 @@ class _AppUpdateScreenState extends ConsumerState<AppUpdateScreen> {
|
|||
} else {
|
||||
url = '';
|
||||
}
|
||||
if (url.isEmpty)
|
||||
if (url.isEmpty) {
|
||||
throw Exception(
|
||||
'could not obtain public url, check bucket CORS and policies',
|
||||
);
|
||||
}
|
||||
_logs.add('Public URL: $url');
|
||||
// upsert new version in a single statement
|
||||
await client.from('app_versions').upsert({
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:ota_update/ota_update.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
|
|
@ -48,26 +49,48 @@ class AppUpdateService {
|
|||
/// (network down, no supabase instance) are simply re‑thrown; the caller can
|
||||
/// decide whether to ignore them or surface them to the user.
|
||||
Future<AppUpdateInfo> checkForUpdate() async {
|
||||
final pkg = await PackageInfo.fromPlatform();
|
||||
final currentBuild = int.tryParse(pkg.buildNumber) ?? 0;
|
||||
|
||||
final serverVersion = await _fetchLatestVersion();
|
||||
|
||||
if (serverVersion == null) {
|
||||
// table empty – nothing to do
|
||||
// only run on Android devices; web and other platforms skip
|
||||
if (!Platform.isAndroid) {
|
||||
return AppUpdateInfo(
|
||||
currentBuildNumber: currentBuild,
|
||||
currentBuildNumber: '',
|
||||
latestVersion: null,
|
||||
isUpdateAvailable: false,
|
||||
isForceUpdate: false,
|
||||
);
|
||||
}
|
||||
|
||||
final available = currentBuild < serverVersion.versionCode;
|
||||
final force = currentBuild < serverVersion.minVersionRequired;
|
||||
final pkg = await PackageInfo.fromPlatform();
|
||||
final currentVersion = pkg.buildNumber.trim();
|
||||
|
||||
final serverVersion = await _fetchLatestVersion();
|
||||
|
||||
if (serverVersion == null) {
|
||||
return AppUpdateInfo(
|
||||
currentBuildNumber: currentVersion,
|
||||
latestVersion: null,
|
||||
isUpdateAvailable: false,
|
||||
isForceUpdate: false,
|
||||
);
|
||||
}
|
||||
|
||||
// compare using semantic versioning where possible
|
||||
Version safeParse(String s) {
|
||||
try {
|
||||
return Version.parse(s);
|
||||
} catch (_) {
|
||||
return Version(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
final vCurrent = safeParse(currentVersion);
|
||||
final vLatest = safeParse(serverVersion.versionCode);
|
||||
final vMin = safeParse(serverVersion.minVersionRequired);
|
||||
|
||||
final available = vCurrent < vLatest;
|
||||
final force = vCurrent < vMin;
|
||||
|
||||
return AppUpdateInfo(
|
||||
currentBuildNumber: currentBuild,
|
||||
currentBuildNumber: currentVersion,
|
||||
latestVersion: serverVersion,
|
||||
isUpdateAvailable: available,
|
||||
isForceUpdate: force,
|
||||
|
|
|
|||
|
|
@ -1438,7 +1438,7 @@ packages:
|
|||
source: hosted
|
||||
version: "2.1.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ dependencies:
|
|||
flutter_image_compress: ^2.4.0
|
||||
dio: ^5.1.2
|
||||
package_info_plus: ^9.0.0
|
||||
pub_semver: ^2.1.1
|
||||
ota_update: ^7.1.0
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user