Skip to content

Commit c12d4ef

Browse files
committed
Fix start/stop task running flashing
1 parent 03103dd commit c12d4ef

File tree

7 files changed

+164
-99
lines changed

7 files changed

+164
-99
lines changed

components/RunningTask.vue

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
11
<script lang="ts" setup>
2-
import {computed, ref} from "vue";
3-
import {useMutation, useQueryClient} from "@tanstack/vue-query";
4-
import {putTask} from "@/api";
5-
import {calculateTime} from "@/helpers";
6-
import {useRunningTaskQuery} from "@/composables/useRunningTaskQuery";
2+
import {putTask} from '@/api'
3+
import {calculateTime} from '@/helpers'
4+
import {useMutation, useQueryClient} from '@tanstack/vue-query'
5+
import {computed, ref} from 'vue'
76
87
const props = defineProps<{
98
project: unknown | null
10-
}>();
9+
}>()
1110
const emit = defineEmits<{
1211
(e: 'stop-task'): void
1312
}>()
1413
15-
const {data: tasks, isPending} = useRunningTaskQuery(props.project?.id ?? null)
16-
const task = computed(() => {
17-
return tasks.value.data[0];
18-
});
14+
const projectId = computed(() => props.project?.id)
15+
const {task} = useRunningTask(projectId)
1916
2017
const queryClient = useQueryClient()
2118
22-
2319
const {mutate: stopTask} = useMutation({
24-
mutationFn: ({task, status}: { task: Record<any, any>, status: string }) => putTask(task.id, {
25-
action: 'stop',
26-
data: {...task, status_id: status}
27-
}),
20+
mutationFn: ({task, status}: {task: Record<any, any>; status: string}) =>
21+
putTask(task.id, {
22+
action: 'stop',
23+
data: {...task, status_id: status}
24+
}),
2825
onMutate: async () => {
29-
await queryClient.cancelQueries({queryKey: ['running-task', props.project?.id ?? null]})
30-
const previousTodos = queryClient.getQueryData(['running-task', props.project?.id ?? null])
31-
queryClient.setQueryData(['running-task', props.project?.id ?? null], (old) => {
26+
await queryClient.cancelQueries({queryKey: ['tasks', props.project?.id ?? null, statuses.running]})
27+
const previousTodos = queryClient.getQueryData(['tasks', props.project?.id ?? null, statuses.running])
28+
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], (old) => {
3229
return {
3330
data: [],
3431
meta: []
@@ -39,33 +36,34 @@ const {mutate: stopTask} = useMutation({
3936
// If the mutation fails,
4037
// use the context returned from onMutate to roll back
4138
onError(_, __, context) {
42-
queryClient.setQueryData(['running-task', props.project?.id ?? null], context.previousTodos)
39+
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], context.previousTodos)
4340
},
4441
// Always refetch after error or success:
4542
onSettled() {
46-
queryClient.invalidateQueries({queryKey: ['running-task', props.project?.id ?? null]})
43+
queryClient.invalidateQueries({queryKey: ['tasks', props.project?.id ?? null, statuses.running]})
44+
queryClient.invalidateQueries({queryKey: ['tasks', props.project?.id ?? null, '']})
4745
queryClient.invalidateQueries({queryKey: ['projects']})
48-
},
46+
}
4947
})
5048
51-
const time = ref(0);
49+
const time = ref(0)
5250
const calculation = calculateTime(task.value.time_log, {
5351
inSeconds: true,
54-
calculateLastTimeLog: false,
55-
});
52+
calculateLastTimeLog: false
53+
})
5654
5755
time.value = Number(calculation)
5856
5957
setInterval(() => {
60-
time.value = (time.value + 1)
61-
}, 1000);
58+
time.value = time.value + 1
59+
}, 1000)
6260
63-
const {statuses} = useSettings();
61+
const {statuses} = useSettings()
6462
</script>
6563

6664
<template>
6765
<div class="project">
68-
<div v-if="isPending">Loading...</div>
66+
<div v-if="task.id === null">Loading...</div>
6967
<h2 v-else>
7068
<p class="time">{{ new Date(time * 1000).toISOString().slice(11, 19) }}</p>
7169
<p>{{ task.description }}</p>

