add pulse manipulate function in Pattern.

This commit is contained in:
Vixalie 2025-03-13 21:46:17 +08:00
parent b3ddf3710e
commit cbaf999692

View File

@ -1,8 +1,9 @@
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { atom, useSetAtom } from 'jotai'; import { atom, useAtomValue, useSetAtom } from 'jotai';
import { atomWithRefresh } from 'jotai/utils'; import { atomWithRefresh } from 'jotai/utils';
import { get, reduce } from 'lodash-es'; import { get, reduce } from 'lodash-es';
import { useCallback } from 'react';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { NotificationType, ToastDuration, useNotification } from '../components/Notifications'; import { NotificationType, ToastDuration, useNotification } from '../components/Notifications';
@ -94,22 +95,76 @@ export class Pattern {
this.smoothRepeat = true; this.smoothRepeat = true;
this.pulses = []; this.pulses = [];
} }
movePulseUp(pulseId: string, step: number) {
const index = this.pulses.findIndex((pulse) => pulse.id === pulseId);
if (index === -1 || index - step < 0) return;
const targetIndex = index - step;
const targetPulse = this.pulses[targetIndex];
const currentPulse = this.pulses[index];
// Swap the pulses
this.pulses[targetIndex] = currentPulse;
this.pulses[index] = targetPulse;
// Swap their order
const tempOrder = currentPulse.order;
currentPulse.order = targetPulse.order;
targetPulse.order = tempOrder;
// Sort pulses by order
this.pulses.sort((a, b) => a.order - b.order);
} }
export function createNewPulse(pattern: Pattern): Pulse { movePulseDown(pulseId: string, step: number) {
const maxOrder = reduce(pattern.pulses, (former, pulse) => Math.max(former, pulse.order), 0); const index = this.pulses.findIndex((pulse) => pulse.id === pulseId);
if (pattern.smoothRepeat) { if (index === -1 || index + step >= this.pulses.length) return;
return new Pulse(
const targetIndex = index + step;
const targetPulse = this.pulses[targetIndex];
const currentPulse = this.pulses[index];
// Swap the pulses
this.pulses[targetIndex] = currentPulse;
this.pulses[index] = targetPulse;
// Swap their order
const tempOrder = currentPulse.order;
currentPulse.order = targetPulse.order;
targetPulse.order = tempOrder;
// Sort pulses by order
this.pulses.sort((a, b) => a.order - b.order);
}
addPulse(): Pulse {
const maxOrder = reduce(this.pulses, (former, pulse) => Math.max(former, pulse.order), 0);
const newPulse = new Pulse(
maxOrder + 1, maxOrder + 1,
get(pattern.pulses, '[0].width', 0), this.smoothRepeat ? get(this.pulses, '[0].width', 0) : get(this.pulses, '[-1].width', 0),
get(pattern.pulses, '[0].frequency', 1), this.smoothRepeat
); ? get(this.pulses, '[0].frequency', 1)
} else { : get(this.pulses, '[-1].frequency', 1),
return new Pulse(
maxOrder + 1,
get(pattern.pulses, '[-1].width', 0),
get(pattern.pulses, '[-1].frequency', 1),
); );
this.pulses.push(newPulse);
return newPulse;
}
updatePulse(pulseId: string, pulse: Pulse) {
const index = this.pulses.findIndex((p) => p.id === pulseId);
if (index !== -1) {
const { id, order, ...rest } = pulse;
this.pulses[index] = { ...this.pulses[index], ...rest };
}
}
deletePulse(pulseId: string) {
this.pulses = this.pulses.filter((pulse) => pulse.id !== pulseId);
this.pulses.sort((a, b) => a.order - b.order);
this.pulses.forEach((pulse, index) => {
pulse.order = index + 1;
});
} }
} }
@ -133,7 +188,7 @@ export const PatternsAtom = atomWithRefresh(async (get) => {
return []; return [];
}); });
export const SelectedPatternIdAtom = atom<string | null>(null); export const SelectedPatternIdAtom = atom<string | null>(null);
export const CurrentPatternAtom = atom<Pattern | null>(async (get) => { export const CurrentPatternAtom = atomWithRefresh<Pattern | null>(async (get) => {
try { try {
const patternId = get(SelectedPatternIdAtom); const patternId = get(SelectedPatternIdAtom);
if (patternId === null) { if (patternId === null) {
@ -146,38 +201,35 @@ export const CurrentPatternAtom = atom<Pattern | null>(async (get) => {
} }
return null; return null;
}); });
export const PulsesInCurrentPatternAtom = atom( export const PulsesInCurrentPatternAtom = atomWithRefresh(
(get) => get(CurrentPatternAtom)?.pulses ?? [], (get) => get(CurrentPatternAtom)?.pulses ?? [],
(get, set, pulse: Pulse) => {
const currentPulses = get(CurrentPatternAtom)?.pulses ?? [];
const newPulses = currentPulses.map((p) => (p.id === pulse.id ? pulse : p));
if (!newPulses.some((p) => p.id === pulse.id)) {
newPulses.push(pulse);
}
newPulses.sort((a, b) => a.order - b.order);
const currentPattern = get(CurrentPatternAtom);
if (currentPattern) {
set(CurrentPatternAtom, {
...currentPattern,
pulses: newPulses,
});
}
},
); );
export const CurrentPatternDuration = atom((get) => { export const CurrentPatternDuration = atom((get) => {
const currentPattern = get(CurrentPatternAtom); const currentPattern = get(CurrentPatternAtom);
if (!currentPattern) return 0; if (!currentPattern) return 0;
return totalDuration(currentPattern); return totalDuration(currentPattern);
}); });
export const SelectedPulseIdAtom = atom<string | null>(null);
export const SelectedPulseAtom = atom<Pulse | null>((get) => {
const pulses = get(PulsesInCurrentPatternAtom);
const selectedPulseId = get(SelectedPulseIdAtom);
return pulses.find((pulse) => pulse.id === selectedPulseId) ?? null;
});
export function useSavePattern() { export function useSavePattern() {
const refreshPatterns = useSetAtom(PatternsAtom); const refreshPatterns = useSetAtom(PatternsAtom);
const selectedPatternId = useAtomValue(SelectedPatternIdAtom);
const refreshSelectedPattern = useSetAtom(CurrentPatternAtom);
const { showToast } = useNotification(); const { showToast } = useNotification();
const savePattern = async (pattern: Pattern) => { const savePattern = useCallback(
async (pattern: Pattern) => {
try { try {
await invoke('save_pattern', { pattern }); await invoke('save_pattern', { pattern });
refreshPatterns(); refreshPatterns();
if (pattern.id === selectedPatternId) {
refreshSelectedPattern();
}
return true; return true;
} catch (error) { } catch (error) {
console.error('[save pattern]', error); console.error('[save pattern]', error);
@ -189,7 +241,9 @@ export function useSavePattern() {
); );
} }
return false; return false;
}; },
[selectedPatternId],
);
return savePattern; return savePattern;
} }