Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions apps/lachesis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ yarn install
bun install
```

## Testing

Run the focused Lachesis profile-query test from the repository root:

```bash
npm test --workspace=@lepse/lachesis
```

This is currently a local/manual check; the release workflow builds Lachesis but does not run this frontend test.

## Development Server

Start the development server on `http://localhost:3000`:
Expand Down
50 changes: 37 additions & 13 deletions apps/lachesis/app/composables/clotho/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,57 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import { accountMutationScope, accountQueryKey, accountQueryOptions } from '~/lib/auth-cache'
import { profileQueryOptions } from '~/lib/profile-query-options'

export const useAuth = () => {
const { $api, $queryClient } = useNuxtApp()
const { $api, $queryClient, $authToken, $authScope, $authLifecycle } = useNuxtApp()

const token = useCookie('auth_token', { maxAge: 60 * 60 * 24 * 365 /* one year */ })
const userQuery = useQuery($api.account.profile.show.queryOptions())
const user = computed(() => userQuery.data.value?.data)
const profileQueryKey = accountQueryKey($api.account.profile.show.queryKey(), $authScope)
const userQuery = useQuery(
accountQueryOptions(
$api.account.profile.show.queryOptions(undefined, profileQueryOptions($authToken)),
$authScope,
$authToken
)
)
const user = computed(() =>
$authLifecycle.isIdentityValidated.value ? userQuery.data.value?.data : undefined
)

const loginMutation = useMutation(
$api.auth.accessToken.store.mutationOptions({
onSuccess: ({ data }) => {
token.value = data.token
$queryClient.setQueryData($api.account.profile.show.queryKey(), { data: data.user })
// Publish first. Any profile request triggered by the reactive query must read this token.
$authLifecycle.setToken(data.token)
$queryClient.setQueryData(profileQueryKey.value, { data: data.user })
$authLifecycle.markIdentityValidated()
},
})
)

const signupMutation = useMutation(
$api.auth.newAccount.store.mutationOptions({
onSuccess: ({ data }) => {
token.value = data.token
$queryClient.setQueryData($api.account.profile.show.queryKey(), { data: data.user })
$authLifecycle.setToken(data.token)
$queryClient.setQueryData(profileQueryKey.value, { data: data.user })
$authLifecycle.markIdentityValidated()
},
})
)

const logoutMutation = useMutation(
$api.auth.accessToken.destroy.mutationOptions({
onSuccess: () => {
token.value = null
$queryClient.resetQueries({ queryKey: $api.account.profile.show.queryKey() })
// Clear observers before the request settles, while the shared token remains available for
// the logout bearer. The settled callback then removes the cookie even if the server is
// offline or the token was already revoked.
onMutate: () => {
const accountScope = $authScope.value
$authLifecycle.clearAccountState()
return { accountScope }
},
onSettled: (_data, _error, _variables, context) => {
if ($authLifecycle.isCurrentTokenScope(accountMutationScope(context))) {
$authLifecycle.setToken(null)
}
},
})
)
Expand All @@ -39,8 +61,10 @@ export const useAuth = () => {

const updateProfileMutation = useMutation(
$api.account.profile.update.mutationOptions({
onSuccess: ({ data }) => {
$queryClient.setQueryData($api.account.profile.show.queryKey(), { data: data.user })
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(profileQueryKey.value, { data: data.user })
},
})
)
Expand Down
10 changes: 7 additions & 3 deletions apps/lachesis/app/composables/clotho/useBackgrounds.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import { accountMutationScope, accountQueryKey } from '~/lib/auth-cache'

export const useBackgrounds = () => {
const { $api, $queryClient } = useNuxtApp()
const { $api, $queryClient, $authScope, $authLifecycle } = useNuxtApp()

const backgroundsQuery = useQuery($api.backgrounds.index.queryOptions())
const backgrounds = computed(() => backgroundsQuery.data.value?.data)
const profileQueryKey = accountQueryKey($api.account.profile.show.queryKey(), $authScope)

const backgroundSelectMutation = useMutation(
$api.backgrounds.select.mutationOptions({
onSuccess: ({ data }) => {
$queryClient.setQueryData($api.account.profile.show.queryKey(), { data: data.user })
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(profileQueryKey.value, { data: data.user })
},
})
)
Expand Down
31 changes: 24 additions & 7 deletions apps/lachesis/app/composables/clotho/useDay.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import { accountMutationScope, accountQueryKey, accountQueryOptions } from '~/lib/auth-cache'

export const useDay = (date: string = getClientDate()) => {
const { $api, $client, $queryClient } = useNuxtApp()
const { $api, $client, $queryClient, $authToken, $authScope, $authLifecycle } = useNuxtApp()

// ─── Session ──────────────────────────────────────────────────────────────

const focusSessionQuery = useQuery($api.day.session.show.queryOptions({ params: { date } }))
const focusSession = computed(() => focusSessionQuery.data.value?.data)
const focusSessionQueryKey = accountQueryKey(
$api.day.session.show.queryKey({ params: { date } }),
$authScope
)
const focusSessionQuery = useQuery(
accountQueryOptions(
$api.day.session.show.queryOptions({ params: { date } }),
$authScope,
$authToken
)
)
const focusSession = computed(() =>
$authLifecycle.isIdentityValidated.value ? focusSessionQuery.data.value?.data : undefined
)

const updateFocusSessionMutation = useMutation({
mutationFn: ({
body,
}: {
body: Parameters<typeof $client.api.day.session.update>[0]['body']
}) => $client.api.day.session.update({ params: { date }, body }),
onSuccess: (data) => {
$queryClient.setQueryData($api.day.session.show.queryKey({ params: { date } }), data)
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: (data, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(focusSessionQueryKey.value, data)
},
})

const destroyFocusSessionMutation = useMutation({
mutationFn: () => $client.api.day.session.destroy({ params: { date } }),
onSuccess: () => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: (_, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.resetQueries({
queryKey: $api.day.session.show.queryKey({ params: { date } }),
queryKey: focusSessionQueryKey.value,
})
},
})
Expand Down
30 changes: 21 additions & 9 deletions apps/lachesis/app/composables/clotho/useGoals.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import { accountMutationScope, accountQueryKey, accountQueryOptions } from '~/lib/auth-cache'

export const useGoals = () => {
const { $api, $queryClient } = useNuxtApp()
const { $api, $queryClient, $authToken, $authScope, $authLifecycle } = useNuxtApp()

const goalsQuery = useQuery($api.goals.index.queryOptions())
const goals = computed(() => goalsQuery.data.value?.data)
const goalsQueryKey = accountQueryKey($api.goals.index.queryKey(), $authScope)
const goalsQuery = useQuery(
accountQueryOptions($api.goals.index.queryOptions(), $authScope, $authToken)
)
const goals = computed(() =>
$authLifecycle.isIdentityValidated.value ? goalsQuery.data.value?.data : undefined
)

const createGoalMutation = useMutation(
$api.goals.store.mutationOptions({
onSuccess: ({ data }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.goals.index.queryKey(),
goalsQueryKey.value,
(old) => old && { ...old, data: [...old.data.filter((i) => i.id !== data.id), data] }
)
},
Expand All @@ -19,9 +27,11 @@ export const useGoals = () => {

const updateGoalMutation = useMutation(
$api.goals.update.mutationOptions({
onSuccess: ({ data }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.goals.index.queryKey(),
goalsQueryKey.value,
(old) => old && { ...old, data: [...old.data.filter((i) => i.id !== data.id), data] }
)
},
Expand All @@ -30,9 +40,11 @@ export const useGoals = () => {

const destroyGoalMutation = useMutation(
$api.goals.destroy.mutationOptions({
onSuccess: (_, { params: { id } }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: (_, { params: { id } }, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.goals.index.queryKey(),
goalsQueryKey.value,
(old) => old && { ...old, data: old.data.filter((i) => i.id !== id) }
)
},
Expand Down
42 changes: 29 additions & 13 deletions apps/lachesis/app/composables/clotho/useTasks.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import { accountMutationScope, accountQueryKey, accountQueryOptions } from '~/lib/auth-cache'

export const useTasks = () => {
const { $api, $queryClient } = useNuxtApp()
const { $api, $queryClient, $authToken, $authScope, $authLifecycle } = useNuxtApp()

const tasksQuery = useQuery($api.tasks.index.queryOptions())
const tasks = computed(() => tasksQuery.data.value?.data)
const tasksQueryKey = accountQueryKey($api.tasks.index.queryKey(), $authScope)
const tasksQuery = useQuery(
accountQueryOptions($api.tasks.index.queryOptions(), $authScope, $authToken)
)
const tasks = computed(() =>
$authLifecycle.isIdentityValidated.value ? tasksQuery.data.value?.data : undefined
)
const focusedTaskId = useState<number>('focusedTaskId', () => -1)

const createTaskMutation = useMutation(
$api.tasks.store.mutationOptions({
onSuccess: ({ data }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.tasks.index.queryKey(),
tasksQueryKey.value,
(old) => old && { ...old, data: [...old.data.filter((i) => i.id !== data.id), data] }
)
},
Expand All @@ -20,9 +28,11 @@ export const useTasks = () => {

const updateTaskMutation = useMutation(
$api.tasks.update.mutationOptions({
onSuccess: ({ data }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.tasks.index.queryKey(),
tasksQueryKey.value,
(old) => old && { ...old, data: [...old.data.filter((i) => i.id !== data.id), data] }
)
},
Expand All @@ -31,9 +41,11 @@ export const useTasks = () => {

const destroyTaskMutation = useMutation(
$api.tasks.destroy.mutationOptions({
onSuccess: (_, { params: { id } }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: (_, { params: { id } }, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.tasks.index.queryKey(),
tasksQueryKey.value,
(old) => old && { ...old, data: old.data.filter((i) => i.id !== id) }
)
},
Expand All @@ -42,9 +54,11 @@ export const useTasks = () => {

const attachGoalMutation = useMutation(
$api.tasks.attachGoal.mutationOptions({
onSuccess: ({ data }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.tasks.index.queryKey(),
tasksQueryKey.value,
(old) => old && { ...old, data: [...old.data.filter((i) => i.id !== data.id), data] }
)
},
Expand All @@ -53,9 +67,11 @@ export const useTasks = () => {

const detachGoalMutation = useMutation(
$api.tasks.attachGoal.mutationOptions({
onSuccess: ({ data }) => {
onMutate: () => ({ accountScope: $authScope.value }),
onSuccess: ({ data }, _variables, context) => {
if (!$authLifecycle.isCurrentScope(accountMutationScope(context))) return
$queryClient.setQueryData(
$api.tasks.index.queryKey(),
tasksQueryKey.value,
(old) => old && { ...old, data: [...old.data.filter((i) => i.id !== data.id), data] }
)
},
Expand Down
Loading