add feature container drafting
This commit is contained in:
@@ -33,6 +33,10 @@ part of 'routes.dart';
|
|||||||
name: 'ContextMenuRoute',
|
name: 'ContextMenuRoute',
|
||||||
path: 'context_menu',
|
path: 'context_menu',
|
||||||
),
|
),
|
||||||
|
TypedGoRoute<ContainerDraftRoute>(
|
||||||
|
name: 'ContainerDraftRoute',
|
||||||
|
path: 'container_draft',
|
||||||
|
),
|
||||||
TypedGoRoute<ContainerListRoute>(
|
TypedGoRoute<ContainerListRoute>(
|
||||||
name: 'ContainerListRoute',
|
name: 'ContainerListRoute',
|
||||||
path: 'containers',
|
path: 'containers',
|
||||||
@@ -107,6 +111,13 @@ class TorProxyRoute extends GoRouteData with $TorProxyRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ContainerDraftRoute extends GoRouteData with $ContainerDraftRoute {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return const ContainerDraftSuggestionsScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ContainerListRoute extends GoRouteData with $ContainerListRoute {
|
class ContainerListRoute extends GoRouteData with $ContainerListRoute {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, GoRouterState state) {
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import 'package:weblibre/features/geckoview/features/contextmenu/presentation/co
|
|||||||
import 'package:weblibre/features/geckoview/features/history/presentation/screens/history.dart';
|
import 'package:weblibre/features/geckoview/features/history/presentation/screens/history.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/search/presentation/screens/search.dart';
|
import 'package:weblibre/features/geckoview/features/search/presentation/screens/search.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_draft_suggestions.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_edit.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_edit.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_selection.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_selection.dart';
|
||||||
|
|||||||
@@ -322,6 +322,11 @@ RouteBase get $browserRoute => GoRouteData.$route(
|
|||||||
name: 'ContextMenuRoute',
|
name: 'ContextMenuRoute',
|
||||||
factory: $ContextMenuRoute._fromState,
|
factory: $ContextMenuRoute._fromState,
|
||||||
),
|
),
|
||||||
|
GoRouteData.$route(
|
||||||
|
path: 'container_draft',
|
||||||
|
name: 'ContainerDraftRoute',
|
||||||
|
factory: $ContainerDraftRoute._fromState,
|
||||||
|
),
|
||||||
GoRouteData.$route(
|
GoRouteData.$route(
|
||||||
path: 'containers',
|
path: 'containers',
|
||||||
name: 'ContainerListRoute',
|
name: 'ContainerListRoute',
|
||||||
@@ -487,6 +492,27 @@ mixin $ContextMenuRoute on GoRouteData {
|
|||||||
context.replace(location, extra: _self.$extra);
|
context.replace(location, extra: _self.$extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixin $ContainerDraftRoute on GoRouteData {
|
||||||
|
static ContainerDraftRoute _fromState(GoRouterState state) =>
|
||||||
|
ContainerDraftRoute();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get location => GoRouteData.$location('/container_draft');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void go(BuildContext context) => context.go(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void pushReplacement(BuildContext context) =>
|
||||||
|
context.pushReplacement(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void replace(BuildContext context) => context.replace(location);
|
||||||
|
}
|
||||||
|
|
||||||
mixin $ContainerListRoute on GoRouteData {
|
mixin $ContainerListRoute on GoRouteData {
|
||||||
static ContainerListRoute _fromState(GoRouterState state) =>
|
static ContainerListRoute _fromState(GoRouterState state) =>
|
||||||
ContainerListRoute();
|
ContainerListRoute();
|
||||||
|
|||||||
+11
-10
@@ -42,7 +42,7 @@ final class GeckoInferenceRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$geckoInferenceRepositoryHash() =>
|
String _$geckoInferenceRepositoryHash() =>
|
||||||
r'eb174c9f80f3537bc075ebbffcef787ec55ad217';
|
r'5f99fd1e46a8ae291b63df044fe11917cd5d415c';
|
||||||
|
|
||||||
abstract class _$GeckoInferenceRepository extends $Notifier<void> {
|
abstract class _$GeckoInferenceRepository extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
@@ -138,13 +138,13 @@ const suggestClustersProvider = SuggestClustersProvider._();
|
|||||||
final class SuggestClustersProvider
|
final class SuggestClustersProvider
|
||||||
extends
|
extends
|
||||||
$FunctionalProvider<
|
$FunctionalProvider<
|
||||||
AsyncValue<List<({List<String> tabIds, String? topic})>?>,
|
AsyncValue<List<SuggestedContainer>?>,
|
||||||
List<({List<String> tabIds, String? topic})>?,
|
List<SuggestedContainer>?,
|
||||||
FutureOr<List<({List<String> tabIds, String? topic})>?>
|
FutureOr<List<SuggestedContainer>?>
|
||||||
>
|
>
|
||||||
with
|
with
|
||||||
$FutureModifier<List<({List<String> tabIds, String? topic})>?>,
|
$FutureModifier<List<SuggestedContainer>?>,
|
||||||
$FutureProvider<List<({List<String> tabIds, String? topic})>?> {
|
$FutureProvider<List<SuggestedContainer>?> {
|
||||||
const SuggestClustersProvider._()
|
const SuggestClustersProvider._()
|
||||||
: super(
|
: super(
|
||||||
from: null,
|
from: null,
|
||||||
@@ -161,16 +161,17 @@ final class SuggestClustersProvider
|
|||||||
|
|
||||||
@$internal
|
@$internal
|
||||||
@override
|
@override
|
||||||
$FutureProviderElement<List<({List<String> tabIds, String? topic})>?>
|
$FutureProviderElement<List<SuggestedContainer>?> $createElement(
|
||||||
$createElement($ProviderPointer pointer) => $FutureProviderElement(pointer);
|
$ProviderPointer pointer,
|
||||||
|
) => $FutureProviderElement(pointer);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<List<({List<String> tabIds, String? topic})>?> create(Ref ref) {
|
FutureOr<List<SuggestedContainer>?> create(Ref ref) {
|
||||||
return suggestClusters(ref);
|
return suggestClusters(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$suggestClustersHash() => r'592058ed092384c37a185dcd5657276a3dde5b4e';
|
String _$suggestClustersHash() => r'3b7da171b3596f708a8039658578251ae61655ea';
|
||||||
|
|
||||||
@ProviderFor(containerTabSuggestions)
|
@ProviderFor(containerTabSuggestions)
|
||||||
const containerTabSuggestionsProvider = ContainerTabSuggestionsFamily._();
|
const containerTabSuggestionsProvider = ContainerTabSuggestionsFamily._();
|
||||||
|
|||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/core/uuid.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||||
|
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
||||||
|
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
||||||
|
|
||||||
|
class ContainerDraftSuggestionsScreen extends HookConsumerWidget {
|
||||||
|
const ContainerDraftSuggestionsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final suggestionsAsync = ref.watch(suggestClustersProvider);
|
||||||
|
|
||||||
|
final selectedContainer = useState<SuggestedContainer?>(null);
|
||||||
|
final selectedContainerTabs = useState(<SuggestedContainer, Set<String>>{});
|
||||||
|
|
||||||
|
final selectedTabs = selectedContainer.value != null
|
||||||
|
? selectedContainerTabs.value[selectedContainer.value!]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Draft Containers')),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: suggestionsAsync.when(
|
||||||
|
data: (suggestions) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
final crossAxisCount = useMemoized(() {
|
||||||
|
final calculatedCount = calculateCrossAxisItemCount(
|
||||||
|
screenWidth: screenWidth,
|
||||||
|
horizontalPadding: 4.0,
|
||||||
|
crossAxisSpacing: 8.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return math.max(
|
||||||
|
math.min(
|
||||||
|
calculatedCount,
|
||||||
|
selectedContainer.value?.tabIds.length ?? 0,
|
||||||
|
),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}, [screenWidth, selectedContainer.value?.tabIds.length]);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: SelectableChips(
|
||||||
|
deleteIcon: false,
|
||||||
|
itemId: (container) => container,
|
||||||
|
itemAvatar: (container) =>
|
||||||
|
const Icon(MdiIcons.creation, size: 20),
|
||||||
|
itemLabel: (container) =>
|
||||||
|
Text(container.topic ?? 'Untitled'),
|
||||||
|
itemBadgeCount: (container) => container.tabIds.length,
|
||||||
|
availableItems: suggestions!,
|
||||||
|
selectedItem: selectedContainer.value,
|
||||||
|
onSelected: (item) {
|
||||||
|
selectedContainer.value = item;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
if (selectedContainer.value != null)
|
||||||
|
Expanded(
|
||||||
|
child: GridView.builder(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
//Sync values for itemHeight calculation _calculateItemHeight
|
||||||
|
childAspectRatio: 0.75,
|
||||||
|
mainAxisSpacing: 8.0,
|
||||||
|
crossAxisSpacing: 8.0,
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
|
),
|
||||||
|
itemCount: selectedContainer.value!.tabIds.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final tabId = selectedContainer.value!.tabIds[index];
|
||||||
|
final equatable = selectedContainer.value!;
|
||||||
|
|
||||||
|
return (selectedContainerTabs.value[equatable]
|
||||||
|
?.contains(tabId) ==
|
||||||
|
true)
|
||||||
|
? TabPreview(
|
||||||
|
tabId: tabId,
|
||||||
|
isActive: false,
|
||||||
|
onDelete: () {
|
||||||
|
selectedContainerTabs.value = {
|
||||||
|
...selectedContainerTabs.value,
|
||||||
|
equatable: {
|
||||||
|
...?selectedContainerTabs
|
||||||
|
.value[equatable],
|
||||||
|
}..remove(tabId),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: SuggestedSingleTabPreview(
|
||||||
|
tabId: tabId,
|
||||||
|
activeTabId: null,
|
||||||
|
onTap: () {
|
||||||
|
selectedContainerTabs.value = {
|
||||||
|
...selectedContainerTabs.value,
|
||||||
|
equatable: {
|
||||||
|
...?selectedContainerTabs
|
||||||
|
.value[equatable],
|
||||||
|
tabId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error: (error, stackTrace) {
|
||||||
|
return FailureWidget(
|
||||||
|
title: 'Failed to create suggestions',
|
||||||
|
onRetry: () {
|
||||||
|
ref.invalidate(suggestClustersProvider);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: (selectedTabs?.isNotEmpty ?? false)
|
||||||
|
? FloatingActionButton(
|
||||||
|
child: const Icon(MdiIcons.folderPlus),
|
||||||
|
onPressed: () async {
|
||||||
|
final initialColor = await ref
|
||||||
|
.read(containerRepositoryProvider.notifier)
|
||||||
|
.unusedRandomContainerColor();
|
||||||
|
|
||||||
|
final initialContainer = ContainerData(
|
||||||
|
id: uuid.v7(),
|
||||||
|
color: initialColor,
|
||||||
|
name: selectedContainer.value?.topic,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
final result = await ContainerCreateRoute(
|
||||||
|
initialContainer,
|
||||||
|
).push<ContainerData?>(context);
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
for (final tab in selectedTabs!) {
|
||||||
|
await ref
|
||||||
|
.read(tabDataRepositoryProvider.notifier)
|
||||||
|
.assignContainer(tab, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedContainerTabs.value = {};
|
||||||
|
selectedContainer.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+107
-113
@@ -23,6 +23,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
import 'package:weblibre/core/logger.dart';
|
import 'package:weblibre/core/logger.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_suggestions.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_suggestions.dart';
|
||||||
@@ -85,126 +86,119 @@ class ContainerChips extends HookConsumerWidget {
|
|||||||
height: 48,
|
height: 48,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (selectedContainer != null || availableContainers.isNotEmpty)
|
Expanded(
|
||||||
Expanded(
|
child: SelectableChips(
|
||||||
child: SelectableChips(
|
deleteIcon: false,
|
||||||
deleteIcon: false,
|
itemId: (container) => container.id,
|
||||||
itemId: (container) => container.id,
|
itemAvatar: (container) => Container(
|
||||||
itemAvatar: (container) => Container(
|
width: 20.0,
|
||||||
width: 20.0,
|
height: 20.0,
|
||||||
height: 20.0,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: container.color,
|
||||||
color: container.color,
|
shape: BoxShape.circle,
|
||||||
shape: BoxShape.circle,
|
),
|
||||||
|
),
|
||||||
|
itemLabel: (container) =>
|
||||||
|
ContainerTitle(container: container),
|
||||||
|
itemBadgeCount: (container) => container.tabCount,
|
||||||
|
itemWrap: (child, container) {
|
||||||
|
return TabDragContainerTarget(
|
||||||
|
container: container,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
prefixListItems: [
|
||||||
|
TabDragContainerTarget(
|
||||||
|
container: null,
|
||||||
|
child: Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final tabCount = ref.watch(
|
||||||
|
containerTabCountProvider(
|
||||||
|
// ignore: provider_parameters
|
||||||
|
ContainerFilterById(containerId: null),
|
||||||
|
).select((value) => value.value ?? 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
return FilterChip(
|
||||||
|
avatar: const Icon(MdiIcons.folderHidden),
|
||||||
|
labelPadding: (tabCount > 0)
|
||||||
|
? null
|
||||||
|
: const EdgeInsets.only(right: 2.0),
|
||||||
|
label: (tabCount > 0)
|
||||||
|
? Text(tabCount.toString())
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
selected: selectedContainer == null,
|
||||||
|
showCheckmark: false,
|
||||||
|
onSelected: (value) {
|
||||||
|
if (value) {
|
||||||
|
onSelected?.call(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
itemLabel: (container) =>
|
if (showGroupSuggestions)
|
||||||
ContainerTitle(container: container),
|
Consumer(
|
||||||
itemBadgeCount: (container) => container.tabCount,
|
builder: (context, ref, child) {
|
||||||
itemWrap: (child, container) {
|
final tabSuggestionsEnabled = ref.watch(
|
||||||
return TabDragContainerTarget(
|
tabSuggestionsControllerProvider,
|
||||||
container: container,
|
);
|
||||||
child: child,
|
final enableAiFeatures = ref.watch(
|
||||||
);
|
generalSettingsWithDefaultsProvider.select(
|
||||||
},
|
(settings) => settings.enableLocalAiFeatures,
|
||||||
prefixListItems: [
|
),
|
||||||
TabDragContainerTarget(
|
);
|
||||||
container: null,
|
|
||||||
child: Consumer(
|
|
||||||
builder: (context, ref, child) {
|
|
||||||
final tabCount = ref.watch(
|
|
||||||
containerTabCountProvider(
|
|
||||||
// ignore: provider_parameters
|
|
||||||
ContainerFilterById(containerId: null),
|
|
||||||
).select((value) => value.value ?? 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
return FilterChip(
|
if (!enableAiFeatures || !tabSuggestionsEnabled) {
|
||||||
avatar: const Icon(MdiIcons.folderHidden),
|
return const SizedBox.shrink();
|
||||||
labelPadding: (tabCount > 0)
|
}
|
||||||
? null
|
|
||||||
: const EdgeInsets.only(right: 2.0),
|
|
||||||
label: (tabCount > 0)
|
|
||||||
? Text(tabCount.toString())
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
selected: selectedContainer == null,
|
|
||||||
showCheckmark: false,
|
|
||||||
onSelected: (value) {
|
|
||||||
if (value) {
|
|
||||||
onSelected?.call(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (showGroupSuggestions)
|
|
||||||
Consumer(
|
|
||||||
builder: (context, ref, child) {
|
|
||||||
final tabSuggestionsEnabled = ref.watch(
|
|
||||||
tabSuggestionsControllerProvider,
|
|
||||||
);
|
|
||||||
final enableAiFeatures = ref.watch(
|
|
||||||
generalSettingsWithDefaultsProvider.select(
|
|
||||||
(settings) => settings.enableLocalAiFeatures,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!enableAiFeatures || !tabSuggestionsEnabled) {
|
final suggestions = ref.watch(
|
||||||
|
suggestClustersProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
return suggestions.when(
|
||||||
|
data: (data) {
|
||||||
|
if (data.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilterChip(
|
||||||
|
avatar: const Icon(MdiIcons.autoFix),
|
||||||
|
label: Text(data!.length.toString()),
|
||||||
|
showCheckmark: false,
|
||||||
|
onSelected: (_) async {
|
||||||
|
await ContainerDraftRoute().push(context);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error: (error, stackTrace) {
|
||||||
|
logger.e(
|
||||||
|
'Error suggesting containers',
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
);
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
},
|
||||||
|
loading: () {
|
||||||
final suggestions = ref.watch(
|
return const FilterChip(
|
||||||
suggestClustersProvider,
|
avatar: Icon(MdiIcons.autoFix),
|
||||||
);
|
label: Skeletonizer(child: Text('0')),
|
||||||
|
showCheckmark: false,
|
||||||
return suggestions.when(
|
onSelected: null,
|
||||||
data: (data) {
|
);
|
||||||
if (data.isEmpty) {
|
},
|
||||||
return const SizedBox.shrink();
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
return FilterChip(
|
|
||||||
avatar: const Icon(MdiIcons.autoFix),
|
|
||||||
label: Text(data!.length.toString()),
|
|
||||||
showCheckmark: false,
|
|
||||||
onSelected: (value) {},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
error: (error, stackTrace) {
|
|
||||||
logger.e(
|
|
||||||
'Error suggesting containers',
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
},
|
|
||||||
loading: () {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
availableItems: availableContainers,
|
|
||||||
selectedItem: selectedContainer,
|
|
||||||
onSelected: onSelected,
|
|
||||||
onDeleted: onDeleted,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Visibility(
|
|
||||||
visible: displayMenu,
|
|
||||||
child: Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Press '>' to manage Containers.",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).hintColor,
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
availableItems: availableContainers,
|
||||||
|
selectedItem: selectedContainer,
|
||||||
|
onSelected: onSelected,
|
||||||
|
onDeleted: onDeleted,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
if (displayMenu)
|
if (displayMenu)
|
||||||
IconButton(
|
IconButton(
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
|||||||
Reference in New Issue
Block a user