mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
feat: improved update messaging on app bar server info
This commit is contained in:
parent
a23dfff6cf
commit
17888e68ae
6 changed files with 83 additions and 49 deletions
|
|
@ -1547,13 +1547,11 @@
|
||||||
"privacy": "Privacy",
|
"privacy": "Privacy",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"profile_drawer_app_logs": "Logs",
|
"profile_drawer_app_logs": "Logs",
|
||||||
"profile_drawer_client_out_of_date_major": "Mobile App is out of date. Please update to the latest major version.",
|
"profile_drawer_client_out_of_date": "Mobile App update available.",
|
||||||
"profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.",
|
|
||||||
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
|
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
|
||||||
"profile_drawer_github": "GitHub",
|
"profile_drawer_github": "GitHub",
|
||||||
"profile_drawer_readonly_mode": "Read-only mode enabled. Long-press the user avatar icon to exit.",
|
"profile_drawer_readonly_mode": "Read-only mode enabled. Long-press the user avatar icon to exit.",
|
||||||
"profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.",
|
"profile_drawer_server_out_of_date": "Server is out of date. Please update to the latest version.",
|
||||||
"profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.",
|
|
||||||
"profile_image_of_user": "Profile image of {user}",
|
"profile_image_of_user": "Profile image of {user}",
|
||||||
"profile_picture_set": "Profile picture set.",
|
"profile_picture_set": "Profile picture set.",
|
||||||
"public_album": "Public album",
|
"public_album": "Public album",
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ class ServerInfo {
|
||||||
final ServerFeatures serverFeatures;
|
final ServerFeatures serverFeatures;
|
||||||
final ServerConfig serverConfig;
|
final ServerConfig serverConfig;
|
||||||
final ServerDiskInfo serverDiskInfo;
|
final ServerDiskInfo serverDiskInfo;
|
||||||
final bool isVersionMismatch;
|
final bool isClientOutOfDate;
|
||||||
|
final bool isServerOutOfDate;
|
||||||
final bool isNewReleaseAvailable;
|
final bool isNewReleaseAvailable;
|
||||||
final String versionMismatchErrorMessage;
|
final String versionMismatchErrorMessage;
|
||||||
|
|
||||||
|
|
@ -19,7 +20,8 @@ class ServerInfo {
|
||||||
required this.serverFeatures,
|
required this.serverFeatures,
|
||||||
required this.serverConfig,
|
required this.serverConfig,
|
||||||
required this.serverDiskInfo,
|
required this.serverDiskInfo,
|
||||||
required this.isVersionMismatch,
|
required this.isClientOutOfDate,
|
||||||
|
required this.isServerOutOfDate,
|
||||||
required this.isNewReleaseAvailable,
|
required this.isNewReleaseAvailable,
|
||||||
required this.versionMismatchErrorMessage,
|
required this.versionMismatchErrorMessage,
|
||||||
});
|
});
|
||||||
|
|
@ -30,7 +32,8 @@ class ServerInfo {
|
||||||
ServerFeatures? serverFeatures,
|
ServerFeatures? serverFeatures,
|
||||||
ServerConfig? serverConfig,
|
ServerConfig? serverConfig,
|
||||||
ServerDiskInfo? serverDiskInfo,
|
ServerDiskInfo? serverDiskInfo,
|
||||||
bool? isVersionMismatch,
|
bool? isClientOutOfDate,
|
||||||
|
bool? isServerOutOfDate,
|
||||||
bool? isNewReleaseAvailable,
|
bool? isNewReleaseAvailable,
|
||||||
String? versionMismatchErrorMessage,
|
String? versionMismatchErrorMessage,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -40,7 +43,8 @@ class ServerInfo {
|
||||||
serverFeatures: serverFeatures ?? this.serverFeatures,
|
serverFeatures: serverFeatures ?? this.serverFeatures,
|
||||||
serverConfig: serverConfig ?? this.serverConfig,
|
serverConfig: serverConfig ?? this.serverConfig,
|
||||||
serverDiskInfo: serverDiskInfo ?? this.serverDiskInfo,
|
serverDiskInfo: serverDiskInfo ?? this.serverDiskInfo,
|
||||||
isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
|
isClientOutOfDate: isClientOutOfDate ?? this.isClientOutOfDate,
|
||||||
|
isServerOutOfDate: isServerOutOfDate ?? this.isServerOutOfDate,
|
||||||
isNewReleaseAvailable: isNewReleaseAvailable ?? this.isNewReleaseAvailable,
|
isNewReleaseAvailable: isNewReleaseAvailable ?? this.isNewReleaseAvailable,
|
||||||
versionMismatchErrorMessage: versionMismatchErrorMessage ?? this.versionMismatchErrorMessage,
|
versionMismatchErrorMessage: versionMismatchErrorMessage ?? this.versionMismatchErrorMessage,
|
||||||
);
|
);
|
||||||
|
|
@ -48,7 +52,7 @@ class ServerInfo {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ServerInfo(serverVersion: $serverVersion, latestVersion: $latestVersion, serverFeatures: $serverFeatures, serverConfig: $serverConfig, serverDiskInfo: $serverDiskInfo, isVersionMismatch: $isVersionMismatch, isNewReleaseAvailable: $isNewReleaseAvailable, versionMismatchErrorMessage: $versionMismatchErrorMessage)';
|
return 'ServerInfo(serverVersion: $serverVersion, latestVersion: $latestVersion, serverFeatures: $serverFeatures, serverConfig: $serverConfig, serverDiskInfo: $serverDiskInfo, isClientOutOfDate: $isClientOutOfDate, isServerOutOfDate: $isServerOutOfDate, isNewReleaseAvailable: $isNewReleaseAvailable, versionMismatchErrorMessage: $versionMismatchErrorMessage)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -61,7 +65,8 @@ class ServerInfo {
|
||||||
other.serverFeatures == serverFeatures &&
|
other.serverFeatures == serverFeatures &&
|
||||||
other.serverConfig == serverConfig &&
|
other.serverConfig == serverConfig &&
|
||||||
other.serverDiskInfo == serverDiskInfo &&
|
other.serverDiskInfo == serverDiskInfo &&
|
||||||
other.isVersionMismatch == isVersionMismatch &&
|
other.isClientOutOfDate == isClientOutOfDate &&
|
||||||
|
other.isServerOutOfDate == isServerOutOfDate &&
|
||||||
other.isNewReleaseAvailable == isNewReleaseAvailable &&
|
other.isNewReleaseAvailable == isNewReleaseAvailable &&
|
||||||
other.versionMismatchErrorMessage == versionMismatchErrorMessage;
|
other.versionMismatchErrorMessage == versionMismatchErrorMessage;
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +78,8 @@ class ServerInfo {
|
||||||
serverFeatures.hashCode ^
|
serverFeatures.hashCode ^
|
||||||
serverConfig.hashCode ^
|
serverConfig.hashCode ^
|
||||||
serverDiskInfo.hashCode ^
|
serverDiskInfo.hashCode ^
|
||||||
isVersionMismatch.hashCode ^
|
isClientOutOfDate.hashCode ^
|
||||||
|
isServerOutOfDate.hashCode ^
|
||||||
isNewReleaseAvailable.hashCode ^
|
isNewReleaseAvailable.hashCode ^
|
||||||
versionMismatchErrorMessage.hashCode;
|
versionMismatchErrorMessage.hashCode;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ class ServerInfoNotifier extends StateNotifier<ServerInfo> {
|
||||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||||
),
|
),
|
||||||
serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0),
|
serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0),
|
||||||
isVersionMismatch: false,
|
isClientOutOfDate: false,
|
||||||
|
isServerOutOfDate: false,
|
||||||
isNewReleaseAvailable: false,
|
isNewReleaseAvailable: false,
|
||||||
versionMismatchErrorMessage: "",
|
versionMismatchErrorMessage: "",
|
||||||
),
|
),
|
||||||
|
|
@ -43,15 +44,16 @@ class ServerInfoNotifier extends StateNotifier<ServerInfo> {
|
||||||
try {
|
try {
|
||||||
final serverVersion = await _serverInfoService.getServerVersion();
|
final serverVersion = await _serverInfoService.getServerVersion();
|
||||||
|
|
||||||
|
// using isClientOutOfDate since that will show to users reguardless of if they are an admin
|
||||||
if (serverVersion == null) {
|
if (serverVersion == null) {
|
||||||
state = state.copyWith(isVersionMismatch: true, versionMismatchErrorMessage: "common_server_error".tr());
|
state = state.copyWith(isClientOutOfDate: true, versionMismatchErrorMessage: "common_server_error".tr());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _checkServerVersionMismatch(serverVersion);
|
await _checkServerVersionMismatch(serverVersion);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
_log.severe("Failed to get server version", e, stackTrace);
|
_log.severe("Failed to get server version", e, stackTrace);
|
||||||
state = state.copyWith(isVersionMismatch: true);
|
state = state.copyWith(isClientOutOfDate: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,39 +65,23 @@ class ServerInfoNotifier extends StateNotifier<ServerInfo> {
|
||||||
|
|
||||||
Map<String, int> appVersion = _getDetailVersion(packageInfo.version);
|
Map<String, int> appVersion = _getDetailVersion(packageInfo.version);
|
||||||
|
|
||||||
if (appVersion["major"]! > serverVersion.major) {
|
if (appVersion["major"]! > serverVersion.major || appVersion["minor"]! > serverVersion.minor) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
isVersionMismatch: true,
|
isServerOutOfDate: true,
|
||||||
versionMismatchErrorMessage: "profile_drawer_server_out_of_date_major".tr(),
|
versionMismatchErrorMessage: "profile_drawer_server_out_of_date".tr(),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appVersion["major"]! < serverVersion.major) {
|
if (appVersion["major"]! < serverVersion.major || appVersion["minor"]! < serverVersion.minor) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
isVersionMismatch: true,
|
isClientOutOfDate: true,
|
||||||
versionMismatchErrorMessage: "profile_drawer_client_out_of_date_major".tr(),
|
versionMismatchErrorMessage: "profile_drawer_client_out_of_date".tr(),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appVersion["minor"]! > serverVersion.minor) {
|
state = state.copyWith(isClientOutOfDate: false, isServerOutOfDate: false, versionMismatchErrorMessage: "");
|
||||||
state = state.copyWith(
|
|
||||||
isVersionMismatch: true,
|
|
||||||
versionMismatchErrorMessage: "profile_drawer_server_out_of_date_minor".tr(),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appVersion["minor"]! < serverVersion.minor) {
|
|
||||||
state = state.copyWith(
|
|
||||||
isVersionMismatch: true,
|
|
||||||
versionMismatchErrorMessage: "profile_drawer_client_out_of_date_minor".tr(),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = state.copyWith(isVersionMismatch: false, versionMismatchErrorMessage: "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewRelease(ServerVersion serverVersion, ServerVersion latestVersion) {
|
handleNewRelease(ServerVersion serverVersion, ServerVersion latestVersion) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||||
|
|
@ -7,8 +9,10 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
||||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/utils/url_helper.dart';
|
import 'package:immich_mobile/utils/url_helper.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class AppBarServerInfo extends HookConsumerWidget {
|
class AppBarServerInfo extends HookConsumerWidget {
|
||||||
const AppBarServerInfo({super.key});
|
const AppBarServerInfo({super.key});
|
||||||
|
|
@ -18,16 +22,35 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
ref.watch(localeProvider);
|
ref.watch(localeProvider);
|
||||||
ServerInfo serverInfoState = ref.watch(serverInfoProvider);
|
ServerInfo serverInfoState = ref.watch(serverInfoProvider);
|
||||||
|
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
|
||||||
final appInfo = useState({});
|
final appInfo = useState({});
|
||||||
const titleFontSize = 12.0;
|
const titleFontSize = 12.0;
|
||||||
const contentFontSize = 11.0;
|
const contentFontSize = 11.0;
|
||||||
|
|
||||||
|
final showWarning =
|
||||||
|
serverInfoState.isClientOutOfDate ||
|
||||||
|
((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable) && false;
|
||||||
|
|
||||||
getPackageInfo() async {
|
getPackageInfo() async {
|
||||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
|
||||||
appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber};
|
appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openUpdateLink() {
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
launchUrl(Uri.parse("https://apps.apple.com/app/id1613945652"), mode: LaunchMode.externalApplication);
|
||||||
|
} else if (Platform.isAndroid) {
|
||||||
|
launchUrl(
|
||||||
|
Uri.parse("https://play.google.com/store/apps/details?id=app.alextran.immich"),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
launchUrl(Uri.parse("https://immich.app/download"), mode: LaunchMode.externalApplication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
getPackageInfo();
|
getPackageInfo();
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -45,17 +68,38 @@ class AppBarServerInfo extends HookConsumerWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
if (showWarning) ...[
|
||||||
padding: const EdgeInsets.all(8.0),
|
SizedBox(
|
||||||
child: Text(
|
width: double.infinity,
|
||||||
serverInfoState.isVersionMismatch
|
child: Container(
|
||||||
? serverInfoState.versionMismatchErrorMessage
|
decoration: const BoxDecoration(
|
||||||
: "profile_drawer_client_server_up_to_date".tr(),
|
color: Color.fromARGB(80, 243, 188, 106),
|
||||||
textAlign: TextAlign.center,
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500),
|
),
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
serverInfoState.versionMismatchErrorMessage,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
if (serverInfoState.isClientOutOfDate)
|
||||||
|
IconButton(
|
||||||
|
onPressed: openUpdateLink,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
style: IconButton.styleFrom(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
||||||
|
icon: const Icon(Icons.open_in_new, size: 16),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
|
||||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
|
],
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
isLabelVisible:
|
isLabelVisible:
|
||||||
serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
|
serverInfoState.isClientOutOfDate || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
|
||||||
offset: const Offset(-2, -12),
|
offset: const Offset(-2, -12),
|
||||||
child: user == null
|
child: user == null
|
||||||
? const Icon(Icons.face_outlined, size: widgetSize)
|
? const Icon(Icons.face_outlined, size: widgetSize)
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ class _ProfileIndicator extends ConsumerWidget {
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
isLabelVisible:
|
isLabelVisible:
|
||||||
serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
|
serverInfoState.isClientOutOfDate || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
|
||||||
offset: const Offset(-2, -12),
|
offset: const Offset(-2, -12),
|
||||||
child: user == null
|
child: user == null
|
||||||
? const Icon(Icons.face_outlined, size: widgetSize)
|
? const Icon(Icons.face_outlined, size: widgetSize)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue