diff --git a/web/src/components/MemoView/components/MemoCompleteList.tsx b/web/src/components/MemoView/components/MemoCompleteList.tsx
index 07509df47..d089ebd18 100644
--- a/web/src/components/MemoView/components/MemoCompleteList.tsx
+++ b/web/src/components/MemoView/components/MemoCompleteList.tsx
@@ -1,19 +1,123 @@
import { Button } from "@/components/ui/button";
-import { Switch } from "@/components/ui/switch";
+import { useMemoViewContext } from "../MemoViewContext";
+import { useCreateMemo, useDeleteMemo, useUpdateMemo } from "@/hooks/useMemoQueries";
+import { extractTasks } from "@/utils/markdown-manipulation";
+import { toast } from "react-hot-toast";
function MemoCompleteList() {
+ const { memo } = useMemoViewContext();
+ const { mutate: updateMemo } = useUpdateMemo();
+ const { mutate: deleteMemo } = useDeleteMemo();
+ const { mutate: createMemo } = useCreateMemo();
+
+ // Parse tasks once per render for both counting and clearing
+ const tasks = extractTasks(memo.content);
+ const completedTasks = tasks.filter((task) => task.checked);
+ const completedCount = completedTasks.length;
+ const hasCompletedTasks = completedCount > 0;
+
+ const handleClearCompleted = () => {
+ if (!hasCompletedTasks) {
+ return;
+ }
+
+ const previousContent = memo.content;
+
+ // Remove completed task lines based on the precomputed task list
+ const lines = previousContent.split("\n");
+ const completedLines = completedTasks
+ .map((task) => task.lineNumber)
+ .sort((a, b) => b - a);
+
+ for (const lineNumber of completedLines) {
+ if (lineNumber >= 0 && lineNumber < lines.length) {
+ lines.splice(lineNumber, 1);
+ }
+ }
+
+ const newContent = lines.join("\n");
+ if (newContent === previousContent) {
+ return;
+ }
+
+ const isNowEmpty = newContent.trim().length === 0;
+
+ if (isNowEmpty) {
+ // Delete memo when clearing completed tasks leaves it empty
+ deleteMemo(memo.name);
+
+ toast.custom(
+ (t) => (
+
+ {completedCount} completed items cleared and memo deleted. Undo?
+
+
+ ),
+ { position: "bottom-center" },
+ );
+ } else {
+ // Just update content when there is still other content
+ updateMemo({
+ update: {
+ name: memo.name,
+ content: newContent,
+ },
+ updateMask: ["content"],
+ });
+
+ toast.custom(
+ (t) => (
+
+ {completedCount} completed items cleared. Undo?
+
+
+ ),
+ { position: "bottom-center" },
+ );
+ }
+ };
+
return (
-
-
- Completed Task
-
-
-
+
- )
+ );
}
-export default MemoCompleteList
\ No newline at end of file
+export default MemoCompleteList;
\ No newline at end of file
diff --git a/web/src/components/MemoView/components/MemoFooter.tsx b/web/src/components/MemoView/components/MemoFooter.tsx
index b42b3e079..d3728ebb8 100644
--- a/web/src/components/MemoView/components/MemoFooter.tsx
+++ b/web/src/components/MemoView/components/MemoFooter.tsx
@@ -1,6 +1,12 @@
+import { useMemoViewContext } from "../MemoViewContext";
import MemoCompleteList from "./MemoCompleteList";
function MemoFooter() {
+ const { memo } = useMemoViewContext();
+ const hasTaskList = memo.property?.hasTaskList;
+ if (!hasTaskList) {
+ return null;
+ }
return (