components/Task.vue

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,63 +23,87 @@
2323
</button>
2424
</div>
2525
</Transition>
26-
<Transition mode="out-in">
26+
<div class="info">
27+
<span class="time">{{ getTime() }}</span>
2728
<button v-if="!task.project_id" class="assign-project-button" @click="enableAssignProjectMode(task)">Assign project</button>
28-
</Transition>
29+
</div>
2930
</div>
3031
</template>
3132
<script lang="ts" setup>
32-
import {computed, ref} from 'vue'
3333
import {deleteTask, putTask} from '@/api'
34-
import {useMutation, useQueryClient} from '@tanstack/vue-query'
3534
import {useAssignTaskProject} from '@/composables/useAssignTaskProject'
36-
import dayjs from 'dayjs'
3735
import {useSettings} from '@/composables/useSettings'
36+
import {calculateTime} from '@/helpers'
37+
import {useMutation, useQueryClient} from '@tanstack/vue-query'
38+
import dayjs from 'dayjs'
39+
import {computed, ref} from 'vue'
3840
3941
const props = defineProps<{
4042
task: Record<any, any>
4143
project?: unknown | null
4244
}>()
4345
4446
const visibleOverlay = ref(false)
47+
const {statuses} = useSettings()
4548
46-
const {mutate: startTask} = useMutation({
49+
const {
50+
mutate: startTask,
51+
isError,
52+
error
53+
} = useMutation({
4754
mutationFn: ({task, status}: {task: Record<any, any>; status: string}) =>
4855
putTask(task.id, {
4956
action: 'start',
5057
data: {...task, status_id: status}
5158
}),
5259
onMutate: async ({task, status}) => {
53-
await queryClient.cancelQueries({queryKey: ['running-task', props.project?.id ?? null]})
54-
const previousTodos = queryClient.getQueryData(['running-task', props.project?.id ?? null])
55-
queryClient.setQueryData(['running-task', props.project?.id ?? null], (old) => {
56-
const date = dayjs()
57-
const unixTimestamp = date.unix()
58-
const timeLogParsed = JSON.parse(task.time_log)
59-
const timeLogRunning = [...timeLogParsed, [unixTimestamp, 0]]
60+
await queryClient.cancelQueries({queryKey: ['tasks', props.project?.id ?? null, statuses.running]})
6061
61-
return {
62-
...old,
63-
data: [
64-
{
65-
...task,
66-
status_id: status,
67-
time_log: JSON.stringify(timeLogRunning)
68-
}
69-
]
62+
// Snapshot the previous value
63+
const previousTodos = queryClient.getQueryData(['tasks', props.project?.id ?? null, ''])
64+
65+
const date = dayjs()
66+
const unixTimestamp = date.unix()
67+
const timeLogParsed = JSON.parse(task.time_log)
68+
const timeLogRunning = [...timeLogParsed, [unixTimestamp, 0]]
69+
const optimisticTask = {
70+
data: [
71+
{
72+
...task,
73+
status_id: status,
74+
time_log: JSON.stringify(timeLogRunning)
75+
}
76+
],
77+
meta: {
78+
pagination: {
79+
total: 1,
80+
count: 1,
81+
per_page: 6,
82+
current_page: 1,
83+
total_pages: 1,
84+
links: {}
85+
}
7086
}
71-
})
87+
}
7288
73-
return {previousTodos}
89+
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], optimisticTask)
90+
91+
return {optimisticTask, previousTodos}
92+
},
93+
onSuccess: (result, variables, context) => {
94+
// Replace optimistic todo in the todos list with the result
95+
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], (old) => ({
96+
data: old.data.map((task) => (task.id === context.optimisticTask.id ? result.data : task))
97+
}))
7498
},
7599
// If the mutation fails,
76100
// use the context returned from onMutate to roll back
77101
onError(_, __, context) {
78-
queryClient.setQueryData(['running-task', props.project?.id ?? null], context.previousTodos)
102+
queryClient.setQueryData(['tasks', props.project?.id ?? null], context.previousTodos)
79103
},
80-
// Always refetch after error or success:
104+
/* Always refetch after error or success: */
81105
onSettled() {
82-
queryClient.invalidateQueries({queryKey: ['running-task', props.project?.id ?? null]})
106+
queryClient.invalidateQueries({queryKey: ['tasks', props.project?.id ?? null]})
83107
}
84108
})
85109
@@ -127,7 +151,6 @@ const confirmRemoveTask = (task: unknown) => {
127151
}
128152
}
129153
130-
const {statuses} = useSettings()
131154
const {mutate: setStatus} = useMutation({
132155
mutationFn: ({task, status}: {task: Record<any, any>; status: string}) => {
133156
return putTask(task.id, {data: {...task, status_id: status}})
@@ -160,6 +183,16 @@ const taskFinished = computed(() => {
160183
}
161184
return false
162185
})
186+
187+
const getTime = () => {
188+
const time = Number(
189+
calculateTime(props.task.time_log, {
190+
inSeconds: true,
191+
calculateLastTimeLog: false
192+
})
193+
)
194+
return new Date(time * 1000).toISOString().slice(11, 19)
195+
}
163196
</script>
164197

165198
<style scoped>
@@ -228,5 +261,19 @@ const taskFinished = computed(() => {
228261
textarea {
229262
color: gray;
230263
}
264+
265+
.time {
266+
color: lightgray;
267+
}
268+
}
269+
270+
.time {
271+
text-align: start;
272+
color: gray;
273+
}
274+
275+
.info {
276+
display: flex;
277+
gap: 0.5em;
231278
}
232279
</style>

composables/useAssignTaskProject.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import {readonly, Ref, ref, watch} from "vue";
2-
import {useMutation} from "@tanstack/vue-query";
3-
import {putTask} from "@/api";
1+
import {putTask} from '@/api'
2+
import {useMutation} from '@tanstack/vue-query'
3+
import {readonly, Ref, ref, watch} from 'vue'
44

55
export const assignProjectMode = ref(false)
66
export const assignProjectModeTask = ref<unknown | null>(null)
77

88
export const useAssignTaskProject = (options?: {
9-
project?: Ref<unknown | null>,
10-
cancelCallback?: () => void,
9+
project?: Ref<unknown | null>
10+
cancelCallback?: () => void
1111
enableCallback?: () => void
1212
}): {
13-
assignProjectMode: Readonly<Ref<boolean>>,
14-
cancelAssignProjectMode?: () => void,
13+
assignProjectMode: Readonly<Ref<boolean>>
14+
cancelAssignProjectMode?: () => void
1515
enableAssignProjectMode?: (task: unknown) => void
16-
assignTaskProject?: () => void,
16+
assignTaskProject?: () => void
1717
} => {
1818
const updateAssignProjectMode = (value: boolean) => {
1919
assignProjectMode.value = value
@@ -38,14 +38,20 @@ export const useAssignTaskProject = (options?: {
3838
}
3939
})
4040

41-
const {mutate: assignTaskProject, isPending, isSuccess, error} = useMutation({
42-
mutationFn: () => putTask(assignProjectModeTask.value.id, {
43-
action: 'assign',
44-
data: {...assignProjectModeTask.value, project_id: options.project.value.id}
45-
}),
41+
const {
42+
mutate: assignTaskProject,
43+
isPending,
44+
isSuccess,
45+
error
46+
} = useMutation({
47+
mutationFn: () =>
48+
putTask(assignProjectModeTask.value.id, {
49+
action: 'assign',
50+
data: {...assignProjectModeTask.value, project_id: options.project.value.id}
51+
}),
4652
onSuccess(variables) {
4753
cancelAssignProjectMode()
48-
},
54+
}
4955
})
5056

5157
watch(error, () => {

composables/useRunningTask.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {isTaskRunning} from '@/helpers'
2+
import {Ref, computed} from 'vue'
3+
import {useTasksQuery} from './useTasksQuery'
4+
5+
export function useRunningTask(projectId: Ref) {
6+
const {data: tasks} = useTasksQuery(projectId)
7+
8+
const task = computed(() => {
9+
return tasks.value?.data[0]
10+
})
11+
const isRunning = computed<boolean>(() => {
12+
if (task.value) {
13+
return isTaskRunning(task.value)
14+
}
15+
return false
16+
})
17+
18+
return {task, isRunning}
19+
}

composables/useRunningTaskQuery.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

composables/useTasksQuery.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {getTasks} from '@/api'
2+
import {useQuery} from '@tanstack/vue-query'
3+
import {Ref, watch} from 'vue'
4+
5+
export function useTasksQuery(projectId: Ref) {
6+
watch(projectId, () => {
7+
console.log('projectId changed', projectId.value)
8+
})
9+
const {statuses} = useSettings()
10+
return useQuery({
11+
queryKey: ['tasks', projectId, statuses.running],
12+
queryFn: () => getTasks({projectId: projectId.value, taskStatusId: statuses.running}),
13+
staleTime: Infinity
14+
})
15+
}

0 commit comments

Comments
 (0)