Skip to content

Commit

Permalink
Fix start/stop task running flashing
Browse files Browse the repository at this point in the history
  • Loading branch information
vanarok committed Jul 18, 2024
1 parent 03103dd commit c12d4ef
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 99 deletions.
54 changes: 26 additions & 28 deletions components/RunningTask.vue
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
<script lang="ts" setup>
import {computed, ref} from "vue";
import {useMutation, useQueryClient} from "@tanstack/vue-query";
import {putTask} from "@/api";
import {calculateTime} from "@/helpers";
import {useRunningTaskQuery} from "@/composables/useRunningTaskQuery";
import {putTask} from '@/api'
import {calculateTime} from '@/helpers'
import {useMutation, useQueryClient} from '@tanstack/vue-query'
import {computed, ref} from 'vue'
const props = defineProps<{
project: unknown | null
}>();
}>()
const emit = defineEmits<{
(e: 'stop-task'): void
}>()
const {data: tasks, isPending} = useRunningTaskQuery(props.project?.id ?? null)
const task = computed(() => {
return tasks.value.data[0];
});
const projectId = computed(() => props.project?.id)
const {task} = useRunningTask(projectId)
const queryClient = useQueryClient()
const {mutate: stopTask} = useMutation({
mutationFn: ({task, status}: { task: Record<any, any>, status: string }) => putTask(task.id, {
action: 'stop',
data: {...task, status_id: status}
}),
mutationFn: ({task, status}: {task: Record<any, any>; status: string}) =>
putTask(task.id, {
action: 'stop',
data: {...task, status_id: status}
}),
onMutate: async () => {
await queryClient.cancelQueries({queryKey: ['running-task', props.project?.id ?? null]})
const previousTodos = queryClient.getQueryData(['running-task', props.project?.id ?? null])
queryClient.setQueryData(['running-task', props.project?.id ?? null], (old) => {
await queryClient.cancelQueries({queryKey: ['tasks', props.project?.id ?? null, statuses.running]})
const previousTodos = queryClient.getQueryData(['tasks', props.project?.id ?? null, statuses.running])
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], (old) => {
return {
data: [],
meta: []
Expand All @@ -39,33 +36,34 @@ const {mutate: stopTask} = useMutation({
// If the mutation fails,
// use the context returned from onMutate to roll back
onError(_, __, context) {
queryClient.setQueryData(['running-task', props.project?.id ?? null], context.previousTodos)
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], context.previousTodos)
},
// Always refetch after error or success:
onSettled() {
queryClient.invalidateQueries({queryKey: ['running-task', props.project?.id ?? null]})
queryClient.invalidateQueries({queryKey: ['tasks', props.project?.id ?? null, statuses.running]})
queryClient.invalidateQueries({queryKey: ['tasks', props.project?.id ?? null, '']})
queryClient.invalidateQueries({queryKey: ['projects']})
},
}
})
const time = ref(0);
const time = ref(0)
const calculation = calculateTime(task.value.time_log, {
inSeconds: true,
calculateLastTimeLog: false,
});
calculateLastTimeLog: false
})
time.value = Number(calculation)
setInterval(() => {
time.value = (time.value + 1)
}, 1000);
time.value = time.value + 1
}, 1000)
const {statuses} = useSettings();
const {statuses} = useSettings()
</script>

