123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- import "dart:async";
- import "package:flutter/material.dart";
- import "package:intl/intl.dart";
- import "package:photos/core/event_bus.dart";
- import 'package:photos/events/embedding_updated_event.dart';
- import "package:photos/generated/l10n.dart";
- import "package:photos/services/feature_flag_service.dart";
- import "package:photos/services/semantic_search/frameworks/ml_framework.dart";
- import "package:photos/services/semantic_search/semantic_search_service.dart";
- import "package:photos/theme/ente_theme.dart";
- import "package:photos/ui/common/loading_widget.dart";
- import "package:photos/ui/components/buttons/icon_button_widget.dart";
- import "package:photos/ui/components/captioned_text_widget.dart";
- import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart";
- import "package:photos/ui/components/menu_section_description_widget.dart";
- import "package:photos/ui/components/menu_section_title.dart";
- import "package:photos/ui/components/title_bar_title_widget.dart";
- import "package:photos/ui/components/title_bar_widget.dart";
- import "package:photos/ui/components/toggle_switch_widget.dart";
- import "package:photos/utils/local_settings.dart";
- class MachineLearningSettingsPage extends StatefulWidget {
- const MachineLearningSettingsPage({super.key});
- @override
- State<MachineLearningSettingsPage> createState() =>
- _MachineLearningSettingsPageState();
- }
- class _MachineLearningSettingsPageState
- extends State<MachineLearningSettingsPage> {
- late InitializationState _state;
- late StreamSubscription<MLFrameworkInitializationUpdateEvent>
- _eventSubscription;
- @override
- void initState() {
- super.initState();
- _eventSubscription =
- Bus.instance.on<MLFrameworkInitializationUpdateEvent>().listen((event) {
- _fetchState();
- setState(() {});
- });
- _fetchState();
- }
- void _fetchState() {
- _state = SemanticSearchService.instance.getFrameworkInitializationState();
- }
- @override
- void dispose() {
- super.dispose();
- _eventSubscription.cancel();
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- body: CustomScrollView(
- primary: false,
- slivers: <Widget>[
- TitleBarWidget(
- flexibleSpaceTitle: TitleBarTitleWidget(
- title: S.of(context).machineLearning,
- ),
- actionIcons: [
- IconButtonWidget(
- icon: Icons.close_outlined,
- iconButtonType: IconButtonType.secondary,
- onTap: () {
- Navigator.pop(context);
- Navigator.pop(context);
- Navigator.pop(context);
- },
- ),
- ],
- ),
- SliverList(
- delegate: SliverChildBuilderDelegate(
- (delegateBuildContext, index) {
- return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 20),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- _getMagicSearchSettings(context),
- ],
- ),
- ),
- );
- },
- childCount: 1,
- ),
- ),
- ],
- ),
- );
- }
- Widget _getMagicSearchSettings(BuildContext context) {
- final colorScheme = getEnteColorScheme(context);
- final hasEnabled = LocalSettings.instance.hasEnabledMagicSearch();
- return Column(
- children: [
- MenuItemWidget(
- captionedTextWidget: CaptionedTextWidget(
- title: S.of(context).magicSearch,
- ),
- menuItemColor: colorScheme.fillFaint,
- trailingWidget: ToggleSwitchWidget(
- value: () => LocalSettings.instance.hasEnabledMagicSearch(),
- onChanged: () async {
- await LocalSettings.instance.setShouldEnableMagicSearch(
- !LocalSettings.instance.hasEnabledMagicSearch(),
- );
- if (LocalSettings.instance.hasEnabledMagicSearch()) {
- unawaited(
- SemanticSearchService.instance
- .init(shouldSyncImmediately: true),
- );
- } else {
- await SemanticSearchService.instance.clearQueue();
- }
- setState(() {});
- },
- ),
- singleBorderRadius: 8,
- alignCaptionedTextToLeft: true,
- isGestureDetectorDisabled: true,
- ),
- const SizedBox(
- height: 4,
- ),
- MenuSectionDescriptionWidget(
- content: S.of(context).magicSearchDescription,
- ),
- const SizedBox(
- height: 12,
- ),
- hasEnabled
- ? Column(
- children: [
- _state == InitializationState.initialized
- ? const MagicSearchIndexStatsWidget()
- : ModelLoadingState(_state),
- const SizedBox(
- height: 12,
- ),
- FeatureFlagService.instance.isInternalUserOrDebugBuild()
- ? MenuItemWidget(
- leadingIcon: Icons.delete_sweep_outlined,
- captionedTextWidget: CaptionedTextWidget(
- title: S.of(context).clearIndexes,
- ),
- menuItemColor: getEnteColorScheme(context).fillFaint,
- singleBorderRadius: 8,
- alwaysShowSuccessState: true,
- onTap: () async {
- await SemanticSearchService.instance.clearIndexes();
- if (mounted) {
- setState(() => {});
- }
- },
- )
- : const SizedBox.shrink(),
- ],
- )
- : const SizedBox.shrink(),
- ],
- );
- }
- }
- class ModelLoadingState extends StatelessWidget {
- final InitializationState state;
- const ModelLoadingState(
- this.state, {
- Key? key,
- }) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return Column(
- children: [
- MenuSectionTitle(title: S.of(context).status),
- MenuItemWidget(
- captionedTextWidget: CaptionedTextWidget(
- title: _getTitle(context),
- ),
- trailingWidget: EnteLoadingWidget(
- size: 12,
- color: getEnteColorScheme(context).fillMuted,
- ),
- singleBorderRadius: 8,
- alignCaptionedTextToLeft: true,
- isGestureDetectorDisabled: true,
- ),
- ],
- );
- }
- String _getTitle(BuildContext context) {
- switch (state) {
- case InitializationState.waitingForNetwork:
- return S.of(context).waitingForWifi;
- default:
- return S.of(context).loadingModel;
- }
- }
- }
- class MagicSearchIndexStatsWidget extends StatefulWidget {
- const MagicSearchIndexStatsWidget({
- super.key,
- });
- @override
- State<MagicSearchIndexStatsWidget> createState() =>
- _MagicSearchIndexStatsWidgetState();
- }
- class _MagicSearchIndexStatsWidgetState
- extends State<MagicSearchIndexStatsWidget> {
- IndexStatus? _status;
- late StreamSubscription<EmbeddingUpdatedEvent> _eventSubscription;
- @override
- void initState() {
- super.initState();
- _eventSubscription =
- Bus.instance.on<EmbeddingUpdatedEvent>().listen((event) {
- _fetchIndexStatus();
- });
- _fetchIndexStatus();
- }
- void _fetchIndexStatus() {
- SemanticSearchService.instance.getIndexStatus().then((status) {
- _status = status;
- setState(() {});
- });
- }
- @override
- void dispose() {
- super.dispose();
- _eventSubscription.cancel();
- }
- @override
- Widget build(BuildContext context) {
- if (_status == null) {
- return const EnteLoadingWidget();
- }
- return Column(
- children: [
- Row(
- children: [
- MenuSectionTitle(title: S.of(context).status),
- Expanded(child: Container()),
- _status!.pendingItems > 0
- ? EnteLoadingWidget(
- color: getEnteColorScheme(context).fillMuted,
- )
- : const SizedBox.shrink(),
- ],
- ),
- MenuItemWidget(
- captionedTextWidget: CaptionedTextWidget(
- title: S.of(context).indexedItems,
- ),
- trailingWidget: Text(
- NumberFormat().format(_status!.indexedItems),
- style: Theme.of(context).textTheme.bodySmall,
- ),
- singleBorderRadius: 8,
- alignCaptionedTextToLeft: true,
- isGestureDetectorDisabled: true,
- // Setting a key here to ensure trailingWidget is refreshed
- key: ValueKey("indexed_items_" + _status!.indexedItems.toString()),
- ),
- MenuItemWidget(
- captionedTextWidget: CaptionedTextWidget(
- title: S.of(context).pendingItems,
- ),
- trailingWidget: Text(
- NumberFormat().format(_status!.pendingItems),
- style: Theme.of(context).textTheme.bodySmall,
- ),
- singleBorderRadius: 8,
- alignCaptionedTextToLeft: true,
- isGestureDetectorDisabled: true,
- // Setting a key here to ensure trailingWidget is refreshed
- key: ValueKey("pending_items_" + _status!.pendingItems.toString()),
- ),
- ],
- );
- }
- }
|