refactor butterchurn state to prevent rerender flicker on cycle

This commit is contained in:
jeffvli
2026-01-20 17:58:58 -08:00
parent a5940a9124
commit 9318d00a5b
2 changed files with 181 additions and 152 deletions
+120 -89
View File
@@ -3,7 +3,7 @@ import mergeWith from 'lodash/mergeWith';
import { nanoid } from 'nanoid';
import { generatePath } from 'react-router';
import { z } from 'zod';
import { devtools, persist } from 'zustand/middleware';
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { shallow } from 'zustand/shallow';
import { createWithEqualityFn } from 'zustand/traditional';
@@ -1631,98 +1631,102 @@ const initialState: SettingsState = {
export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
persist(
devtools(
immer((set) => ({
actions: {
reset: () => {
localStorage.removeItem('store_settings');
window.location.reload();
},
resetSampleRate: () => {
set((state) => {
state.playback.mpvProperties.audioSampleRateHz = 0;
});
},
setArtistItems: (items) => {
set((state) => {
state.general.artistItems = items;
});
},
setArtistReleaseTypeItems: (items: SortableItem<ArtistReleaseTypeItem>[]) => {
set((state) => {
state.general.artistReleaseTypeItems = items;
});
},
setGenreBehavior: (target: GenreTarget) => {
set((state) => {
state.general.genreTarget = target;
});
},
setHomeItems: (items: SortableItem<HomeItem>[]) => {
set((state) => {
state.general.homeItems = items;
});
},
setList: (type: ItemListKey, data: DeepPartial<ItemListSettings>) => {
set((state) => {
const listState = state.lists[type];
subscribeWithSelector(
immer((set) => ({
actions: {
reset: () => {
localStorage.removeItem('store_settings');
window.location.reload();
},
resetSampleRate: () => {
set((state) => {
state.playback.mpvProperties.audioSampleRateHz = 0;
});
},
setArtistItems: (items) => {
set((state) => {
state.general.artistItems = items;
});
},
setArtistReleaseTypeItems: (
items: SortableItem<ArtistReleaseTypeItem>[],
) => {
set((state) => {
state.general.artistReleaseTypeItems = items;
});
},
setGenreBehavior: (target: GenreTarget) => {
set((state) => {
state.general.genreTarget = target;
});
},
setHomeItems: (items: SortableItem<HomeItem>[]) => {
set((state) => {
state.general.homeItems = items;
});
},
setList: (type: ItemListKey, data: DeepPartial<ItemListSettings>) => {
set((state) => {
const listState = state.lists[type];
if (listState && data.table) {
Object.assign(listState.table, data.table);
delete data.table;
}
if (listState && data.table) {
Object.assign(listState.table, data.table);
delete data.table;
}
if (listState && data.grid) {
Object.assign(listState.grid, data.grid);
delete data.grid;
}
if (listState && data.grid) {
Object.assign(listState.grid, data.grid);
delete data.grid;
}
if (listState) {
Object.assign(listState, data);
}
});
if (listState) {
Object.assign(listState, data);
}
});
},
setPlaybackFilters: (filters: PlayerFilter[]) => {
set((state) => {
state.playback.filters = filters;
});
},
setSettings: (data) => {
set((state) => {
deepMergeIntoState(state, data);
});
},
setSidebarItems: (items: SidebarItemType[]) => {
set((state) => {
state.general.sidebarItems = items;
});
},
setTable: (type: ItemListKey, data: DataTableProps) => {
set((state) => {
const listState = state.lists[type];
if (listState) {
listState.table = data;
}
});
},
setTranscodingConfig: (config) => {
set((state) => {
state.playback.transcode = config;
});
},
toggleMediaSession: () => {
set((state) => {
state.playback.mediaSession = !state.playback.mediaSession;
});
},
toggleSidebarCollapseShare: () => {
set((state) => {
state.general.sidebarCollapseShared =
!state.general.sidebarCollapseShared;
});
},
},
setPlaybackFilters: (filters: PlayerFilter[]) => {
set((state) => {
state.playback.filters = filters;
});
},
setSettings: (data) => {
set((state) => {
deepMergeIntoState(state, data);
});
},
setSidebarItems: (items: SidebarItemType[]) => {
set((state) => {
state.general.sidebarItems = items;
});
},
setTable: (type: ItemListKey, data: DataTableProps) => {
set((state) => {
const listState = state.lists[type];
if (listState) {
listState.table = data;
}
});
},
setTranscodingConfig: (config) => {
set((state) => {
state.playback.transcode = config;
});
},
toggleMediaSession: () => {
set((state) => {
state.playback.mediaSession = !state.playback.mediaSession;
});
},
toggleSidebarCollapseShare: () => {
set((state) => {
state.general.sidebarCollapseShared =
!state.general.sidebarCollapseShared;
});
},
},
...initialState,
})),
...initialState,
})),
),
{ name: 'store_settings' },
),
{
@@ -2175,3 +2179,30 @@ export const useShowVisualizerInSidebar = () =>
export const useAutoDJSettings = () => useSettingsStore((store) => store.autoDJ, shallow);
export const useVisualizerSettings = () => useSettingsStore((store) => store.visualizer, shallow);
export const subscribeButterchurnPreset = (
onChange: (preset: string | undefined, prevPreset: string | undefined) => void,
) => {
return useSettingsStore.subscribe(
(state) => state.visualizer.butterchurn.currentPreset,
(preset, prevPreset) => {
onChange(preset, prevPreset);
},
);
};
export const useButterchurnSettings = () => {
return useSettingsStore((store) => {
return {
blendTime: store.visualizer.butterchurn.blendTime,
cyclePresets: store.visualizer.butterchurn.cyclePresets,
cycleTime: store.visualizer.butterchurn.cycleTime,
ignoredPresets: store.visualizer.butterchurn.ignoredPresets,
includeAllPresets: store.visualizer.butterchurn.includeAllPresets,
maxFPS: store.visualizer.butterchurn.maxFPS,
opacity: store.visualizer.butterchurn.opacity,
randomizeNextPreset: store.visualizer.butterchurn.randomizeNextPreset,
selectedPresets: store.visualizer.butterchurn.selectedPresets,
};
}, shallow);
};