Implemented Task Pause and Resume

This commit is contained in:
2026-03-04 07:12:00 +08:00
parent 81bc618eee
commit 94088a8796
3 changed files with 327 additions and 17 deletions
+92
View File
@@ -54,6 +54,12 @@ String _uiFingerprint(TaskActivityLog log) {
return '${log.taskId}|${log.actionType}';
}
// Pause/resume events are sequence-sensitive for execution-time calculations.
// Keep each row distinct to avoid collapsing multiple toggles in the same minute.
if (log.actionType == 'paused' || log.actionType == 'resumed') {
return '${log.taskId}|${log.id}';
}
// The UI displays time as "MMM dd, yyyy hh:mm AA". Grouping by year, month,
// day, hour, and minute ensures we collapse duplicates that look identical on screen.
final visualMinute =
@@ -610,6 +616,92 @@ class TasksController {
// SupabaseClient instance.
final dynamic _client;
Future<bool> _isTaskCurrentlyPaused(String taskId) async {
try {
final rows = await _client
.from('task_activity_logs')
.select('action_type, created_at')
.eq('task_id', taskId)
.inFilter('action_type', [
'started',
'paused',
'resumed',
'completed',
'cancelled',
])
.order('created_at', ascending: false)
.limit(1);
if (rows is List && rows.isNotEmpty) {
final latest = rows.first;
final action = latest['action_type']?.toString() ?? '';
return action == 'paused';
}
} catch (_) {}
return false;
}
Future<void> pauseTask({required String taskId}) async {
final row = await _client
.from('tasks')
.select('status')
.eq('id', taskId)
.maybeSingle();
if (row is! Map<String, dynamic>) {
throw Exception('Task not found');
}
final status = (row['status'] as String?)?.trim() ?? '';
if (status == 'completed' || status == 'cancelled' || status == 'closed') {
throw Exception('Cannot pause a terminal task.');
}
if (status != 'in_progress') {
throw Exception('Only in-progress tasks can be paused.');
}
final alreadyPaused = await _isTaskCurrentlyPaused(taskId);
if (alreadyPaused) {
return;
}
final actorId = _client.auth.currentUser?.id;
await _insertActivityRows(_client, {
'task_id': taskId,
'actor_id': actorId,
'action_type': 'paused',
});
}
Future<void> resumeTask({required String taskId}) async {
final row = await _client
.from('tasks')
.select('status')
.eq('id', taskId)
.maybeSingle();
if (row is! Map<String, dynamic>) {
throw Exception('Task not found');
}
final status = (row['status'] as String?)?.trim() ?? '';
if (status == 'completed' || status == 'cancelled' || status == 'closed') {
throw Exception('Cannot resume a terminal task.');
}
if (status != 'in_progress') {
throw Exception('Only in-progress tasks can be resumed.');
}
final isPaused = await _isTaskCurrentlyPaused(taskId);
if (!isPaused) {
return;
}
final actorId = _client.auth.currentUser?.id;
await _insertActivityRows(_client, {
'task_id': taskId,
'actor_id': actorId,
'action_type': 'resumed',
});
}
Future<void> createTask({
required String title,
required String description,