add pulse manipulate function in Pattern.
This commit is contained in:
parent
b3ddf3710e
commit
cbaf999692
|
@ -1,8 +1,9 @@
|
|||
import { invoke } from '@tauri-apps/api/core';
|
||||
import dayjs from 'dayjs';
|
||||
import { atom, useSetAtom } from 'jotai';
|
||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
||||
import { atomWithRefresh } from 'jotai/utils';
|
||||
import { get, reduce } from 'lodash-es';
|
||||
import { useCallback } from 'react';
|
||||
import { v4 } from 'uuid';
|
||||
import { NotificationType, ToastDuration, useNotification } from '../components/Notifications';
|
||||
|
||||
|
@ -94,22 +95,76 @@ export class Pattern {
|
|||
this.smoothRepeat = true;
|
||||
this.pulses = [];
|
||||
}
|
||||
}
|
||||
|
||||
export function createNewPulse(pattern: Pattern): Pulse {
|
||||
const maxOrder = reduce(pattern.pulses, (former, pulse) => Math.max(former, pulse.order), 0);
|
||||
if (pattern.smoothRepeat) {
|
||||
return new Pulse(
|
||||
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);
|
||||
}
|
||||
|
||||
movePulseDown(pulseId: string, step: number) {
|
||||
const index = this.pulses.findIndex((pulse) => pulse.id === pulseId);
|
||||
if (index === -1 || index + step >= this.pulses.length) 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);
|
||||
}
|
||||
|
||||
addPulse(): Pulse {
|
||||
const maxOrder = reduce(this.pulses, (former, pulse) => Math.max(former, pulse.order), 0);
|
||||
const newPulse = new Pulse(
|
||||
maxOrder + 1,
|
||||
get(pattern.pulses, '[0].width', 0),
|
||||
get(pattern.pulses, '[0].frequency', 1),
|
||||
);
|
||||
} else {
|
||||
return new Pulse(
|
||||
maxOrder + 1,
|
||||
get(pattern.pulses, '[-1].width', 0),
|
||||
get(pattern.pulses, '[-1].frequency', 1),
|
||||
this.smoothRepeat ? get(this.pulses, '[0].width', 0) : get(this.pulses, '[-1].width', 0),
|
||||
this.smoothRepeat
|
||||
? get(this.pulses, '[0].frequency', 1)
|
||||
: get(this.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 [];
|
||||
});
|
||||
export const SelectedPatternIdAtom = atom<string | null>(null);
|
||||
export const CurrentPatternAtom = atom<Pattern | null>(async (get) => {
|
||||
export const CurrentPatternAtom = atomWithRefresh<Pattern | null>(async (get) => {
|
||||
try {
|
||||
const patternId = get(SelectedPatternIdAtom);
|
||||
if (patternId === null) {
|
||||
|
@ -146,50 +201,49 @@ export const CurrentPatternAtom = atom<Pattern | null>(async (get) => {
|
|||
}
|
||||
return null;
|
||||
});
|
||||
export const PulsesInCurrentPatternAtom = atom(
|
||||
export const PulsesInCurrentPatternAtom = atomWithRefresh(
|
||||
(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) => {
|
||||
const currentPattern = get(CurrentPatternAtom);
|
||||
if (!currentPattern) return 0;
|
||||
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() {
|
||||
const refreshPatterns = useSetAtom(PatternsAtom);
|
||||
const selectedPatternId = useAtomValue(SelectedPatternIdAtom);
|
||||
const refreshSelectedPattern = useSetAtom(CurrentPatternAtom);
|
||||
const { showToast } = useNotification();
|
||||
|
||||
const savePattern = async (pattern: Pattern) => {
|
||||
try {
|
||||
await invoke('save_pattern', { pattern });
|
||||
refreshPatterns();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[save pattern]', error);
|
||||
showToast(
|
||||
NotificationType.ERROR,
|
||||
'Failed to save pattern. Please try again.',
|
||||
'material-symbols-light:error-outline',
|
||||
ToastDuration.MEDIUM,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const savePattern = useCallback(
|
||||
async (pattern: Pattern) => {
|
||||
try {
|
||||
await invoke('save_pattern', { pattern });
|
||||
refreshPatterns();
|
||||
if (pattern.id === selectedPatternId) {
|
||||
refreshSelectedPattern();
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[save pattern]', error);
|
||||
showToast(
|
||||
NotificationType.ERROR,
|
||||
'Failed to save pattern. Please try again.',
|
||||
'material-symbols-light:error-outline',
|
||||
ToastDuration.MEDIUM,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[selectedPatternId],
|
||||
);
|
||||
|
||||
return savePattern;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user