Audio file metadata and thumbnails

This commit is contained in:
Robin E. R. Davies
2025-05-31 21:06:34 -04:00
parent 13e2f6b5bb
commit c4e0b0ff46
44 changed files with 3490 additions and 661 deletions
+155
View File
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2025 Robin E. R. Davies
* All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { useEffect } from 'react';
import ButtonBase, { ButtonBaseProps } from "@mui/material/ButtonBase";
export interface DraggableButtonBaseProps extends ButtonBaseProps {
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onLongPressStart?: (currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement>) => void;
onLongPressMove?: (e: React.PointerEvent<HTMLButtonElement>) => void;
onLongPressEnd?: (e: React.PointerEvent<HTMLButtonElement>) => void;
longPressDelay? : number;
}
interface Point {
x: number;
y: number;
}
function screenToClient(element: HTMLElement, point: Point): Point {
const dpr = (window.devicePixelRatio || 1) as number;;
let rect = element.getBoundingClientRect();
const cssScreenX = point.x / dpr;
const cssScreenY = point.y / dpr;
const cssWindowX = window.screenX / dpr;
const cssWindowY = window.screenY / dpr;
const clientX = cssScreenX - cssWindowX - rect.left;
const clientY = cssScreenY - cssWindowY - rect.top;
return { x: clientX, y: clientY };
}
export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
// Ensure that the props are spread correctly
const { onClick, onLongPressStart, onLongPressEnd,longPressDelay, ...rest } = props;
let [hTimeout, setHTimeout] = React.useState<number | null>(null);
let [pointerId, setPointerId] = React.useState<number | null>(null);
let [longPressed, setLongPressed] = React.useState<boolean>(false);
let [suppressClick, setSuppressClick] = React.useState<boolean>(false);
let [pointerDownPoint, setPointerDownPoint] = React.useState<Point >({x: 0, y: 0});
function cancelLongPress() {
if (hTimeout !== null) {
window.clearTimeout(hTimeout);
setHTimeout(null);
}
setPointerId(null);
setLongPressed(false);
setSuppressClick(false);
}
useEffect(() => {
return () => {
cancelLongPress();
};
}, []);
return (
<ButtonBase
{...rest}
onPointerDown={(e) => {
e.currentTarget.setPointerCapture(e.pointerId);
setPointerId(e.pointerId);
setPointerDownPoint(screenToClient(e.currentTarget,{ x: e.screenX, y: e.screenY }));
let currentTarget = e.currentTarget as HTMLButtonElement;
if (longPressDelay === undefined || longPressDelay >= 0)
{
setHTimeout(window.setTimeout(() => {
if (props.onLongPressStart) {
props.onLongPressStart(currentTarget,e);
}
setLongPressed(true);
}, longPressDelay??1250));
}
}}
onPointerMove={(e) => {
if (e.pointerId === pointerId) {
if(longPressed) {
if (props.onLongPressMove) {
props.onLongPressMove(e);
}
} else {
let clientPoint = screenToClient(e.currentTarget as HTMLElement, {x: e.screenX, y: e.screenY} );
let dx = pointerDownPoint.x- clientPoint.x;
let dy = pointerDownPoint.y - clientPoint.y;
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
cancelLongPress();
}
}
}
}}
onPointerUp={(e) => {
if (e.pointerId === pointerId) {
e.currentTarget.releasePointerCapture(e.pointerId);
setPointerId(null);
if (longPressed) {
if (props.onLongPressEnd) {
props.onLongPressEnd(e);
}
}
cancelLongPress();
if (e.isPropagationStopped()) {
setSuppressClick(true); // only way to cancel the click
return;
}
}
}}
onClick={(e) => {
if (suppressClick) {
e.stopPropagation();
e.preventDefault();
setSuppressClick(false);
} else {
if (props.onClick) {
props.onClick(e);
}
}
}}
onPointerCancelCapture={(e) => {
if (pointerId !== null) {
e.currentTarget.releasePointerCapture(e.pointerId);
if (longPressed && props.onLongPressEnd) {
props.onLongPressEnd(e)
}
cancelLongPress();
}
}}>
{props.children}
</ButtonBase>
);
}