Skip to content

Commit eb4f78c

Browse files
committed
refactor: split user-preferences composables and streamline sync flow
1 parent a24bcf9 commit eb4f78c

32 files changed

Lines changed: 405 additions & 308 deletions

app/components/CollapsibleSection.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ const props = withDefaults(defineProps<Props>(), {
1616
headingLevel: 'h2',
1717
})
1818
19-
const { userLocalSettings } = useUserLocalSettings()
19+
const { localSettings } = useUserLocalSettings()
2020
2121
const buttonId = `${props.id}-collapsible-button`
2222
const contentId = `${props.id}-collapsible-content`
2323
2424
const isOpen = shallowRef(true)
2525
2626
onPrehydrate(() => {
27-
const sidebar = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
28-
const collapsed: string[] = sidebar?.sidebar?.collapsed || []
27+
const settings = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
28+
const collapsed: string[] = settings?.sidebar?.collapsed || []
2929
for (const id of collapsed) {
3030
if (!document.documentElement.dataset.collapsed?.split(' ').includes(id)) {
3131
document.documentElement.dataset.collapsed = (
@@ -48,16 +48,16 @@ onMounted(() => {
4848
function toggle() {
4949
isOpen.value = !isOpen.value
5050
51-
const removed = userLocalSettings.value.sidebar.collapsed.filter(c => c !== props.id)
51+
const removed = localSettings.value.sidebar.collapsed.filter(c => c !== props.id)
5252
5353
if (isOpen.value) {
54-
userLocalSettings.value.sidebar.collapsed = removed
54+
localSettings.value.sidebar.collapsed = removed
5555
} else {
5656
removed.push(props.id)
57-
userLocalSettings.value.sidebar.collapsed = removed
57+
localSettings.value.sidebar.collapsed = removed
5858
}
5959
60-
document.documentElement.dataset.collapsed = userLocalSettings.value.sidebar.collapsed.join(' ')
60+
document.documentElement.dataset.collapsed = localSettings.value.sidebar.collapsed.join(' ')
6161
}
6262
6363
const ariaLabel = computed(() => {

app/components/Header/ConnectorModal.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const { isConnected, isConnecting, npmUser, error, hasOperations, connect, disconnect } =
33
useConnector()
44
5-
const { userLocalSettings } = useUserLocalSettings()
5+
const { localSettings } = useUserLocalSettings()
66
77
const tokenInput = shallowRef('')
88
const portInput = shallowRef('31415')
@@ -67,7 +67,7 @@ const executeNpmxConnectorCommand = computed(() => {
6767
<div class="flex flex-col gap-2">
6868
<SettingsToggle
6969
:label="$t('connector.modal.auto_open_url')"
70-
v-model="userLocalSettings.connector.autoOpenURL"
70+
v-model="localSettings.connector.autoOpenURL"
7171
/>
7272
</div>
7373

@@ -158,7 +158,7 @@ const executeNpmxConnectorCommand = computed(() => {
158158
<div class="flex flex-col gap-2">
159159
<SettingsToggle
160160
:label="$t('connector.modal.auto_open_url')"
161-
v-model="userLocalSettings.connector.autoOpenURL"
161+
v-model="localSettings.connector.autoOpenURL"
162162
/>
163163
</div>
164164
</div>

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const props = defineProps<{
1919
2020
const router = useRouter()
2121
const route = useRoute()
22-
const { settings } = useSettings()
22+
const { localSettings } = useUserLocalSettings()
2323
2424
const chartModal = useModal('chart-modal')
2525
const hasChartModalTransitioned = shallowRef(false)

app/components/Settings/AccentColorPicker.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<script setup lang="ts">
2-
import { useAccentColor } from '~/composables/useUserPreferences'
3-
42
const { accentColors, selectedAccentColor, setAccentColor } = useAccentColor()
53
64
onPrehydrate(el => {

app/composables/npm/useSearch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { NpmSearchResponse, NpmSearchResult } from '#shared/types'
2-
import type { SearchProvider } from '~/composables/useSettings'
2+
import type { SearchProvider } from '#shared/schemas/userPreferences'
33
import type { AlgoliaMultiSearchChecks } from './useAlgoliaSearch'
44
import { type SearchSuggestion, emptySearchResponse, parseSuggestionIntent } from './search-utils'
55
import { isValidNewPackageName, checkPackageExists } from '~/utils/package-name'

app/composables/useConnector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const STORAGE_KEY = 'npmx-connector'
5757
const DEFAULT_PORT = 31415
5858

5959
export const useConnector = createSharedComposable(function useConnector() {
60-
const { userLocalSettings } = useUserLocalSettings()
60+
const { localSettings } = useUserLocalSettings()
6161

6262
// Persisted connection config
6363
const config = useState<{ token: string; port: number } | null>('connector-config', () => null)
@@ -308,7 +308,7 @@ export const useConnector = createSharedComposable(function useConnector() {
308308
body: {
309309
otp,
310310
interactive: !otp,
311-
openUrls: userLocalSettings.value.connector.autoOpenURL,
311+
openUrls: localSettings.value.connector.autoOpenURL,
312312
},
313313
})
314314
if (response?.success) {

app/composables/useInstallCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function useInstallCommand(
1212
installVersionOverride?: MaybeRefOrGetter<string | null>,
1313
) {
1414
const selectedPM = useSelectedPackageManager()
15-
const { preferences } = useUserPreferences()
15+
const { preferences } = useUserPreferencesState()
1616

1717
// Check if we should show @types in install command
1818
const showTypesInInstall = computed(() => {

app/composables/useUserLocalSettings.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
interface UserLocalSettings {
22
sidebar: {
33
collapsed: string[]
4+
animateSparkline: boolean
45
}
56
connector: {
67
autoOpenURL: boolean
@@ -11,13 +12,14 @@ const STORAGE_KEY = 'npmx-settings'
1112
const DEFAULT_USER_LOCAL_SETTINGS: UserLocalSettings = {
1213
sidebar: {
1314
collapsed: [],
15+
animateSparkline: true,
1416
},
1517
connector: {
1618
autoOpenURL: false,
1719
},
1820
}
1921

20-
let userLocalSettingsRef: Ref<UserLocalSettings> | null = null
22+
let localSettingsRef: Ref<UserLocalSettings> | null = null
2123

2224
/**
2325
* Composable for managing local user settings.
@@ -26,8 +28,8 @@ let userLocalSettingsRef: Ref<UserLocalSettings> | null = null
2628
* This is for settings that are purely local and don't need to be synced
2729
*/
2830
export function useUserLocalSettings() {
29-
if (!userLocalSettingsRef) {
30-
userLocalSettingsRef = useLocalStorage<UserLocalSettings>(
31+
if (!localSettingsRef) {
32+
localSettingsRef = useLocalStorage<UserLocalSettings>(
3133
STORAGE_KEY,
3234
DEFAULT_USER_LOCAL_SETTINGS,
3335
{
@@ -37,6 +39,6 @@ export function useUserLocalSettings() {
3739
}
3840

3941
return {
40-
userLocalSettings: userLocalSettingsRef,
42+
localSettings: localSettingsRef,
4143
}
4244
}

app/composables/useUserPreferences.ts

Lines changed: 0 additions & 159 deletions
This file was deleted.

app/composables/useUserPreferencesProvider.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,24 @@
1313

1414
import type { RemovableRef } from '@vueuse/core'
1515
import { useLocalStorage } from '@vueuse/core'
16-
import type { UserPreferences } from '#shared/schemas/userPreferences'
16+
import { DEFAULT_USER_PREFERENCES, type UserPreferences } from '#shared/schemas/userPreferences'
1717

1818
const STORAGE_KEY = 'npmx-user-preferences'
1919

20+
function arePreferencesEqual(a: UserPreferences, b: UserPreferences): boolean {
21+
const keys = Object.keys(DEFAULT_USER_PREFERENCES) as (keyof typeof DEFAULT_USER_PREFERENCES)[]
22+
return keys.every(key => a[key] === b[key])
23+
}
24+
2025
export type HydratedUserPreferences = Required<Omit<UserPreferences, 'updatedAt'>> &
2126
Pick<UserPreferences, 'updatedAt'>
2227

2328
let dataRef: RemovableRef<HydratedUserPreferences> | null = null
2429
let syncInitialized = false
2530

26-
/**
27-
* User preferences provider with server sync support.
28-
*/
29-
export function useUserPreferencesProvider(defaultValue: HydratedUserPreferences) {
31+
export function useUserPreferencesProvider(
32+
defaultValue: HydratedUserPreferences = DEFAULT_USER_PREFERENCES,
33+
) {
3034
if (!dataRef) {
3135
dataRef = useLocalStorage<HydratedUserPreferences>(STORAGE_KEY, defaultValue, {
3236
mergeDefaults: true,
@@ -63,7 +67,10 @@ export function useUserPreferencesProvider(defaultValue: HydratedUserPreferences
6367
if (isAuthenticated.value) {
6468
const serverPrefs = await loadFromServer()
6569
if (serverPrefs) {
66-
preferences.value = { ...preferences.value, ...serverPrefs }
70+
const merged = { ...preferences.value, ...serverPrefs }
71+
if (!arePreferencesEqual(preferences.value, merged)) {
72+
preferences.value = merged
73+
}
6774
}
6875
}
6976

@@ -81,7 +88,10 @@ export function useUserPreferencesProvider(defaultValue: HydratedUserPreferences
8188
if (newIsAuth) {
8289
const serverPrefs = await loadFromServer()
8390
if (serverPrefs) {
84-
preferences.value = { ...defaultValue, ...preferences.value, ...serverPrefs }
91+
const merged = { ...defaultValue, ...preferences.value, ...serverPrefs }
92+
if (!arePreferencesEqual(preferences.value, merged)) {
93+
preferences.value = merged
94+
}
8595
}
8696
}
8797
})

0 commit comments

Comments
 (0)