version>=2008 使用了新的重命名接口、EQ 排序功能
This commit is contained in:
+61
-26
@@ -30,12 +30,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";
|
||||
@@ -113,6 +115,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;
|
||||
@@ -520,19 +528,21 @@ export default function EQPage() {
|
||||
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
|
||||
resolvedPeqSelect
|
||||
);
|
||||
syncPeqCatalogFromState(remote, deviceState?.peqSelect);
|
||||
syncPeqCatalogFromState(remote, resolvedPeqSelect);
|
||||
|
||||
const items = remote.peq ?? [];
|
||||
if (items.length === 0) {
|
||||
@@ -542,7 +552,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(
|
||||
@@ -901,10 +911,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 组对比
|
||||
@@ -1968,6 +1979,7 @@ export default function EQPage() {
|
||||
open={isPresetManageDialogOpen}
|
||||
items={peqItems}
|
||||
selectedIdx={headphoneIdx}
|
||||
reorderEnabled={peqReorderEnabled}
|
||||
onClose={() => setIsPresetManageDialogOpen(false)}
|
||||
eqUi={eqUi}
|
||||
onDeletePresets={async (names) => {
|
||||
@@ -1995,9 +2007,25 @@ export default function EQPage() {
|
||||
}}
|
||||
onRenamePreset={async (oldName, newName, item) => {
|
||||
try {
|
||||
// normalizePeqFiltersForSubmit returns filters with string type values
|
||||
// (e.g. "LOW_SHELF", "PEAKING") — but the device expects numeric type.
|
||||
// Must convert with getFilterType() before submitting to info.cgi.
|
||||
if (isDemoMode || !api) {
|
||||
const nextItems = peqItems.map(it =>
|
||||
it.name === oldName ? { ...it, name: newName } : it
|
||||
);
|
||||
forgetPeqPresetCache(oldName);
|
||||
applyPeqStateToUI({ peq: nextItems, peqSelect: headphoneIdx });
|
||||
toast.success(eqUi.toastRenamed);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (supportsPeqPresetRenameAndSort(firmwareVersion)) {
|
||||
await api.renamePeq(oldName, newName);
|
||||
forgetPeqPresetCache(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,
|
||||
@@ -2017,28 +2045,12 @@ export default function EQPage() {
|
||||
},
|
||||
};
|
||||
|
||||
if (isDemoMode || !api) {
|
||||
const nextItems = peqItems.map(it =>
|
||||
it.name === oldName ? { ...it, name: newName } : it
|
||||
);
|
||||
forgetPeqPresetCache(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.
|
||||
await api.upgradePeqChange(payload);
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
// Step 2: peqRemove — delete the old preset
|
||||
forgetPeqPresetCache(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);
|
||||
return true;
|
||||
@@ -2047,6 +2059,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;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<BrandDrawer
|
||||
|
||||
Reference in New Issue
Block a user