feat: add camera/gallery choice for photos & sync AI profile pref to server
This commit is contained in:
@@ -26,6 +26,7 @@ describe('appearancePrefs', () => {
|
||||
await expect(fetchAppearancePrefs()).resolves.toEqual({
|
||||
theme: 'auto',
|
||||
colorScheme: 'auto',
|
||||
aiAuthorized: false,
|
||||
persisted: false
|
||||
})
|
||||
expect(mockedApiJson).not.toHaveBeenCalled()
|
||||
@@ -36,6 +37,7 @@ describe('appearancePrefs', () => {
|
||||
mockedApiJson.mockResolvedValueOnce({
|
||||
theme: 'ocean',
|
||||
colorScheme: 'dark',
|
||||
aiAuthorized: true,
|
||||
persisted: true
|
||||
})
|
||||
|
||||
@@ -46,6 +48,7 @@ describe('appearancePrefs', () => {
|
||||
|
||||
expect(localStorage.getItem(`user_pref_theme_${USER_ID}`)).toBe('ocean')
|
||||
expect(localStorage.getItem(`user_pref_color_scheme_${USER_ID}`)).toBe('dark')
|
||||
expect(localStorage.getItem(`user_pref_ai_authorized_${USER_ID}`)).toBe('true')
|
||||
expect(changed).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
@@ -53,20 +56,20 @@ describe('appearancePrefs', () => {
|
||||
localStorage.setItem('active_userid', USER_ID)
|
||||
setThemePreference(USER_ID, 'material')
|
||||
mockedApiJson
|
||||
.mockResolvedValueOnce({ theme: 'auto', colorScheme: 'auto', persisted: false })
|
||||
.mockResolvedValueOnce({ theme: 'material', colorScheme: 'auto', persisted: true })
|
||||
.mockResolvedValueOnce({ theme: 'auto', colorScheme: 'auto', aiAuthorized: false, persisted: false })
|
||||
.mockResolvedValueOnce({ theme: 'material', colorScheme: 'auto', aiAuthorized: false, persisted: true })
|
||||
|
||||
await syncAppearancePrefs(USER_ID)
|
||||
|
||||
expect(mockedApiJson).toHaveBeenCalledTimes(2)
|
||||
expect(mockedApiJson).toHaveBeenLastCalledWith('/api/auth/appearance-prefs', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ theme: 'material', colorScheme: 'auto' })
|
||||
body: JSON.stringify({ theme: 'material', colorScheme: 'auto', aiAuthorized: false })
|
||||
})
|
||||
})
|
||||
|
||||
it('saveAppearancePrefsToServer skips when not authenticated', async () => {
|
||||
await saveAppearancePrefsToServer('ocean', 'light')
|
||||
await saveAppearancePrefsToServer('ocean', 'light', true)
|
||||
expect(mockedApiJson).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -76,6 +79,7 @@ describe('appearancePrefs', () => {
|
||||
mockedApiJson.mockResolvedValue({
|
||||
theme: 'material',
|
||||
colorScheme: 'dark',
|
||||
aiAuthorized: false,
|
||||
persisted: true
|
||||
})
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import {
|
||||
getColorSchemePreference,
|
||||
getThemePreference,
|
||||
setColorSchemePreference,
|
||||
setThemePreference
|
||||
setThemePreference,
|
||||
getAiAuthorized,
|
||||
setAiAuthorized
|
||||
} from './userPreferences.js'
|
||||
|
||||
const API_BASE = '/api/auth/appearance-prefs'
|
||||
@@ -13,13 +15,15 @@ const API_BASE = '/api/auth/appearance-prefs'
|
||||
export interface AppearancePrefs {
|
||||
theme: string
|
||||
colorScheme: string
|
||||
aiAuthorized: boolean
|
||||
persisted: boolean
|
||||
}
|
||||
|
||||
function hasLocalAppearancePrefs(userId: string): boolean {
|
||||
return (
|
||||
localStorage.getItem(`user_pref_theme_${userId}`) != null ||
|
||||
localStorage.getItem(`user_pref_color_scheme_${userId}`) != null
|
||||
localStorage.getItem(`user_pref_color_scheme_${userId}`) != null ||
|
||||
localStorage.getItem(`user_pref_ai_authorized_${userId}`) != null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +39,7 @@ function resolveSyncedUserId(userId?: string | null): string | null {
|
||||
|
||||
export async function fetchAppearancePrefs(userId?: string | null): Promise<AppearancePrefs> {
|
||||
if (!resolveSyncedUserId(userId)) {
|
||||
return { theme: 'auto', colorScheme: 'auto', persisted: false }
|
||||
return { theme: 'auto', colorScheme: 'auto', aiAuthorized: false, persisted: false }
|
||||
}
|
||||
|
||||
return apiJson<AppearancePrefs>(API_BASE)
|
||||
@@ -44,13 +48,14 @@ export async function fetchAppearancePrefs(userId?: string | null): Promise<Appe
|
||||
export async function saveAppearancePrefsToServer(
|
||||
theme: string,
|
||||
colorScheme: string,
|
||||
aiAuthorized: boolean,
|
||||
userId?: string | null
|
||||
): Promise<void> {
|
||||
if (!resolveSyncedUserId(userId)) return
|
||||
|
||||
await apiJson<AppearancePrefs>(API_BASE, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ theme, colorScheme })
|
||||
body: JSON.stringify({ theme, colorScheme, aiAuthorized })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,8 +70,14 @@ export async function syncAppearancePrefs(userId?: string | null): Promise<void>
|
||||
if (server.persisted) {
|
||||
setThemePreference(id, server.theme)
|
||||
setColorSchemePreference(id, server.colorScheme)
|
||||
setAiAuthorized(id, server.aiAuthorized)
|
||||
} else if (hasLocalAppearancePrefs(id)) {
|
||||
await saveAppearancePrefsToServer(getThemePreference(id), getColorSchemePreference(id), id)
|
||||
await saveAppearancePrefsToServer(
|
||||
getThemePreference(id),
|
||||
getColorSchemePreference(id),
|
||||
getAiAuthorized(id),
|
||||
id
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Failed to sync appearance preferences:', err)
|
||||
|
||||
Reference in New Issue
Block a user