Fix: see #2212: actually save custom themes as visited

This commit is contained in:
Pieter Vander Vennet 2024-10-17 02:10:25 +02:00
parent 91f5c8f166
commit 9427083939
19 changed files with 129 additions and 75 deletions

View file

@ -183,7 +183,7 @@ export default class GeoLocationHandler {
} }
private initUserLocationTrail() { private initUserLocationTrail() {
const features = LocalStorageSource.GetParsed<Feature[]>("gps_location_history", []) const features = LocalStorageSource.getParsed<Feature[]>("gps_location_history", [])
const now = new Date().getTime() const now = new Date().getTime()
features.data = features.data.filter((ff) => { features.data = features.data.filter((ff) => {
if (ff.properties === undefined) { if (ff.properties === undefined) {

View file

@ -31,7 +31,7 @@ export default class InitialMapPositioning {
deflt: number, deflt: number,
docs: string docs: string
): UIEventSource<number> { ): UIEventSource<number> {
const localStorage = LocalStorageSource.Get(key) const localStorage = LocalStorageSource.get(key)
const previousValue = localStorage.data const previousValue = localStorage.data
const src = UIEventSource.asFloat( const src = UIEventSource.asFloat(
QueryParameters.GetQueryParameter(key, "" + deflt, docs).syncWith(localStorage) QueryParameters.GetQueryParameter(key, "" + deflt, docs).syncWith(localStorage)

View file

@ -132,14 +132,14 @@ export default class DetermineLayout {
let json: any let json: any
// layoutFromBase64 contains the name of the theme. This is partly to do tracking with goat counter // layoutFromBase64 contains the name of the theme. This is partly to do tracking with goat counter
const dedicatedHashFromLocalStorage = LocalStorageSource.Get( const dedicatedHashFromLocalStorage = LocalStorageSource.get(
"user-layout-" + userLayoutParam.data?.replace(" ", "_") "user-layout-" + userLayoutParam.data?.replace(" ", "_")
) )
if (dedicatedHashFromLocalStorage.data?.length < 10) { if (dedicatedHashFromLocalStorage.data?.length < 10) {
dedicatedHashFromLocalStorage.setData(undefined) dedicatedHashFromLocalStorage.setData(undefined)
} }
const hashFromLocalStorage = LocalStorageSource.Get("last-loaded-user-layout") const hashFromLocalStorage = LocalStorageSource.get("last-loaded-user-layout")
if (hash.length < 10) { if (hash.length < 10) {
hash = dedicatedHashFromLocalStorage.data ?? hashFromLocalStorage.data hash = dedicatedHashFromLocalStorage.data ?? hashFromLocalStorage.data
} else { } else {

View file

@ -24,7 +24,7 @@ import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesSto
*/ */
export class Changes { export class Changes {
public readonly pendingChanges: UIEventSource<ChangeDescription[]> = public readonly pendingChanges: UIEventSource<ChangeDescription[]> =
LocalStorageSource.GetParsed<ChangeDescription[]>("pending-changes", []) LocalStorageSource.getParsed<ChangeDescription[]>("pending-changes", [])
public readonly allChanges = new UIEventSource<ChangeDescription[]>(undefined) public readonly allChanges = new UIEventSource<ChangeDescription[]>(undefined)
public readonly state: { public readonly state: {
allElements?: IndexedFeatureSource allElements?: IndexedFeatureSource

View file

@ -210,7 +210,7 @@ export class OsmConnection {
console.log("Trying to log in...") console.log("Trying to log in...")
this.updateAuthObject() this.updateAuthObject()
LocalStorageSource.Get("location_before_login").setData( LocalStorageSource.get("location_before_login").setData(
Utils.runningFromConsole ? undefined : window.location.href Utils.runningFromConsole ? undefined : window.location.href
) )
this.auth.xhr( this.auth.xhr(
@ -521,7 +521,7 @@ export class OsmConnection {
this.auth.authenticate(function () { this.auth.authenticate(function () {
// Fully authed at this point // Fully authed at this point
console.log("Authentication successful!") console.log("Authentication successful!")
const previousLocation = LocalStorageSource.Get("location_before_login") const previousLocation = LocalStorageSource.get("location_before_login")
callback(previousLocation.data) callback(previousLocation.data)
}) })
} }

View file

@ -6,7 +6,11 @@ import { Utils } from "../../Utils"
export class OsmPreferences { export class OsmPreferences {
private preferences: Record<string, UIEventSource<string>> = {} /**
* A 'cache' of all the preference stores
* @private
*/
private readonly preferences: Record<string, UIEventSource<string>> = {}
private localStorageInited: Set<string> = new Set() private localStorageInited: Set<string> = new Set()
/** /**
@ -15,6 +19,10 @@ export class OsmPreferences {
*/ */
private seenKeys: string[] = [] private seenKeys: string[] = []
/**
* Contains a dictionary which has all preferences
* @private
*/
private readonly _allPreferences: UIEventSource<Record<string, string>> = new UIEventSource({}) private readonly _allPreferences: UIEventSource<Record<string, string>> = new UIEventSource({})
public readonly allPreferences: Store<Readonly<Record<string, string>>> = this._allPreferences public readonly allPreferences: Store<Readonly<Record<string, string>>> = this._allPreferences
private readonly _fakeUser: boolean private readonly _fakeUser: boolean
@ -51,6 +59,7 @@ export class OsmPreferences {
this.setPreferencesAll(key, value) this.setPreferencesAll(key, value)
} }
pref.addCallback(v => { pref.addCallback(v => {
console.log("Got an update:", key, "--->", v)
this.uploadKvSplit(key, v) this.uploadKvSplit(key, v)
this.setPreferencesAll(key, v) this.setPreferencesAll(key, v)
}) })
@ -101,11 +110,11 @@ export class OsmPreferences {
key = key.replace(/[:/"' {}.%\\]/g, "") key = key.replace(/[:/"' {}.%\\]/g, "")
const localStorage = LocalStorageSource.Get(key) const localStorage = LocalStorageSource.get(key) // cached
if (localStorage.data === "null" || localStorage.data === "undefined") { if (localStorage.data === "null" || localStorage.data === "undefined") {
localStorage.set(undefined) localStorage.set(undefined)
} }
const pref: UIEventSource<string> = this.initPreference(key, localStorage.data ?? defaultValue) const pref: UIEventSource<string> = this.initPreference(key, localStorage.data ?? defaultValue) // cached
if (this.localStorageInited.has(key)) { if (this.localStorageInited.has(key)) {
return pref return pref
} }

View file

@ -58,7 +58,7 @@ export class GeoLocationState {
* @private * @private
*/ */
private readonly _previousLocationGrant: UIEventSource<boolean> = private readonly _previousLocationGrant: UIEventSource<boolean> =
LocalStorageSource.GetParsed<boolean>("geolocation-permissions", false) LocalStorageSource.getParsed<boolean>("geolocation-permissions", false)
/** /**
* Used to detect a permission retraction * Used to detect a permission retraction

View file

@ -43,7 +43,7 @@ export class OptionallySyncedHistory<T> {
"sync", "sync",
) )
const synced = this.synced = UIEventSource.asObject<T[]>(osmconnection.getPreference(key + "-history"), []) const synced = this.synced = UIEventSource.asObject<T[]>(osmconnection.getPreference(key + "-history"), [])
const local = this.local = LocalStorageSource.GetParsed<T[]>(key + "-history", []) const local = this.local = LocalStorageSource.getParsed<T[]>(key + "-history", [])
const thisSession = this.thisSession = new UIEventSource<T[]>([], "optionally-synced:" + key + "(session only)") const thisSession = this.thisSession = new UIEventSource<T[]>([], "optionally-synced:" + key + "(session only)")
this.syncPreference.addCallback(syncmode => { this.syncPreference.addCallback(syncmode => {
if (syncmode === "sync") { if (syncmode === "sync") {
@ -272,7 +272,19 @@ export default class UserRelatedState {
} }
} }
public getUnofficialTheme(id: string): (MinimalLayoutInformation & { definition }) | undefined { /**
* Adds a newly visited unofficial theme (or update the info).
*
* @param themeInfo note that themeInfo.id should be the URL where it was found
*/
public addUnofficialTheme(themeInfo: MinimalLayoutInformation) {
const pref = this.osmConnection.getPreference("unofficial-theme-" + themeInfo.id)
this.osmConnection.isLoggedIn.when(
() => pref.set(JSON.stringify(themeInfo))
)
}
public getUnofficialTheme(id: string): MinimalLayoutInformation | undefined {
const pref = this.osmConnection.getPreference("unofficial-theme-" + id) const pref = this.osmConnection.getPreference("unofficial-theme-" + id)
const str = pref.data const str = pref.data
@ -282,7 +294,7 @@ export default class UserRelatedState {
} }
try { try {
return <MinimalLayoutInformation & { definition: string }>JSON.parse(str) return JSON.parse(str)
} catch (e) { } catch (e) {
console.warn( console.warn(
"Removing theme " + "Removing theme " +

View file

@ -23,7 +23,7 @@ export class Stores {
} }
public static FromPromiseWithErr<T>( public static FromPromiseWithErr<T>(
promise: Promise<T> promise: Promise<T>,
): Store<{ success: T } | { error: any }> { ): Store<{ success: T } | { error: any }> {
return UIEventSource.FromPromiseWithErr(promise) return UIEventSource.FromPromiseWithErr(promise)
} }
@ -133,13 +133,13 @@ export abstract class Store<T> implements Readable<T> {
abstract map<J>( abstract map<J>(
f: (t: T) => J, f: (t: T) => J,
extraStoresToWatch: Store<any>[], extraStoresToWatch: Store<any>[],
callbackDestroyFunction: (f: () => void) => void callbackDestroyFunction: (f: () => void) => void,
): Store<J> ): Store<J>
public mapD<J>( public mapD<J>(
f: (t: Exclude<T, undefined | null>) => J, f: (t: Exclude<T, undefined | null>) => J,
extraStoresToWatch?: Store<any>[], extraStoresToWatch?: Store<any>[],
callbackDestroyFunction?: (f: () => void) => void callbackDestroyFunction?: (f: () => void) => void,
): Store<J> { ): Store<J> {
return this.map((t) => { return this.map((t) => {
if (t === undefined) { if (t === undefined) {
@ -176,7 +176,7 @@ export abstract class Store<T> implements Readable<T> {
abstract addCallbackAndRun(callback: (data: T) => void): () => void abstract addCallbackAndRun(callback: (data: T) => void): () => void
public withEqualityStabilized( public withEqualityStabilized(
comparator: (t: T | undefined, t1: T | undefined) => boolean comparator: (t: T | undefined, t1: T | undefined) => boolean,
): Store<T> { ): Store<T> {
let oldValue = undefined let oldValue = undefined
return this.map((v) => { return this.map((v) => {
@ -342,6 +342,16 @@ export abstract class Store<T> implements Readable<T> {
} }
public abstract destroy() public abstract destroy()
when(callback: () => void, condition?: (v:T) => boolean) {
condition ??= v => v === true
this.addCallbackAndRunD(v => {
if ( condition(v)) {
callback()
return true
}
})
}
} }
export class ImmutableStore<T> extends Store<T> { export class ImmutableStore<T> extends Store<T> {
@ -384,7 +394,7 @@ export class ImmutableStore<T> extends Store<T> {
map<J>( map<J>(
f: (t: T) => J, f: (t: T) => J,
extraStores: Store<any>[] = undefined, extraStores: Store<any>[] = undefined,
ondestroyCallback?: (f: () => void) => void ondestroyCallback?: (f: () => void) => void,
): ImmutableStore<J> { ): ImmutableStore<J> {
if (extraStores?.length > 0) { if (extraStores?.length > 0) {
return new MappedStore(this, f, extraStores, undefined, f(this.data), ondestroyCallback) return new MappedStore(this, f, extraStores, undefined, f(this.data), ondestroyCallback)
@ -454,7 +464,7 @@ class ListenerTracker<T> {
let endTime = new Date().getTime() / 1000 let endTime = new Date().getTime() / 1000
if (endTime - startTime > 500) { if (endTime - startTime > 500) {
console.trace( console.trace(
"Warning: a ping took more then 500ms; this is probably a performance issue" "Warning: a ping took more then 500ms; this is probably a performance issue",
) )
} }
if (toDelete !== undefined) { if (toDelete !== undefined) {
@ -496,7 +506,7 @@ class MappedStore<TIn, T> extends Store<T> {
extraStores: Store<any>[], extraStores: Store<any>[],
upstreamListenerHandler: ListenerTracker<TIn> | undefined, upstreamListenerHandler: ListenerTracker<TIn> | undefined,
initialState: T, initialState: T,
onDestroy?: (f: () => void) => void onDestroy?: (f: () => void) => void,
) { ) {
super() super()
this._upstream = upstream this._upstream = upstream
@ -536,7 +546,7 @@ class MappedStore<TIn, T> extends Store<T> {
map<J>( map<J>(
f: (t: T) => J, f: (t: T) => J,
extraStores: Store<any>[] = undefined, extraStores: Store<any>[] = undefined,
ondestroyCallback?: (f: () => void) => void ondestroyCallback?: (f: () => void) => void,
): Store<J> { ): Store<J> {
let stores: Store<any>[] = undefined let stores: Store<any>[] = undefined
if (extraStores?.length > 0 || this._extraStores?.length > 0) { if (extraStores?.length > 0 || this._extraStores?.length > 0) {
@ -558,7 +568,7 @@ class MappedStore<TIn, T> extends Store<T> {
stores, stores,
this._callbacks, this._callbacks,
f(this.data), f(this.data),
ondestroyCallback ondestroyCallback,
) )
} }
@ -614,7 +624,7 @@ class MappedStore<TIn, T> extends Store<T> {
this._unregisterFromUpstream = this._upstream.addCallback((_) => self.update()) this._unregisterFromUpstream = this._upstream.addCallback((_) => self.update())
this._unregisterFromExtraStores = this._extraStores?.map((store) => this._unregisterFromExtraStores = this._extraStores?.map((store) =>
store?.addCallback((_) => self.update()) store?.addCallback((_) => self.update()),
) )
this._callbacksAreRegistered = true this._callbacksAreRegistered = true
} }
@ -651,7 +661,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
public static flatten<X>( public static flatten<X>(
source: Store<Store<X>>, source: Store<Store<X>>,
possibleSources?: Store<object>[] possibleSources?: Store<object>[],
): UIEventSource<X> { ): UIEventSource<X> {
const sink = new UIEventSource<X>(source.data?.data) const sink = new UIEventSource<X>(source.data?.data)
@ -680,7 +690,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
*/ */
public static FromPromise<T>( public static FromPromise<T>(
promise: Promise<T>, promise: Promise<T>,
onError: (e) => void = undefined onError: (e) => void = undefined,
): UIEventSource<T> { ): UIEventSource<T> {
const src = new UIEventSource<T>(undefined) const src = new UIEventSource<T>(undefined)
promise?.then((d) => src.setData(d)) promise?.then((d) => src.setData(d))
@ -701,7 +711,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
* @constructor * @constructor
*/ */
public static FromPromiseWithErr<T>( public static FromPromiseWithErr<T>(
promise: Promise<T> promise: Promise<T>,
): UIEventSource<{ success: T } | { error: any } | undefined> { ): UIEventSource<{ success: T } | { error: any } | undefined> {
const src = new UIEventSource<{ success: T } | { error: any }>(undefined) const src = new UIEventSource<{ success: T } | { error: any }>(undefined)
promise promise
@ -733,7 +743,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
return undefined return undefined
} }
return "" + fl return "" + fl
} },
) )
} }
@ -764,7 +774,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
return undefined return undefined
} }
return "" + fl return "" + fl
} },
) )
} }
@ -772,7 +782,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
return stringUIEventSource.sync( return stringUIEventSource.sync(
(str) => str === "true", (str) => str === "true",
[], [],
(b) => "" + b (b) => "" + b,
) )
} }
@ -790,7 +800,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
} }
}, },
[], [],
(b) => JSON.stringify(b) ?? "" (b) => JSON.stringify(b) ?? "",
) )
} }
@ -880,7 +890,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
public map<J>( public map<J>(
f: (t: T) => J, f: (t: T) => J,
extraSources: Store<any>[] = [], extraSources: Store<any>[] = [],
onDestroy?: (f: () => void) => void onDestroy?: (f: () => void) => void,
): Store<J> { ): Store<J> {
return new MappedStore(this, f, extraSources, this._callbacks, f(this.data), onDestroy) return new MappedStore(this, f, extraSources, this._callbacks, f(this.data), onDestroy)
} }
@ -892,7 +902,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
public mapD<J>( public mapD<J>(
f: (t: Exclude<T, undefined | null>) => J, f: (t: Exclude<T, undefined | null>) => J,
extraSources: Store<any>[] = [], extraSources: Store<any>[] = [],
callbackDestroyFunction?: (f: () => void) => void callbackDestroyFunction?: (f: () => void) => void,
): Store<J | undefined> { ): Store<J | undefined> {
return new MappedStore( return new MappedStore(
this, this,
@ -910,7 +920,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
this.data === undefined || this.data === null this.data === undefined || this.data === null
? <undefined | null>this.data ? <undefined | null>this.data
: f(<any>this.data), : f(<any>this.data),
callbackDestroyFunction callbackDestroyFunction,
) )
} }
@ -930,7 +940,7 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> {
f: (t: T) => J, f: (t: T) => J,
extraSources: Store<any>[], extraSources: Store<any>[],
g: (j: J, t: T) => T, g: (j: J, t: T) => T,
allowUnregister = false allowUnregister = false,
): UIEventSource<J> { ): UIEventSource<J> {
const self = this const self = this

View file

@ -4,8 +4,11 @@ import { UIEventSource } from "../UIEventSource"
* UIEventsource-wrapper around localStorage * UIEventsource-wrapper around localStorage
*/ */
export class LocalStorageSource { export class LocalStorageSource {
static GetParsed<T>(key: string, defaultValue: T): UIEventSource<T> {
return LocalStorageSource.Get(key).sync( private static readonly _cache: Record<string, UIEventSource<string>> = {}
static getParsed<T>(key: string, defaultValue: T): UIEventSource<T> {
return LocalStorageSource.get(key).sync(
(str) => { (str) => {
if (str === undefined) { if (str === undefined) {
return defaultValue return defaultValue
@ -17,16 +20,24 @@ export class LocalStorageSource {
} }
}, },
[], [],
(value) => JSON.stringify(value) (value) => JSON.stringify(value),
) )
} }
static Get(key: string, defaultValue: string = undefined): UIEventSource<string> { static get(key: string, defaultValue: string = undefined): UIEventSource<string> {
const cached = LocalStorageSource._cache[key]
if (cached) {
return cached
}
let saved = defaultValue
try { try {
let saved = localStorage.getItem(key) saved = localStorage.getItem(key)
if (saved === "undefined") { if (saved === "undefined") {
saved = undefined saved = undefined
} }
} catch (e) {
console.error("Could not get value", key, "from local storage")
}
const source = new UIEventSource<string>(saved ?? defaultValue, "localstorage:" + key) const source = new UIEventSource<string>(saved ?? defaultValue, "localstorage:" + key)
source.addCallback((data) => { source.addCallback((data) => {
@ -38,13 +49,11 @@ export class LocalStorageSource {
localStorage.setItem(key, data) localStorage.setItem(key, data)
} catch (e) { } catch (e) {
// Probably exceeded the quota with this item! // Probably exceeded the quota with this item!
// Lets nuke everything // Let's nuke everything
localStorage.clear() localStorage.clear()
} }
}) })
LocalStorageSource._cache[key] = source
return source return source
} catch (e) {
return new UIEventSource<string>(defaultValue)
}
} }
} }

View file

@ -86,7 +86,7 @@ export default class FilteredLayer {
) { ) {
let isDisplayed: UIEventSource<boolean> let isDisplayed: UIEventSource<boolean>
if (layer.syncSelection === "local") { if (layer.syncSelection === "local") {
isDisplayed = LocalStorageSource.GetParsed( isDisplayed = LocalStorageSource.getParsed(
context + "-layer-" + layer.id + "-enabled", context + "-layer-" + layer.id + "-enabled",
layer.shownByDefault, layer.shownByDefault,
) )

View file

@ -57,7 +57,7 @@ export class MenuState {
}) })
} }
const visitedBefore = LocalStorageSource.GetParsed<boolean>( const visitedBefore = LocalStorageSource.getParsed<boolean>(
themeid + "thememenuisopened", themeid + "thememenuisopened",
false false
) )

View file

@ -370,7 +370,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.changes, this.changes,
this.geolocation.geolocationState.currentGPSLocation, this.geolocation.geolocationState.currentGPSLocation,
this.indexedFeatures, this.indexedFeatures,
this.reportError this.reportError,
) )
this.favourites = new FavouritesFeatureSource(this) this.favourites = new FavouritesFeatureSource(this)
const longAgo = new Date() const longAgo = new Date()
@ -908,6 +908,21 @@ export default class ThemeViewState implements SpecialVisualizationState {
* Setup various services for which no reference are needed * Setup various services for which no reference are needed
*/ */
private initActors() { private initActors() {
if (!this.layout.official) {
// Add custom themes to the "visited custom themes"
const th = this.layout
this.userRelatedState.addUnofficialTheme({
id: th.id,
icon: th.icon,
title: th.title.translations,
shortDescription: th.shortDescription.translations ,
layers: th.layers.filter(l => l.isNormal()).map(l => l.id)
})
}
this.selectedElement.addCallback((selected) => { this.selectedElement.addCallback((selected) => {
if (selected === undefined) { if (selected === undefined) {
this.focusOnMap() this.focusOnMap()

View file

@ -55,7 +55,6 @@
const customThemes: Store<MinimalLayoutInformation[]> = Stores.ListStabilized<string>(state.installedUserThemes) const customThemes: Store<MinimalLayoutInformation[]> = Stores.ListStabilized<string>(state.installedUserThemes)
.mapD(stableIds => Utils.NoNullInplace(stableIds.map(id => state.getUnofficialTheme(id)))) .mapD(stableIds => Utils.NoNullInplace(stableIds.map(id => state.getUnofficialTheme(id))))
function filtered(themes: Store<MinimalLayoutInformation[]>): Store<MinimalLayoutInformation[]> { function filtered(themes: Store<MinimalLayoutInformation[]>): Store<MinimalLayoutInformation[]> {
return searchStable.map(search => { return searchStable.map(search => {
if (!search) { if (!search) {

View file

@ -42,8 +42,8 @@
}) })
} }
let customWidth = LocalStorageSource.Get("custom-png-width", "20") let customWidth = LocalStorageSource.get("custom-png-width", "20")
let customHeight = LocalStorageSource.Get("custom-png-height", "20") let customHeight = LocalStorageSource.get("custom-png-height", "20")
async function offerCustomPng(): Promise<Blob> { async function offerCustomPng(): Promise<Blob> {
console.log( console.log(

View file

@ -24,7 +24,7 @@
export let coordinate: UIEventSource<{ lon: number; lat: number }> export let coordinate: UIEventSource<{ lon: number; lat: number }>
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text") let comment: UIEventSource<string> = LocalStorageSource.get("note-text")
let created = false let created = false
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note") let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note")

View file

@ -37,7 +37,7 @@ export abstract class EditJsonState<T> {
public readonly osmConnection: OsmConnection public readonly osmConnection: OsmConnection
public readonly showIntro: UIEventSource<"no" | "intro" | "tagrenderings"> = <any>( public readonly showIntro: UIEventSource<"no" | "intro" | "tagrenderings"> = <any>(
LocalStorageSource.Get("studio-show-intro", "intro") LocalStorageSource.get("studio-show-intro", "intro")
) )
public readonly expertMode: UIEventSource<boolean> public readonly expertMode: UIEventSource<boolean>

View file

@ -29,7 +29,7 @@
const store = state.getStoreFor(path) const store = state.getStoreFor(path)
let value = store.data let value = store.data
let hasSeenIntro = UIEventSource.asBoolean( let hasSeenIntro = UIEventSource.asBoolean(
LocalStorageSource.Get("studio-seen-tagrendering-tutorial", "false") LocalStorageSource.get("studio-seen-tagrendering-tutorial", "false")
) )
onMount(() => { onMount(() => {
if (!hasSeenIntro.data) { if (!hasSeenIntro.data) {

View file

@ -74,7 +74,7 @@ export default class Locale {
if (typeof navigator !== "undefined") { if (typeof navigator !== "undefined") {
browserLanguage = Locale.getBestSupportedLanguage() browserLanguage = Locale.getBestSupportedLanguage()
} }
source = LocalStorageSource.Get("language", browserLanguage) source = LocalStorageSource.get("language", browserLanguage)
} }
if (!Utils.runningFromConsole && typeof document !== undefined) { if (!Utils.runningFromConsole && typeof document !== undefined) {