Skip to content

Commit 6bdb0e4

Browse files
committed
fix:settings lost
1 parent ce9fe3d commit 6bdb0e4

File tree

5 files changed

+71
-8
lines changed

5 files changed

+71
-8
lines changed

packages/core/src/composables/useDataSyncPersistence.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ async function fetchServerMeta(types: SyncType[], client?: SupabaseClient | null
144144
async function fetchServerData(type: SyncType, client?: SupabaseClient | null): Promise<RemoteDataRow | null> {
145145
const sb = getSyncClient(client)
146146
if (!sb) return null
147+
console.log('Fetching server data', type)
147148
try {
148149
const { data, error } = await sb
149150
.from('typewords_data')
@@ -189,6 +190,7 @@ async function upsertServerData(
189190
): Promise<boolean> {
190191
const sb = getSyncClient(client)
191192
if (!sb) return false
193+
console.log('Upserting server data', type)
192194
const data_version = getDataVersion(type)
193195
try {
194196
const { error } = await (sb as any)
@@ -234,7 +236,6 @@ async function applyRemoteDataByType(
234236
await persistLocalState(type, row.data, row.updated_at ?? now)
235237
}
236238

237-
238239
type HashBackupIndexItem = {
239240
hash: string
240241
key: string
@@ -373,7 +374,7 @@ export function useDataSyncPersistence() {
373374
return compareResult
374375
}
375376

376-
async function forcePushLocalDataToRemote(data: BackupData['val'],client?: SupabaseClient | null): Promise<boolean> {
377+
async function forcePushLocalDataToRemote(data: BackupData['val'], client?: SupabaseClient | null): Promise<boolean> {
377378
let syncResult = true
378379
const updated_at = new Date().toISOString()
379380
const sb = getSyncClient(client)

packages/core/src/composables/useInit.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APP_VERSION, AppEnv, DictId, LOCAL_FILE_KEY } from '../config/env.ts'
2-
import { debounce, shakeCommonDict } from '../utils'
2+
import { _dateFormat, debounce, shakeCommonDict } from '../utils'
33
import { get, set } from 'idb-keyval'
44
import { syncSetting } from '../apis'
55
import { useBaseStore } from '../stores/base.ts'
@@ -9,6 +9,7 @@ import { useUserStore } from '../stores/user.ts'
99
import { CompareResult } from '../types'
1010
import { Supabase } from '../utils/supabase.ts'
1111
import { ensureHashGuardBeforeInit, useDataSyncPersistence } from './useDataSyncPersistence'
12+
import dayjs from 'dayjs'
1213

1314
let unsub = null
1415
let unsub2 = null
@@ -122,6 +123,7 @@ export function useInit() {
122123
await dataSync.pullRemoteIfNewer(['setting', 'dict'])
123124
console.timeEnd('init')
124125
store.load = true
126+
console.log('注册时间', _dateFormat(settingStore.firstTime))
125127
isInitializing = false // 初始化完成,允许保存数据
126128

127129
runtimeStore.isNew = APP_VERSION.version > Number(settingStore.webAppVersion)

packages/core/src/config/env.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ export const SAVE_DICT_KEY = {
7272
}
7373
//18版本,移除单独保存的 app version字段,转移到 settingStore的webAppVersion里面
7474
//19:修复快捷键下一个单词和跳过单词重复了
75+
//20:修复19版本未导入变量,导致抛错所有用户setting变默认值的bug
7576
export const SAVE_SETTING_KEY = {
7677
key: 'typing-word-setting',
77-
version: 19,
78+
version: 20,
7879
}
7980

8081
//5版本,不再单独保存 app version字段

packages/core/src/stores/setting.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { defineStore } from 'pinia'
22
import { checkAndUpgradeSaveSetting, cloneDeep } from '../utils'
3-
import { get } from 'idb-keyval'
3+
import { get, set } from 'idb-keyval'
44
import { APP_VERSION, AppEnv, DefaultShortcutKeyMap, SAVE_SETTING_KEY } from '../config/env'
55
import { getSetting } from '../apis'
66
import { WordPracticeMode, WordPracticeType } from '../types'
@@ -152,12 +152,28 @@ export const useSettingStore = defineStore('setting', {
152152
return new Promise(async resolve => {
153153
let configStr = await get(SAVE_SETTING_KEY.key)
154154
let data = await checkAndUpgradeSaveSetting(configStr)
155+
156+
//特殊处理
157+
const shouldRefreshUpdatedAt = !!(data as any)?.__firstTimePatchedFromSnapshot ?? false
158+
delete (data as any)?.__firstTimePatchedFromSnapshot
159+
if (shouldRefreshUpdatedAt) {
160+
await set(
161+
SAVE_SETTING_KEY.key,
162+
JSON.stringify({
163+
val: data,
164+
version: SAVE_SETTING_KEY.version,
165+
updated_at: new Date().toISOString(),
166+
})
167+
)
168+
}
169+
155170
if (AppEnv.CAN_REQUEST) {
156171
let res = await getSetting()
157172
if (res.success) {
158173
Object.assign(data, res.data)
159174
}
160175
}
176+
161177
this.setState({ ...data, load: true })
162178
resolve(true)
163179
})

packages/core/src/utils/index.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ import type { BaseState } from '../stores/base'
22
import { getDefaultBaseState, useBaseStore } from '../stores/base'
33
import type { SettingState } from '../stores/setting'
44
import { getDefaultSettingState } from '../stores/setting'
5-
import { Dict, ShortcutKey } from '../types'
65
import type { DictResource } from '../types'
7-
import { CompareResult, DictType, getDefaultDict, getDefaultWord } from '../types'
6+
import { CompareResult, Dict, DictType, getDefaultDict, getDefaultWord, ShortcutKey } from '../types'
87
import { useRouter } from 'vue-router'
98
import { useRuntimeStore } from '../stores/runtime'
109
import dayjs from 'dayjs'
11-
import { APP_VERSION, AppEnv, DefaultShortcutKeyMap, DictId, ENV, RESOURCE_PATH, SAVE_DICT_KEY } from '../config/env'
10+
import {
11+
APP_VERSION,
12+
AppEnv,
13+
BACKUP_INDEX_KEY,
14+
DefaultShortcutKeyMap,
15+
DictId,
16+
ENV,
17+
RESOURCE_PATH,
18+
SAVE_DICT_KEY,
19+
} from '../config/env'
1220
import { nextTick } from 'vue'
1321
import { Toast } from '@typewords/base'
1422
import duration from 'dayjs/plugin/duration'
@@ -121,13 +129,48 @@ export async function checkAndUpgradeSaveSetting(val: any) {
121129
if (version <= 18) {
122130
defaultState.shortcutKeyMap[ShortcutKey.Next] = DefaultShortcutKeyMap[ShortcutKey.Next]
123131
}
132+
//3/20晚上10点25推的代码,这个地方出了一个bug,ShortcutKey没导入,导致抛异常后返回了默认值,所有的用户的setting都变成默认值了。
133+
//在这里读取之前的快照,如果存在则从里面读取setting的firstTime,
134+
//判断是否与当前值相等,不相等则取快照的值并将本地的update_at更新,以免被远程覆盖
135+
if (version === 19) {
136+
try {
137+
let firstTimePatchedFromSnapshot = false
138+
const snapshotCutoffTime = new Date('2026-03-20T22:25:00+08:00').getTime()
139+
const rawIndex = (await get(BACKUP_INDEX_KEY)) as Array<{ key?: string; createdAt?: number }> | null
140+
const index = Array.isArray(rawIndex) ? rawIndex : []
141+
const targetSnapshot = index
142+
.filter(item => typeof item?.key === 'string' && Number(item?.createdAt) <= snapshotCutoffTime)
143+
.sort((a, b) => Number(b.createdAt) - Number(a.createdAt))[0]
144+
if (targetSnapshot?.key) {
145+
const snapshot = await get(targetSnapshot.key)
146+
const snapshotSettingRaw = snapshot?.data?.setting
147+
let snapshotSettingState: any = null
148+
if (typeof snapshotSettingRaw === 'string') {
149+
snapshotSettingState = JSON.parse(snapshotSettingRaw)?.val ?? null
150+
} else if (snapshotSettingRaw && typeof snapshotSettingRaw === 'object') {
151+
snapshotSettingState = snapshotSettingRaw?.val ?? null
152+
}
153+
const snapshotFirstTime = Number(snapshotSettingState?.firstTime)
154+
const currentFirstTime = Number(state?.firstTime)
155+
if (Number.isFinite(snapshotFirstTime) && snapshotFirstTime > 0 && snapshotFirstTime !== currentFirstTime) {
156+
state.firstTime = snapshotFirstTime
157+
firstTimePatchedFromSnapshot = true
158+
}
159+
;(defaultState as any).__firstTimePatchedFromSnapshot = firstTimePatchedFromSnapshot
160+
}
161+
} catch (e) {
162+
console.warn('firstTime 快照回填跳过或失败,忽略并继续', e)
163+
}
164+
}
165+
124166
//为了保持永远是最新的快捷键选项列表,但保留住用户的自定义设置,去掉无效的快捷键选项
125167
//例: 2版本,可能有快捷键A。3版本没有了
126168
checkRiskKey(defaultState.shortcutKeyMap, state.shortcutKeyMap)
127169
delete state.shortcutKeyMap
128170
checkRiskKey(defaultState, state)
129171
return defaultState
130172
} catch (e) {
173+
await saveHashSnapshot('数据解析异常-自动备份', '')
131174
return defaultState
132175
}
133176
}

0 commit comments

Comments
 (0)