From d870db59043c75ef5f7a5a259c91a54b3edf7b4c Mon Sep 17 00:00:00 2001 From: Ilia Date: Fri, 27 Oct 2023 23:14:57 +0300 Subject: [PATCH] refactor: use hand-written migrations for stores --- src/stores/cache.js | 4 ++- src/stores/migration.js | 62 +++++++++++------------------------- src/stores/migration.test.js | 60 ---------------------------------- src/stores/settings.js | 21 ++++++++++-- 4 files changed, 41 insertions(+), 106 deletions(-) delete mode 100644 src/stores/migration.test.js diff --git a/src/stores/cache.js b/src/stores/cache.js index a3e7445..0066927 100644 --- a/src/stores/cache.js +++ b/src/stores/cache.js @@ -8,10 +8,12 @@ const initial = { searches: [], } +const migrations = {} + /** Cache store */ export const cache = localStore('cache', initial) /** Migrate cache */ export function migrateCache() { - migrateStore(cache, initial) + migrateStore(cache, migrations, initial.version) } diff --git a/src/stores/migration.js b/src/stores/migration.js index e9eee24..4f3b05d 100644 --- a/src/stores/migration.js +++ b/src/stores/migration.js @@ -1,50 +1,26 @@ import { get } from 'svelte/store' -export function migrateStore(store, initial) { +/** + * Migrate `store` using `migrations` to `version` + * @param {import('svelte/store').Writable} store Store to migrate + * @param {{ [key: number]: (data: object, store: import('svelte/store').Writable) => {}}} + * migrations Migrations to apply + * @param {number} version To which version migrate + */ +export function migrateStore(store, migrations, version) { let old = get(store) // version could be undefined - if ((old.version ?? 0) === initial.version) { + if ((old.version ?? 0) === version) { return } - let updated = { - version: initial.version, - } - updateFields(initial, old, updated) - for (let [k, v] of Object.entries(updated)) { - store.set(k, v) - } -} - -/** Merge ini and old, put to acc */ -export function updateFields(ini, old, acc) { - let keys = new Set([...Object.keys(ini), ...Object.keys(old)]) - for (let k of keys) { - const iniv = ini[k] - const oldv = old[k] - - if (typeof iniv === 'undefined') { - // new value not defined - acc[k] = oldv - } else if (typeof oldv === 'undefined') { - // old value not defined - acc[k] = iniv - } else if (typeof iniv === 'object' && iniv !== null) { - if (typeof oldv === 'object') { - if (oldv !== null) { - acc[k] = oldv - } else { - acc[k] = iniv - } - } else { - // new value is complex object - acc[k] = {} - updateFields(iniv, oldv, acc[k]) - } - } else if (typeof iniv !== typeof oldv) { - // new type of value, we want to replace it - acc[k] = iniv - } else { - acc[k] = oldv - } - } + Object.keys(migrations) + .map((v) => parseInt(v)) + .filter((v) => v > old.version && v <= version) + .sort() + .forEach((v) => { + store.set('version', v) + // its possible case that `old` will be changed inside migration, + // and this will affect next migrations + migrations[v](old, store) + }) } diff --git a/src/stores/migration.test.js b/src/stores/migration.test.js deleted file mode 100644 index a64d8f8..0000000 --- a/src/stores/migration.test.js +++ /dev/null @@ -1,60 +0,0 @@ -import { it, expect } from 'vitest' -import { updateFields } from './migration' - -it('src/stores/migration:updateFields', () => { - let table = [ - { - initial: {}, - old: {}, - expected: {}, - name: 'empty', - }, - { - initial: {}, - old: { a: 5 }, - expected: { a: 5 }, - name: 'init empty, old have simple value', - }, - { - initial: { a: 5 }, - old: {}, - expected: { a: 5 }, - name: 'old empty, init have simple value', - }, - { - initial: {}, - old: { b: { c: 7 } }, - expected: { b: { c: 7 } }, - name: 'init empty, old have object value', - }, - { - initial: { a: 5 }, - old: { a: 8 }, - expected: { a: 8 }, - name: 'both have simple value, get from old', - }, - { - initial: { a: { c: 8 } }, - old: { a: 5 }, - expected: { a: { c: 8 } }, - name: 'replace simple value with object', - }, - { - initial: { a: 5 }, - old: { a: { c: 8 } }, - expected: { a: 5 }, - name: 'replace object with simple value', - }, - { - initial: { a: { c: 8 } }, - old: { a: { c: 5 } }, - expected: { a: { c: 5 } }, - name: 'merge', - }, - ] - for (let { initial, old, expected, name } of table) { - let acc = {} - updateFields(initial, old, acc) - expect(acc, name).toMatchObject(expected) - } -}) diff --git a/src/stores/settings.js b/src/stores/settings.js index 4158862..03b7c6f 100644 --- a/src/stores/settings.js +++ b/src/stores/settings.js @@ -3,6 +3,10 @@ import { localStore } from 'svelte-storages' import { migrateStore } from 'src/stores/migration' +function getDefaultMapsProvider() { + return isMobile({ tablet: true }) ? 'google' : 'osm' +} + const initial = { version: 3, theme: 'system', @@ -17,7 +21,20 @@ const initial = { tz: Intl.DateTimeFormat().resolvedOptions().timeZone, /** @type {import('src/utils/types').OsmProvider} */ // set to google on mobile to support opening links in mobile app - maps_provider: isMobile({ tablet: true }) ? 'google' : 'osm', + maps_provider: getDefaultMapsProvider(), +} + +const migrations = { + 2: (_, s) => { + s.maps_provider = getDefaultMapsProvider() + }, + 3: (data, s) => { + let [name, country] = data.current_city.name.split(', ') + let current_city = data.current_city + current_city.name = name + current_city.country = country || '' + s.set('current_city', current_city) + }, } /** Settings store */ @@ -25,5 +42,5 @@ export const settings = localStore('settings', initial) /** Migrate settings */ export function migrateSettings() { - migrateStore(settings, initial) + migrateStore(settings, migrations, initial.version) }