mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 02:35:42 +00:00
fix(Map): Add common migration mechanism. ATTENTION! This is a non-reversible stored map settings commit — it means we do not guarantee that settings will work if you check out back. We’ve tried to migrate old settings, but it may not work well or may NOT work at all.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { MapUserSettings, SettingsWithVersion } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
import { MapUserSettings, SettingsWrapper } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
|
|
||||||
export const REQUIRED_KEYS = [
|
export const REQUIRED_KEYS = [
|
||||||
'widgets',
|
'widgets',
|
||||||
@@ -19,11 +19,8 @@ export class MapUserSettingsParseError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isNumber = (v: unknown): v is number => typeof v === 'number' && !Number.isNaN(v);
|
/** Minimal check that an object matches SettingsWrapper<*> */
|
||||||
|
const isSettings = (v: unknown): v is SettingsWrapper<unknown> => typeof v === 'object' && v !== null;
|
||||||
/** Minimal check that an object matches SettingsWithVersion<*> */
|
|
||||||
const isSettingsWithVersion = (v: unknown): v is SettingsWithVersion<unknown> =>
|
|
||||||
typeof v === 'object' && v !== null && isNumber((v as any).version) && 'settings' in (v as any);
|
|
||||||
|
|
||||||
/** Ensure every required key is present */
|
/** Ensure every required key is present */
|
||||||
const hasAllRequiredKeys = (v: unknown): v is Record<RequiredKeys, unknown> =>
|
const hasAllRequiredKeys = (v: unknown): v is Record<RequiredKeys, unknown> =>
|
||||||
@@ -52,8 +49,8 @@ export const parseMapUserSettings = (json: unknown): MapUserSettings => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const key of REQUIRED_KEYS) {
|
for (const key of REQUIRED_KEYS) {
|
||||||
if (!isSettingsWithVersion((data as any)[key])) {
|
if (!isSettings((data as any)[key])) {
|
||||||
throw new MapUserSettingsParseError(`"${key}" must match SettingsWithVersion<T>`);
|
throw new MapUserSettingsParseError(`"${key}" must match SettingsWrapper<T>`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
|
|||||||
import { saveTextFile } from '@/hooks/Mapper/utils/saveToFile.ts';
|
import { saveTextFile } from '@/hooks/Mapper/utils/saveToFile.ts';
|
||||||
import { SplitButton } from 'primereact/splitbutton';
|
import { SplitButton } from 'primereact/splitbutton';
|
||||||
import { loadTextFile } from '@/hooks/Mapper/utils';
|
import { loadTextFile } from '@/hooks/Mapper/utils';
|
||||||
|
import { applyMigrations } from '@/hooks/Mapper/mapRootProvider/migrations';
|
||||||
|
|
||||||
export const ImportExport = () => {
|
export const ImportExport = () => {
|
||||||
const {
|
const {
|
||||||
@@ -22,8 +23,9 @@ export const ImportExport = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// INFO: WE NOT SUPPORT MIGRATIONS FOR OLD FILES AND Clipboard
|
||||||
const parsed = parseMapUserSettings(text);
|
const parsed = parseMapUserSettings(text);
|
||||||
if (applySettings(parsed)) {
|
if (applySettings(applyMigrations(parsed))) {
|
||||||
toast.current?.show({
|
toast.current?.show({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
summary: 'Import',
|
summary: 'Import',
|
||||||
@@ -59,8 +61,9 @@ export const ImportExport = () => {
|
|||||||
try {
|
try {
|
||||||
const text = await loadTextFile();
|
const text = await loadTextFile();
|
||||||
|
|
||||||
|
// INFO: WE NOT SUPPORT MIGRATIONS FOR OLD FILES AND Clipboard
|
||||||
const parsed = parseMapUserSettings(text);
|
const parsed = parseMapUserSettings(text);
|
||||||
if (applySettings(parsed)) {
|
if (applySettings(applyMigrations(parsed))) {
|
||||||
toast.current?.show({
|
toast.current?.show({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
summary: 'Import',
|
summary: 'Import',
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Toast } from 'primereact/toast';
|
import { Toast } from 'primereact/toast';
|
||||||
import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
|
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { OutCommand } from '@/hooks/Mapper/types';
|
import { OutCommand } from '@/hooks/Mapper/types';
|
||||||
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
||||||
@@ -9,6 +8,7 @@ import { callToastSuccess } from '@/hooks/Mapper/helpers';
|
|||||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||||
import { RemoteAdminSettingsResponse } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
import { RemoteAdminSettingsResponse } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
|
import { applyMigrations } from '@/hooks/Mapper/mapRootProvider/migrations';
|
||||||
|
|
||||||
export const ServerSettings = () => {
|
export const ServerSettings = () => {
|
||||||
const {
|
const {
|
||||||
@@ -34,7 +34,8 @@ export const ServerSettings = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
applySettings(parseMapUserSettings(res.default_settings));
|
//INFO: INSTEAD CHECK WE WILL TRY TO APPLY MIGRATION
|
||||||
|
applySettings(applyMigrations(JSON.parse(res.default_settings)));
|
||||||
callToastSuccess(toast.current, 'Settings synchronized successfully');
|
callToastSuccess(toast.current, 'Settings synchronized successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
applySettings(createDefaultWidgetSettings());
|
applySettings(createDefaultWidgetSettings());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MapUserSettings, MigrationTypes, SettingsWithVersion } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
import { MapUserSettings, SettingsTypes, SettingsWrapper } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
import {
|
import {
|
||||||
DEFAULT_KILLS_WIDGET_SETTINGS,
|
DEFAULT_KILLS_WIDGET_SETTINGS,
|
||||||
DEFAULT_ON_THE_MAP_SETTINGS,
|
DEFAULT_ON_THE_MAP_SETTINGS,
|
||||||
@@ -8,45 +8,44 @@ import {
|
|||||||
STORED_INTERFACE_DEFAULT_VALUES,
|
STORED_INTERFACE_DEFAULT_VALUES,
|
||||||
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
|
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
|
||||||
import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures.ts';
|
import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures.ts';
|
||||||
import { SETTING_VERSIONS } from '@/hooks/Mapper/mapRootProvider/versions.ts';
|
import { STORED_SETTINGS_VERSION } from '@/hooks/Mapper/mapRootProvider/version.ts';
|
||||||
|
|
||||||
// TODO - we need provide and compare version
|
// TODO - we need provide and compare version
|
||||||
export const createWidgetSettingsWithVersion = <T>(version: number, settings: T) => {
|
export const createWidgetSettings = <T>(settings: T) => {
|
||||||
return {
|
return settings;
|
||||||
version,
|
|
||||||
settings,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createDefaultWidgetSettings = (): MapUserSettings => {
|
export const createDefaultWidgetSettings = (): MapUserSettings => {
|
||||||
return {
|
return {
|
||||||
killsWidget: createWidgetSettingsWithVersion(SETTING_VERSIONS.kills, DEFAULT_KILLS_WIDGET_SETTINGS),
|
version: STORED_SETTINGS_VERSION,
|
||||||
localWidget: createWidgetSettingsWithVersion(SETTING_VERSIONS.localWidget, DEFAULT_WIDGET_LOCAL_SETTINGS),
|
migratedFromOld: true,
|
||||||
widgets: createWidgetSettingsWithVersion(SETTING_VERSIONS.widgets, getDefaultWidgetProps()),
|
killsWidget: createWidgetSettings(DEFAULT_KILLS_WIDGET_SETTINGS),
|
||||||
routes: createWidgetSettingsWithVersion(SETTING_VERSIONS.routes, DEFAULT_ROUTES_SETTINGS),
|
localWidget: createWidgetSettings(DEFAULT_WIDGET_LOCAL_SETTINGS),
|
||||||
onTheMap: createWidgetSettingsWithVersion(SETTING_VERSIONS.onTheMap, DEFAULT_ON_THE_MAP_SETTINGS),
|
widgets: createWidgetSettings(getDefaultWidgetProps()),
|
||||||
signaturesWidget: createWidgetSettingsWithVersion(SETTING_VERSIONS.signatures, DEFAULT_SIGNATURE_SETTINGS),
|
routes: createWidgetSettings(DEFAULT_ROUTES_SETTINGS),
|
||||||
interface: createWidgetSettingsWithVersion(SETTING_VERSIONS.interface, STORED_INTERFACE_DEFAULT_VALUES),
|
onTheMap: createWidgetSettings(DEFAULT_ON_THE_MAP_SETTINGS),
|
||||||
|
signaturesWidget: createWidgetSettings(DEFAULT_SIGNATURE_SETTINGS),
|
||||||
|
interface: createWidgetSettings(STORED_INTERFACE_DEFAULT_VALUES),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// INFO - in another case need to generate complex type - but looks like it unnecessary
|
// INFO - in another case need to generate complex type - but looks like it unnecessary
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const getDefaultSettingsByType = (type: MigrationTypes): SettingsWithVersion<any> => {
|
export const getDefaultSettingsByType = (type: SettingsTypes): SettingsWrapper<any> => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MigrationTypes.killsWidget:
|
case SettingsTypes.killsWidget:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.kills, DEFAULT_KILLS_WIDGET_SETTINGS);
|
return createWidgetSettings(DEFAULT_KILLS_WIDGET_SETTINGS);
|
||||||
case MigrationTypes.localWidget:
|
case SettingsTypes.localWidget:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.localWidget, DEFAULT_WIDGET_LOCAL_SETTINGS);
|
return createWidgetSettings(DEFAULT_WIDGET_LOCAL_SETTINGS);
|
||||||
case MigrationTypes.widgets:
|
case SettingsTypes.widgets:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.widgets, getDefaultWidgetProps());
|
return createWidgetSettings(getDefaultWidgetProps());
|
||||||
case MigrationTypes.routes:
|
case SettingsTypes.routes:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.routes, DEFAULT_ROUTES_SETTINGS);
|
return createWidgetSettings(DEFAULT_ROUTES_SETTINGS);
|
||||||
case MigrationTypes.onTheMap:
|
case SettingsTypes.onTheMap:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.onTheMap, DEFAULT_ON_THE_MAP_SETTINGS);
|
return createWidgetSettings(DEFAULT_ON_THE_MAP_SETTINGS);
|
||||||
case MigrationTypes.signaturesWidget:
|
case SettingsTypes.signaturesWidget:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.signatures, DEFAULT_SIGNATURE_SETTINGS);
|
return createWidgetSettings(DEFAULT_SIGNATURE_SETTINGS);
|
||||||
case MigrationTypes.interface:
|
case SettingsTypes.interface:
|
||||||
return createWidgetSettingsWithVersion(SETTING_VERSIONS.interface, STORED_INTERFACE_DEFAULT_VALUES);
|
return createWidgetSettings(STORED_INTERFACE_DEFAULT_VALUES);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
RemoteAdminSettingsResponse,
|
RemoteAdminSettingsResponse,
|
||||||
} from '@/hooks/Mapper/mapRootProvider/types.ts';
|
} from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
||||||
import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
|
import { applyMigrations } from '@/hooks/Mapper/mapRootProvider/migrations';
|
||||||
|
|
||||||
interface UseActualizeRemoteMapSettingsProps {
|
interface UseActualizeRemoteMapSettingsProps {
|
||||||
outCommand: OutCommandHandler;
|
outCommand: OutCommandHandler;
|
||||||
@@ -42,7 +42,7 @@ export const useActualizeRemoteMapSettings = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
applySettings(parseMapUserSettings(res.default_settings));
|
applySettings(applyMigrations(JSON.parse(res.default_settings)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
applySettings(createDefaultWidgetSettings());
|
applySettings(createDefaultWidgetSettings());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import fastDeepEqual from 'fast-deep-equal';
|
|||||||
import { OutCommandHandler } from '@/hooks/Mapper/types';
|
import { OutCommandHandler } from '@/hooks/Mapper/types';
|
||||||
import { useActualizeRemoteMapSettings } from '@/hooks/Mapper/mapRootProvider/hooks/useActualizeRemoteMapSettings.ts';
|
import { useActualizeRemoteMapSettings } from '@/hooks/Mapper/mapRootProvider/hooks/useActualizeRemoteMapSettings.ts';
|
||||||
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
||||||
import { applyMigrations, migrations } from '@/hooks/Mapper/mapRootProvider/migrations';
|
import { applyMigrations, extractData } from '@/hooks/Mapper/mapRootProvider/migrations';
|
||||||
|
import { LS_KEY, LS_KEY_LEGASY } from '@/hooks/Mapper/mapRootProvider/version.ts';
|
||||||
|
|
||||||
const EMPTY_OBJ = {};
|
const EMPTY_OBJ = {};
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ export const useMapUserSettings = ({ map_slug }: MapRootData, outCommand: OutCom
|
|||||||
const [isReady, setIsReady] = useState(false);
|
const [isReady, setIsReady] = useState(false);
|
||||||
const [hasOldSettings, setHasOldSettings] = useState(false);
|
const [hasOldSettings, setHasOldSettings] = useState(false);
|
||||||
|
|
||||||
const [mapUserSettings, setMapUserSettings] = useLocalStorageState<MapUserSettingsStructure>('map-user-settings', {
|
const [mapUserSettings, setMapUserSettings] = useLocalStorageState<MapUserSettingsStructure>(LS_KEY, {
|
||||||
defaultValue: EMPTY_OBJ,
|
defaultValue: EMPTY_OBJ,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,18 +102,27 @@ export const useMapUserSettings = ({ map_slug }: MapRootData, outCommand: OutCom
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapUserSettings[map_slug] == null) {
|
const currentMapUserSettings = mapUserSettings[map_slug];
|
||||||
|
if (currentMapUserSettings == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const migratedResult = applyMigrations(map_slug, migrations);
|
try {
|
||||||
if (!migratedResult) {
|
// INFO: after migrations migratedFromOld always will be true
|
||||||
return;
|
const migratedResult = applyMigrations(
|
||||||
|
!currentMapUserSettings.migratedFromOld ? extractData(LS_KEY_LEGASY) : currentMapUserSettings,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!migratedResult) {
|
||||||
|
setIsReady(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapUserSettings({ ...mapUserSettings, [map_slug]: migratedResult });
|
||||||
|
setIsReady(true);
|
||||||
|
} catch (error) {
|
||||||
|
setIsReady(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMapUserSettings(migratedResult);
|
|
||||||
|
|
||||||
setIsReady(true);
|
|
||||||
}, [isReady, mapUserSettings, map_slug, setMapUserSettings]);
|
}, [isReady, mapUserSettings, map_slug, setMapUserSettings]);
|
||||||
|
|
||||||
const checkOldSettings = useCallback(() => {
|
const checkOldSettings = useCallback(() => {
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import { Dispatch, SetStateAction, useCallback, useMemo, useRef } from 'react';
|
import { Dispatch, SetStateAction, useCallback, useMemo, useRef } from 'react';
|
||||||
import {
|
import { MapUserSettings, MapUserSettingsStructure, SettingsWrapper } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
MapUserSettings,
|
|
||||||
MapUserSettingsStructure,
|
|
||||||
SettingsWithVersion,
|
|
||||||
} from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
type ExtractSettings<S extends keyof MapUserSettings> =
|
type ExtractSettings<S extends keyof MapUserSettings> = MapUserSettings[S] extends SettingsWrapper<infer U> ? U : never;
|
||||||
MapUserSettings[S] extends SettingsWithVersion<infer U> ? U : never;
|
|
||||||
|
|
||||||
type Setter<S extends keyof MapUserSettings> = (
|
type Setter<S extends keyof MapUserSettings> = (
|
||||||
value: Partial<ExtractSettings<S>> | ((prev: ExtractSettings<S>) => Partial<ExtractSettings<S>>),
|
value: Partial<ExtractSettings<S>> | ((prev: ExtractSettings<S>) => Partial<ExtractSettings<S>>),
|
||||||
@@ -25,21 +20,20 @@ export const useSettingsValueAndSetter = <S extends keyof MapUserSettings>(
|
|||||||
if (!mapId) return {} as ExtractSettings<S>;
|
if (!mapId) return {} as ExtractSettings<S>;
|
||||||
|
|
||||||
const mapSettings = settings[mapId];
|
const mapSettings = settings[mapId];
|
||||||
return (mapSettings?.[setting]?.settings ?? ({} as ExtractSettings<S>)) as ExtractSettings<S>;
|
return (mapSettings?.[setting] ?? ({} as ExtractSettings<S>)) as ExtractSettings<S>;
|
||||||
}, [mapId, setting, settings]);
|
}, [mapId, setting, settings]);
|
||||||
|
|
||||||
const refData = useRef({ mapId, setting, setSettings });
|
const refData = useRef({ mapId, setting, setSettings });
|
||||||
refData.current = { mapId, setting, setSettings };
|
refData.current = { mapId, setting, setSettings };
|
||||||
|
|
||||||
const setter = useCallback<Setter<S>>((value, v) => {
|
const setter = useCallback<Setter<S>>(value => {
|
||||||
const { mapId, setting, setSettings } = refData.current;
|
const { mapId, setting, setSettings } = refData.current;
|
||||||
|
|
||||||
if (!mapId) return;
|
if (!mapId) return;
|
||||||
|
|
||||||
setSettings(all => {
|
setSettings(all => {
|
||||||
const currentMap = all[mapId];
|
const currentMap = all[mapId];
|
||||||
const prev = currentMap[setting].settings as ExtractSettings<S>;
|
const prev = currentMap[setting] as ExtractSettings<S>;
|
||||||
const version = v != null ? v : currentMap[setting].version;
|
|
||||||
|
|
||||||
const patch =
|
const patch =
|
||||||
typeof value === 'function' ? (value as (p: ExtractSettings<S>) => Partial<ExtractSettings<S>>)(prev) : value;
|
typeof value === 'function' ? (value as (p: ExtractSettings<S>) => Partial<ExtractSettings<S>>)(prev) : value;
|
||||||
@@ -48,10 +42,7 @@ export const useSettingsValueAndSetter = <S extends keyof MapUserSettings>(
|
|||||||
...all,
|
...all,
|
||||||
[mapId]: {
|
[mapId]: {
|
||||||
...currentMap,
|
...currentMap,
|
||||||
[setting]: {
|
[setting]: { ...(prev as any), ...patch } as ExtractSettings<S>,
|
||||||
version,
|
|
||||||
settings: { ...(prev as any), ...patch } as ExtractSettings<S>,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { to_1 } from './to_1.ts';
|
|
||||||
import { to_2 } from './to_2.ts';
|
|
||||||
import { to_3 } from './to_3.ts';
|
|
||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [to_1, to_2, to_3] as MigrationStructure[];
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { MigrationStructure, MigrationTypes } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export const to_1: MigrationStructure = {
|
|
||||||
to: 1,
|
|
||||||
type: MigrationTypes.interface,
|
|
||||||
run: (prev: any) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
test1: 'lol ke',
|
|
||||||
kek1: 'kek',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { MigrationStructure, MigrationTypes } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
import { omit } from '@/hooks/Mapper/utils/omit.ts';
|
|
||||||
|
|
||||||
export const to_2: MigrationStructure = {
|
|
||||||
to: 2,
|
|
||||||
type: MigrationTypes.interface,
|
|
||||||
run: (prev: any) => {
|
|
||||||
return {
|
|
||||||
...omit(prev, ['test1', 'kek1']),
|
|
||||||
test2: 'lol ke1',
|
|
||||||
kek2: 'kek1',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { MigrationStructure, MigrationTypes } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
import { omit } from '@/hooks/Mapper/utils/omit.ts';
|
|
||||||
|
|
||||||
export const to_3: MigrationStructure = {
|
|
||||||
to: 3,
|
|
||||||
type: MigrationTypes.interface,
|
|
||||||
run: (prev: any) => {
|
|
||||||
return {
|
|
||||||
...omit(prev, ['test2', 'kek2']),
|
|
||||||
test3: 'lol ke1333',
|
|
||||||
kek3: 'kek1333',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { MapUserSettingsStructure, MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
import { MapUserSettingsStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
import { getDefaultSettingsByType } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
import { STORED_SETTINGS_VERSION } from '@/hooks/Mapper/mapRootProvider/version.ts';
|
||||||
|
import { migrations } from '@/hooks/Mapper/mapRootProvider/migrations/index.ts';
|
||||||
|
|
||||||
const extractData = (localStoreKey = 'map-user-settings'): MapUserSettingsStructure | null => {
|
export const extractData = (localStoreKey = 'map-user-settings'): MapUserSettingsStructure | null => {
|
||||||
const val = localStorage.getItem(localStoreKey);
|
const val = localStorage.getItem(localStoreKey);
|
||||||
if (!val) {
|
if (!val) {
|
||||||
return null;
|
return null;
|
||||||
@@ -10,38 +11,46 @@ const extractData = (localStoreKey = 'map-user-settings'): MapUserSettingsStruct
|
|||||||
return JSON.parse(val);
|
return JSON.parse(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const applyMigrations = (
|
export const applyMigrations = (mapSettings: any) => {
|
||||||
mapId: string,
|
let currentMapSettings = { ...mapSettings };
|
||||||
migrations: MigrationStructure[],
|
|
||||||
localStoreKey = 'map-user-settings',
|
|
||||||
) => {
|
|
||||||
const currentLSData = extractData(localStoreKey);
|
|
||||||
|
|
||||||
// INFO if we have NO any data in store expected that we will use default
|
// INFO if we have NO any data in store expected that we will use default
|
||||||
if (!currentLSData) {
|
if (!currentMapSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentMapSettings = currentLSData[mapId];
|
const direction = STORED_SETTINGS_VERSION - (currentMapSettings.version || 0);
|
||||||
|
if (direction === 0) {
|
||||||
for (const migration of migrations) {
|
if (currentMapSettings.version == null) {
|
||||||
const { to, run, type } = migration;
|
return { ...currentMapSettings, version: STORED_SETTINGS_VERSION, migratedFromOld: true };
|
||||||
const currentValue = currentMapSettings[type];
|
|
||||||
|
|
||||||
if (!currentValue) {
|
|
||||||
currentMapSettings[type] = getDefaultSettingsByType(type);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we skip if current version is older
|
return;
|
||||||
if (currentValue.version > to) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = run(currentValue.settings);
|
|
||||||
currentMapSettings[type].version = to;
|
|
||||||
currentMapSettings[type].settings = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentLSData;
|
// Upgrade
|
||||||
|
if (direction > 0) {
|
||||||
|
const preparedMigrations = migrations.sort((a, b) => a.to - b.to).filter(x => x.to <= STORED_SETTINGS_VERSION);
|
||||||
|
|
||||||
|
for (const migration of preparedMigrations) {
|
||||||
|
const { to, up } = migration;
|
||||||
|
|
||||||
|
const next = up(currentMapSettings);
|
||||||
|
currentMapSettings = { ...next, version: to, migratedFromOld: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentMapSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOWNGRADE
|
||||||
|
const preparedMigrations = migrations.sort((a, b) => b.to - a.to).filter(x => x.to - 1 >= STORED_SETTINGS_VERSION);
|
||||||
|
|
||||||
|
for (const migration of preparedMigrations) {
|
||||||
|
const { to, down } = migration;
|
||||||
|
|
||||||
|
const next = down(currentMapSettings);
|
||||||
|
currentMapSettings = { ...next, version: to - 1, migratedFromOld: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentMapSettings;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,4 @@
|
|||||||
import m_interface from './interface';
|
import list from './list';
|
||||||
import killsWidget from './killsWidget';
|
|
||||||
import localWidget from './localWidget';
|
|
||||||
import onTheMap from './onTheMap';
|
|
||||||
import routes from './routes';
|
|
||||||
import signaturesWidget from './signaturesWidget';
|
|
||||||
import widgets from './widgets';
|
|
||||||
export * from './applyMigrations.ts';
|
export * from './applyMigrations.ts';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [...list];
|
||||||
...m_interface,
|
|
||||||
...killsWidget,
|
|
||||||
...localWidget,
|
|
||||||
...onTheMap,
|
|
||||||
...routes,
|
|
||||||
...signaturesWidget,
|
|
||||||
...widgets,
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { to_1 } from './to_1.ts';
|
||||||
|
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
|
|
||||||
|
export default [to_1 /*to_2, to_3*/] as MigrationStructure[];
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||||
|
|
||||||
|
export const to_1: MigrationStructure = {
|
||||||
|
to: 1,
|
||||||
|
up: (prev: any) => {
|
||||||
|
return Object.keys(prev).reduce((acc, k) => {
|
||||||
|
return { ...acc, [k]: prev[k].settings };
|
||||||
|
}, Object.create(null));
|
||||||
|
},
|
||||||
|
down: (prev: any) => {
|
||||||
|
return Object.keys(prev).reduce((acc, k) => {
|
||||||
|
return { ...acc, [k]: { version: 0, settings: prev[k] } };
|
||||||
|
}, Object.create(null));
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { MigrationStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
|
||||||
|
|
||||||
export default [] as MigrationStructure[];
|
|
||||||
@@ -64,19 +64,18 @@ export type KillsWidgetSettings = {
|
|||||||
timeRange: number;
|
timeRange: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SettingsWithVersion<T> = {
|
export type SettingsWrapper<T> = T;
|
||||||
version: number;
|
|
||||||
settings: T;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MapUserSettings = {
|
export type MapUserSettings = {
|
||||||
widgets: SettingsWithVersion<WindowStoreInfo>;
|
migratedFromOld: boolean;
|
||||||
interface: SettingsWithVersion<InterfaceStoredSettings>;
|
version: number;
|
||||||
onTheMap: SettingsWithVersion<OnTheMapSettingsType>;
|
widgets: SettingsWrapper<WindowStoreInfo>;
|
||||||
routes: SettingsWithVersion<RoutesType>;
|
interface: SettingsWrapper<InterfaceStoredSettings>;
|
||||||
localWidget: SettingsWithVersion<LocalWidgetSettings>;
|
onTheMap: SettingsWrapper<OnTheMapSettingsType>;
|
||||||
signaturesWidget: SettingsWithVersion<SignatureSettingsType>;
|
routes: SettingsWrapper<RoutesType>;
|
||||||
killsWidget: SettingsWithVersion<KillsWidgetSettings>;
|
localWidget: SettingsWrapper<LocalWidgetSettings>;
|
||||||
|
signaturesWidget: SettingsWrapper<SignatureSettingsType>;
|
||||||
|
killsWidget: SettingsWrapper<KillsWidgetSettings>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MapUserSettingsStructure = {
|
export type MapUserSettingsStructure = {
|
||||||
@@ -87,7 +86,7 @@ export type WdResponse<T> = T;
|
|||||||
|
|
||||||
export type RemoteAdminSettingsResponse = { default_settings?: string };
|
export type RemoteAdminSettingsResponse = { default_settings?: string };
|
||||||
|
|
||||||
export enum MigrationTypes {
|
export enum SettingsTypes {
|
||||||
killsWidget = 'killsWidget',
|
killsWidget = 'killsWidget',
|
||||||
localWidget = 'localWidget',
|
localWidget = 'localWidget',
|
||||||
widgets = 'widgets',
|
widgets = 'widgets',
|
||||||
@@ -100,6 +99,6 @@ export enum MigrationTypes {
|
|||||||
export type MigrationFunc = (prev: any) => any;
|
export type MigrationFunc = (prev: any) => any;
|
||||||
export type MigrationStructure = {
|
export type MigrationStructure = {
|
||||||
to: number;
|
to: number;
|
||||||
type: MigrationTypes;
|
up: MigrationFunc;
|
||||||
run: MigrationFunc;
|
down: MigrationFunc;
|
||||||
};
|
};
|
||||||
|
|||||||
4
assets/js/hooks/Mapper/mapRootProvider/version.ts
Normal file
4
assets/js/hooks/Mapper/mapRootProvider/version.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const STORED_SETTINGS_VERSION = 1;
|
||||||
|
|
||||||
|
export const LS_KEY_LEGASY = 'map-user-settings';
|
||||||
|
export const LS_KEY = 'map-user-settings-v2';
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// TODO IF YOU BUMP VERSION YOU SHOULD ADD MIGRATION!!!
|
|
||||||
// CHECK assets/js/hooks/Mapper/mapRootProvider/migrations
|
|
||||||
// FOR EACH TYPE AND EACH VERSION OF SETTINGS SHOULD BE ADDED MIGRATION
|
|
||||||
export const SETTING_VERSIONS = {
|
|
||||||
interface: 0,
|
|
||||||
routes: 0,
|
|
||||||
localWidget: 0,
|
|
||||||
onTheMap: 0,
|
|
||||||
kills: 0,
|
|
||||||
widgets: 0,
|
|
||||||
signatures: 0,
|
|
||||||
};
|
|
||||||
@@ -2,3 +2,4 @@ export * from './contextStore';
|
|||||||
export * from './getQueryVariable';
|
export * from './getQueryVariable';
|
||||||
export * from './loadTextFile';
|
export * from './loadTextFile';
|
||||||
export * from './saveToFile';
|
export * from './saveToFile';
|
||||||
|
export * from './omit';
|
||||||
|
|||||||
Reference in New Issue
Block a user