Release Healer Man 0.1.29
This commit is contained in:
1 parent
8c9809d3e0
commit
544c1bee91
120 files changed
+4363
-1036
No files matched your search
@@ -212,7 +212,7 @@ describe("local profile repository", () => {
|
||||
}));
|
||||
expect(loadCombatPresentationSettings("offline-roster", storage))
|
||||
.toEqual(DEFAULT_COMBAT_PRESENTATION_SETTINGS);
|
||||
expect(JSON.parse(storage.getItem(PROFILE_STORAGE_KEYS.database)!).version).toBe(5);
|
||||
expect(JSON.parse(storage.getItem(PROFILE_STORAGE_KEYS.database)!).version).toBe(6);
|
||||
}
|
||||
|
||||
const legacyV3Storage = new MemoryStorage();
|
||||
@@ -231,13 +231,13 @@ describe("local profile repository", () => {
|
||||
},
|
||||
}));
|
||||
expect(loadCombatPresentationSettings("offline-roster", legacyV3Storage)).toMatchObject({
|
||||
vfxMode: "generated",
|
||||
vfxMode: "original",
|
||||
vfxQuality: "low",
|
||||
});
|
||||
expect(JSON.parse(legacyV3Storage.getItem(PROFILE_STORAGE_KEYS.database)!)
|
||||
.presentationSettingsByOwner["offline-roster"].vfxMode).toBe("generated");
|
||||
.presentationSettingsByOwner["offline-roster"].vfxMode).toBe("original");
|
||||
expect(JSON.parse(legacyV3Storage.getItem(PROFILE_STORAGE_KEYS.database)!)
|
||||
.presentationSettingsByOwner["offline-roster"].originalMediaEnabled).toBe(false);
|
||||
.presentationSettingsByOwner["offline-roster"].originalMediaEnabled).toBe(true);
|
||||
|
||||
const storage = new MemoryStorage();
|
||||
const firstOwner = (await createLocalAccount("EffectsOne", "eight-plus", storage)).session!.ownerId;
|
||||
@@ -258,6 +258,37 @@ describe("local profile repository", () => {
|
||||
.toEqual(DEFAULT_COMBAT_PRESENTATION_SETTINGS);
|
||||
});
|
||||
|
||||
it("migrates the previous media default once while preserving combat controls", () => {
|
||||
const storage = new MemoryStorage();
|
||||
storage.setItem(PROFILE_STORAGE_KEYS.database, JSON.stringify({
|
||||
version: 5,
|
||||
accounts: [],
|
||||
rosters: { "offline-roster": [] },
|
||||
presentationSettingsByOwner: {
|
||||
"offline-roster": {
|
||||
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
|
||||
originalMediaEnabled: false,
|
||||
vfxMode: "generated",
|
||||
vfxQuality: "low",
|
||||
audioEnabled: { player: true, party: false, enemy: true },
|
||||
},
|
||||
},
|
||||
}));
|
||||
expect(loadCombatPresentationSettings("offline-roster", storage)).toMatchObject({
|
||||
originalMediaEnabled: true,
|
||||
vfxMode: "original",
|
||||
vfxQuality: "low",
|
||||
audioEnabled: { player: true, party: false, enemy: true },
|
||||
});
|
||||
updateCombatPresentationSettings("offline-roster", {
|
||||
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
|
||||
originalMediaEnabled: false, vfxMode: "generated",
|
||||
}, storage);
|
||||
expect(loadCombatPresentationSettings("offline-roster", storage)).toMatchObject({
|
||||
originalMediaEnabled: false, vfxMode: "generated",
|
||||
});
|
||||
});
|
||||
|
||||
it("migrates custom-class profiles into the CoA category", () => {
|
||||
const storage = new MemoryStorage();
|
||||
const offline = createOfflineSession();
|
||||
|
||||
@@ -61,7 +61,7 @@ interface LocalAccount {
|
||||
}
|
||||
|
||||
interface ProfileDatabase {
|
||||
version: 5;
|
||||
version: 6;
|
||||
accounts: LocalAccount[];
|
||||
rosters: Record<string, CharacterProfile[]>;
|
||||
presentationSettingsByOwner: Record<string, CombatPresentationSettings>;
|
||||
@@ -104,7 +104,7 @@ function browserSessionStorage(): StorageLike | null {
|
||||
}
|
||||
|
||||
function emptyDatabase(): ProfileDatabase {
|
||||
return { version: 5, accounts: [], rosters: {}, presentationSettingsByOwner: {} };
|
||||
return { version: 6, accounts: [], rosters: {}, presentationSettingsByOwner: {} };
|
||||
}
|
||||
|
||||
function isCharacterProfile(value: unknown): value is CharacterProfile {
|
||||
@@ -206,7 +206,7 @@ function readDatabase(storage: StorageLike | null = browserLocalStorage()): Prof
|
||||
presentationSettingsByOwner?: unknown;
|
||||
uiPreferencesByOwner?: unknown;
|
||||
}) | null;
|
||||
if (!parsed || ![1, 2, 3, 4, 5].includes(parsed.version ?? 0) || !Array.isArray(parsed.accounts) || !parsed.rosters || typeof parsed.rosters !== "object") {
|
||||
if (!parsed || ![1, 2, 3, 4, 5, 6].includes(parsed.version ?? 0) || !Array.isArray(parsed.accounts) || !parsed.rosters || typeof parsed.rosters !== "object") {
|
||||
return emptyDatabase();
|
||||
}
|
||||
const accounts = parsed.accounts.filter((account): account is LocalAccount => Boolean(
|
||||
@@ -232,13 +232,22 @@ function readDatabase(storage: StorageLike | null = browserLocalStorage()): Prof
|
||||
let presentationSettingsMigrated = false;
|
||||
const presentationSettingsByOwner = Object.fromEntries(
|
||||
Object.entries(rawPresentationSettings).map(([ownerId, settings]) => {
|
||||
const normalized = normalizeCombatPresentationSettings(settings);
|
||||
const storedMode = settings && typeof settings === "object" && !Array.isArray(settings)
|
||||
? (settings as { vfxMode?: unknown }).vfxMode
|
||||
: undefined;
|
||||
const storedOriginalMediaEnabled = settings && typeof settings === "object" && !Array.isArray(settings)
|
||||
? (settings as { originalMediaEnabled?: unknown }).originalMediaEnabled
|
||||
: undefined;
|
||||
// Move saves using the former generated-only default to the new default
|
||||
// once, retaining their quality, visibility, mute and volume preferences.
|
||||
const migrateMediaDefault = (parsed.version ?? 0) < 6
|
||||
&& storedMode === "generated"
|
||||
&& storedOriginalMediaEnabled !== true;
|
||||
const normalized = normalizeCombatPresentationSettings(migrateMediaDefault ? {
|
||||
...(settings as Record<string, unknown>),
|
||||
originalMediaEnabled: true,
|
||||
vfxMode: "original",
|
||||
} : settings);
|
||||
if (
|
||||
storedMode !== normalized.vfxMode
|
||||
|| storedOriginalMediaEnabled !== normalized.originalMediaEnabled
|
||||
@@ -248,8 +257,8 @@ function readDatabase(storage: StorageLike | null = browserLocalStorage()): Prof
|
||||
);
|
||||
const rawUi = parsed.uiPreferencesByOwner && typeof parsed.uiPreferencesByOwner === "object" ? parsed.uiPreferencesByOwner : {};
|
||||
const uiPreferencesByOwner = Object.fromEntries(Object.entries(rawUi).map(([owner, value]) => [owner, normalizeUiPreferences(value)]));
|
||||
const database: ProfileDatabase = { version: 5, accounts, rosters, presentationSettingsByOwner, uiPreferencesByOwner };
|
||||
if (parsed.version !== 5 || presentationSettingsMigrated) writeDatabase(database, storage);
|
||||
const database: ProfileDatabase = { version: 6, accounts, rosters, presentationSettingsByOwner, uiPreferencesByOwner };
|
||||
if (parsed.version !== 6 || presentationSettingsMigrated) writeDatabase(database, storage);
|
||||
return database;
|
||||
} catch {
|
||||
return emptyDatabase();
|
||||
|
||||
@@ -58,7 +58,7 @@ describe("RoM dual-class persistence", () => {
|
||||
"priest:none": { "primary:face-bottom": "priest-flash-heal" },
|
||||
},
|
||||
});
|
||||
expect(JSON.parse(storage.getItem(PROFILE_STORAGE_KEYS.database)!).version).toBe(5);
|
||||
expect(JSON.parse(storage.getItem(PROFILE_STORAGE_KEYS.database)!).version).toBe(6);
|
||||
});
|
||||
|
||||
it("locks selection to level ten and preserves separate bars through atomic swaps", () => {
|
||||
|
||||
@@ -13,9 +13,13 @@ describe("profile UI persistence", () => {
|
||||
it("isolates owners and persists templates without requiring a character", () => {
|
||||
const local = storage(); const ui = normalizeUiPreferences(null);
|
||||
ui.slots.pc[1] = { name: "Healer", layout: ui.current.pc };
|
||||
ui.keybinds.pc["move-forward"] = "ArrowUp";
|
||||
ui.keybinds.controller.jump = "Button4&Button3";
|
||||
saveUiPreferences("one", ui, local);
|
||||
expect(loadUiPreferences("one", local).slots.pc[1]?.name).toBe("Healer");
|
||||
expect(loadUiPreferences("two", local).slots.pc[1]).toBeNull();
|
||||
expect(loadUiPreferences("one", local).keybinds).toEqual(ui.keybinds);
|
||||
expect(loadUiPreferences("two", local).keybinds.pc["move-forward"]).toBe("KeyW");
|
||||
ui.slots.pc[1].name = "Unsaved change";
|
||||
expect(loadUiPreferences("one", local).slots.pc[1]?.name).toBe("Healer");
|
||||
});
|
||||
@@ -34,6 +38,9 @@ describe("profile UI persistence", () => {
|
||||
const ui = normalizeUiPreferences(null);
|
||||
ui.slots.pc[2] = { name: "Desktop raid", layout: ui.current.pc };
|
||||
ui.slots.mobile[1] = { name: "On the go", layout: ui.current.mobile };
|
||||
ui.keybinds.pc["move-forward"] = "ArrowUp";
|
||||
ui.keybinds.pc["look-up"] = null;
|
||||
ui.keybinds.controller.jump = "Button4&Button3";
|
||||
saveUiPreferences(session.ownerId, ui);
|
||||
await vi.advanceTimersByTimeAsync(501);
|
||||
expect(client.useCloudSaveStatus.getState().state).toBe("synced");
|
||||
|
||||
Reference in new issue
Block a user