reorganize pinia colada composables

This commit is contained in:
Gauthier Roebroeck 2025-07-08 14:41:30 +08:00
parent 1c887c759b
commit 6be284b568
29 changed files with 170 additions and 175 deletions

View file

@ -1,6 +1,6 @@
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest'
import { server } from '@/mocks/api/node'
import { useActuatorInfo } from '@/colada/queries/actuator-info'
import { useActuatorInfo } from '@/colada/actuator-info'
import { createMockColada } from '@/mocks/pinia-colada'
import { enableAutoUnmount } from '@vue/test-utils'
import type { ErrorCause } from '@/api/komga-client'

View file

@ -1,7 +1,7 @@
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest'
import { server } from '@/mocks/api/node'
import { createMockColada } from '@/mocks/pinia-colada'
import { useAnnouncements } from '@/colada/queries/announcements'
import { useAnnouncements } from '@/colada/announcements'
import { enableAutoUnmount } from '@vue/test-utils'
beforeAll(() => server.listen())

View file

@ -0,0 +1,36 @@
import { defineMutation, defineQuery, useMutation, useQuery, useQueryCache } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
export const QUERY_KEYS_ANNOUNCEMENTS = {
root: ['announcements'] as const,
}
export const useAnnouncements = defineQuery(() => {
const { data, ...rest } = useQuery({
key: () => QUERY_KEYS_ANNOUNCEMENTS.root,
query: () =>
komgaClient
.GET('/api/v1/announcements')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
// 1 hour
staleTime: 60 * 60 * 1000,
gcTime: false,
})
const unreadCount = computed(
() => data.value?.items?.filter((x) => false == x._komga?.read)?.length || 0,
)
return { ...rest, data, unreadCount }
})
export const useMarkAnnouncementsRead = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (announcementIds: string[]) =>
komgaClient.PUT('/api/v1/announcements', { body: announcementIds }),
onSuccess: () => {
void queryCache.invalidateQueries({ key: QUERY_KEYS_ANNOUNCEMENTS.root })
},
})
})

View file

@ -1,7 +1,7 @@
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest'
import { server } from '@/mocks/api/node'
import { createMockColada } from '@/mocks/pinia-colada'
import { useAppReleases } from '@/colada/queries/app-releases'
import { useAppReleases } from '@/colada/app-releases'
import { enableAutoUnmount } from '@vue/test-utils'
beforeAll(() => server.listen())

View file

@ -1,6 +1,6 @@
import { defineQuery, useQuery } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
import { useActuatorInfo } from '@/colada/queries/actuator-info'
import { useActuatorInfo } from '@/colada/actuator-info'
export const useAppReleases = defineQuery(() => {
const { data, ...rest } = useQuery({

View file

@ -1,15 +0,0 @@
import { defineMutation, useMutation, useQueryCache } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
export const useLogout = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: () => komgaClient.POST('/api/logout'),
onSuccess: () => {
void queryCache.invalidateQueries({ key: ['current-user'] })
},
onError: (error) => {
console.log('logout error', error)
},
})
})

View file

@ -1,16 +0,0 @@
import { defineMutation, useMutation, useQueryCache } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
export const useMarkAnnouncementsRead = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (announcementIds: string[]) =>
komgaClient.PUT('/api/v1/announcements', { body: announcementIds }),
onSuccess: () => {
void queryCache.invalidateQueries({ key: ['announcements'] })
},
onError: (error) => {
console.log('announcements mark read error', error)
},
})
})

View file

@ -1,54 +0,0 @@
import { defineMutation, useMutation, useQueryCache } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
import type { components } from '@/generated/openapi/komga'
export const useCreateUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (user: components['schemas']['UserCreationDto']) =>
komgaClient.POST('/api/v2/users', {
body: user,
}),
onSuccess: () => {
void queryCache.invalidateQueries({ key: ['users'] })
},
})
})
export const useUpdateUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (user: components['schemas']['UserDto']) =>
komgaClient.PATCH('/api/v2/users/{id}', {
params: { path: { id: user.id } },
body: user,
}),
onSuccess: () => {
void queryCache.invalidateQueries({ key: ['users'] })
},
})
})
export const useUpdateUserPassword = defineMutation(() => {
return useMutation({
mutation: ({ userId, newPassword }: { userId: string; newPassword: string }) =>
komgaClient.PATCH('/api/v2/users/{id}/password', {
params: { path: { id: userId } },
body: {
password: newPassword,
},
}),
})
})
export const useDeleteUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (userId: string) =>
komgaClient.DELETE('/api/v2/users/{id}', {
params: { path: { id: userId } },
}),
onSuccess: () => {
void queryCache.invalidateQueries({ key: ['users'] })
},
})
})

View file

