237 lines
7.2 KiB
TypeScript
237 lines
7.2 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
DndContext,
|
|
closestCenter,
|
|
PointerSensor,
|
|
useSensor,
|
|
useSensors,
|
|
} from '@dnd-kit/core';
|
|
import {
|
|
arrayMove,
|
|
SortableContext,
|
|
verticalListSortingStrategy,
|
|
} from '@dnd-kit/sortable';
|
|
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
|
import AudioTrimmer from './AudioTrimer';
|
|
import { ClipMetadata } from '../../redux/types';
|
|
import { useAppDispatch, useAppSelector } from '../hooks';
|
|
|
|
export interface ClipListProps {
|
|
collection: string;
|
|
}
|
|
|
|
export default function ClipList({ collection }: ClipListProps) {
|
|
const metadata = useAppSelector(
|
|
(state) =>
|
|
state.collections.find((col) => col.name === collection) || { clips: [] },
|
|
);
|
|
const dispatch = useAppDispatch();
|
|
|
|
const sensors = useSensors(
|
|
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
|
);
|
|
|
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
|
event.preventDefault();
|
|
console.log('Files dropped:', event.dataTransfer.files);
|
|
const files = Array.from(event.dataTransfer.files).filter((file) =>
|
|
file.type.startsWith('audio/'),
|
|
);
|
|
if (files.length > 0) {
|
|
const formData = new FormData();
|
|
files.forEach((file) => formData.append('files', file));
|
|
|
|
// todo send the file to the backend and add to the collection
|
|
|
|
// fetch('http://localhost:5010/file/upload', {
|
|
// method: 'POST',
|
|
// body: formData,
|
|
// })
|
|
// .then((res) => res.json())
|
|
// .catch((err) => console.error('Error uploading files:', err));
|
|
// Implement your onDrop logic here
|
|
// onDrop(files, selectedCollection);
|
|
}
|
|
};
|
|
|
|
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
|
event.preventDefault();
|
|
};
|
|
|
|
async function handleDragEnd(event: any) {
|
|
const { active, over } = event;
|
|
if (active.id !== over?.id) {
|
|
const oldIndex = metadata.clips.findIndex(
|
|
(item) => item.filename === active.id,
|
|
);
|
|
const newIndex = metadata.clips.findIndex(
|
|
(item) => item.filename === over.id,
|
|
);
|
|
const newMetadata = {
|
|
...metadata,
|
|
clips: arrayMove(metadata.clips, oldIndex, newIndex),
|
|
};
|
|
console.log('New order:', newMetadata);
|
|
dispatch({
|
|
type: 'metadata/setCollections',
|
|
payload: { collection, newMetadata },
|
|
});
|
|
try {
|
|
const response = await fetch(
|
|
'http://localhost:5010/meta/collection/clips/reorder',
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
name: collection,
|
|
clips: newMetadata.clips,
|
|
}),
|
|
},
|
|
);
|
|
const data = await response.json();
|
|
console.log('handle reorder return:', data.collections);
|
|
dispatch({ type: 'metadata/setAllData', payload: data });
|
|
} catch (error) {
|
|
console.error('Error saving new clip order:', error);
|
|
}
|
|
// setMetadata(newMetadata);
|
|
}
|
|
}
|
|
|
|
async function handleDelete(meta: ClipMetadata) {
|
|
dispatch({
|
|
type: 'metadata/deleteClip',
|
|
payload: { collection, clip: meta },
|
|
});
|
|
fetch('http://localhost:5010/meta/collection/clips/remove', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
name: collection,
|
|
clip: meta,
|
|
}),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((err) => console.error('Error deleting clip:', err));
|
|
console.log('Deleting clip:', meta);
|
|
}
|
|
|
|
async function handleClipMove(targetCollection: string, meta: ClipMetadata) {
|
|
console.log('Moving clip:', meta, 'to collection:', targetCollection);
|
|
dispatch({
|
|
type: 'metadata/moveClip',
|
|
payload: { sourceCollection: collection, targetCollection, clip: meta },
|
|
});
|
|
fetch('http://localhost:5010/meta/collection/clips/move', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
sourceCollection: collection,
|
|
targetCollection,
|
|
clip: meta,
|
|
}),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((err) => console.error('Error moving clip:', err));
|
|
}
|
|
|
|
async function handleClipSave(meta: ClipMetadata) {
|
|
try {
|
|
dispatch({
|
|
type: 'metadata/editClip',
|
|
payload: { collection, clip: meta },
|
|
});
|
|
const response = await fetch(
|
|
'http://localhost:5010/meta/collection/clips/edit',
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
name: collection,
|
|
clip: meta,
|
|
}),
|
|
},
|
|
);
|
|
await response.json();
|
|
// console.log('handle clip save return:', data.collections);
|
|
dispatch({
|
|
type: 'metadata/editClip',
|
|
payload: { collection, clip: meta },
|
|
});
|
|
} catch (error) {
|
|
console.error('Error saving clip metadata:', error);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="min-h-full flex flex-col justify-start bg-midnight text-offwhite"
|
|
onDrop={handleDrop}
|
|
onDragOver={handleDragOver}
|
|
>
|
|
<DndContext
|
|
sensors={sensors}
|
|
collisionDetection={closestCenter}
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
onDragEnd={handleDragEnd}
|
|
modifiers={[restrictToVerticalAxis]}
|
|
>
|
|
<SortableContext
|
|
items={metadata.clips.map((item) => item.filename)}
|
|
strategy={verticalListSortingStrategy}
|
|
>
|
|
{metadata.clips.map((trimmer, idx) => (
|
|
<React.Fragment key={trimmer.filename}>
|
|
<AudioTrimmer
|
|
metadata={trimmer}
|
|
onSave={handleClipSave}
|
|
onDelete={handleDelete}
|
|
onMove={handleClipMove}
|
|
/>
|
|
{(idx + 1) % 10 === 0 && idx !== metadata.clips.length - 1 && (
|
|
<div className="my-4 border-t border-gray-500">
|
|
<p className="text-center text-sm text-gray-400">
|
|
-- Page {Math.ceil((idx + 1) / 10) + 1} --
|
|
</p>
|
|
</div>
|
|
)}
|
|
</React.Fragment>
|
|
))}
|
|
{/* {metadata.map((trimmer) => (
|
|
<AudioTrimmer
|
|
key={trimmer.filename}
|
|
filename={trimmer.filename}
|
|
onSave={handleClipSave}
|
|
/>
|
|
))} */}
|
|
</SortableContext>
|
|
</DndContext>
|
|
</div>
|
|
// <div className="min-h-screen flex flex-col justify-center bg-midnight text-offwhite">
|
|
// <AudioTrimmer
|
|
// title="audio_capture_20251206_123108.wav"
|
|
// filePath="C:\\Users\\mickl\\Music\\clips\\original\\audio_capture_20250118_000351.wav"
|
|
// // section="Section 1"
|
|
// />
|
|
// <AudioTrimmer
|
|
// title="audio_capture_20251206_123108.wav"
|
|
// filePath="C:\\Users\\mickl\\Music\\clips\\original\\audio_capture_20250118_000351.wav"
|
|
// // section="Section 1"
|
|
// />
|
|
// <AudioTrimmer
|
|
// title="audio_capture_20251206_123108.wav"
|
|
// filePath="C:\\Users\\mickl\\Music\\clips\\original\\audio_capture_20250118_000351.wav"
|
|
// // section="Section 1"
|
|
// />
|
|
// </div>
|
|
);
|
|
}
|