f8c3422715
Extract _TaskCard from the list screen into ProgrammerTaskCard, a shared ConsumerWidget that reads programmerRunStatesProvider so the paused-task pill and Start/Resume/Complete buttons are consistent across any surface that embeds it. Move the _complete body verbatim to completeProgrammerTaskWithConfirm in programmer_task_actions.dart; the detail screen's _ActionBar now calls the shared function. Co-Authored-By: claude-flow <ruv@ruv.net>
98 lines
3.2 KiB
Dart
98 lines
3.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../models/programmer_task.model.dart';
|
|
import '../../providers/programmer_tasks_provider.dart';
|
|
|
|
/// Confirms with the user and completes [task]. Body moved verbatim from the
|
|
/// detail screen's `_complete` so it can be called from both the card and the
|
|
/// detail action bar.
|
|
Future<void> completeProgrammerTaskWithConfirm(
|
|
BuildContext context,
|
|
WidgetRef ref,
|
|
ProgrammerTask task,
|
|
) async {
|
|
final messenger = ScaffoldMessenger.of(context);
|
|
final ok = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
semanticLabel: 'Complete task',
|
|
title: const Text('Complete task?'),
|
|
content: Text('Mark "${task.title}" as completed?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
child: const Text('Cancel'),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.of(context).pop(true),
|
|
child: const Text('Complete'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (ok != true) return;
|
|
await ref
|
|
.read(programmerTasksControllerProvider)
|
|
.completeTask(taskId: task.id);
|
|
messenger.showSnackBar(const SnackBar(content: Text('Task completed')));
|
|
}
|
|
|
|
/// Starts (or resumes) [task] for the current user, enforcing the single-active
|
|
/// task discipline: if the user already has a *running* task, they are prompted
|
|
/// to pause it first. This is the headline "proper time management" behavior and
|
|
/// is shared by the list and detail screens.
|
|
///
|
|
/// Returns true if the task was started/resumed, false if the user cancelled.
|
|
Future<bool> startProgrammerTaskWithPausePrompt(
|
|
BuildContext context,
|
|
WidgetRef ref,
|
|
ProgrammerTask task,
|
|
) async {
|
|
final controller = ref.read(programmerTasksControllerProvider);
|
|
final messenger = ScaffoldMessenger.of(context);
|
|
|
|
final running = await controller.findRunningTaskForCurrentUser();
|
|
if (running != null && running.id != task.id) {
|
|
if (!context.mounted) return false;
|
|
final confirmed = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
semanticLabel: 'Pause current task',
|
|
title: const Text('Pause current task?'),
|
|
content: Text(
|
|
"You're currently working on \"${running.title}\". "
|
|
'Pause it and start "${task.title}"?',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
child: const Text('Cancel'),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.of(context).pop(true),
|
|
child: const Text('Pause & Start'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (confirmed != true) return false;
|
|
await controller.pauseTask(taskId: running.id);
|
|
}
|
|
|
|
try {
|
|
if (task.status == ProgrammerTaskStatus.queued) {
|
|
await controller.startTask(taskId: task.id);
|
|
} else if (task.status == ProgrammerTaskStatus.inProgress) {
|
|
await controller.resumeTask(taskId: task.id);
|
|
}
|
|
messenger.showSnackBar(
|
|
SnackBar(content: Text('Started "${task.title}"')),
|
|
);
|
|
return true;
|
|
} catch (e) {
|
|
messenger.showSnackBar(SnackBar(content: Text('Could not start task: $e')));
|
|
return false;
|
|
}
|
|
}
|