BLoC/Cubit state management, ADHD-friendly theme (calming teal, no red), GetIt DI, GoRouter navigation. Screens: task dashboard, focus mode, task create/detail, streaks, time perception, settings, onboarding, auth. Custom widgets: TaskCard, RewardPopup, StreakRing, GentleNudgeCard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
85 lines
2.5 KiB
Dart
85 lines
2.5 KiB
Dart
import 'package:equatable/equatable.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:focusflow_shared/focusflow_shared.dart';
|
|
|
|
import '../../../../core/network/api_client.dart';
|
|
import '../../../../main.dart';
|
|
|
|
// ── States ─────────────────────────────────────────────────────────
|
|
|
|
sealed class NextTaskState extends Equatable {
|
|
const NextTaskState();
|
|
@override
|
|
List<Object?> get props => [];
|
|
}
|
|
|
|
class NextTaskLoading extends NextTaskState {}
|
|
|
|
class NextTaskLoaded extends NextTaskState {
|
|
final Task task;
|
|
const NextTaskLoaded(this.task);
|
|
@override
|
|
List<Object?> get props => [task];
|
|
}
|
|
|
|
class NextTaskEmpty extends NextTaskState {}
|
|
|
|
class NextTaskError extends NextTaskState {
|
|
final String message;
|
|
const NextTaskError(this.message);
|
|
@override
|
|
List<Object?> get props => [message];
|
|
}
|
|
|
|
// ── Cubit ──────────────────────────────────────────────────────────
|
|
|
|
/// Simple cubit for focus mode: "just do the next thing."
|
|
///
|
|
/// Loads the single highest-priority task from the API.
|
|
class NextTaskCubit extends Cubit<NextTaskState> {
|
|
final ApiClient _api = getIt<ApiClient>();
|
|
|
|
NextTaskCubit() : super(NextTaskLoading());
|
|
|
|
/// Fetch the next recommended task.
|
|
Future<void> loadNext() async {
|
|
emit(NextTaskLoading());
|
|
try {
|
|
final response = await _api.fetchNextTask();
|
|
final data = response.data;
|
|
if (data != null && data['status'] == 'success' && data['data'] != null) {
|
|
final task = Task.fromJson(data['data'] as Map<String, dynamic>);
|
|
emit(NextTaskLoaded(task));
|
|
} else {
|
|
emit(NextTaskEmpty());
|
|
}
|
|
} catch (_) {
|
|
emit(const NextTaskError('Could not load your next task.'));
|
|
}
|
|
}
|
|
|
|
/// Mark the current task as done, then load the next one.
|
|
Future<void> complete() async {
|
|
final current = state;
|
|
if (current is! NextTaskLoaded) return;
|
|
try {
|
|
await _api.completeTask(current.task.id);
|
|
} catch (_) {
|
|
// Best-effort — still load next.
|
|
}
|
|
await loadNext();
|
|
}
|
|
|
|
/// Skip the current task, then load the next one.
|
|
Future<void> skip() async {
|
|
final current = state;
|
|
if (current is! NextTaskLoaded) return;
|
|
try {
|
|
await _api.skipTask(current.task.id);
|
|
} catch (_) {
|
|
// Best-effort.
|
|
}
|
|
await loadNext();
|
|
}
|
|
}
|