11import 'package:flutter/material.dart' ;
22import 'package:get/get.dart' ;
3+ import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart' ;
34import 'package:google_fonts/google_fonts.dart' ;
5+ import 'package:flutter_slidable/flutter_slidable.dart' ;
6+ import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart' ;
47import 'package:taskwarrior/app/routes/app_pages.dart' ;
58import 'package:taskwarrior/app/utils/app_settings/app_settings.dart' ;
6- import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart' ;
79import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart' ;
810import 'package:taskwarrior/app/utils/themes/theme_extension.dart' ;
911import 'package:taskwarrior/app/utils/language/sentence_manager.dart' ;
12+ import 'package:taskwarrior/app/v3/champion/Replica.dart' ;
1013import 'package:taskwarrior/app/v3/champion/models/task_for_replica.dart' ;
1114
12- /// A lightweight view builder for TaskForReplica objects.
13- /// This mirrors `TaskViewBuilder` but only uses fields available on the
14- /// `TaskForReplica` model (modified, due, status, description, tags, uuid, priority).
1515class TaskReplicaViewBuilder extends StatelessWidget {
1616 const TaskReplicaViewBuilder ({
1717 super .key,
@@ -32,22 +32,15 @@ class TaskReplicaViewBuilder extends StatelessWidget {
3232 Theme .of (context).extension < TaskwarriorColorTheme > ()! ;
3333
3434 return Obx (() {
35- // We receive the tasks list from the caller so we copy it here.
3635 List <TaskForReplica > tasks = List <TaskForReplica >.from (replicaTasks);
37-
38- // Project filtering is not meaningful for TaskForReplica if project is not set
3936 if (project != null && project != 'All Projects' ) {
40- // TaskForReplica doesn't have a ` project` field by default, so skip filtering.
37+ tasks = tasks. where ((task) => task. project == project). toList ();
4138 }
42-
43- // Default sort: by modified desc if available
4439 tasks.sort ((a, b) {
4540 final am = a.modified ?? 0 ;
4641 final bm = b.modified ?? 0 ;
4742 return bm.compareTo (am);
4843 });
49-
50- // Apply pending/completed filter
5144 tasks = tasks.where ((task) {
5245 if (pendingFilter) {
5346 return task.status == 'pending' ;
@@ -56,7 +49,6 @@ class TaskReplicaViewBuilder extends StatelessWidget {
5649 }
5750 }).toList ();
5851
59- // Only allow sorting by fields that exist on TaskForReplica
6052 tasks.sort ((a, b) {
6153 switch (selectedSort) {
6254 case 'Modified+' :
@@ -106,7 +98,26 @@ class TaskReplicaViewBuilder extends StatelessWidget {
10698 itemCount: tasks.length,
10799 itemBuilder: (context, index) {
108100 final task = tasks[index];
109- return Card (
101+ // Determine if due is within 24 hours or already past (only for pending filter)
102+ final bool isDueSoon = (() {
103+ if (! pendingFilter) return false ;
104+ // Only apply due-soon highlighting when delay-task setting is enabled
105+ try {
106+ final HomeController hc = Get .find <HomeController >();
107+ if (! hc.useDelayTask.value) return false ;
108+ } catch (_) {
109+ return false ;
110+ }
111+ final dueStr = task.due;
112+ if (dueStr == null || dueStr.isEmpty) return false ;
113+ final parsed = DateTime .tryParse (dueStr);
114+ if (parsed == null ) return false ;
115+ final now = DateTime .now ().toUtc ();
116+ final threshold = now.add (const Duration (hours: 24 ));
117+ return parsed.toUtc ().isBefore (threshold);
118+ })();
119+
120+ final card = Card (
110121 color: tColors.secondaryBackgroundColor,
111122 child: InkWell (
112123 splashColor: tColors.primaryBackgroundColor,
@@ -115,7 +126,9 @@ class TaskReplicaViewBuilder extends StatelessWidget {
115126 child: Container (
116127 decoration: BoxDecoration (
117128 border: Border .all (
118- color: tColors.primaryTextColor! ,
129+ color: isDueSoon
130+ ? Colors .red
131+ : tColors.primaryTextColor! ,
119132 ),
120133 color: tColors.primaryBackgroundColor,
121134 borderRadius: BorderRadius .circular (8.0 ),
@@ -144,6 +157,49 @@ class TaskReplicaViewBuilder extends StatelessWidget {
144157 ),
145158 ),
146159 );
160+
161+ // Only enable swipe actions (complete/delete) when pendingFilter is true
162+ if (pendingFilter) {
163+ return Slidable (
164+ startActionPane: ActionPane (
165+ motion: const BehindMotion (),
166+ children: [
167+ SlidableAction (
168+ onPressed: (ctx) {
169+ completeTask (task);
170+ },
171+ icon: Icons .done,
172+ label: SentenceManager (
173+ currentLanguage:
174+ AppSettings .selectedLanguage)
175+ .sentences
176+ .complete,
177+ backgroundColor: TaskWarriorColors .green,
178+ ),
179+ ],
180+ ),
181+ endActionPane: ActionPane (
182+ motion: const DrawerMotion (),
183+ children: [
184+ SlidableAction (
185+ onPressed: (ctx) {
186+ deleteTask (task);
187+ },
188+ icon: Icons .delete,
189+ label: SentenceManager (
190+ currentLanguage:
191+ AppSettings .selectedLanguage)
192+ .sentences
193+ .delete,
194+ backgroundColor: TaskWarriorColors .red,
195+ ),
196+ ],
197+ ),
198+ child: card,
199+ );
200+ }
201+
202+ return card;
147203 },
148204 ),
149205 );
@@ -162,4 +218,14 @@ class TaskReplicaViewBuilder extends StatelessWidget {
162218 return Colors .grey;
163219 }
164220 }
221+
222+ void completeTask (TaskForReplica task) async {
223+ await Replica .modifyTaskInReplica (task.copyWith (status: 'completed' ));
224+ Get .find <HomeController >().refreshReplicaTaskList ();
225+ }
226+
227+ void deleteTask (TaskForReplica task) async {
228+ await Replica .deleteTaskFromReplica (task.uuid);
229+ Get .find <HomeController >().refreshReplicaTaskList ();
230+ }
165231}
0 commit comments