@ -1,22 +0,0 @@
import { defineQuery, useQuery } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
export const useAnnouncements = defineQuery(() => {
const { data, ...rest } = useQuery({
key: () => ['announcements'],
query: () =>
komgaClient
.GET('/api/v1/announcements')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
// 1 hour
staleTime: 60 * 60 * 1000,
gcTime: false,
})
const unreadCount = computed(
() => data.value?.items?.filter((x) => false == x._komga?.read)?.length || 0,
)
return { ...rest, data, unreadCount }
})

View file

@ -1,28 +0,0 @@
import { defineQuery, useQuery } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
import { UserRoles } from '@/types/UserRoles'
export const useCurrentUser = defineQuery(() => {
const { data, ...rest } = useQuery({
key: () => ['current-user'],
query: () =>
komgaClient
.GET('/api/v2/users/me')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
// 10 minutes
staleTime: 10 * 60 * 1000,
gcTime: false,
autoRefetch: true,
})
const hasRole = (role: UserRoles) => data.value?.roles.includes(role)
const isAdmin = computed(() => hasRole(UserRoles.ADMIN))
return {
data,
...rest,
hasRole,
isAdmin,
}
})

View file

@ -1,13 +0,0 @@
import { defineQuery, useQuery } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
export const useUsers = defineQuery(() => {
return useQuery({
key: () => ['users'],
query: () =>
komgaClient
.GET('/api/v2/users')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
})
})

107
next-ui/src/colada/users.ts Normal file
View file

@ -0,0 +1,107 @@
import { defineMutation, defineQuery, useMutation, useQuery, useQueryCache } from '@pinia/colada'
import { komgaClient } from '@/api/komga-client'
import { UserRoles } from '@/types/UserRoles'
import type { components } from '@/generated/openapi/komga'
export const QUERY_KEYS_USERS = {
root: ['users'] as const,
currentUser: ['current-user'] as const,
}
export const useUsers = defineQuery(() => {
return useQuery({
key: () => QUERY_KEYS_USERS.root,
query: () =>
komgaClient
.GET('/api/v2/users')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
})
})
export const useCurrentUser = defineQuery(() => {
const { data, ...rest } = useQuery({
key: () => QUERY_KEYS_USERS.currentUser,
query: () =>
komgaClient
.GET('/api/v2/users/me')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
// 10 minutes
staleTime: 10 * 60 * 1000,
gcTime: false,
autoRefetch: true,
})
const hasRole = (role: UserRoles) => data.value?.roles.includes(role)
const isAdmin = computed(() => hasRole(UserRoles.ADMIN))
return {
data,
...rest,
hasRole,
isAdmin,
}
})
export const useLogout = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: () => komgaClient.POST('/api/logout'),
onSuccess: () => {
void queryCache.invalidateQueries({ key: QUERY_KEYS_USERS.currentUser })
},
})
})
export const useCreateUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (user: components['schemas']['UserCreationDto']) =>
komgaClient.POST('/api/v2/users', {
body: user,
}),
onSuccess: () => {
void queryCache.invalidateQueries({ key: QUERY_KEYS_USERS.root })
},
})
})
export const useUpdateUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (user: components['schemas']['UserDto']) =>
komgaClient.PATCH('/api/v2/users/{id}', {
params: { path: { id: user.id } },
body: user,
}),
onSuccess: () => {
void queryCache.invalidateQueries({ key: QUERY_KEYS_USERS.root })
},
})
})
export const useUpdateUserPassword = defineMutation(() => {
return useMutation({
mutation: ({ userId, newPassword }: { userId: string; newPassword: string }) =>
komgaClient.PATCH('/api/v2/users/{id}/password', {
params: { path: { id: userId } },
body: {
password: newPassword,
},
}),
})
})
export const useDeleteUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (userId: string) =>
komgaClient.DELETE('/api/v2/users/{id}', {
params: { path: { id: userId } },
}),
onSuccess: () => {
void queryCache.invalidateQueries({ key: QUERY_KEYS_USERS.root })
},
})
})

View file

@ -14,7 +14,7 @@
</template>
<script setup lang="ts">
import { useActuatorInfo } from '@/colada/queries/actuator-info'
import { useActuatorInfo } from '@/colada/actuator-info'
import { commonMessages } from '@/utils/i18n/common-messages'
const { commitId, isLoading } = useActuatorInfo()

View file

@ -19,7 +19,7 @@
</template>
<script setup lang="ts">
import { useAppReleases } from '@/colada/queries/app-releases'
import { useAppReleases } from '@/colada/app-releases'
import { commonMessages } from '@/utils/i18n/common-messages'
const { buildVersion, isLatestVersion, isLoading } = useAppReleases()

View file

