Skip to content

Commit

Permalink
refactor: use hand-written migrations for stores
Browse files Browse the repository at this point in the history
  • Loading branch information
istudyatuni committed Oct 27, 2023
1 parent 89e8191 commit d870db5
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 106 deletions.
4 changes: 3 additions & 1 deletion src/stores/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
62 changes: 19 additions & 43 deletions src/stores/migration.js
Original file line number Diff line number Diff line change
@@ -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)
})
}
60 changes: 0 additions & 60 deletions src/stores/migration.test.js

This file was deleted.

21 changes: 19 additions & 2 deletions src/stores/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -17,13 +21,26 @@ 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 */
export const settings = localStore('settings', initial)

/** Migrate settings */
export function migrateSettings() {
migrateStore(settings, initial)
migrateStore(settings, migrations, initial.version)
}

0 comments on commit d870db5

Please sign in to comment.