merge main

This commit is contained in:
shenlong-tanwen
2026-05-14 01:11:26 +05:30
480 changed files with 19769 additions and 4914 deletions
+3 -8
View File
@@ -22,10 +22,10 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/open_in_browse
import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/set_album_cover.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/set_profile_picture_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/similar_photos_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/set_profile_picture_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart';
@@ -44,7 +44,6 @@ class ActionButtonContext {
final ActionSource source;
final bool isCasting;
final TimelineOrigin timelineOrigin;
final ThemeData? originalTheme;
final int selectedCount;
const ActionButtonContext({
@@ -59,7 +58,6 @@ class ActionButtonContext {
required this.source,
this.isCasting = false,
this.timelineOrigin = TimelineOrigin.main,
this.originalTheme,
this.selectedCount = 1,
});
}
@@ -244,7 +242,6 @@ enum ActionButtonType {
origin: context.timelineOrigin,
iconOnly: iconOnly,
menuItem: menuItem,
iconColor: context.originalTheme?.iconTheme.color,
),
ActionButtonType.similarPhotos => SimilarPhotosActionButton(
assetId: (context.asset as RemoteAsset).id,
@@ -259,14 +256,12 @@ enum ActionButtonType {
ActionButtonType.openInfo => BaseActionButton(
label: 'info'.tr(),
iconData: Icons.info_outline,
iconColor: context.originalTheme?.iconTheme.color,
menuItem: true,
onPressed: () => EventStream.shared.emit(const ViewerShowDetailsEvent()),
),
ActionButtonType.viewInTimeline => BaseActionButton(
label: 'view_in_timeline'.tr(),
iconData: Icons.image_search,
iconColor: context.originalTheme?.iconTheme.color,
iconOnly: iconOnly,
menuItem: menuItem,
onPressed: buildContext == null
@@ -320,7 +315,7 @@ class ActionButtonBuilder {
return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList();
}
static List<Widget> buildViewerKebabMenu(ActionButtonContext context, BuildContext buildContext, WidgetRef ref) {
static List<Widget> buildViewerKebabMenu(ActionButtonContext context, BuildContext buildContext) {
final visibleButtons = defaultViewerKebabMenuOrder
.where((type) => !defaultViewerBottomBarButtons.contains(type) && type.shouldShow(context))
.toList();
@@ -336,7 +331,7 @@ class ActionButtonBuilder {
if (lastGroup != null && type.kebabMenuGroup != lastGroup) {
result.add(const Divider(height: 1));
}
result.add(type.buildButton(context, buildContext, false, true).build(buildContext, ref));
result.add(type.buildButton(context, buildContext, false, true));
lastGroup = type.kebabMenuGroup;
}
+3 -1
View File
@@ -18,7 +18,9 @@ String formatBytes(int bytes) {
}
String formatHumanReadableBytes(int bytes, int decimals) {
if (bytes <= 0) return "0 B";
if (bytes <= 0) {
return "0 B";
}
const suffixes = ["B", "KiB", "MiB", "GiB", "TiB"];
var i = (log(bytes) / log(1024)).floor();
return '${(bytes / pow(1024, i)).toStringAsFixed(decimals)} ${suffixes[i]}';
+39 -13
View File
@@ -34,7 +34,9 @@ Future<void> migrateDatabaseIfNeeded(Drift drift) async {
Future<void> _migrateTo25() async {
final accessToken = Store.tryGet(StoreKey.accessToken);
if (accessToken == null || accessToken.isEmpty) return;
if (accessToken == null || accessToken.isEmpty) {
return;
}
final urls = <String>[];
final serverEndpoint = Store.tryGet(StoreKey.serverEndpoint);
@@ -50,10 +52,14 @@ Future<void> _migrateTo25() async {
final List<dynamic> list = jsonDecode(externalJson);
for (final entry in list) {
final url = AuxilaryEndpoint.fromJson(entry).url;
if (url.isNotEmpty) urls.add(url);
if (url.isNotEmpty) {
urls.add(url);
}
}
}
if (urls.isEmpty) return;
if (urls.isEmpty) {
return;
}
final customHeadersStr = Store.get(StoreKey.legacyCustomHeaders, "");
final headers = customHeadersStr.isEmpty
@@ -125,7 +131,9 @@ Future<void> _migrateTo26(Drift drift) async {
Future<void> _migrateExternalEndpointList(Drift drift, _StoreMigrator migrator) async {
final raw = await migrator.readLegacyStoreString(StoreKey.legacyExternalEndpointList.id);
if (raw == null) return;
if (raw == null) {
return;
}
final urls = <String>[];
try {
@@ -133,7 +141,9 @@ Future<void> _migrateExternalEndpointList(Drift drift, _StoreMigrator migrator)
if (decoded is List) {
for (final entry in decoded) {
final url = AuxilaryEndpoint.fromJson(entry).url;
if (url.isNotEmpty) urls.add(url);
if (url.isNotEmpty) {
urls.add(url);
}
}
}
} on FormatException {
@@ -152,14 +162,18 @@ Future<void> _migrateExternalEndpointList(Drift drift, _StoreMigrator migrator)
Future<void> _migrateCustomHeaders(Drift drift, _StoreMigrator migrator) async {
final raw = await migrator.readLegacyStoreString(StoreKey.legacyCustomHeaders.id);
if (raw == null) return;
if (raw == null) {
return;
}
final headers = <String, String>{};
try {
final decoded = jsonDecode(raw);
if (decoded is Map) {
decoded.forEach((key, value) {
if (key is String && value is String) headers[key] = value;
if (key is String && value is String) {
headers[key] = value;
}
});
}
} on FormatException {
@@ -185,7 +199,9 @@ class _StoreMigrator {
Future<void> migrateEnumIndex<T extends Enum>(StoreKey<int> legacyKey, MetadataKey<T> newKey, List<T> values) async {
final index = await readLegacyStoreInt(legacyKey.id);
if (index == null) return;
if (index == null) {
return;
}
final enumValue = values.elementAtOrNull(index) ?? newKey.defaultValue;
_cache[newKey] = enumValue;
@@ -198,7 +214,9 @@ class _StoreMigrator {
List<T> values,
) async {
final name = await readLegacyStoreString(legacyKey.id);
if (name == null) return;
if (name == null) {
return;
}
final enumValue = values.firstWhere((e) => e.name == name, orElse: () => newKey.defaultValue);
_cache[newKey] = enumValue;
@@ -207,7 +225,9 @@ class _StoreMigrator {
Future<void> migrateBool(StoreKey<bool> legacyKey, MetadataKey<bool> newKey) async {
final intValue = await readLegacyStoreInt(legacyKey.id);
if (intValue == null) return;
if (intValue == null) {
return;
}
final boolValue = intValue != 0;
_cache[newKey] = boolValue;
@@ -216,7 +236,9 @@ class _StoreMigrator {
Future<void> migrateInt(StoreKey<int> legacyKey, MetadataKey<int> newKey) async {
final intValue = await readLegacyStoreInt(legacyKey.id);
if (intValue == null) return;
if (intValue == null) {
return;
}
_cache[newKey] = intValue;
_migratedStoreIds.add(legacyKey.id);
@@ -224,7 +246,9 @@ class _StoreMigrator {
Future<void> migrateString(StoreKey<String> legacyKey, MetadataKey<String> newKey) async {
final value = await readLegacyStoreString(legacyKey.id);
if (value == null) return;
if (value == null) {
return;
}
_cache[newKey] = value;
_migratedStoreIds.add(legacyKey.id);
@@ -254,7 +278,9 @@ class _StoreMigrator {
}
Future<void> deleteLegacyStoreRows(List<int> ids) async {
if (ids.isEmpty) return;
if (ids.isEmpty) {
return;
}
await (_db.storeEntity.delete()..where((t) => t.id.isIn(ids))).go();
}
}
+3 -1
View File
@@ -63,7 +63,9 @@ class SemVer {
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (identical(this, other)) {
return true;
}
return other is SemVer && other.major == major && other.minor == minor && other.patch == patch;
}
+9 -3
View File
@@ -42,13 +42,17 @@ String? getServerUrl() {
///
String punycodeEncodeUrl(String serverUrl) {
final serverUri = Uri.tryParse(serverUrl);
if (serverUri == null || serverUri.host.isEmpty) return '';
if (serverUri == null || serverUri.host.isEmpty) {
return '';
}
final encodedHost = Uri.decodeComponent(serverUri.host)
.split('.')
.map((segment) {
// If segment is already ASCII, then return as it is.
if (segment.runes.every((c) => c < 0x80)) return segment;
if (segment.runes.every((c) => c < 0x80)) {
return segment;
}
return 'xn--${punycodeEncode(segment)}';
})
.join('.');
@@ -75,7 +79,9 @@ String punycodeEncodeUrl(String serverUrl) {
///
String? punycodeDecodeUrl(String? serverUrl) {
final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null;
if (serverUri == null || serverUri.host.isEmpty) return null;
if (serverUri == null || serverUri.host.isEmpty) {
return null;
}
final decodedHost = serverUri.host
.split('.')