Skip to content

Commit 86e5c6c

Browse files
committed
settings save, and types as schema
1 parent 16b012a commit 86e5c6c

5 files changed

Lines changed: 70 additions & 32 deletions

File tree

app/composables/useSettings.ts

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,8 @@
11
import type { RemovableRef } from '@vueuse/core'
22
import { useLocalStorage } from '@vueuse/core'
33
import { ACCENT_COLORS } from '#shared/utils/constants'
4-
import type { LocaleObject } from '@nuxtjs/i18n'
54
import { BACKGROUND_THEMES } from '#shared/utils/constants'
6-
7-
type BackgroundThemeId = keyof typeof BACKGROUND_THEMES
8-
9-
type AccentColorId = keyof typeof ACCENT_COLORS.light
10-
11-
/**
12-
* Application settings stored in localStorage
13-
*/
14-
export interface AppSettings {
15-
/** Display dates as relative (e.g., "3 days ago") instead of absolute */
16-
relativeDates: boolean
17-
/** Include @types/* package in install command for packages without built-in types */
18-
includeTypesInInstall: boolean
19-
/** Accent color theme */
20-
accentColorId: AccentColorId | null
21-
/** Preferred background shade */
22-
preferredBackgroundTheme: BackgroundThemeId | null
23-
/** Hide platform-specific packages (e.g., @scope/pkg-linux-x64) from search results */
24-
hidePlatformPackages: boolean
25-
/** User-selected locale */
26-
selectedLocale: LocaleObject['code'] | null
27-
sidebar: {
28-
collapsed: string[]
29-
}
30-
}
5+
import type { AccentColorId, BackgroundThemeId, AppSettings } from '#shared/schemas/app-settings'
316

327
const DEFAULT_SETTINGS: AppSettings = {
338
relativeDates: false,
@@ -41,6 +16,14 @@ const DEFAULT_SETTINGS: AppSettings = {
4116
},
4217
}
4318

19+
export const syncSettings = async (settings: AppSettings) => {
20+
// DO some error handling
21+
await $fetch('/api/auth/settings', {
22+
method: 'POST',
23+
body: settings,
24+
})
25+
}
26+
4427
const STORAGE_KEY = 'npmx-settings'
4528

4629
// Shared settings instance (singleton per app)
@@ -133,6 +116,7 @@ export function useBackgroundTheme() {
133116
document.documentElement.removeAttribute('data-bg-theme')
134117
}
135118
settings.value.preferredBackgroundTheme = id
119+
syncSettings(settings.value)
136120
}
137121

138122
return {

server/api/auth/settings.get.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//Want to check the cooki login state, can be slightly more laxed?
2+
//
3+
export default defineEventHandler(async _ => {})

server/api/auth/settings.post.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as v from 'valibot'
2+
import { AppSettingsSchema } from '#shared/schemas/app-settings'
3+
import type { AppSettings } from '#shared/schemas/app-settings'
4+
5+
export default eventHandlerWithOAuthSession(async (event, oauthSession) => {
6+
// TODO: prob find a better spot. Can't be event handler cause i need oauth on session.delete
7+
if (oauthSession == undefined) {
8+
return createError({
9+
statusCode: 401,
10+
statusMessage: 'Unauthorized',
11+
})
12+
}
13+
const body = v.parse(AppSettingsSchema, await readBody(event))
14+
15+
const storage = useStorage('atproto:generic')
16+
const storageKey = `settings:${oauthSession.did}`
17+
storage.setItem<AppSettings>(storageKey, body)
18+
})

server/utils/atproto/oauth.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,6 @@ export function getOauthClientMetadata() {
4343
}) as OAuthClientMetadataInput
4444
}
4545

46-
type EventHandlerWithOAuthSession<T extends EventHandlerRequest, D> = (
47-
event: H3Event<T>,
48-
session: OAuthSession | undefined,
49-
serverSession: SessionManager,
50-
) => Promise<D>
51-
5246
async function getOAuthSession(
5347
event: H3Event,
5448
): Promise<{ oauthSession: OAuthSession | undefined; serverSession: SessionManager }> {
@@ -100,11 +94,23 @@ export async function throwOnMissingOAuthScope(oAuthSession: OAuthSession, requi
10094
}
10195
}
10296

97+
type EventHandlerWithOAuthSession<T extends EventHandlerRequest, D> = (
98+
event: H3Event<T>,
99+
session: OAuthSession | undefined,
100+
serverSession: SessionManager,
101+
) => Promise<D>
102+
103+
/**
104+
* Handler with a valid OAuth session that is ready to be used
105+
* @param handler
106+
* @returns
107+
*/
103108
export function eventHandlerWithOAuthSession<T extends EventHandlerRequest, D>(
104109
handler: EventHandlerWithOAuthSession<T, D>,
105110
) {
106111
return defineEventHandler(async event => {
107112
const { oauthSession, serverSession } = await getOAuthSession(event)
113+
108114
return await handler(event, oauthSession, serverSession)
109115
})
110116
}

shared/schemas/app-settings.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as v from 'valibot'
2+
import { ACCENT_COLORS, BACKGROUND_THEMES } from '#shared/utils/constants'
3+
4+
type AccentColorKey = keyof typeof ACCENT_COLORS.light
5+
type BackgroundThemeKey = keyof typeof BACKGROUND_THEMES
6+
7+
const accentColorIds = Object.keys(ACCENT_COLORS.light) as [AccentColorKey, ...AccentColorKey[]]
8+
const backgroundThemeIds = Object.keys(BACKGROUND_THEMES) as [
9+
BackgroundThemeKey,
10+
...BackgroundThemeKey[],
11+
]
12+
13+
export const AppSettingsSchema = v.object({
14+
relativeDates: v.boolean(),
15+
includeTypesInInstall: v.boolean(),
16+
accentColorId: v.nullable(v.picklist(accentColorIds)),
17+
preferredBackgroundTheme: v.nullable(v.picklist(backgroundThemeIds)),
18+
hidePlatformPackages: v.boolean(),
19+
selectedLocale: v.nullable(v.string()),
20+
sidebar: v.object({
21+
collapsed: v.array(v.string()),
22+
}),
23+
})
24+
25+
export type AppSettings = v.InferOutput<typeof AppSettingsSchema>
26+
export type AccentColorId = AppSettings['accentColorId']
27+
export type BackgroundThemeId = AppSettings['preferredBackgroundTheme']

0 commit comments

Comments
 (0)