Skip to content

Commit f49b056

Browse files
authored
Merge pull request #76 from cytechmobile/feat/e0dbc12_refactor-tasks-store
refactor(tasks): refactor tasks store
2 parents a8705b4 + caa02cf commit f49b056

File tree

11 files changed

+483
-1143
lines changed

11 files changed

+483
-1143
lines changed

components/Column.vue

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script setup lang="ts">
2-
import { VueDraggable } from 'vue-draggable-plus'
2+
import { type SortableEvent, VueDraggable } from 'vue-draggable-plus'
33
import type { Task } from '../types/tasks'
4-
import type { VueDraggableEvent } from '~/types/vue-draggable-plus'
54
import { doneTaskStatuses } from '~/constants/tasks'
65
76
const props = defineProps<{
@@ -29,23 +28,28 @@ watchEffect(() => {
2928
}
3029
})
3130
32-
function handleMoveTask(event: VueDraggableEvent) {
33-
const { id, kind } = event.item.dataset
34-
const { column } = event.to.dataset
31+
function handleMoveTask(event: SortableEvent) {
32+
const { oldIndex, newIndex, item, to } = event
33+
const { id, kind } = item.dataset
34+
const { column } = to.dataset
35+
if (!id || !kind || !column || oldIndex === undefined || newIndex === undefined) {
36+
throw new Error('Invalid task move event')
37+
}
38+
3539
const task = props.tasks.find((task) => task.rpb.kind === kind && task.id === id)
36-
if (!task || !column || (task.rpb.column === column && event.oldIndex === event.newIndex)) {
40+
if (!task || !column || (task.rpb.column === column && oldIndex === newIndex)) {
3741
return
3842
}
3943
40-
void tasks.moveTask({
44+
tasks.moveTask({
4145
task,
4246
column,
43-
index: event.newIndex,
47+
index: newIndex,
4448
})
4549
}
4650
4751
function handleCreateIssue(title: string) {
48-
void tasks.createIssue({ title, column: props.title })
52+
tasks.createIssue({ title, column: props.title })
4953
}
5054
5155
const columnIcon = computed(() => {

components/Header.vue

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,10 @@ const aboutLink = new URL(
8080
</button>
8181
</UTooltip>
8282
<UTooltip v-if="isDebugging" text="Reset card order. All changes will be lost!">
83-
<UButton
84-
:icon="tasks.isResettingPriority ? 'i-heroicons-arrow-path' : undefined"
85-
:disabled="tasks.isLoading || tasks.isResettingPriority"
86-
@click="tasks.resetPriority"
87-
>
83+
<button class="button-solid" type="button" @click="tasks.resetPriority">
84+
<Icon name="octicon:sync" size="16" />
8885
Reset Order
89-
</UButton>
86+
</button>
9087
</UTooltip>
9188
</template>
9289
</div>

components/TaskKindSelect.vue

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import type { Writable } from 'ts-essentials'
3+
import { assertUnreachable } from '~/utils/assertions'
34
45
const board = useBoardStore()
56
@@ -14,19 +15,17 @@ const optionIcons = {
1415
1516
const selected = computed(() => {
1617
const { taskKind } = board.state.filter
17-
if (!taskKind) {
18-
return 'All'
19-
}
20-
21-
if (taskKind === 'issue') {
22-
return 'Issues'
23-
}
2418
25-
if (taskKind === 'patch') {
26-
return 'Patches'
19+
switch (taskKind) {
20+
case undefined:
21+
return 'All'
22+
case 'issue':
23+
return 'Issues'
24+
case 'patch':
25+
return 'Patches'
26+
default:
27+
return assertUnreachable(taskKind)
2728
}
28-
29-
throw new Error(`Invalid task kind: ${taskKind}`)
3029
})
3130
3231
function handleChange(selected: (typeof options)[number]) {
@@ -41,7 +40,7 @@ function handleChange(selected: (typeof options)[number]) {
4140
board.state.filter.taskKind = 'patch'
4241
break
4342
default:
44-
throw new Error(`Invalid selected option: ${selected}`)
43+
assertUnreachable(selected)
4544
}
4645
}
4746
</script>

composables/use-tasks-fetch.ts

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { RadicleIssue, RadiclePatch } from '~/types/httpd'
22
import type { Issue, Patch, Task } from '~/types/tasks'
3+
import { assertUnreachable } from '~/utils/assertions'
34

45
export function useTasksFetch() {
56
const { $httpd } = useNuxtApp()
67
const route = useRoute('node-rid')
78

8-
async function fetchIssue(id: string): Promise<Issue> {
9+
async function fetchIssueById(id: string): Promise<Issue> {
910
const radicleIssue = await $httpd('/projects/{rid}/issues/{issue}', {
1011
path: { rid: route.params.rid, issue: id },
1112
})
@@ -14,7 +15,7 @@ export function useTasksFetch() {
1415
return issue
1516
}
1617

17-
async function fetchIssues(): Promise<Issue[]> {
18+
async function fetchAllIssues(): Promise<Issue[]> {
1819
const issueStatuses = ['open', 'closed'] satisfies RadicleIssue['state']['status'][]
1920

2021
const radicleIssuesByStatus = await Promise.all(
@@ -38,7 +39,7 @@ export function useTasksFetch() {
3839
return issues
3940
}
4041

41-
async function fetchPatch(id: string): Promise<Patch> {
42+
async function fetchPatchById(id: string): Promise<Patch> {
4243
const radiclePatch = await $httpd('/projects/{rid}/patches/{patch}', {
4344
path: { rid: route.params.rid, patch: id },
4445
})
@@ -47,7 +48,7 @@ export function useTasksFetch() {
4748
return patch
4849
}
4950

50-
async function fetchPatches(): Promise<Patch[]> {
51+
async function fetchAllPatches(): Promise<Patch[]> {
5152
const patchStatuses = [
5253
'draft',
5354
'open',
@@ -76,25 +77,10 @@ export function useTasksFetch() {
7677
return patches
7778
}
7879

79-
async function refreshSpecificTasks(tasks: Task[]): Promise<void> {
80-
await Promise.all(
81-
tasks.map(async (task) => {
82-
switch (task.rpb.kind) {
83-
case 'issue':
84-
task = await fetchIssue(task.id)
85-
break
86-
case 'patch':
87-
task = await fetchPatch(task.id)
88-
break
89-
default:
90-
throw new Error('Unsupported task kind')
91-
}
92-
}),
93-
)
94-
}
95-
9680
async function updateTaskLabels(task: Task, labels: string[]) {
97-
switch (task.rpb.kind) {
81+
const { kind } = task.rpb
82+
83+
switch (kind) {
9884
case 'issue':
9985
return await $httpd(`/projects/{rid}/issues/{issue}`, {
10086
path: { rid: route.params.rid, issue: task.id },
@@ -114,26 +100,67 @@ export function useTasksFetch() {
114100
},
115101
})
116102
default:
117-
throw new Error('Unsupported task kind')
103+
return assertUnreachable(kind)
118104
}
119105
}
120106

121107
const {
122108
data: tasks,
123109
pending: areTasksPending,
124-
refresh: refreshTasks,
110+
refresh: refetchAllTasks,
125111
} = useAsyncData('tasks', async () => {
126-
const issuesAndPatches = await Promise.all([fetchIssues(), fetchPatches()])
112+
const issuesAndPatches = await Promise.all([fetchAllIssues(), fetchAllPatches()])
127113
const tasks = issuesAndPatches.flat()
128114

129115
return tasks
130116
})
131117

118+
async function refetchTask(task: Task): Promise<void> {
119+
if (!tasks.value) {
120+
return
121+
}
122+
123+
let refetchedTask: Task
124+
const { kind } = task.rpb
125+
126+
switch (kind) {
127+
case 'issue':
128+
refetchedTask = await fetchIssueById(task.id)
129+
break
130+
case 'patch':
131+
refetchedTask = await fetchPatchById(task.id)
132+
break
133+
default:
134+
assertUnreachable(kind)
135+
}
136+
137+
const taskIndex = tasks.value.indexOf(task)
138+
if (taskIndex === undefined || taskIndex === -1) {
139+
return
140+
}
141+
142+
tasks.value[taskIndex] = refetchedTask
143+
}
144+
145+
async function refetchSpecificTasks(tasksToRefetch: Task[]): Promise<void> {
146+
await Promise.all(
147+
tasksToRefetch.map(async (task) => {
148+
await refetchTask(task)
149+
}),
150+
)
151+
}
152+
153+
async function fetchIssueByIdAndAddToTasks(id: string): Promise<void> {
154+
const issue = await fetchIssueById(id)
155+
tasks.value?.push(issue)
156+
}
157+
132158
return {
133159
tasks,
134160
areTasksPending,
135-
refreshTasks,
136-
refreshSpecificTasks,
161+
refetchAllTasks,
162+
refetchSpecificTasks,
137163
updateTaskLabels,
164+
fetchIssueByIdAndAddToTasks,
138165
}
139166
}

layouts/default.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<script setup lang="ts">
22
useTheme()
33
const tasksStore = useTasksStore()
4+
5+
const isLoading = computed(() => !tasksStore.tasksByColumn)
46
</script>
57

68
<template>
7-
<div class="flex h-dvh flex-col" aria-live="polite" :aria-busy="!tasksStore.isReady">
8-
<Loading v-if="!tasksStore.isReady" />
9+
<div class="flex h-dvh flex-col" aria-live="polite" :aria-busy="isLoading">
10+
<Loading v-if="isLoading" />
911
<template v-else>
1012
<Header />
1113

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@iconify/json": "^2.2.219",
2626
"@nuxt/ui": "2.15.2",
2727
"@pinia/nuxt": "^0.5.1",
28+
"@tanstack/vue-query": "^5.36.0",
2829
"@vueuse/nuxt": "^10.11.0",
2930
"deepmerge": "^4.3.1",
3031
"nuxt-icon": "^0.6.9",

pages/[node]/[rid].vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script setup lang="ts">
2-
import { VueDraggable } from 'vue-draggable-plus'
2+
import { type SortableEvent, VueDraggable } from 'vue-draggable-plus'
33
import { elementIds } from '~/constants/elements'
4-
import type { VueDraggableEvent } from '~/types/vue-draggable-plus'
54
65
const auth = useAuthStore()
76
const tasks = useTasksStore()
@@ -15,7 +14,11 @@ watchEffect(() => {
1514
}
1615
})
1716
18-
function handleMoveColumn({ oldIndex, newIndex }: VueDraggableEvent) {
17+
function handleMoveColumn({ oldIndex, newIndex }: SortableEvent) {
18+
if (!oldIndex || !newIndex) {
19+
throw new Error('Invalid column move event')
20+
}
21+
1922
const column = board.state.columns[oldIndex]
2023
if (!column) {
2124
return

plugins/vue-query.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { VueQueryPlugin } from '@tanstack/vue-query'
2+
3+
export default defineNuxtPlugin((nuxtApp) => {
4+
nuxtApp.vueApp.use(VueQueryPlugin)
5+
})

0 commit comments

Comments
 (0)