Skip to content

Commit

Permalink
[Reclaim] fixes issues and added new features (raycast#10640)
Browse files Browse the repository at this point in the history
  • Loading branch information
maximilianzuern authored Feb 8, 2024
1 parent 109f3f6 commit 87c8e87
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 93 deletions.
7 changes: 7 additions & 0 deletions extensions/reclaim-ai/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# reclaim Changelog

## [Update] - 2024-02-07
- Resolves issue of updating task priority to "low priority"
- Displays a warning icon in the task list if a task is "at risk".
- Implemented optimistic updates for listed tasks.
- Added new "Send to Up Next" action for tasks.
- Simplified the task update process.

## [Fixes] - 2024-01-31
- Removed top-level Join Meeting command, deferring instead to My Calendar → Choose Event → Join meeting.

Expand Down
26 changes: 7 additions & 19 deletions extensions/reclaim-ai/src/hooks/useTask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { NativePreferences } from "../types/preferences";
import { Task } from "../types/task";
import { axiosPromiseData } from "../utils/axiosPromise";
import reclaimApi from "./useApi";
import { ApiResponseTasks, CreateTaskProps } from "./useTask.types";
import { CreateTaskProps } from "./useTask.types";

const useTask = () => {
const { fetcher } = reclaimApi();
Expand All @@ -22,7 +22,7 @@ const useTask = () => {
);

const useFetchTasks = () =>
useFetch<ApiResponseTasks>(`${apiUrl}/tasks`, {
useFetch<[Task]>(`${apiUrl}/tasks?instances=true`, {
headers,
keepPreviousData: true,
});
Expand Down Expand Up @@ -77,16 +77,6 @@ const useTask = () => {
}
};

const getAllTasks = async () => {
try {
const [tasks, error] = await axiosPromiseData<ApiResponseTasks>(fetcher("/tasks"));
if (!tasks && error) throw error;
return tasks;
} catch (error) {
console.error("Error while fetching tasks", error);
}
};

// Add time
const addTime = async (task: Task, time: number) => {
try {
Expand All @@ -101,20 +91,19 @@ const useTask = () => {
};

// Update task
const updateTask = async (task: Task) => {
const updateTask = async (task: Partial<Task>, payload: Partial<Task>) => {
try {
const [updatedTask, error] = await axiosPromiseData(
const [updatedTask] = await axiosPromiseData(
fetcher(`/tasks/${task.id}`, {
method: "PUT",
method: "PATCH",
responseType: "json",
data: task,
data: payload,
})
);

if (!updatedTask || error) throw error;
return updatedTask;
} catch (error) {
console.error("Error while updating task", error);
throw error;
}
};

Expand Down Expand Up @@ -149,7 +138,6 @@ const useTask = () => {
createTask,
handleStartTask,
handleStopTask,
getAllTasks,
addTime,
updateTask,
doneTask,
Expand Down
4 changes: 0 additions & 4 deletions extensions/reclaim-ai/src/hooks/useTask.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Task } from "../types/task";

export interface CreateTaskProps {
title: string;
timePolicy: string;
Expand All @@ -13,5 +11,3 @@ export interface CreateTaskProps {
priority: string;
onDeck: boolean;
}

export type ApiResponseTasks = Task[];
139 changes: 69 additions & 70 deletions extensions/reclaim-ai/src/list-tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const StatusDropdown = (props: StatusDropdownProps) => {
);
};

// Main Function
function TaskList() {
const [selectedStatus, setSelectedStatus] = useState<DropdownStatus | undefined>();
const { currentUser } = useUser();
Expand All @@ -57,85 +56,63 @@ function TaskList() {
const { useFetchTasks, addTime, updateTask, doneTask, incompleteTask } = useTask();

const { data: tasksData, isLoading } = useFetchTasks();
const tasks = useMemo(() => tasksData ?? [], [tasksData]);

const handleUpdatePriority = async (task: Task, priority: string) => {
await showToast(Toast.Style.Animated, "Updating priority...");
try {
task.priority = priority;
const updatedPriority = await updateTask(task);
if (updatedPriority) {
showToast(Toast.Style.Success, `Updated priority successfully!`);
} else {
throw new Error("Update task priority failed.");
}
} catch (error) {
showToast({ style: Toast.Style.Failure, title: "Error while updating priority", message: String(error) });
}
};
const [tasks, setTasks] = useState<Task[]>(tasksData ?? []);

// Add time to task function
const handleAddTime = async (task: Task, time: number) => {
await showToast(Toast.Style.Animated, "Adding time...");
try {
const updatedTime = await addTime(task, time);
if (updatedTime) {
showToast(Toast.Style.Success, `Added ${formatStrDuration(time + "m")} to "${task.title}" successfully!`);
} else {
throw new Error("Update time request failed.");
}
await addTime(task, time);
} catch (error) {
showToast({ style: Toast.Style.Failure, title: "Error while updating time", message: String(error) });
return;
}
// optimistic update
const updatedTime = task.timeChunksRemaining + time / TIME_BLOCK_IN_MINUTES;
setTasks((prevTasks) => prevTasks.map((t) => (t.id === task.id ? { ...t, timeChunksRemaining: updatedTime } : t)));
showToast(Toast.Style.Success, `Added ${formatStrDuration(time + "m")} to "${task.title}" successfully!`);
};

// Set task to done
const handleDoneTask = async (task: Task) => {
await showToast(Toast.Style.Animated, "Updating task...");
try {
const setTaskDone = await doneTask(task);
if (setTaskDone) {
showToast(Toast.Style.Success, `Task '${task.title}' marked done. Nice work!`);
} else {
throw new Error("Update task request failed.");
}
await doneTask(task);
} catch (error) {
showToast({ style: Toast.Style.Failure, title: "Error while updating task", message: String(error) });
return;
}
// optimistic update
setTasks((prevTasks) => prevTasks.map((t) => (t.id === task.id ? { ...t, status: "ARCHIVED" } : t)));
showToast(Toast.Style.Success, `Task '${task.title}' marked done. Nice work!`);
};

// Set task to incomplete
const handleIncompleteTask = async (task: Task) => {
await showToast(Toast.Style.Animated, "Updating task...");
try {
const setTaskDone = await incompleteTask(task);
if (setTaskDone) {
showToast(Toast.Style.Success, `Task '${task.title}' marked incomplete!`);
} else {
throw new Error("Update task request failed.");
}
await incompleteTask(task);
} catch (error) {
showToast({ style: Toast.Style.Failure, title: "Error while updating task", message: String(error) });
return;
}
// optimistic update
setTasks((prevTasks) => prevTasks.map((t) => (t.id === task.id ? { ...t, status: "NEW" } : t)));
showToast(Toast.Style.Success, `Task '${task.title}' marked incomplete!`);
};

// Update due date
const handleUpdateTask = async (task: Task) => {
await showToast(Toast.Style.Animated, "Updating due date...");
// Update tasks
const handleUpdateTask = async (task: Partial<Task>, payload: Partial<Task>) => {
await showToast(Toast.Style.Animated, `Updating '${task.title}'...`);
try {
const updatedTask = await updateTask(task);
if (updatedTask) {
showToast(Toast.Style.Success, `Updated due date for "${task.title}" successfully!`);
} else {
throw new Error("Update due date request failed.");
}
await updateTask(task, payload);
} catch (error) {
showToast({
style: Toast.Style.Failure,
title: "Error while updating due date",
message: String(error),
});
showToast({ style: Toast.Style.Failure, title: `Error while updating '${task.title}'!`, message: String(error) });
return;
}
// optimistic update
setTasks((prevTasks) => prevTasks.map((t) => (t.id === task.id ? { ...t, ...payload } : t)));
showToast(Toast.Style.Success, `Updated '${task.title}'!`);
};

// Filter tasks by status
Expand Down Expand Up @@ -163,23 +140,25 @@ function TaskList() {
const getListAccessories = (task: Task) => {
const list = [];

if (task.status !== "ARCHIVED" && task.atRisk) {
list.push({
tag: {
value: "",
color: Color.Red,
},
icon: Icon.ExclamationMark,
tooltip: "Task at risk!",
});
}

if (defaults.schedulerVersion > 14) {
if (task.onDeck) {
list.push({
tag: {
value: "",
color: Color.Green,
color: Color.Yellow,
},
tooltip: "Remove from Up Next",
icon: Icon.ArrowNe,
});
} else {
list.push({
tag: {
value: "",
color: Color.SecondaryText,
},
tooltip: "Send to Up Next",
tooltip: "Task is Up Next",
icon: Icon.ArrowNe,
});
}
Expand Down Expand Up @@ -298,34 +277,34 @@ function TaskList() {
icon={{ source: Icon.FullSignal }}
title="Critical"
onAction={() => {
const priority = "P1";
handleUpdatePriority(task, priority);
const payload = { priority: "P1" };
handleUpdateTask(task, payload);
}}
/>

<Action
icon={{ source: Icon.Signal3 }}
title="High Priority"
onAction={() => {
const priority = "P2";
handleUpdatePriority(task, priority);
const payload = { priority: "P2" };
handleUpdateTask(task, payload);
}}
/>

<Action
icon={{ source: Icon.Signal2 }}
title="Medium Priority"
onAction={() => {
const priority = "P3";
handleUpdatePriority(task, priority);
const payload = { priority: "P3" };
handleUpdateTask(task, payload);
}}
/>
<Action
icon={{ source: Icon.Signal1 }}
title="Low Priority"
onAction={() => {
const priority = "P1";
handleUpdatePriority(task, priority);
const payload = { priority: "P4" };
handleUpdateTask(task, payload);
}}
/>
</ActionPanel.Submenu>
Expand Down Expand Up @@ -382,12 +361,32 @@ function TaskList() {
shortcut={{ modifiers: ["cmd"], key: "d" }}
onChange={(date: Date | null) => {
if (date) {
handleUpdateTask({ ...task, due: date.toISOString() });
const payload = { due: date.toISOString() };
handleUpdateTask(task, payload);
}
}}
/>
{task.onDeck ? (
<Action
icon={{ source: Icon.ArrowDown, tintColor: Color.Red }}
title="Remove From Up Next"
onAction={() => {
const payload = { onDeck: false };
handleUpdateTask(task, payload);
}}
/>
) : (
<Action
icon={{ source: Icon.ArrowNe, tintColor: Color.Yellow }}
title="Send to Up Next"
onAction={() => {
const payload = { onDeck: true };
handleUpdateTask(task, payload);
}}
/>
)}
<Action
icon={Icon.Checkmark}
icon={{ source: Icon.Checkmark, tintColor: Color.Green }}
title="Mark as Done"
onAction={() => {
handleDoneTask(task);
Expand Down

0 comments on commit 87c8e87

Please sign in to comment.