immich/mobile/lib/providers/server_info.provider.dart
Brandon Wees 23a34bee6f
feat: improved update messaging on app bar server info (#22938)
* feat: improved update messaging on app bar server info

* chore: message improvements

* chore: failed to fetch version error message

* feat: open latest release when tapping "Update" on server out of date message

* fix: text alignment states

* chore: code review updates

* Apply suggestion from @alextran1502

Co-authored-by: Alex <alex.tran1502@gmail.com>

* Apply suggestion from @alextran1502

Co-authored-by: Alex <alex.tran1502@gmail.com>

* chore: lots of rework of the version checking code to be cleaner

Added a semver utility class to simplify comparisons, broke the update notification logic into own widget, reworked view construction and colors.

* fix: show warnign without having to tap on app bar icon

* chore: colors

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-10-20 21:13:31 +00:00

110 lines
4.3 KiB
Dart

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/models/server_info/server_config.model.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/models/server_info/server_features.model.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:immich_mobile/models/server_info/server_version.model.dart';
import 'package:immich_mobile/services/server_info.service.dart';
import 'package:immich_mobile/utils/semver.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
class ServerInfoNotifier extends StateNotifier<ServerInfo> {
ServerInfoNotifier(this._serverInfoService)
: super(
const ServerInfo(
serverVersion: ServerVersion(major: 0, minor: 0, patch: 0),
latestVersion: ServerVersion(major: 0, minor: 0, patch: 0),
serverFeatures: ServerFeatures(map: true, trash: true, oauthEnabled: false, passwordLogin: true),
serverConfig: ServerConfig(
trashDays: 30,
oauthButtonText: '',
externalDomain: '',
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
),
serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0),
versionStatus: VersionStatus.upToDate,
),
);
final ServerInfoService _serverInfoService;
final _log = Logger("ServerInfoNotifier");
Future<void> getServerInfo() async {
await getServerVersion();
await getServerFeatures();
await getServerConfig();
}
Future<void> getServerVersion() async {
try {
final serverVersion = await _serverInfoService.getServerVersion();
// using isClientOutOfDate since that will show to users reguardless of if they are an admin
if (serverVersion == null) {
state = state.copyWith(versionStatus: VersionStatus.error);
return;
}
await _checkServerVersionMismatch(serverVersion);
} catch (e, stackTrace) {
_log.severe("Failed to get server version", e, stackTrace);
state = state.copyWith(versionStatus: VersionStatus.error);
return;
}
}
_checkServerVersionMismatch(ServerVersion serverVersion, {ServerVersion? latestVersion}) async {
state = state.copyWith(serverVersion: serverVersion, latestVersion: latestVersion);
var packageInfo = await PackageInfo.fromPlatform();
SemVer clientVersion = SemVer.fromString(packageInfo.version);
if (serverVersion < clientVersion || (latestVersion != null && serverVersion < latestVersion)) {
state = state.copyWith(versionStatus: VersionStatus.serverOutOfDate);
return;
}
if (clientVersion < serverVersion) {
state = state.copyWith(versionStatus: VersionStatus.clientOutOfDate);
return;
}
state = state.copyWith(versionStatus: VersionStatus.upToDate);
}
handleReleaseInfo(ServerVersion serverVersion, ServerVersion latestVersion) {
// Update local server version
_checkServerVersionMismatch(serverVersion, latestVersion: latestVersion);
}
getServerFeatures() async {
final serverFeatures = await _serverInfoService.getServerFeatures();
if (serverFeatures == null) {
return;
}
state = state.copyWith(serverFeatures: serverFeatures);
}
getServerConfig() async {
final serverConfig = await _serverInfoService.getServerConfig();
if (serverConfig == null) {
return;
}
state = state.copyWith(serverConfig: serverConfig);
}
}
final serverInfoProvider = StateNotifierProvider<ServerInfoNotifier, ServerInfo>((ref) {
return ServerInfoNotifier(ref.read(serverInfoServiceProvider));
});
final versionWarningPresentProvider = Provider.family<bool, UserDto?>((ref, user) {
final serverInfo = ref.watch(serverInfoProvider);
return serverInfo.versionStatus == VersionStatus.clientOutOfDate ||
serverInfo.versionStatus == VersionStatus.error ||
((user?.isAdmin ?? false) && serverInfo.versionStatus == VersionStatus.serverOutOfDate);
});