@ -93,10 +93,11 @@
<script lang="ts" setup>
import { komgaClient } from '@/api/komga-client'
import type { components } from '@/generated/openapi/komga'
import { useCurrentUser } from '@/colada/queries/current-user'
import { UserRoles } from '@/types/UserRoles'
import { defineMessage, useIntl } from 'vue-intl'
import { useCurrentUser } from '@/colada/users'
const intl = useIntl()
const { users = [], loading = false } = defineProps<{

View file

@ -208,8 +208,8 @@
<script setup lang="ts">
import { UserRoles } from '@/types/UserRoles'
import type { components } from '@/generated/openapi/komga'
import { useLibraries } from '@/colada/queries/libraries'
import { useSharingLabels } from '@/colada/queries/referential'
import { useLibraries } from '@/colada/libraries'
import { useSharingLabels } from '@/colada/referential'
import { useIntl } from 'vue-intl'
import { commonMessages } from '@/utils/i18n/common-messages'

View file

@ -34,7 +34,7 @@
</template>
<script setup lang="ts">
import { useCurrentUser } from '@/colada/queries/current-user'
import { useCurrentUser } from '@/colada/users'
const { isAdmin } = useCurrentUser()
</script>

View file

@ -13,7 +13,7 @@
</template>
<script setup lang="ts">
import { useLogout } from '@/colada/mutations/logout'
import { useLogout } from '@/colada/users'
const router = useRouter()
const { mutateAsync: logoutAsync } = useLogout()

View file

@ -10,7 +10,7 @@
</template>
<script setup lang="ts">
import { useCurrentUser } from '@/colada/queries/current-user'
import { useCurrentUser } from '@/colada/users'
const { isAdmin } = useCurrentUser()
</script>

View file

@ -99,7 +99,7 @@
</template>
<script setup lang="ts">
import { useAnnouncements } from '@/colada/queries/announcements'
import { useAnnouncements } from '@/colada/announcements'
const { unreadCount } = useAnnouncements()
</script>

View file

@ -57,8 +57,7 @@
</template>
<script lang="ts" setup>
import { useAnnouncements } from '@/colada/queries/announcements'
import { useMarkAnnouncementsRead } from '@/colada/mutations/mark-announcements-read'
import { useAnnouncements, useMarkAnnouncementsRead } from '@/colada/announcements'
import { commonMessages } from '@/utils/i18n/common-messages'
const { data: announcements, error, unreadCount, isPending } = useAnnouncements()

View file

@ -60,7 +60,7 @@
</template>
<script lang="ts" setup>
import { useAppReleases } from '@/colada/queries/app-releases'
import { useAppReleases } from '@/colada/app-releases'
import { commonMessages } from '@/utils/i18n/common-messages'
const {

View file

@ -23,17 +23,10 @@
</template>
<script lang="ts" setup>
import { useUsers } from '@/colada/queries/users'
import { type ErrorCause } from '@/api/komga-client'
import type { components } from '@/generated/openapi/komga'
import { UserRoles } from '@/types/UserRoles'
import {
useCreateUser,
useDeleteUser,
useUpdateUser,
useUpdateUserPassword,
} from '@/colada/mutations/update-user'
import { useLibraries } from '@/colada/queries/libraries'
import { useLibraries } from '@/colada/libraries'
import { commonMessages } from '@/utils/i18n/common-messages'
import { storeToRefs } from 'pinia'
import { useDialogsStore } from '@/stores/dialogs'
@ -43,6 +36,13 @@ import { useDisplay } from 'vuetify'
import UserDeletionWarning from '@/components/user/DeletionWarning.vue'
import UserFormCreateEdit from '@/fragments/fragment/user/form/CreateEdit.vue'
import UserFormChangePassword from '@/components/user/form/ChangePassword.vue'
import {
useCreateUser,
useDeleteUser,
useUpdateUser,
useUpdateUserPassword,
useUsers,
} from '@/colada/users'
const intl = useIntl()

View file

@ -9,7 +9,7 @@
</template>
<script lang="ts" setup>
import { useCurrentUser } from '@/colada/queries/current-user'
import { useCurrentUser } from '@/colada/users'
async function checkAuthenticated() {
const router = useRouter()

View file

@ -1,5 +1,5 @@
import type { Router } from 'vue-router'
import { useCurrentUser } from '@/colada/queries/current-user'
import { useCurrentUser } from '@/colada/users'
// check if the user is authenticated before navigating to any page
// the authentication is cached by Pinia Colada

View file

@ -1,5 +1,5 @@
import type { Router } from 'vue-router'
import { useCurrentUser } from '@/colada/queries/current-user'
import { useCurrentUser } from '@/colada/users'
// check if the user has the necessary role before navigating to restricted pages
// the authentication is cached by Pinia Colada