Skip to content

Commit 19c04b3

Browse files
authored
Merge pull request #254 from medyo/fix-stores-hydration
feat: enhance remote config handling and local state management
2 parents 145e575 + c7c7df4 commit 19c04b3

File tree

7 files changed

+71
-67
lines changed

7 files changed

+71
-67
lines changed

src/features/remoteConfig/api/getRemoteConfig.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useQuery } from '@tanstack/react-query'
22
import { axios } from 'src/lib/axios'
33
import { ExtractFnReturnType, QueryConfig } from 'src/lib/react-query'
4-
import { useRemoteConfigStore } from '../stores/remoteConfig'
54
import { RemoteConfig } from '../types'
65

76
const getRemoteConfig = async (): Promise<RemoteConfig> => {
@@ -15,9 +14,6 @@ type UseGetRemoteConfigOptions = {
1514
}
1615
export const useGetRemoteConfig = ({ config }: UseGetRemoteConfigOptions = {}) => {
1716
return useQuery<ExtractFnReturnType<QueryFnType>>({
18-
onSuccess(remoteConfig) {
19-
useRemoteConfigStore.getState().setRemoteConfig(remoteConfig)
20-
},
2117
...config,
2218
queryKey: ['remote-config', 'v2'],
2319
queryFn: () => getRemoteConfig(),

src/features/remoteConfig/providers/ConfigurationWrapper.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import React from 'react'
1+
import React, { useEffect } from 'react'
22
import BeatLoader from 'react-spinners/BeatLoader'
33
import { useAsyncError } from 'src/hooks/useAsyncError'
44
import { useGetRemoteConfig } from '../api/getRemoteConfig'
5+
import { useRemoteConfigStore } from '../stores/remoteConfig'
56

67
type ConfigurationWrapperProps = {
78
children: React.ReactNode
89
}
910

1011
export const ConfigurationWrapper = ({ children }: ConfigurationWrapperProps) => {
12+
const setRemoteConfig = useRemoteConfigStore((s) => s.setRemoteConfig)
13+
1114
const {
1215
isLoading,
1316
isError,
@@ -21,6 +24,12 @@ export const ConfigurationWrapper = ({ children }: ConfigurationWrapperProps) =>
2124
})
2225
const throwError = useAsyncError()
2326

27+
useEffect(() => {
28+
if (remoteConfig) {
29+
setRemoteConfig(remoteConfig)
30+
}
31+
}, [remoteConfig, setRemoteConfig])
32+
2433
if (isLoading) {
2534
return (
2635
<div className="appLoading">

src/features/remoteConfig/stores/remoteConfig.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export const useRemoteConfigStore = create(
2222
{
2323
name: 'remote_config_storage',
2424
version: 2,
25+
migrate: (state) => {
26+
return state as RemoteConfigStore
27+
},
2528
}
2629
)
2730
)

src/features/settings/components/TopicSettings.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const CATEGORY_TO_ICON: Record<string, React.ReactNode> = {
2626
export const TopicSettings = () => {
2727
const { userSelectedTags, occupation, followTag, unfollowTag } = useUserPreferences()
2828

29-
const { tags } = useRemoteConfigStore()
29+
const tags = useRemoteConfigStore((s) => s.tags)
30+
3031
const [searchKeyword, setSearchKeyword] = useState<string>('')
3132
const filteredTags = useMemo(() => {
3233
if (searchKeyword.trim() === '') {

src/stores/preferences.ts

Lines changed: 31 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Tag, useRemoteConfigStore } from 'src/features/remoteConfig'
2-
import { enhanceTags } from 'src/utils/DataEnhancement'
1+
import { Tag } from 'src/features/remoteConfig'
2+
import localStateStore from 'src/utils/LocalStateStorage'
3+
import { capitalize } from 'src/utils/String'
34
import { create } from 'zustand'
4-
import { StateStorage, createJSONStorage, persist } from 'zustand/middleware'
5+
import { createJSONStorage, persist } from 'zustand/middleware'
56
import {
67
CardSettingsType,
78
DNDDuration,
@@ -61,62 +62,6 @@ type UserPreferencesStoreActions = {
6162
setAdvStatus: (status: boolean) => void
6263
}
6364

64-
const defaultStorage: StateStorage = {
65-
getItem: (name: string) => {
66-
const item = window.localStorage.getItem(name)
67-
if (!item) {
68-
return null
69-
}
70-
71-
try {
72-
let {
73-
version,
74-
state,
75-
}: {
76-
version: number
77-
state: UserPreferencesState
78-
} = JSON.parse(item)
79-
80-
const newState = {
81-
...state,
82-
}
83-
if (version == 0) {
84-
const MAP_OLD_TAGS: Record<string, string> = {
85-
'artificial-intelligence': 'artificial intelligence',
86-
'machine-learning': 'machine learning',
87-
cpp: 'c++',
88-
csharp: 'c#',
89-
'data-science': 'data science',
90-
'objective-c': 'objectivec',
91-
}
92-
93-
const stateTags = state.userSelectedTags as unknown as string[]
94-
const newTags = stateTags.map((tag) => {
95-
if (MAP_OLD_TAGS[tag]) {
96-
return MAP_OLD_TAGS[tag]
97-
}
98-
return tag
99-
})
100-
newState.userSelectedTags = enhanceTags(useRemoteConfigStore.getState(), newTags)
101-
}
102-
103-
return JSON.stringify({ state: newState, version })
104-
} catch (e) {
105-
return null
106-
}
107-
},
108-
setItem: (name: string, value: string) => {
109-
try {
110-
window.localStorage.setItem(name, value)
111-
} catch (e) {
112-
window.localStorage.setItem(name, '')
113-
}
114-
},
115-
removeItem: (name: string) => {
116-
window.localStorage.removeItem(name)
117-
},
118-
}
119-
12065
export const useUserPreferences = create(
12166
persist<UserPreferencesState & UserPreferencesStoreActions>(
12267
(set, get) => ({
@@ -237,16 +182,41 @@ export const useUserPreferences = create(
237182
{
238183
name: 'preferences_storage',
239184
version: 1,
240-
storage: createJSONStorage(() => defaultStorage),
185+
storage: createJSONStorage(() => localStateStore),
241186
migrate: (persistedState, version) => {
242187
const state = persistedState as unknown as UserPreferencesState &
243188
UserPreferencesStoreActions
189+
244190
if (version === 0) {
245-
console.log('Migrating preferences_storage to version 1', state)
191+
const previousTags = state.userSelectedTags as unknown as string[]
192+
193+
const MAP_OLD_TAGS: Record<string, string> = {
194+
'artificial-intelligence': 'artificial intelligence',
195+
'machine-learning': 'machine learning',
196+
cpp: 'c++',
197+
csharp: 'c#',
198+
'data-science': 'data science',
199+
'objective-c': 'objectivec',
200+
}
201+
202+
const newTags = previousTags
203+
.map((tag) => {
204+
if (MAP_OLD_TAGS[tag]) {
205+
return MAP_OLD_TAGS[tag]
206+
}
207+
return tag
208+
})
209+
.map((tag) => {
210+
return {
211+
label: capitalize(tag),
212+
value: tag,
213+
}
214+
})
246215

247216
return {
248217
...state,
249218
onboardingCompleted: true,
219+
userSelectedTags: newTags,
250220
occupation: state.onboardingResult?.title || '',
251221
}
252222
}

src/utils/LocalStateStorage.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { StateStorage } from 'zustand/middleware'
2+
3+
const localStateStore: StateStorage = {
4+
getItem: (name: string) => {
5+
const value = localStorage.getItem(name)
6+
return value ? value : null
7+
},
8+
setItem: (name: string, value: string) => {
9+
try {
10+
window.localStorage.setItem(name, value)
11+
} catch (e) {
12+
window.localStorage.setItem(name, '')
13+
}
14+
},
15+
removeItem: (name: string) => {
16+
window.localStorage.removeItem(name)
17+
},
18+
}
19+
20+
export default localStateStore

src/utils/String.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
export const pluralize = (count: number, noun: string, suffix = 's') =>
22
`${count} ${noun}${count !== 1 ? suffix : ''}`
3+
4+
export const capitalize = (s: string) => {
5+
if (typeof s !== 'string') return ''
6+
return s.charAt(0).toUpperCase() + s.slice(1)
7+
}

0 commit comments

Comments
 (0)