mirror of
https://github.com/immich-app/immich.git
synced 2025-11-10 00:33:17 -05:00
feat: check server feature to render OCR search option (#23325)
This commit is contained in:
parent
9676da27c9
commit
106effca2e
@ -5,33 +5,37 @@ class ServerFeatures {
|
|||||||
final bool map;
|
final bool map;
|
||||||
final bool oauthEnabled;
|
final bool oauthEnabled;
|
||||||
final bool passwordLogin;
|
final bool passwordLogin;
|
||||||
|
final bool ocr;
|
||||||
|
|
||||||
const ServerFeatures({
|
const ServerFeatures({
|
||||||
required this.trash,
|
required this.trash,
|
||||||
required this.map,
|
required this.map,
|
||||||
required this.oauthEnabled,
|
required this.oauthEnabled,
|
||||||
required this.passwordLogin,
|
required this.passwordLogin,
|
||||||
|
this.ocr = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin}) {
|
ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin, bool? ocr}) {
|
||||||
return ServerFeatures(
|
return ServerFeatures(
|
||||||
trash: trash ?? this.trash,
|
trash: trash ?? this.trash,
|
||||||
map: map ?? this.map,
|
map: map ?? this.map,
|
||||||
oauthEnabled: oauthEnabled ?? this.oauthEnabled,
|
oauthEnabled: oauthEnabled ?? this.oauthEnabled,
|
||||||
passwordLogin: passwordLogin ?? this.passwordLogin,
|
passwordLogin: passwordLogin ?? this.passwordLogin,
|
||||||
|
ocr: ocr ?? this.ocr,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ServerFeatures(trash: $trash, map: $map, oauthEnabled: $oauthEnabled, passwordLogin: $passwordLogin)';
|
return 'ServerFeatures(trash: $trash, map: $map, oauthEnabled: $oauthEnabled, passwordLogin: $passwordLogin, ocr: $ocr)';
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerFeatures.fromDto(ServerFeaturesDto dto)
|
ServerFeatures.fromDto(ServerFeaturesDto dto)
|
||||||
: trash = dto.trash,
|
: trash = dto.trash,
|
||||||
map = dto.map,
|
map = dto.map,
|
||||||
oauthEnabled = dto.oauth,
|
oauthEnabled = dto.oauth,
|
||||||
passwordLogin = dto.passwordLogin;
|
passwordLogin = dto.passwordLogin,
|
||||||
|
ocr = dto.ocr;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant ServerFeatures other) {
|
bool operator ==(covariant ServerFeatures other) {
|
||||||
@ -40,11 +44,12 @@ class ServerFeatures {
|
|||||||
return other.trash == trash &&
|
return other.trash == trash &&
|
||||||
other.map == map &&
|
other.map == map &&
|
||||||
other.oauthEnabled == oauthEnabled &&
|
other.oauthEnabled == oauthEnabled &&
|
||||||
other.passwordLogin == passwordLogin;
|
other.passwordLogin == passwordLogin &&
|
||||||
|
other.ocr == ocr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return trash.hashCode ^ map.hashCode ^ oauthEnabled.hashCode ^ passwordLogin.hashCode;
|
return trash.hashCode ^ map.hashCode ^ oauthEnabled.hashCode ^ passwordLogin.hashCode ^ ocr.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart
|
|||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/feature_check.dart';
|
||||||
import 'package:immich_mobile/widgets/common/search_field.dart';
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
import 'package:immich_mobile/widgets/search/search_filter/camera_picker.dart';
|
import 'package:immich_mobile/widgets/search/search_filter/camera_picker.dart';
|
||||||
import 'package:immich_mobile/widgets/search/search_filter/display_option_picker.dart';
|
import 'package:immich_mobile/widgets/search/search_filter/display_option_picker.dart';
|
||||||
@ -503,23 +504,26 @@ class DriftSearchPage extends HookConsumerWidget {
|
|||||||
searchHintText.value = 'search_by_description_example'.t(context: context);
|
searchHintText.value = 'search_by_description_example'.t(context: context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuItemButton(
|
FeatureCheck(
|
||||||
child: ListTile(
|
feature: (features) => features.ocr,
|
||||||
leading: const Icon(Icons.document_scanner_outlined),
|
child: MenuItemButton(
|
||||||
title: Text(
|
child: ListTile(
|
||||||
'search_by_ocr'.t(context: context),
|
leading: const Icon(Icons.document_scanner_outlined),
|
||||||
style: context.textTheme.bodyLarge?.copyWith(
|
title: Text(
|
||||||
fontWeight: FontWeight.w500,
|
'search_by_ocr'.t(context: context),
|
||||||
color: textSearchType.value == TextSearchType.ocr ? context.colorScheme.primary : null,
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: textSearchType.value == TextSearchType.ocr ? context.colorScheme.primary : null,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
selectedColor: context.colorScheme.primary,
|
||||||
|
selected: textSearchType.value == TextSearchType.ocr,
|
||||||
),
|
),
|
||||||
selectedColor: context.colorScheme.primary,
|
onPressed: () {
|
||||||
selected: textSearchType.value == TextSearchType.ocr,
|
textSearchType.value = TextSearchType.ocr;
|
||||||
|
searchHintText.value = 'search_by_ocr_example'.t(context: context);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
textSearchType.value = TextSearchType.ocr;
|
|
||||||
searchHintText.value = 'search_by_ocr_example'.t(context: context);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -46,6 +46,11 @@ dynamic upgradeDto(dynamic value, String targetType) {
|
|||||||
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
||||||
addDefault(value, 'hasProfileImage', false);
|
addDefault(value, 'hasProfileImage', false);
|
||||||
}
|
}
|
||||||
|
case 'ServerFeaturesDto':
|
||||||
|
if (value is Map) {
|
||||||
|
addDefault(value, 'ocr', false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
mobile/lib/widgets/common/feature_check.dart
Normal file
38
mobile/lib/widgets/common/feature_check.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_features.model.dart';
|
||||||
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
|
|
||||||
|
/// A utility widget that conditionally renders its child based on a server feature flag.
|
||||||
|
///
|
||||||
|
/// Example usage:
|
||||||
|
/// ```dart
|
||||||
|
/// FeatureCheck(
|
||||||
|
/// feature: (features) => features.ocr,
|
||||||
|
/// child: Text('OCR is enabled'),
|
||||||
|
/// fallback: Text('OCR is not available'),
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
class FeatureCheck extends ConsumerWidget {
|
||||||
|
/// A function that extracts the specific feature flag from ServerFeatures
|
||||||
|
final bool Function(ServerFeatures) feature;
|
||||||
|
|
||||||
|
/// The widget to display when the feature is enabled
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
/// Optional widget to display when the feature is disabled
|
||||||
|
final Widget? fallback;
|
||||||
|
|
||||||
|
const FeatureCheck({super.key, required this.feature, required this.child, this.fallback});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final serverFeatures = ref.watch(serverInfoProvider.select((s) => s.serverFeatures));
|
||||||
|
final isFeatureEnabled = feature(serverFeatures);
|
||||||
|
if (isFeatureEnabled) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback ?? const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user