feat(ui): unify child surfaces and show web task creation in a modal (#1633)

This commit is contained in:
Levi authored and GitHub committed 2026-11-02 23:54:05 +08:00
1 parent b0d45226d7
commit d69874e13e
30 files changed
+618 -85

No files matched your search

@@ -11,7 +11,7 @@ import '../../../../app/application/app_runtime_controller.dart';
import '../../../../core/capabilities/app_capabilities.dart';
import '../../../../core/utils/breakpoints.dart';
import '../../../../core/utils/file_explorer.dart';
import '../../../../core/window/app_window_launcher.dart';
import '../../../../app/router/app_router.dart';
import '../../../../core/window/app_window_chrome.dart';
import '../../../../shared/theme/app_design_tokens.dart';
import '../../../../shared/theme/app_palette.dart';
@@ -580,19 +580,7 @@ class _HomePageState extends ConsumerState<HomePage> {
}
}
void _handleAddTask() {
if (!Util.isDesktop()) {
context.go('/create');
return;
}
unawaited(
AppWindowLauncher.openCreateTaskWindow().then((opened) {
if (!opened && mounted) {
context.go('/create');
}
}),
);
}
void _handleAddTask() => unawaited(AppRouter.openCreateTask());
Future<void> _runTaskAction(Future<void> Function() action) async {
try {
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart' show Icons;
import 'package:flutter/widgets.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/window/app_window_chrome.dart';
@@ -28,7 +27,21 @@ class PrimaryRail extends StatelessWidget {
children: [
SizedBox(
height: AppDesignTokens.contentHeaderHeight,
child: const Center(child: GopeedAppMark(key: ValueKey('primary-rail-app-mark'))),
child: Center(
child: AppTooltip(
message: context.l10n.task,
child: Semantics(
label: context.l10n.task,
button: true,
child: GhostButton(
key: const ValueKey('primary-rail-home-button'),
density: ButtonDensity.icon,
onPressed: () => context.go('/'),
child: const ExcludeSemantics(child: GopeedAppMark(key: ValueKey('primary-rail-app-mark'))),
),
),
),
),
),
const SizedBox(height: AppDesignTokens.space8),
_RailItem(
@@ -44,10 +44,12 @@ import '../widgets/create_task_action_buttons.dart';
import '../widgets/resolve_download_dialog.dart';
class CreateTaskWindowPage extends ConsumerStatefulWidget {
const CreateTaskWindowPage({super.key, this.windowController, this.initialTask});
const CreateTaskWindowPage({super.key, this.windowController, this.initialTask, this.onClose, this.modal = false});
final multi_window.WindowController? windowController;
final CreateTask? initialTask;
final ValueChanged<bool>? onClose;
final bool modal;
@override
ConsumerState<CreateTaskWindowPage> createState() => _CreateTaskWindowPageState();
@@ -133,7 +135,7 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
if (widget.initialTask == null) {
ref.read(pendingCreateTaskProvider.notifier).clear();
}
if (widget.windowController == null && widget.initialTask == null) {
if (widget.windowController == null) {
// Navigating to /create again reuses this page, so consume subsequent
// browser/share requests as well as the initial pending task.
ref.listenManual(pendingCreateTaskProvider, (_, next) {
@@ -371,7 +373,9 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
child: Padding(
key: const ValueKey('create-task-safe-content'),
padding: EdgeInsets.only(
top: AppWindowChrome.reservesHeaderInset
top: widget.modal
? 0
: AppWindowChrome.reservesHeaderInset
? AppDesignTokens.windowHeaderHeight
: MediaQuery.paddingOf(context).top,
),
@@ -867,6 +871,10 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
}
Future<void> _closeWindow() async {
if (widget.onClose != null) {
widget.onClose!(false);
return;
}
if (widget.windowController == null && mounted) {
context.go('/');
return;
@@ -888,6 +896,10 @@ class _CreateTaskWindowPageState extends ConsumerState<CreateTaskWindowPage> {
}
}
if (!mounted || session != _createSession) return;
if (widget.onClose != null) {
widget.onClose!(true);
return;
}
if (widget.windowController != null) {
// Navigation is best-effort after a successful submission. A navigation
// failure must not leave a completed form available for resubmission.