<template>
<div class="project">
<div v-if="isPending">Loading...</div>
<div v-if="task.id === null">Loading...</div>
<h2 v-else>
<p class="time">{{ new Date(time * 1000).toISOString().slice(11, 19) }}</p>
<p>{{ task.description }}</p>
Expand Down
103 changes: 75 additions & 28 deletions components/Task.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,63 +23,87 @@
</button>
</div>
</Transition>
<Transition mode="out-in">
<div class="info">
<span class="time">{{ getTime() }}</span>
<button v-if="!task.project_id" class="assign-project-button" @click="enableAssignProjectMode(task)">Assign project</button>
</Transition>
</div>
</div>
</template>
<script lang="ts" setup>
import {computed, ref} from 'vue'
import {deleteTask, putTask} from '@/api'
import {useMutation, useQueryClient} from '@tanstack/vue-query'
import {useAssignTaskProject} from '@/composables/useAssignTaskProject'
import dayjs from 'dayjs'
import {useSettings} from '@/composables/useSettings'
import {calculateTime} from '@/helpers'
import {useMutation, useQueryClient} from '@tanstack/vue-query'
import dayjs from 'dayjs'
import {computed, ref} from 'vue'
const props = defineProps<{
task: Record<any, any>
project?: unknown | null
}>()
const visibleOverlay = ref(false)
const {statuses} = useSettings()
const {mutate: startTask} = useMutation({
const {
mutate: startTask,
isError,
error
} = useMutation({
mutationFn: ({task, status}: {task: Record<any, any>; status: string}) =>
putTask(task.id, {
action: 'start',
data: {...task, status_id: status}
}),
onMutate: async ({task, status}) => {
await queryClient.cancelQueries({queryKey: ['running-task', props.project?.id ?? null]})
const previousTodos = queryClient.getQueryData(['running-task', props.project?.id ?? null])
queryClient.setQueryData(['running-task', props.project?.id ?? null], (old) => {
const date = dayjs()
const unixTimestamp = date.unix()
const timeLogParsed = JSON.parse(task.time_log)
const timeLogRunning = [...timeLogParsed, [unixTimestamp, 0]]
await queryClient.cancelQueries({queryKey: ['tasks', props.project?.id ?? null, statuses.running]})
return {
...old,
data: [
{
...task,
status_id: status,
time_log: JSON.stringify(timeLogRunning)
}
]
// Snapshot the previous value
const previousTodos = queryClient.getQueryData(['tasks', props.project?.id ?? null, ''])
const date = dayjs()
const unixTimestamp = date.unix()
const timeLogParsed = JSON.parse(task.time_log)
const timeLogRunning = [...timeLogParsed, [unixTimestamp, 0]]
const optimisticTask = {
data: [
{
...task,
status_id: status,
time_log: JSON.stringify(timeLogRunning)
}
],
meta: {
pagination: {
total: 1,
count: 1,
per_page: 6,
current_page: 1,
total_pages: 1,
links: {}
}
}
})
}
return {previousTodos}
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], optimisticTask)
return {optimisticTask, previousTodos}
},
onSuccess: (result, variables, context) => {
// Replace optimistic todo in the todos list with the result
queryClient.setQueryData(['tasks', props.project?.id ?? null, statuses.running], (old) => ({
data: old.data.map((task) => (task.id === context.optimisticTask.id ? result.data : task))
}))
},
// If the mutation fails,
// use the context returned from onMutate to roll back
onError(_, __, context) {
queryClient.setQueryData(['running-task', props.project?.id ?? null], context.previousTodos)
queryClient.setQueryData(['tasks', props.project?.id ?? null], context.previousTodos)
},
// Always refetch after error or success:
/* Always refetch after error or success: */
onSettled() {
queryClient.invalidateQueries({queryKey: ['running-task', props.project?.id ?? null]})
queryClient.invalidateQueries({queryKey: ['tasks', props.project?.id ?? null]})
}
})
Expand Down Expand Up @@ -127,7 +151,6 @@ const confirmRemoveTask = (task: unknown) => {
}
}
const {statuses} = useSettings()
const {mutate: setStatus} = useMutation({
mutationFn: ({task, status}: {task: Record<any, any>; status: string}) => {
return putTask(task.id, {data: {...task, status_id: status}})
Expand Down Expand Up @@ -160,6 +183,16 @@ const taskFinished = computed(() => {
}
return false
})
const getTime = () => {
const time = Number(
calculateTime(props.task.time_log, {
inSeconds: true,
calculateLastTimeLog: false
})
)
return new Date(time * 1000).toISOString().slice(11, 19)
}
</script>

<style scoped>
Expand Down Expand Up @@ -228,5 +261,19 @@ const taskFinished = computed(() => {
textarea {
color: gray;
}
.time {
color: lightgray;
}
}
.time {
text-align: start;
color: gray;
}
.info {
display: flex;
gap: 0.5em;
}
</style>
34 changes: 20 additions & 14 deletions composables/useAssignTaskProject.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {readonly, Ref, ref, watch} from "vue";
import {useMutation} from "@tanstack/vue-query";
import {putTask} from "@/api";
import {putTask} from '@/api'
import {useMutation} from '@tanstack/vue-query'
import {readonly, Ref, ref, watch} from 'vue'

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

export const useAssignTaskProject = (options?: {
project?: Ref<unknown | null>,
cancelCallback?: () => void,
project?: Ref<unknown | null>
cancelCallback?: () => void
enableCallback?: () => void
}): {
assignProjectMode: Readonly<Ref<boolean>>,
cancelAssignProjectMode?: () => void,
assignProjectMode: Readonly<Ref<boolean>>
cancelAssignProjectMode?: () => void
enableAssignProjectMode?: (task: unknown) => void
assignTaskProject?: () => void,
assignTaskProject?: () => void
} => {
const updateAssignProjectMode = (value: boolean) => {
assignProjectMode.value = value
Expand All @@ -38,14 +38,20 @@ export const useAssignTaskProject = (options?: {
}
})

const {mutate: assignTaskProject, isPending, isSuccess, error} = useMutation({
mutationFn: () => putTask(assignProjectModeTask.value.id, {
action: 'assign',
data: {...assignProjectModeTask.value, project_id: options.project.value.id}
}),
const {
mutate: assignTaskProject,
isPending,
isSuccess,
error
} = useMutation({
mutationFn: () =>
putTask(assignProjectModeTask.value.id, {
action: 'assign',
data: {...assignProjectModeTask.value, project_id: options.project.value.id}
}),
onSuccess(variables) {
cancelAssignProjectMode()
},
}
})

watch(error, () => {
Expand Down
19 changes: 19 additions & 0 deletions composables/useRunningTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {isTaskRunning} from '@/helpers'
import {Ref, computed} from 'vue'
import {useTasksQuery} from './useTasksQuery'

export function useRunningTask(projectId: Ref) {
const {data: tasks} = useTasksQuery(projectId)

const task = computed(() => {
return tasks.value?.data[0]
})
const isRunning = computed<boolean>(() => {
if (task.value) {
return isTaskRunning(task.value)
}
return false
})

return {task, isRunning}
}
12 changes: 0 additions & 12 deletions composables/useRunningTaskQuery.ts

This file was deleted.

15 changes: 15 additions & 0 deletions composables/useTasksQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {getTasks} from '@/api'
import {useQuery} from '@tanstack/vue-query'
import {Ref, watch} from 'vue'

export function useTasksQuery(projectId: Ref) {
watch(projectId, () => {
console.log('projectId changed', projectId.value)
})
const {statuses} = useSettings()
return useQuery({
queryKey: ['tasks', projectId, statuses.running],
queryFn: () => getTasks({projectId: projectId.value, taskStatusId: statuses.running}),
staleTime: Infinity
})
}
Loading

0 comments on commit c12d4ef

Please sign in to comment.