version>=29 使用了新的重命名接口、EQ 排序功能
This commit is contained in:
+63
-21
@@ -21,12 +21,14 @@ import { toast } from "sonner";
|
||||
import {
|
||||
fetchLuxsinAudioCurve,
|
||||
normalizePeqFiltersForSubmit,
|
||||
parseFirmwareVersion,
|
||||
type PeqFilter,
|
||||
type PeqApplyPayload,
|
||||
type PeqChangePayload,
|
||||
type PeqState,
|
||||
buildPeqPresetBody,
|
||||
} from "@/lib/luxsinApi";
|
||||
import { supportsPeqPresetRenameAndSort } from "@/config/firmwareFeatures";
|
||||
import { getFilterType, getFilterShortName } from "@/lib/peqAudio";
|
||||
import localeZh from "@/locales/data-zh.json";
|
||||
import localeEn from "@/locales/data-en.json";
|
||||
@@ -101,6 +103,12 @@ export default function EQPage() {
|
||||
return (peq.eqUi ?? (localeEn.peq as typeof localeEn.peq).eqUi) as PeqEqUi;
|
||||
}, [deviceState?.language]);
|
||||
|
||||
const firmwareVersion = useMemo(
|
||||
() => parseFirmwareVersion(deviceState?.version),
|
||||
[deviceState?.version],
|
||||
);
|
||||
const peqReorderEnabled = supportsPeqPresetRenameAndSort(firmwareVersion);
|
||||
|
||||
const peqCardLabels = useMemo(() => {
|
||||
const pack = resolveLocalePack(deviceState?.language);
|
||||
return pack.peq ?? localeEn.peq;
|
||||
@@ -526,15 +534,18 @@ export default function EQPage() {
|
||||
const applyPeqStateToUI = (
|
||||
remote: { peq?: Array<{ name: string; filters?: any[] | string }>; peqSelect?: number; filters?: PeqFilter[] },
|
||||
) => {
|
||||
const resolvedPeqSelect =
|
||||
remote.peqSelect ?? deviceState?.peqSelect ?? peqState?.peqSelect ?? 0;
|
||||
|
||||
if (remote.peq !== undefined) {
|
||||
syncPeqCatalog({
|
||||
filters: remote.filters ?? peqState?.filters ?? [],
|
||||
peq: remote.peq,
|
||||
peqSelect: remote.peqSelect ?? deviceState?.peqSelect ?? peqState?.peqSelect,
|
||||
peqSelect: resolvedPeqSelect,
|
||||
});
|
||||
}
|
||||
lastPeqCatalogSyncKeyRef.current = buildPeqCatalogSyncKey(remote, deviceState?.peqSelect);
|
||||
syncPeqCatalogFromState(remote, deviceState?.peqSelect);
|
||||
lastPeqCatalogSyncKeyRef.current = buildPeqCatalogSyncKey(remote, resolvedPeqSelect);
|
||||
syncPeqCatalogFromState(remote, resolvedPeqSelect);
|
||||
|
||||
const items = mergeRemotePeqCatalog((remote.peq ?? []) as PeqCatalogItem[]);
|
||||
if (items.length === 0) {
|
||||
@@ -544,7 +555,7 @@ export default function EQPage() {
|
||||
}
|
||||
|
||||
const nextIdx = Math.min(
|
||||
Math.max(deviceState?.peqSelect ?? remote.peqSelect ?? 0, 0),
|
||||
Math.max(resolvedPeqSelect, 0),
|
||||
items.length - 1,
|
||||
);
|
||||
const nextBands = normalizeFiltersFromPeq(items[nextIdx] as { filters?: any[] | string });
|
||||
@@ -866,10 +877,11 @@ export default function EQPage() {
|
||||
// msgCount 轮询更新 peqState 后,同步耳机列表与当前选中项
|
||||
useEffect(() => {
|
||||
if (!peqState?.peq) return;
|
||||
const key = buildPeqCatalogSyncKey(peqState, deviceState?.peqSelect);
|
||||
const resolvedPeqSelect = peqState.peqSelect ?? deviceState?.peqSelect;
|
||||
const key = buildPeqCatalogSyncKey(peqState, resolvedPeqSelect);
|
||||
if (!key || key === lastPeqCatalogSyncKeyRef.current) return;
|
||||
lastPeqCatalogSyncKeyRef.current = key;
|
||||
syncPeqCatalogFromState(peqState, deviceState?.peqSelect);
|
||||
syncPeqCatalogFromState(peqState, resolvedPeqSelect);
|
||||
}, [peqState, deviceState?.peqSelect, syncPeqCatalogFromState]);
|
||||
|
||||
// 切换耳机时默认回到 A 组对比
|
||||
@@ -1872,6 +1884,7 @@ export default function EQPage() {
|
||||
open={isPresetManageDialogOpen}
|
||||
items={peqItems}
|
||||
selectedIdx={headphoneIdx}
|
||||
reorderEnabled={peqReorderEnabled}
|
||||
onClose={() => setIsPresetManageDialogOpen(false)}
|
||||
eqUi={eqUi}
|
||||
onDeletePresets={async (names) => {
|
||||
@@ -1896,6 +1909,26 @@ export default function EQPage() {
|
||||
}}
|
||||
onRenamePreset={async (oldName, newName, item) => {
|
||||
try {
|
||||
if (isDemoMode || !api) {
|
||||
const nextItems = peqItems.map(it =>
|
||||
it.name === oldName ? { ...it, name: newName } : it
|
||||
);
|
||||
delete peqPresetCacheRef.current[oldName];
|
||||
applyPeqStateToUI({ peq: nextItems, peqSelect: headphoneIdx });
|
||||
toast.success(eqUi.toastRenamed);
|
||||
return true;
|
||||
}
|
||||
|
||||
const firmwareVersion = parseFirmwareVersion(deviceState?.version);
|
||||
if (supportsPeqPresetRenameAndSort(firmwareVersion)) {
|
||||
await api.renamePeq(oldName, newName);
|
||||
delete peqPresetCacheRef.current[oldName];
|
||||
const latest = await fetchEqSyncPeq(api, "renamePreset");
|
||||
applyPeqStateToUI(latest);
|
||||
toast.success(eqUi.toastRenamed);
|
||||
return true;
|
||||
}
|
||||
|
||||
const rawFilters = normalizePeqFiltersForSubmit(item.filters);
|
||||
const filters = rawFilters.map(f => ({
|
||||
...f,
|
||||
@@ -1915,26 +1948,12 @@ export default function EQPage() {
|
||||
},
|
||||
};
|
||||
|
||||
if (isDemoMode || !api) {
|
||||
const nextItems = peqItems.map(it =>
|
||||
it.name === oldName ? { ...it, name: newName } : it
|
||||
);
|
||||
delete peqPresetCacheRef.current[oldName];
|
||||
applyPeqStateToUI({ peq: nextItems, peqSelect: headphoneIdx });
|
||||
toast.success(eqUi.toastRenamed);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 1: peqChange — create new preset with new name + same EQ data
|
||||
// Use api.upgradePeqChange directly (NOT the DeviceContext wrapper)
|
||||
// to avoid applyPeqFiltersToState side-effect that overwrites current UI bands.
|
||||
// Legacy (< 29): peqChange then peqRemove
|
||||
await api.upgradePeqChange(payload);
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
// Step 2: peqRemove — delete the old preset
|
||||
delete peqPresetCacheRef.current[oldName];
|
||||
await api.removePeq([oldName]);
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
// Step 3: sync latest state from device
|
||||
const latest = await fetchEqSyncPeq(api, "renamePreset");
|
||||
applyPeqStateToUI(latest);
|
||||
toast.success(eqUi.toastRenamed);
|
||||
@@ -1944,6 +1963,29 @@ export default function EQPage() {
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
onConfirmOrder={async (names, peqSelect) => {
|
||||
try {
|
||||
if (isDemoMode || !api) {
|
||||
const byName = new Map(peqItems.map(it => [it.name, it]));
|
||||
const nextItems = names
|
||||
.map(name => byName.get(name))
|
||||
.filter((it): it is NonNullable<typeof it> => !!it);
|
||||
applyPeqStateToUI({ peq: nextItems, peqSelect });
|
||||
toast.success(eqUi.toastOrderSaved);
|
||||
return true;
|
||||
}
|
||||
await api.sortPeq(names, peqSelect);
|
||||
const latest = await fetchEqSyncPeq(api, "sortPresets");
|
||||
const nextSelect = latest.peqSelect ?? peqSelect;
|
||||
applyPeqStateToUI({ ...latest, peqSelect: nextSelect });
|
||||
updateSetting({ peqSelect: nextSelect });
|
||||
toast.success(eqUi.toastOrderSaved);
|
||||
return true;
|
||||
} catch {
|
||||
toast.error(eqUi.toastOrderFail);
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<ShareDialog
|
||||
|
||||
Reference in New Issue
Block a user