1111 * a ref with defaults (no real localStorage); on the client, there's only one app instance.
1212 */
1313
14- import type { RemovableRef } from '@vueuse/core'
1514import { useLocalStorage } from '@vueuse/core'
1615import { DEFAULT_USER_PREFERENCES } from '#shared/schemas/userPreferences'
1716import {
@@ -22,56 +21,83 @@ import {
2221
2322const STORAGE_KEY = 'npmx-user-preferences'
2423
25- let dataRef : RemovableRef < HydratedUserPreferences > | null = null
24+ let cached : ReturnType < typeof createProvider > | null = null
2625let syncInitialized = false
2726
28- export function useUserPreferencesProvider (
29- defaultValue : HydratedUserPreferences = DEFAULT_USER_PREFERENCES ,
30- ) {
31- if ( ! dataRef ) {
32- dataRef = useLocalStorage < HydratedUserPreferences > ( STORAGE_KEY , defaultValue , {
33- mergeDefaults : true ,
34- } )
35- }
27+ function createProvider ( defaultValue : HydratedUserPreferences ) {
28+ const preferences = useLocalStorage < HydratedUserPreferences > ( STORAGE_KEY , defaultValue , {
29+ mergeDefaults : true ,
30+ } )
3631
37- // After the guard above, dataRef is guaranteed to be initialized.
38- const preferences : RemovableRef < HydratedUserPreferences > = dataRef
39-
40- const { user } = useAtproto ( )
41-
42- const isAuthenticated = computed ( ( ) => ! ! user . value ?. did )
43- const {
44- status,
45- lastSyncedAt,
46- error,
47- loadFromServer,
48- scheduleSync,
49- setupRouteGuard,
50- setupBeforeUnload,
51- } = useUserPreferencesSync ( )
32+ const isAuthenticated = shallowRef ( false )
33+ const status = shallowRef < 'idle' | 'syncing' | 'synced' | 'error' > ( 'idle' )
34+ const lastSyncedAt = shallowRef < Date | null > ( null )
35+ const error = shallowRef < string | null > ( null )
5236
5337 const isSyncing = computed ( ( ) => status . value === 'syncing' )
5438 const isSynced = computed ( ( ) => status . value === 'synced' )
5539 const hasError = computed ( ( ) => status . value === 'error' )
5640
57- async function syncWithServer ( ) : Promise < void > {
58- const serverResult = await loadFromServer ( )
59-
60- // If the server load failed, keep current local preferences untouched
61- if ( hasError . value ) return
62-
63- const { merged, shouldPushToServer } = mergePreferences ( preferences . value , serverResult )
64- if ( shouldPushToServer ) {
65- scheduleSync ( preferences . value )
66- } else if ( ! arePreferencesEqual ( preferences . value , merged ) ) {
67- preferences . value = merged
68- }
69- }
70-
7141 async function initSync ( ) : Promise < void > {
7242 if ( syncInitialized || import . meta. server ) return
7343 syncInitialized = true
7444
45+ // Resolve auth + sync dependencies lazily
46+ const { user } = useAtproto ( )
47+ watch (
48+ ( ) => ! ! user . value ?. did ,
49+ v => {
50+ isAuthenticated . value = v
51+ } ,
52+ { immediate : true } ,
53+ )
54+
55+ const {
56+ status : syncStatus ,
57+ lastSyncedAt : syncLastSyncedAt ,
58+ error : syncError ,
59+ loadFromServer,
60+ scheduleSync,
61+ setupRouteGuard,
62+ setupBeforeUnload,
63+ } = useUserPreferencesSync ( isAuthenticated )
64+
65+ watch (
66+ syncStatus ,
67+ v => {
68+ status . value = v
69+ } ,
70+ { immediate : true } ,
71+ )
72+ watch (
73+ syncLastSyncedAt ,
74+ v => {
75+ lastSyncedAt . value = v
76+ } ,
77+ { immediate : true } ,
78+ )
79+ watch (
80+ syncError ,
81+ v => {
82+ error . value = v
83+ } ,
84+ { immediate : true } ,
85+ )
86+
87+ async function syncWithServer ( ) : Promise < void > {
88+ const serverResult = await loadFromServer ( )
89+
90+ // If the server load failed, keep current local preferences untouched
91+ if ( hasError . value ) return
92+
93+ const { merged, shouldPushToServer } = mergePreferences ( preferences . value , serverResult )
94+ if ( shouldPushToServer ) {
95+ scheduleSync ( preferences . value )
96+ } else if ( ! arePreferencesEqual ( preferences . value , merged ) ) {
97+ preferences . value = merged
98+ }
99+ }
100+
75101 setupRouteGuard ( ( ) => preferences . value )
76102 setupBeforeUnload ( ( ) => preferences . value )
77103
@@ -108,12 +134,21 @@ export function useUserPreferencesProvider(
108134 }
109135}
110136
137+ export function useUserPreferencesProvider (
138+ defaultValue : HydratedUserPreferences = DEFAULT_USER_PREFERENCES ,
139+ ) {
140+ if ( ! cached ) {
141+ cached = createProvider ( defaultValue )
142+ }
143+ return cached
144+ }
145+
111146/**
112147 * Reset module-level singleton state. Test-only — do not use in production code.
113148 */
114149export function __resetPreferencesForTest ( ) : void {
115150 if ( import . meta. test ) {
116- dataRef = null
151+ cached = null
117152 syncInitialized = false
118153 }
119154}
0 commit comments