implement quick actions and wait for first initialization to push routes
This commit is contained in:
+118
@@ -21,12 +21,16 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.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:quick_actions/quick_actions.dart';
|
||||||
import 'package:weblibre/core/logger.dart';
|
import 'package:weblibre/core/logger.dart';
|
||||||
|
import 'package:weblibre/core/providers/router.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/browser_extension.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/browser_extension.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
|
||||||
@@ -34,10 +38,14 @@ import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
|
|||||||
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/web_extensions_state.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/web_extensions_state.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/providers/intent.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/providers/lifecycle.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/providers/lifecycle.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/engine_settings_replication.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/engine_settings_replication.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
|
||||||
|
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
|
||||||
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/cache.dart';
|
import 'package:weblibre/features/user/domain/repositories/cache.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/features/user/domain/services/local_authentication.dart';
|
import 'package:weblibre/features/user/domain/services/local_authentication.dart';
|
||||||
@@ -84,6 +92,8 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final initializationCompleter = useMemoized(() => Completer());
|
||||||
|
|
||||||
useOnInitialization(() async {
|
useOnInitialization(() async {
|
||||||
await ref
|
await ref
|
||||||
.read(generalSettingsRepositoryProvider.notifier)
|
.read(generalSettingsRepositoryProvider.notifier)
|
||||||
@@ -138,6 +148,73 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ref.listen(
|
||||||
|
engineBoundIntentStreamProvider,
|
||||||
|
(previous, next) async {
|
||||||
|
await initializationCompleter.future;
|
||||||
|
|
||||||
|
next.whenData((sharedContent) async {
|
||||||
|
final router = await ref.read(routerProvider.future);
|
||||||
|
final settings = ref.read(generalSettingsWithDefaultsProvider);
|
||||||
|
|
||||||
|
switch (settings.tabIntentOpenSetting) {
|
||||||
|
case TabIntentOpenSetting.regular:
|
||||||
|
case TabIntentOpenSetting.private:
|
||||||
|
switch (sharedContent) {
|
||||||
|
case SharedUrl():
|
||||||
|
await ref
|
||||||
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.addTab(
|
||||||
|
url: sharedContent.url,
|
||||||
|
private:
|
||||||
|
settings.tabIntentOpenSetting ==
|
||||||
|
TabIntentOpenSetting.private,
|
||||||
|
launchedFromIntent: true,
|
||||||
|
);
|
||||||
|
case SharedText():
|
||||||
|
final defaultSearchBang =
|
||||||
|
ref.read(selectedBangDataProvider()) ??
|
||||||
|
await ref.read(defaultSearchBangDataProvider.future);
|
||||||
|
|
||||||
|
await ref
|
||||||
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.addTab(
|
||||||
|
url: defaultSearchBang?.getTemplateUrl(
|
||||||
|
sharedContent.text,
|
||||||
|
),
|
||||||
|
private:
|
||||||
|
settings.tabIntentOpenSetting ==
|
||||||
|
TabIntentOpenSetting.private,
|
||||||
|
launchedFromIntent: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case TabIntentOpenSetting.ask:
|
||||||
|
switch (sharedContent) {
|
||||||
|
case SharedUrl():
|
||||||
|
final route = OpenSharedContentRoute(sharedContent.url);
|
||||||
|
await router.push(route.location, extra: route.$extra);
|
||||||
|
case SharedText():
|
||||||
|
final route = SearchRoute(
|
||||||
|
tabType:
|
||||||
|
ref.read(selectedTabTypeProvider) ??
|
||||||
|
settings.defaultCreateTabType,
|
||||||
|
searchText: sharedContent.text,
|
||||||
|
launchedFromIntent: true, //launched from intent
|
||||||
|
);
|
||||||
|
await router.push(route.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error, stackTrace) {
|
||||||
|
logger.e(
|
||||||
|
'Error listening to engineBoundIntentStreamProvider',
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: hasTab,
|
visible: hasTab,
|
||||||
replacement: SizedBox.expand(child: Container(color: Colors.grey[800])),
|
replacement: SizedBox.expand(child: Container(color: Colors.grey[800])),
|
||||||
@@ -159,6 +236,47 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
|||||||
},
|
},
|
||||||
postInitializationStep: () async {
|
postInitializationStep: () async {
|
||||||
await widget.postInitializationStep?.call();
|
await widget.postInitializationStep?.call();
|
||||||
|
|
||||||
|
if (!initializationCompleter.isCompleted) {
|
||||||
|
const quickActions = QuickActions();
|
||||||
|
|
||||||
|
//Debounce: https://github.com/flutter/flutter/issues/131121
|
||||||
|
DateTime? lastAction;
|
||||||
|
await quickActions.initialize((type) async {
|
||||||
|
if (lastAction == null ||
|
||||||
|
DateTime.now().difference(lastAction!) >
|
||||||
|
const Duration(seconds: 5)) {
|
||||||
|
if (type == 'new_tab') {
|
||||||
|
lastAction = DateTime.now();
|
||||||
|
|
||||||
|
final router = await ref.read(routerProvider.future);
|
||||||
|
const route = SearchRoute(tabType: TabType.regular);
|
||||||
|
|
||||||
|
await router.push(route.location);
|
||||||
|
} else if (type == 'new_private_tab') {
|
||||||
|
lastAction = DateTime.now();
|
||||||
|
|
||||||
|
final router = await ref.read(routerProvider.future);
|
||||||
|
const route = SearchRoute(tabType: TabType.private);
|
||||||
|
|
||||||
|
await router.push(route.location);
|
||||||
|
} else {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await quickActions.setShortcutItems([
|
||||||
|
//TODO: add icons
|
||||||
|
const ShortcutItem(type: 'new_tab', localizedTitle: 'New Tab'),
|
||||||
|
const ShortcutItem(
|
||||||
|
type: 'new_private_tab',
|
||||||
|
localizedTitle: 'New Private Tab',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
initializationCompleter.complete();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,18 +19,8 @@
|
|||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:weblibre/core/logger.dart';
|
|
||||||
import 'package:weblibre/core/providers/router.dart';
|
import 'package:weblibre/core/providers/router.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
|
||||||
import 'package:weblibre/domain/services/app_initialization.dart';
|
import 'package:weblibre/domain/services/app_initialization.dart';
|
||||||
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/providers/intent.dart';
|
|
||||||
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
|
|
||||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
|
||||||
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
||||||
|
|
||||||
class MainApp extends HookConsumerWidget {
|
class MainApp extends HookConsumerWidget {
|
||||||
@@ -50,71 +40,6 @@ class MainApp extends HookConsumerWidget {
|
|||||||
final initializationResult = ref.watch(appInitializationServiceProvider);
|
final initializationResult = ref.watch(appInitializationServiceProvider);
|
||||||
final router = ref.watch(routerProvider);
|
final router = ref.watch(routerProvider);
|
||||||
|
|
||||||
ref.listen(
|
|
||||||
engineBoundIntentStreamProvider,
|
|
||||||
(previous, next) {
|
|
||||||
next.whenData((sharedContent) async {
|
|
||||||
final router = await ref.read(routerProvider.future);
|
|
||||||
final settings = ref.read(generalSettingsWithDefaultsProvider);
|
|
||||||
|
|
||||||
switch (settings.tabIntentOpenSetting) {
|
|
||||||
case TabIntentOpenSetting.regular:
|
|
||||||
case TabIntentOpenSetting.private:
|
|
||||||
switch (sharedContent) {
|
|
||||||
case SharedUrl():
|
|
||||||
await ref
|
|
||||||
.read(tabRepositoryProvider.notifier)
|
|
||||||
.addTab(
|
|
||||||
url: sharedContent.url,
|
|
||||||
private:
|
|
||||||
settings.tabIntentOpenSetting ==
|
|
||||||
TabIntentOpenSetting.private,
|
|
||||||
launchedFromIntent: true,
|
|
||||||
);
|
|
||||||
case SharedText():
|
|
||||||
final defaultSearchBang =
|
|
||||||
ref.read(selectedBangDataProvider()) ??
|
|
||||||
await ref.read(defaultSearchBangDataProvider.future);
|
|
||||||
|
|
||||||
await ref
|
|
||||||
.read(tabRepositoryProvider.notifier)
|
|
||||||
.addTab(
|
|
||||||
url: defaultSearchBang?.getTemplateUrl(
|
|
||||||
sharedContent.text,
|
|
||||||
),
|
|
||||||
private:
|
|
||||||
settings.tabIntentOpenSetting ==
|
|
||||||
TabIntentOpenSetting.private,
|
|
||||||
launchedFromIntent: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case TabIntentOpenSetting.ask:
|
|
||||||
switch (sharedContent) {
|
|
||||||
case SharedUrl():
|
|
||||||
final route = OpenSharedContentRoute(sharedContent.url);
|
|
||||||
await router.push(route.location, extra: route.$extra);
|
|
||||||
case SharedText():
|
|
||||||
final route = SearchRoute(
|
|
||||||
tabType:
|
|
||||||
ref.read(selectedTabTypeProvider) ??
|
|
||||||
settings.defaultCreateTabType,
|
|
||||||
searchText: sharedContent.text,
|
|
||||||
launchedFromIntent: true, //launched from intent
|
|
||||||
);
|
|
||||||
await router.push(route.location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: (error, stackTrace) {
|
|
||||||
logger.e(
|
|
||||||
'Error listening to engineBoundIntentStreamProvider',
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return initializationResult.fold(
|
return initializationResult.fold(
|
||||||
(initializationState) {
|
(initializationState) {
|
||||||
if (!initializationState.initialized) {
|
if (!initializationState.initialized) {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ dependencies:
|
|||||||
pluggable_transports_proxy:
|
pluggable_transports_proxy:
|
||||||
path: ../packages/pluggable_transports_proxy
|
path: ../packages/pluggable_transports_proxy
|
||||||
pretty_qr_code: ^3.5.0
|
pretty_qr_code: ^3.5.0
|
||||||
|
quick_actions: ^1.1.0
|
||||||
riverpod: ^3.0.0
|
riverpod: ^3.0.0
|
||||||
riverpod_annotation: ^3.0.0
|
riverpod_annotation: ^3.0.0
|
||||||
rss_dart: ^1.0.13
|
rss_dart: ^1.0.13
|
||||||
|
|||||||
Reference in New Issue
Block a user