ml download progress reporting
This commit is contained in:
@@ -205,3 +205,43 @@ class EngineReadyState extends _$EngineReadyState {
|
||||
return currentState;
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream of ML model progress events
|
||||
@Riverpod(keepAlive: true)
|
||||
Stream<MlProgressData> mlProgressEvents(Ref ref) {
|
||||
final service = ref.watch(eventServiceProvider);
|
||||
return service.mlProgressEvents;
|
||||
}
|
||||
|
||||
/// Tracks active ML model downloads
|
||||
@Riverpod()
|
||||
class MlDownloadState extends _$MlDownloadState {
|
||||
@override
|
||||
MlProgressData? build() {
|
||||
ref.listen(
|
||||
mlProgressEventsProvider,
|
||||
(previous, next) {
|
||||
next.whenData((progress) {
|
||||
if (progress.type == MlProgressType.downloading) {
|
||||
if (progress.status == MlProgressStatus.done) {
|
||||
// Keep showing for 2 seconds after completion
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
if (state != null && state!.id == progress.id) {
|
||||
state = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
state = progress;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,3 +298,106 @@ abstract class _$EngineReadyState extends $Notifier<bool> {
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream of ML model progress events
|
||||
|
||||
@ProviderFor(mlProgressEvents)
|
||||
const mlProgressEventsProvider = MlProgressEventsProvider._();
|
||||
|
||||
/// Stream of ML model progress events
|
||||
|
||||
final class MlProgressEventsProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<MlProgressData>,
|
||||
MlProgressData,
|
||||
Stream<MlProgressData>
|
||||
>
|
||||
with $FutureModifier<MlProgressData>, $StreamProvider<MlProgressData> {
|
||||
/// Stream of ML model progress events
|
||||
const MlProgressEventsProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'mlProgressEventsProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$mlProgressEventsHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$StreamProviderElement<MlProgressData> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $StreamProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Stream<MlProgressData> create(Ref ref) {
|
||||
return mlProgressEvents(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$mlProgressEventsHash() => r'41c1e6aece7f9ee2bebe5d189c6bec753c2fefc8';
|
||||
|
||||
/// Tracks active ML model downloads
|
||||
|
||||
@ProviderFor(MlDownloadState)
|
||||
const mlDownloadStateProvider = MlDownloadStateProvider._();
|
||||
|
||||
/// Tracks active ML model downloads
|
||||
final class MlDownloadStateProvider
|
||||
extends $NotifierProvider<MlDownloadState, MlProgressData?> {
|
||||
/// Tracks active ML model downloads
|
||||
const MlDownloadStateProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'mlDownloadStateProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$mlDownloadStateHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
MlDownloadState create() => MlDownloadState();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(MlProgressData? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<MlProgressData?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$mlDownloadStateHash() => r'10be3767d448ea8fca8bf35578c343f3b9ddbb4e';
|
||||
|
||||
/// Tracks active ML model downloads
|
||||
|
||||
abstract class _$MlDownloadState extends $Notifier<MlProgressData?> {
|
||||
MlProgressData? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<MlProgressData?, MlProgressData?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<MlProgressData?, MlProgressData?>,
|
||||
MlProgressData?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -30,8 +30,12 @@ part 'tab_view_controllers.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class TabSuggestionsController extends _$TabSuggestionsController {
|
||||
void toggle() {
|
||||
state = !state;
|
||||
void enable() {
|
||||
state = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
state = false;
|
||||
}
|
||||
|
||||
void hide() {
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ final class TabSuggestionsControllerProvider
|
||||
}
|
||||
|
||||
String _$tabSuggestionsControllerHash() =>
|
||||
r'c60912e474177f2a0f15167743db439d157cd852';
|
||||
r'27a22674f9e4efb01e70120338202e95acef4791';
|
||||
|
||||
abstract class _$TabSuggestionsController extends $Notifier<bool> {
|
||||
bool build();
|
||||
|
||||
+68
-12
@@ -6,6 +6,7 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_view_controllers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
|
||||
@@ -142,20 +143,75 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
final tabSuggestionsEnabled = ref.watch(
|
||||
tabSuggestionsControllerProvider,
|
||||
);
|
||||
final downloadProgress = ref.watch(
|
||||
mlDownloadStateProvider,
|
||||
);
|
||||
|
||||
return IconButton.filledTonal(
|
||||
icon: const Icon(MdiIcons.imageAutoAdjust),
|
||||
isSelected: tabSuggestionsEnabled,
|
||||
iconSize: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(
|
||||
tabSuggestionsControllerProvider
|
||||
.notifier,
|
||||
return Badge(
|
||||
isLabelVisible: downloadProgress != null,
|
||||
offset: const Offset(-2, 2),
|
||||
label: downloadProgress != null
|
||||
? Text(
|
||||
'${downloadProgress.progress.toInt()}%',
|
||||
style: const TextStyle(fontSize: 10),
|
||||
)
|
||||
.toggle();
|
||||
},
|
||||
: null,
|
||||
child: IconButton.filledTonal(
|
||||
icon: const Icon(MdiIcons.imageAutoAdjust),
|
||||
isSelected: tabSuggestionsEnabled,
|
||||
iconSize: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () async {
|
||||
if (!tabSuggestionsEnabled) {
|
||||
final result = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
icon: const Icon(MdiIcons.download),
|
||||
title: const Text(
|
||||
'Enable AI Tab Suggestions',
|
||||
),
|
||||
content: const Text(
|
||||
'Enabling this feature may require downloading AI models. '
|
||||
'The download size and progress cannot be determined in advance.\n\n'
|
||||
'Do you want to continue?',
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: const Text('Enable'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
ref
|
||||
.read(
|
||||
tabSuggestionsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.enable();
|
||||
}
|
||||
} else {
|
||||
ref
|
||||
.read(
|
||||
tabSuggestionsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.disable();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user