mirror of
https://github.com/inventree/inventree-app.git
synced 2026-06-10 08:27:15 +00:00
Check for update (#830)
- Closes https://github.com/inventree/inventree-app/issues/730
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- Update file and image selection packages
|
- Update file and image selection packages
|
||||||
|
- Check for app updates
|
||||||
|
- Updated translations
|
||||||
|
|
||||||
## 0.24.2 - May 2026
|
## 0.24.2 - May 2026
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import "dart:convert";
|
||||||
|
|
||||||
|
import "package:http/http.dart" as http;
|
||||||
|
import "package:package_info_plus/package_info_plus.dart";
|
||||||
|
|
||||||
|
const String _githubReleasesUrl =
|
||||||
|
"https://api.github.com/repos/inventree/inventree-app/releases/latest";
|
||||||
|
|
||||||
|
const String _githubReleasesHtmlUrl =
|
||||||
|
"https://github.com/inventree/inventree-app/releases/latest";
|
||||||
|
|
||||||
|
class UpdateChecker {
|
||||||
|
factory UpdateChecker() => _instance;
|
||||||
|
|
||||||
|
UpdateChecker._();
|
||||||
|
|
||||||
|
static final UpdateChecker _instance = UpdateChecker._();
|
||||||
|
|
||||||
|
bool _fetched = false;
|
||||||
|
bool _newVersionAvailable = false;
|
||||||
|
String _latestVersion = "";
|
||||||
|
String _releaseUrl = _githubReleasesHtmlUrl;
|
||||||
|
|
||||||
|
bool get newVersionAvailable => _newVersionAvailable;
|
||||||
|
String get latestVersion => _latestVersion;
|
||||||
|
String get releaseUrl => _releaseUrl;
|
||||||
|
|
||||||
|
Future<void> checkForUpdate() async {
|
||||||
|
if (_fetched) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final PackageInfo info = await PackageInfo.fromPlatform();
|
||||||
|
final String currentVersion = info.version;
|
||||||
|
|
||||||
|
final response = await http
|
||||||
|
.get(
|
||||||
|
Uri.parse(_githubReleasesUrl),
|
||||||
|
headers: {"Accept": "application/vnd.github+json"},
|
||||||
|
)
|
||||||
|
.timeout(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
if (response.statusCode != 200) return;
|
||||||
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
String tagName = (data["tag_name"] as String?) ?? "";
|
||||||
|
final String htmlUrl =
|
||||||
|
(data["html_url"] as String?) ?? _githubReleasesHtmlUrl;
|
||||||
|
|
||||||
|
if (tagName.startsWith("v")) {
|
||||||
|
tagName = tagName.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName.isEmpty) return;
|
||||||
|
|
||||||
|
_latestVersion = tagName;
|
||||||
|
_releaseUrl = htmlUrl;
|
||||||
|
_newVersionAvailable = _isNewerVersion(tagName, currentVersion);
|
||||||
|
_fetched = true;
|
||||||
|
} catch (_) {
|
||||||
|
// Fail silently — no network, parse error, API rate limit, etc.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isNewerVersion(String latest, String current) {
|
||||||
|
try {
|
||||||
|
// Strip any pre-release suffix (e.g. "0.24.4-rc1" → "0.24.4")
|
||||||
|
final latestClean = latest.split("-").first;
|
||||||
|
final currentClean = current.split("-").first;
|
||||||
|
|
||||||
|
final latestParts = latestClean.split(".").map(int.parse).toList();
|
||||||
|
final currentParts = currentClean.split(".").map(int.parse).toList();
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
final l = i < latestParts.length ? latestParts[i] : 0;
|
||||||
|
final c = i < currentParts.length ? currentParts[i] : 0;
|
||||||
|
if (l > c) return true;
|
||||||
|
if (l < c) return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1872,6 +1872,9 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"@version": {},
|
"@version": {},
|
||||||
|
|
||||||
|
"versionNewer": "New version available",
|
||||||
|
"@versionNewer": {},
|
||||||
|
|
||||||
"viewDetails": "View Details",
|
"viewDetails": "View Details",
|
||||||
"@viewDetails": {},
|
"@viewDetails": {},
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/inventree/update_check.dart";
|
||||||
import "package:inventree/settings/release.dart";
|
import "package:inventree/settings/release.dart";
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
@@ -209,9 +210,24 @@ class InvenTreeAboutWidget extends StatelessWidget {
|
|||||||
title: Text(L10().version),
|
title: Text(L10().version),
|
||||||
subtitle: Text("${info.version} - Build ${info.buildNumber}"),
|
subtitle: Text("${info.version} - Build ${info.buildNumber}"),
|
||||||
leading: Icon(TablerIcons.info_circle),
|
leading: Icon(TablerIcons.info_circle),
|
||||||
|
trailing: UpdateChecker().newVersionAvailable
|
||||||
|
? Icon(TablerIcons.alert_circle, color: COLOR_WARNING)
|
||||||
|
: Icon(TablerIcons.circle_check, color: COLOR_SUCCESS),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
UpdateChecker().checkForUpdate();
|
||||||
|
|
||||||
|
if (!UpdateChecker().newVersionAvailable) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().versionNewer),
|
||||||
|
leading: Icon(TablerIcons.alert_circle, color: COLOR_WARNING),
|
||||||
|
trailing: LargeText(UpdateChecker().latestVersion),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().releaseNotes),
|
title: Text(L10().releaseNotes),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
|||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
|
import "package:inventree/inventree/update_check.dart";
|
||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
import "package:inventree/inventree/sales_order.dart";
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
@@ -44,6 +45,9 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage>
|
|||||||
// Initially load the profile and attempt server connection
|
// Initially load the profile and attempt server connection
|
||||||
_loadProfile();
|
_loadProfile();
|
||||||
|
|
||||||
|
// Check GitHub for a newer app version
|
||||||
|
_checkForUpdate();
|
||||||
|
|
||||||
InvenTreeAPI().registerCallback(() {
|
InvenTreeAPI().registerCallback(() {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -183,6 +187,16 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _checkForUpdate() async {
|
||||||
|
UpdateChecker().checkForUpdate().then((_) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
// Update the display if a new version is available
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadSettings() async {
|
Future<void> _loadSettings() async {
|
||||||
homeShowSubscribed =
|
homeShowSubscribed =
|
||||||
await InvenTreeSettingsManager().getValue(
|
await InvenTreeSettingsManager().getValue(
|
||||||
|
|||||||
Reference in New Issue
Block a user