File dialog multiselect
This commit is contained in:
@@ -122,11 +122,11 @@ void json_writer::write(string_view v,bool enforceValidUtf8Encoding)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((uc >= UTF16_SURROGATE_1_BASE && uc <= UTF16_SURROGATE_1_BASE + UTF16_SURROGATE_MASK) || (uc >= UTF16_SURROGATE_2_BASE && uc <= UTF16_SURROGATE_2_BASE + UTF16_SURROGATE_MASK))
|
// if ((uc >= UTF16_SURROGATE_1_BASE && uc <= UTF16_SURROGATE_1_BASE + UTF16_SURROGATE_MASK) || (uc >= UTF16_SURROGATE_2_BASE && uc <= UTF16_SURROGATE_2_BASE + UTF16_SURROGATE_MASK))
|
||||||
{
|
// {
|
||||||
// MUST not encode UTF16 surrogates in UTF8.
|
// // MUST not encode UTF16 surrogates in UTF8.
|
||||||
throw_encoding_error();
|
// throw_encoding_error();
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (uc == '"' || uc == '\\')
|
if (uc == '"' || uc == '\\')
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import ButtonBase, { ButtonBaseProps } from "@mui/material/ButtonBase";
|
|||||||
|
|
||||||
export interface DraggableButtonBaseProps extends ButtonBaseProps {
|
export interface DraggableButtonBaseProps extends ButtonBaseProps {
|
||||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
onLongPressStart?: (currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement>) => void;
|
onLongPressStart?: (currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement>) => boolean;
|
||||||
onLongPressMove?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
onLongPressMove?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
||||||
onLongPressEnd?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
onLongPressEnd?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
||||||
longPressDelay? : number;
|
longPressDelay? : number;
|
||||||
@@ -69,9 +69,34 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
let [hTimeout, setHTimeout] = React.useState<number | null>(null);
|
let [hTimeout, setHTimeout] = React.useState<number | null>(null);
|
||||||
let [pointerId, setPointerId] = React.useState<number | null>(null);
|
let [pointerId, setPointerId] = React.useState<number | null>(null);
|
||||||
let [longPressed, setLongPressed] = React.useState<boolean>(false);
|
let [longPressed, setLongPressed] = React.useState<boolean>(false);
|
||||||
let [suppressClick, setSuppressClick] = React.useState<boolean>(false);
|
let [clickSuppressed, setClickSuppressed] = React.useState<boolean>(false);
|
||||||
let [pointerDownPoint, setPointerDownPoint] = React.useState<Point >({x: 0, y: 0});
|
let [pointerDownPoint, setPointerDownPoint] = React.useState<Point >({x: 0, y: 0});
|
||||||
|
|
||||||
|
|
||||||
|
function handleSuppressClick(e: MouseEvent) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
setSuppressClick(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSuppressClick(value: boolean) {
|
||||||
|
|
||||||
|
if (value !== clickSuppressed) {
|
||||||
|
setClickSuppressed(value);
|
||||||
|
if (value) {
|
||||||
|
window.addEventListener(
|
||||||
|
"click",
|
||||||
|
handleSuppressClick,
|
||||||
|
{
|
||||||
|
once: true,
|
||||||
|
capture: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.removeEventListener("click", handleSuppressClick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function cancelLongPress() {
|
function cancelLongPress() {
|
||||||
if (hTimeout !== null) {
|
if (hTimeout !== null) {
|
||||||
window.clearTimeout(hTimeout);
|
window.clearTimeout(hTimeout);
|
||||||
@@ -82,8 +107,10 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
setSuppressClick(false);
|
setSuppressClick(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
setSuppressClick(false);
|
||||||
cancelLongPress();
|
cancelLongPress();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
@@ -106,10 +133,15 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
if (longPressDelay === undefined || longPressDelay >= 0)
|
if (longPressDelay === undefined || longPressDelay >= 0)
|
||||||
{
|
{
|
||||||
setHTimeout(window.setTimeout(() => {
|
setHTimeout(window.setTimeout(() => {
|
||||||
|
setHTimeout(null);
|
||||||
if (props.onLongPressStart) {
|
if (props.onLongPressStart) {
|
||||||
props.onLongPressStart(currentTarget,e);
|
if (!props.onLongPressStart(currentTarget,e))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setLongPressed(true);
|
setLongPressed(true);
|
||||||
|
setSuppressClick(true);
|
||||||
}, longPressDelay??1250));
|
}, longPressDelay??1250));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -147,7 +179,7 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (suppressClick) {
|
if (clickSuppressed) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSuppressClick(false);
|
setSuppressClick(false);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ import UploadFileDialog from './UploadFileDialog';
|
|||||||
import OkCancelDialog from './OkCancelDialog';
|
import OkCancelDialog from './OkCancelDialog';
|
||||||
import HomeIcon from '@mui/icons-material/Home';
|
import HomeIcon from '@mui/icons-material/Home';
|
||||||
import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDialog';
|
import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDialog';
|
||||||
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
||||||
|
|
||||||
interface Point {
|
interface Point {
|
||||||
x: number;
|
x: number;
|
||||||
@@ -156,11 +156,15 @@ export interface FilePropertyDialogState {
|
|||||||
newFilePath: string;
|
newFilePath: string;
|
||||||
} | null;
|
} | null;
|
||||||
menuAnchorEl: null | HTMLElement;
|
menuAnchorEl: null | HTMLElement;
|
||||||
|
menuMultiselectAnchorEl: null | HTMLElement;
|
||||||
newFolderDialogOpen: boolean;
|
newFolderDialogOpen: boolean;
|
||||||
renameDialogOpen: boolean;
|
renameDialogOpen: boolean;
|
||||||
moveDialogOpen: boolean;
|
moveDialogOpen: boolean;
|
||||||
copyDialogOpen: boolean;
|
copyDialogOpen: boolean;
|
||||||
initialSelection: string;
|
initialSelection: string;
|
||||||
|
multiSelect: boolean,
|
||||||
|
selectedFiles: string[],
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DragState {
|
class DragState {
|
||||||
@@ -220,11 +224,14 @@ export default withStyles(
|
|||||||
openConfirmDeleteDialog: false,
|
openConfirmDeleteDialog: false,
|
||||||
confirmCopyDialogState: null,
|
confirmCopyDialogState: null,
|
||||||
menuAnchorEl: null,
|
menuAnchorEl: null,
|
||||||
|
menuMultiselectAnchorEl: null,
|
||||||
newFolderDialogOpen: false,
|
newFolderDialogOpen: false,
|
||||||
renameDialogOpen: false,
|
renameDialogOpen: false,
|
||||||
moveDialogOpen: false,
|
moveDialogOpen: false,
|
||||||
copyDialogOpen: false,
|
copyDialogOpen: false,
|
||||||
initialSelection: this.props.selectedFile
|
initialSelection: this.props.selectedFile,
|
||||||
|
multiSelect: false,
|
||||||
|
selectedFiles: []
|
||||||
};
|
};
|
||||||
this.requestScroll = true;
|
this.requestScroll = true;
|
||||||
}
|
}
|
||||||
@@ -325,58 +332,86 @@ export default withStyles(
|
|||||||
dragToPosition: number = -1;
|
dragToPosition: number = -1;
|
||||||
maxPosition: number = 0;
|
maxPosition: number = 0;
|
||||||
|
|
||||||
handleLongPressStart(currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement>) {
|
handleMultisSelectClose() {
|
||||||
if (!this.isTracksDirectory()) {
|
this.setState({
|
||||||
return;
|
multiSelect: false, selectedFiles: [], menuMultiselectAnchorEl: null
|
||||||
}
|
});
|
||||||
this.dragFromPosition = parseInt(currentTarget.getAttribute("data-position") as string, 10);
|
}
|
||||||
this.dragToPosition = this.dragToPosition;
|
|
||||||
let element = currentTarget;
|
|
||||||
element.style.position = "relative";
|
handleLongPressStart(
|
||||||
element.style.zIndex = "1000";
|
currentTarget: HTMLButtonElement,
|
||||||
element.style.top = "5px";
|
e: React.PointerEvent<HTMLButtonElement>,
|
||||||
element.style.left = "5px";
|
fileEntry: FileEntry): boolean {
|
||||||
element.style.background = isDarkMode() ? "#555" : "#EEF" // xxx: dark mode.
|
if (this.state.reordering) {
|
||||||
if (this.lastDivRef) {
|
|
||||||
this.longPressStartPoint = screenToClient(this.lastDivRef, { x: e.screenX, y: e.screenY });
|
if (!this.isTracksDirectory()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.dragFromPosition = parseInt(currentTarget.getAttribute("data-position") as string, 10);
|
||||||
|
this.dragToPosition = this.dragToPosition;
|
||||||
|
let element = currentTarget;
|
||||||
|
element.style.position = "relative";
|
||||||
|
element.style.zIndex = "1000";
|
||||||
|
element.style.top = "5px";
|
||||||
|
element.style.left = "5px";
|
||||||
|
element.style.background = isDarkMode() ? "#555" : "#EEF" // xxx: dark mode.
|
||||||
|
if (this.lastDivRef) {
|
||||||
|
this.longPressStartPoint = screenToClient(this.lastDivRef, { x: e.screenX, y: e.screenY });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (!this.state.multiSelect) {
|
||||||
|
if (fileEntry.isProtected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
multiSelect: true,
|
||||||
|
selectedFiles: [fileEntry.pathname],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
handleLongPressMove(e: React.PointerEvent<HTMLButtonElement>) {
|
handleLongPressMove(e: React.PointerEvent<HTMLButtonElement>) {
|
||||||
if (!this.isTracksDirectory()) {
|
if (this.state.reordering) {
|
||||||
return;
|
if (!this.isTracksDirectory()) {
|
||||||
}
|
return;
|
||||||
if (this.lastDivRef) {
|
}
|
||||||
let element = e.target as HTMLButtonElement;
|
if (this.lastDivRef) {
|
||||||
let point = screenToClient(this.lastDivRef, { x: e.screenX, y: e.screenY });
|
let element = e.target as HTMLButtonElement;
|
||||||
if (this.longPressStartPoint) {
|
let point = screenToClient(this.lastDivRef, { x: e.screenX, y: e.screenY });
|
||||||
let dy = point.y - this.longPressStartPoint.y;
|
if (this.longPressStartPoint) {
|
||||||
element.style.left = (5) + "px";
|
let dy = point.y - this.longPressStartPoint.y;
|
||||||
element.style.top = (5 + dy) + "px";
|
element.style.left = (5) + "px";
|
||||||
let height = element.clientHeight;
|
element.style.top = (5 + dy) + "px";
|
||||||
let positionChange_: number = 0;
|
let height = element.clientHeight;
|
||||||
if (dy > 0) {
|
let positionChange_: number = 0;
|
||||||
positionChange_ = Math.floor((dy + height / 2) / height);
|
if (dy > 0) {
|
||||||
} else {
|
positionChange_ = Math.floor((dy + height / 2) / height);
|
||||||
positionChange_ = Math.ceil((dy - height / 2) / height)
|
} else {
|
||||||
}
|
positionChange_ = Math.ceil((dy - height / 2) / height)
|
||||||
let toPosition = this.dragFromPosition + positionChange_;
|
}
|
||||||
if (toPosition < 0) {
|
let toPosition = this.dragFromPosition + positionChange_;
|
||||||
toPosition = 0;
|
if (toPosition < 0) {
|
||||||
}
|
toPosition = 0;
|
||||||
if (toPosition > this.maxPosition) {
|
}
|
||||||
toPosition = this.maxPosition;
|
if (toPosition > this.maxPosition) {
|
||||||
}
|
toPosition = this.maxPosition;
|
||||||
if (toPosition !== this.dragToPosition) {
|
}
|
||||||
this.dragToPosition = toPosition;
|
if (toPosition !== this.dragToPosition) {
|
||||||
this.setState({
|
this.dragToPosition = toPosition;
|
||||||
dragState: {
|
this.setState({
|
||||||
activeItem: element.getAttribute("data-pathname") || "",
|
dragState: {
|
||||||
height: height,
|
activeItem: element.getAttribute("data-pathname") || "",
|
||||||
from: this.dragFromPosition,
|
height: height,
|
||||||
to: toPosition
|
from: this.dragFromPosition,
|
||||||
}
|
to: toPosition
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,27 +462,40 @@ export default withStyles(
|
|||||||
this.model.moveAudioFile(
|
this.model.moveAudioFile(
|
||||||
this.state.currentDirectory, from, to);
|
this.state.currentDirectory, from, to);
|
||||||
}
|
}
|
||||||
handleLongPressEnd(e: React.PointerEvent<HTMLButtonElement>) {
|
handleLongPressEnd(e: React.PointerEvent<HTMLButtonElement>, fileEntry: FileEntry) {
|
||||||
if (!this.isTracksDirectory()) {
|
if (this.state.reordering) {
|
||||||
return;
|
if (!this.isTracksDirectory()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let dragState = this.state.dragState;
|
||||||
|
if (dragState && dragState.to != dragState.from) {
|
||||||
|
this.handleReorderFiles(dragState.from, dragState.to);
|
||||||
|
|
||||||
|
}
|
||||||
|
// prevent onClick from firing.
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let element = e.currentTarget as HTMLButtonElement;
|
||||||
|
|
||||||
|
element.style.position = "";
|
||||||
|
element.style.zIndex = "";
|
||||||
|
element.style.top = "";
|
||||||
|
element.style.left = "";
|
||||||
|
element.style.background = ""; // xxx: dark mode.
|
||||||
|
this.setState({ dragState: null });
|
||||||
|
} else if (!this.state.multiSelect) {
|
||||||
|
let selectedFile = fileEntry.pathname;
|
||||||
|
if (selectedFile === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
multiSelect: true,
|
||||||
|
selectedFiles: [selectedFile],
|
||||||
|
});
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
let dragState = this.state.dragState;
|
|
||||||
if (dragState && dragState.to != dragState.from) {
|
|
||||||
this.handleReorderFiles(dragState.from, dragState.to);
|
|
||||||
|
|
||||||
}
|
|
||||||
// prevent onClick from firing.
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
let element = e.currentTarget as HTMLButtonElement;
|
|
||||||
|
|
||||||
element.style.position = "";
|
|
||||||
element.style.zIndex = "";
|
|
||||||
element.style.top = "";
|
|
||||||
element.style.left = "";
|
|
||||||
element.style.background = ""; // xxx: dark mode.
|
|
||||||
this.setState({ dragState: null });
|
|
||||||
}
|
}
|
||||||
onMeasureRef(div: HTMLDivElement | null) {
|
onMeasureRef(div: HTMLDivElement | null) {
|
||||||
this.lastDivRef = div;
|
this.lastDivRef = div;
|
||||||
@@ -544,10 +592,57 @@ export default withStyles(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectValue(fileEntry: FileEntry) {
|
|
||||||
|
handleFileClick(event: React.MouseEvent<HTMLElement>, fileEntry: FileEntry) {
|
||||||
if (this.state.reordering) {
|
if (this.state.reordering) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (event.ctrlKey || this.state.multiSelect) {
|
||||||
|
if (fileEntry.isProtected) {
|
||||||
|
// <none> cannot be multiselected.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.state.multiSelect) {
|
||||||
|
let selectedFiles = this.state.selectedFiles.slice();
|
||||||
|
let ix = selectedFiles.indexOf(fileEntry.pathname);
|
||||||
|
if (ix === -1) {
|
||||||
|
selectedFiles.push(fileEntry.pathname);
|
||||||
|
} else {
|
||||||
|
selectedFiles.splice(ix, 1);
|
||||||
|
}
|
||||||
|
if (selectedFiles.length === 0) {
|
||||||
|
this.setState({
|
||||||
|
multiSelect: false,
|
||||||
|
selectedFiles: []
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
multiSelect: true,
|
||||||
|
selectedFiles: selectedFiles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let selectedFiles = [fileEntry.pathname];
|
||||||
|
if (this.state.hasSelection) {
|
||||||
|
if (this.state.selectedFile !== "") {
|
||||||
|
selectedFiles.push(this.state.selectedFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
multiSelect: true,
|
||||||
|
selectedFiles: selectedFiles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.shiftKey) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
this.requestScroll = true;
|
this.requestScroll = true;
|
||||||
if (!fileEntry.isDirectory) {
|
if (!fileEntry.isDirectory) {
|
||||||
if (!this.isFolderArtwork(fileEntry.pathname)) {
|
if (!this.isFolderArtwork(fileEntry.pathname)) {
|
||||||
@@ -576,8 +671,18 @@ export default withStyles(
|
|||||||
handleMenuClose() {
|
handleMenuClose() {
|
||||||
this.setState({ menuAnchorEl: null });
|
this.setState({ menuAnchorEl: null });
|
||||||
}
|
}
|
||||||
|
handleMenuMultiSelectOpen(event: React.MouseEvent<HTMLElement>) {
|
||||||
|
this.setState({ menuMultiselectAnchorEl: event.currentTarget });
|
||||||
|
|
||||||
|
}
|
||||||
|
handleMenuMultiSelectClose() {
|
||||||
|
this.setState({ menuMultiselectAnchorEl: null });
|
||||||
|
}
|
||||||
|
canDeleteEntry(fileEntry: FileEntry): boolean {
|
||||||
|
return fileEntry.isProtected;
|
||||||
|
}
|
||||||
handleDelete() {
|
handleDelete() {
|
||||||
if (this.state.selectedFileProtected) {
|
if (this.state.selectedFileProtected && !this.state.multiSelect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ openConfirmDeleteDialog: true });
|
this.setState({ openConfirmDeleteDialog: true });
|
||||||
@@ -589,66 +694,93 @@ export default withStyles(
|
|||||||
let file = this.state.selectedFile;
|
let file = this.state.selectedFile;
|
||||||
this.model.downloadAudioFile(file);
|
this.model.downloadAudioFile(file);
|
||||||
}
|
}
|
||||||
handleConfirmDelete() {
|
async handleConfirmDelete() {
|
||||||
if (this.state.selectedFileProtected) {
|
this.setState({ openConfirmDeleteDialog: false });
|
||||||
|
let files: string[] = [];
|
||||||
|
if (this.state.multiSelect) {
|
||||||
|
files = this.state.selectedFiles;
|
||||||
|
} else {
|
||||||
|
if (this.state.selectedFileProtected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
files = [this.state.selectedFile];
|
||||||
|
}
|
||||||
|
let selectedFile = (!this.state.multiSelect && this.state.hasSelection) ? this.state.selectedFile : undefined;
|
||||||
|
let newSelectedFiles = this.state.selectedFiles.slice();
|
||||||
|
let multiSelect = this.state.multiSelect;
|
||||||
|
let resultFiles = this.state.fileResult.files;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let file of files) {
|
||||||
|
if (!multiSelect) {
|
||||||
|
if (selectedFile) {
|
||||||
|
let position = -1;
|
||||||
|
for (let i = 0; i < resultFiles.length; ++i) {
|
||||||
|
let file = resultFiles[i];
|
||||||
|
if (file.pathname === selectedFile) {
|
||||||
|
position = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (position >= 0 && position < resultFiles.length - 1) {
|
||||||
|
selectedFile = this.state.fileResult.files[position + 1].pathname;
|
||||||
|
} else if (position === this.state.fileResult.files.length - 1) {
|
||||||
|
if (position !== 0) {
|
||||||
|
selectedFile = this.state.fileResult.files[position - 1].pathname;
|
||||||
|
} else {
|
||||||
|
selectedFile = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedFile = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let ix = newSelectedFiles.indexOf(file);
|
||||||
|
if (ix >= 0) {
|
||||||
|
newSelectedFiles.splice(ix, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.model.deleteUserFile(file);
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
this.model.showAlert(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update state for what we've done.
|
||||||
|
if (!this.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ openConfirmDeleteDialog: false });
|
if (multiSelect) {
|
||||||
if (this.state.hasSelection) {
|
if (newSelectedFiles.length === 0) {
|
||||||
let selectedFile = this.state.selectedFile;
|
this.setState({
|
||||||
let position = -1;
|
multiSelect: false,
|
||||||
for (let i = 0; i < this.state.fileResult.files.length; ++i) {
|
selectedFiles: [],
|
||||||
let file = this.state.fileResult.files[i];
|
});
|
||||||
if (file.pathname === selectedFile) {
|
} else {
|
||||||
position = i;
|
this.setState({
|
||||||
}
|
multiSelect: true,
|
||||||
|
selectedFiles: newSelectedFiles,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let newSelection: FileEntry | undefined = undefined;
|
} else {
|
||||||
if (position >= 0 && position < this.state.fileResult.files.length - 1) {
|
if (selectedFile) {
|
||||||
newSelection = this.state.fileResult.files[position + 1];
|
this.setState({
|
||||||
} else if (position === this.state.fileResult.files.length - 1) {
|
selectedFile: selectedFile
|
||||||
if (position !== 0) {
|
});
|
||||||
newSelection = this.state.fileResult.files[position - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.model.deleteUserFile(this.state.selectedFile)
|
|
||||||
.then(
|
|
||||||
() => {
|
|
||||||
if (newSelection) {
|
|
||||||
this.setState({
|
|
||||||
selectedFile: newSelection.pathname,
|
|
||||||
selectedFileIsDirectory: newSelection.isDirectory,
|
|
||||||
selectedFileProtected: newSelection.isProtected,
|
|
||||||
hasSelection: true,
|
|
||||||
hasFileSelection: !newSelection.isDirectory && !newSelection.isProtected
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
selectedFile: "",
|
|
||||||
selectedFileIsDirectory: false,
|
|
||||||
selectedFileProtected: true,
|
|
||||||
hasSelection: false,
|
|
||||||
hasFileSelection: false
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
this.requestFiles(this.state.navDirectory);
|
|
||||||
}
|
|
||||||
).catch(
|
|
||||||
(e: any) => {
|
|
||||||
this.model.showAlert(e.toString());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
this.requestFiles(this.state.navDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
navigate(relativeDirectory: string) {
|
navigate(relativeDirectory: string) {
|
||||||
this.requestFiles(relativeDirectory);
|
this.requestFiles(relativeDirectory);
|
||||||
this.setState({ navDirectory: relativeDirectory });
|
this.setState({ navDirectory: relativeDirectory });
|
||||||
}
|
}
|
||||||
|
handleBreadcrumbNavigate(relativeDirectory: string) {
|
||||||
|
if (!this.state.reordering && !this.state.multiSelect) {
|
||||||
|
this.navigate(relativeDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
getCompactTrackTitle(fileEntry: FileEntry): string {
|
getCompactTrackTitle(fileEntry: FileEntry): string {
|
||||||
let metadata = fileEntry.metadata;
|
let metadata = fileEntry.metadata;
|
||||||
let title = getTrackTitle(fileEntry.pathname, metadata);
|
let title = getTrackTitle(fileEntry.pathname, metadata);
|
||||||
@@ -679,7 +811,7 @@ export default withStyles(
|
|||||||
let breadcrumbs: React.ReactElement[] = [(
|
let breadcrumbs: React.ReactElement[] = [(
|
||||||
<Button variant="text"
|
<Button variant="text"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
key="h" onClick={() => { this.navigate(""); }}
|
key="h" onClick={() => { this.handleBreadcrumbNavigate(""); }}
|
||||||
style={{ textTransform: "none", flexShrink: 0 }}
|
style={{ textTransform: "none", flexShrink: 0 }}
|
||||||
startIcon={(<HomeIcon sx={{ mr: 0.6 }} fontSize="inherit" />)}
|
startIcon={(<HomeIcon sx={{ mr: 0.6 }} fontSize="inherit" />)}
|
||||||
>
|
>
|
||||||
@@ -700,7 +832,7 @@ export default withStyles(
|
|||||||
<Button variant="text"
|
<Button variant="text"
|
||||||
key={"bc" + i}
|
key={"bc" + i}
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={() => { this.navigate(breadcrumb.pathname) }}
|
onClick={() => { this.handleBreadcrumbNavigate(breadcrumb.pathname) }}
|
||||||
style={{ minWidth: 0, textTransform: "none", flexShrink: 0 }}
|
style={{ minWidth: 0, textTransform: "none", flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
<Typography variant="body2" style={{}} noWrap>{breadcrumb.displayName}</Typography>
|
<Typography variant="body2" style={{}} noWrap>{breadcrumb.displayName}</Typography>
|
||||||
@@ -835,6 +967,8 @@ export default withStyles(
|
|||||||
onEnterKey={() => {
|
onEnterKey={() => {
|
||||||
if (this.state.reordering) {
|
if (this.state.reordering) {
|
||||||
this.setState({ reordering: false, dragState: null });
|
this.setState({ reordering: false, dragState: null });
|
||||||
|
} else if (this.state.multiSelect) {
|
||||||
|
this.setState({ multiSelect: false, selectedFiles: [] });
|
||||||
} else {
|
} else {
|
||||||
this.openSelectedFile();
|
this.openSelectedFile();
|
||||||
}
|
}
|
||||||
@@ -854,12 +988,12 @@ export default withStyles(
|
|||||||
>
|
>
|
||||||
<DialogTitle style={{
|
<DialogTitle style={{
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
background: this.state.reordering ?
|
background: this.state.reordering || this.state.multiSelect ?
|
||||||
(isDarkMode() ? "#523" : "#EDF")
|
(isDarkMode() ? "#523" : "#EDF")
|
||||||
: undefined,
|
: undefined,
|
||||||
marginBottom: 16, paddingTop: 0
|
marginBottom: 16, paddingTop: 0
|
||||||
}} >
|
}} >
|
||||||
{this.state.reordering ? (
|
{this.state.reordering && (
|
||||||
<Toolbar style={{ padding: 0 }}>
|
<Toolbar style={{ padding: 0 }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
edge="start"
|
edge="start"
|
||||||
@@ -876,7 +1010,65 @@ export default withStyles(
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
||||||
) : (
|
)}
|
||||||
|
{this.state.multiSelect && (
|
||||||
|
<Toolbar style={{ padding: 0 }}>
|
||||||
|
<IconButton
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="cancel"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={() => {
|
||||||
|
this.handleMultisSelectClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon style={{ width: 24, height: 24 }} />
|
||||||
|
</IconButton>
|
||||||
|
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||||
|
{this.state.selectedFiles.length.toString() + " files selected."}
|
||||||
|
</Typography>
|
||||||
|
<Button variant="dialogSecondary"
|
||||||
|
startIcon={<OldDeleteIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
this.handleDelete();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="More..."
|
||||||
|
aria-label="display more actions"
|
||||||
|
edge="end"
|
||||||
|
color="inherit"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={(ev) => { this.handleMenuMultiSelectOpen(ev); }}
|
||||||
|
disabled={protectedDirectory}
|
||||||
|
|
||||||
|
>
|
||||||
|
<MoreIcon />
|
||||||
|
</IconButtonEx>
|
||||||
|
<Menu
|
||||||
|
id="menu-multi-select"
|
||||||
|
anchorEl={this.state.menuMultiselectAnchorEl}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'bottom',
|
||||||
|
horizontal: 'right',
|
||||||
|
}}
|
||||||
|
keepMounted
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'right',
|
||||||
|
}}
|
||||||
|
open={this.state.menuMultiselectAnchorEl !== null}
|
||||||
|
onClose={() => { this.handleMenuMultiSelectClose(); }}
|
||||||
|
>
|
||||||
|
{canMove && (<MenuItem onClick={() => { this.handleMenuMultiSelectClose(); this.handleMove(); }}>Move</MenuItem>)}
|
||||||
|
{canMove && (<MenuItem onClick={() => { this.handleMenuMultiSelectClose(); this.handleCopy(); }}>Copy</MenuItem>)}
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
</Toolbar>
|
||||||
|
)}
|
||||||
|
{!(this.state.reordering || this.state.multiSelect) && (
|
||||||
<>
|
<>
|
||||||
<Toolbar style={{ padding: 0 }}>
|
<Toolbar style={{ padding: 0 }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -934,8 +1126,8 @@ export default withStyles(
|
|||||||
>
|
>
|
||||||
<MenuItem onClick={() => { this.handleMenuClose(); this.onNewFolder(); }}>New folder</MenuItem>
|
<MenuItem onClick={() => { this.handleMenuClose(); this.onNewFolder(); }}>New folder</MenuItem>
|
||||||
{(needsDivider) && (<Divider />)}
|
{(needsDivider) && (<Divider />)}
|
||||||
{canMove && (<MenuItem onClick={() => { this.handleMenuClose(); this.onMove(); }}>Move</MenuItem>)}
|
{canMove && (<MenuItem onClick={() => { this.handleMenuClose(); this.handleMove(); }}>Move</MenuItem>)}
|
||||||
{canMove && (<MenuItem onClick={() => { this.handleMenuClose(); this.onCopy(); }}>Copy</MenuItem>)}
|
{canMove && (<MenuItem onClick={() => { this.handleMenuClose(); this.handleCopy(); }}>Copy</MenuItem>)}
|
||||||
{canRename && this.hasSelectedFileOrFolder() && !protectedItem && (<MenuItem onClick={() => {
|
{canRename && this.hasSelectedFileOrFolder() && !protectedItem && (<MenuItem onClick={() => {
|
||||||
this.handleMenuClose(); this.onRename();
|
this.handleMenuClose(); this.onRename();
|
||||||
}}>Rename</MenuItem>)}
|
}}>Rename</MenuItem>)}
|
||||||
@@ -943,12 +1135,12 @@ export default withStyles(
|
|||||||
<MenuItem onClick={() => { this.handleMenuClose(); this.onReorder(); }}>Reorder</MenuItem>)}
|
<MenuItem onClick={() => { this.handleMenuClose(); this.onReorder(); }}>Reorder</MenuItem>)}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<div style={{ flex: "0 0 auto", marginLeft: 0, marginRight: 0, overflowX: "clip" }}>
|
|
||||||
{this.renderBreadcrumbs()}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
<div style={{ flex: "0 0 auto", marginLeft: 0, marginRight: 0, overflowX: "clip" }}>
|
||||||
|
{this.renderBreadcrumbs()}
|
||||||
|
</div>
|
||||||
|
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent style={{
|
<DialogContent style={{
|
||||||
@@ -1001,7 +1193,14 @@ export default withStyles(
|
|||||||
++trackPosition;
|
++trackPosition;
|
||||||
this.maxPosition = trackPosition;
|
this.maxPosition = trackPosition;
|
||||||
}
|
}
|
||||||
let selected = value.pathname === this.state.selectedFile && !this.state.reordering;
|
let selected = false;
|
||||||
|
if (this.state.reordering) {
|
||||||
|
selected = false;
|
||||||
|
} else if (this.state.multiSelect) {
|
||||||
|
selected = this.state.selectedFiles.indexOf(value.pathname) !== -1;
|
||||||
|
} else {
|
||||||
|
selected = value.pathname === this.state.selectedFile;
|
||||||
|
}
|
||||||
let selectBg = selected ? "rgba(0,0,0,0.15)" : "rgba(0,0,0,0.0)";
|
let selectBg = selected ? "rgba(0,0,0,0.15)" : "rgba(0,0,0,0.0)";
|
||||||
if (isDarkMode()) {
|
if (isDarkMode()) {
|
||||||
selectBg = selected ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.0)";
|
selectBg = selected ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.0)";
|
||||||
@@ -1023,7 +1222,7 @@ export default withStyles(
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DraggableButtonBase key={value.pathname}
|
<DraggableButtonBase key={value.pathname}
|
||||||
longPressDelay={this.state.reordering ? 0 : -1}
|
longPressDelay={this.state.reordering ? 0 : undefined}
|
||||||
data-position={dataPosition}
|
data-position={dataPosition}
|
||||||
data-pathname={
|
data-pathname={
|
||||||
value.metadata ? value.metadata.fileName : null
|
value.metadata ? value.metadata.fileName : null
|
||||||
@@ -1034,15 +1233,15 @@ export default withStyles(
|
|||||||
position: "relative",
|
position: "relative",
|
||||||
top: dragOffset + "px",
|
top: dragOffset + "px",
|
||||||
}}
|
}}
|
||||||
onClick={() => this.onSelectValue(value)}
|
onClick={(e) => this.handleFileClick(e, value)}
|
||||||
onDoubleClick={() => { this.onDoubleClickValue(value.pathname); }}
|
onDoubleClick={() => { this.onDoubleClickValue(value.pathname); }}
|
||||||
|
|
||||||
onLongPressEnd={(e) => {
|
onLongPressEnd={(e) => {
|
||||||
this.handleLongPressEnd(e);
|
this.handleLongPressEnd(e, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onLongPressStart={(currentTarget, e) => {
|
onLongPressStart={(currentTarget, e) => {
|
||||||
this.handleLongPressStart(currentTarget, e);
|
return this.handleLongPressStart(currentTarget, e, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onLongPressMove={(e) => {
|
onLongPressMove={(e) => {
|
||||||
@@ -1059,20 +1258,20 @@ export default withStyles(
|
|||||||
display: "flex", flexFlow: "row nowrap", textOverflow: "ellipsis",
|
display: "flex", flexFlow: "row nowrap", textOverflow: "ellipsis",
|
||||||
justifyContent: "start", alignItems: "center", width: "100%", height: "100%"
|
justifyContent: "start", alignItems: "center", width: "100%", height: "100%"
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
this.isFolderArtwork(value.pathname) ? (
|
this.isFolderArtwork(value.pathname) ? (
|
||||||
<img
|
<img
|
||||||
onDragStart={(e) => { e.preventDefault(); }}
|
onDragStart={(e) => { e.preventDefault(); }}
|
||||||
src={this.getTrackThumbnail(value)}
|
src={this.getTrackThumbnail(value)}
|
||||||
style={{ width: 24, height: 24, margin: 20, borderRadius: 4 }} />
|
style={{ width: 24, height: 24, margin: 20, borderRadius: 4 }} />
|
||||||
):(
|
) : (
|
||||||
<img
|
<img
|
||||||
onDragStart={(e) => { e.preventDefault(); }}
|
onDragStart={(e) => { e.preventDefault(); }}
|
||||||
src={this.getTrackThumbnail(value)}
|
src={this.getTrackThumbnail(value)}
|
||||||
style={{ width: 48, height: 48, margin: 8, borderRadius: 4 }} />
|
style={{ width: 48, height: 48, margin: 8, borderRadius: 4 }} />
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div style={{
|
<div style={{
|
||||||
flex: "1 1 1px", display: "flex", flexFlow: "column nowrap",
|
flex: "1 1 1px", display: "flex", flexFlow: "column nowrap",
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
@@ -1131,7 +1330,7 @@ export default withStyles(
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
{!this.state.reordering && (
|
{(!this.state.reordering && !this.state.multiSelect) && (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
<Divider />
|
||||||
<DialogActions style={{ justifyContent: "stretch" }}>
|
<DialogActions style={{ justifyContent: "stretch" }}>
|
||||||
@@ -1239,10 +1438,15 @@ export default withStyles(
|
|||||||
|
|
||||||
/>
|
/>
|
||||||
<OkCancelDialog open={this.state.openConfirmDeleteDialog}
|
<OkCancelDialog open={this.state.openConfirmDeleteDialog}
|
||||||
|
|
||||||
text={
|
text={
|
||||||
(this.isDirectory(this.state.selectedFile))
|
this.state.multiSelect ?
|
||||||
? "Are you sure you want to delete the selected directory and all its contents?"
|
("Are you sure you want to delete the selected files and directories and all their contents?")
|
||||||
: "Are you sure you want to delete the selected file?"}
|
: (this.state.selectedFileIsDirectory
|
||||||
|
? "Are you sure you want to delete the selected directory and all its contents?"
|
||||||
|
: "Are you sure you want to delete the selected file?"
|
||||||
|
)
|
||||||
|
}
|
||||||
okButtonText="Delete"
|
okButtonText="Delete"
|
||||||
onOk={() => this.handleConfirmDelete()}
|
onOk={() => this.handleConfirmDelete()}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@@ -1296,7 +1500,7 @@ export default withStyles(
|
|||||||
onClose={() => { this.setState({ moveDialogOpen: false, copyDialogOpen: false }); }}
|
onClose={() => { this.setState({ moveDialogOpen: false, copyDialogOpen: false }); }}
|
||||||
onOk={
|
onOk={
|
||||||
(path) => {
|
(path) => {
|
||||||
this.setState({ moveDialogOpen: false });
|
this.setState({ moveDialogOpen: false, copyDialogOpen: false });
|
||||||
this.setDefaultPath(path);
|
this.setDefaultPath(path);
|
||||||
if (this.state.copyDialogOpen) {
|
if (this.state.copyDialogOpen) {
|
||||||
this.onExecuteCopy(path);
|
this.onExecuteCopy(path);
|
||||||
@@ -1314,6 +1518,9 @@ export default withStyles(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
openSelectedFile(): void {
|
openSelectedFile(): void {
|
||||||
|
if (this.state.multiSelect || this.state.reordering) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.isFolderArtwork(this.state.selectedFile)) {
|
if (this.isFolderArtwork(this.state.selectedFile)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1334,13 +1541,54 @@ export default withStyles(
|
|||||||
return pathFileNameOnly(name);
|
return pathFileNameOnly(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private onMove(): void {
|
private handleMove(): void {
|
||||||
this.setState({ moveDialogOpen: true });
|
this.setState({ moveDialogOpen: true });
|
||||||
}
|
}
|
||||||
private onCopy(): void {
|
private handleCopy(): void {
|
||||||
this.setState({ copyDialogOpen: true });
|
this.setState({ copyDialogOpen: true });
|
||||||
}
|
}
|
||||||
|
private async onExecuteMoveMulti(newDirectory: string) {
|
||||||
|
let files = this.state.selectedFiles.slice();
|
||||||
|
let newFileList = this.state.selectedFiles.slice();
|
||||||
|
try {
|
||||||
|
for (let file of files) {
|
||||||
|
let fileName = pathFileName(file);
|
||||||
|
let oldFilePath = pathConcat(this.state.navDirectory, fileName);
|
||||||
|
let newFilePath = pathConcat(newDirectory, fileName);
|
||||||
|
|
||||||
|
await this.model.renameFilePropertyFile(oldFilePath, newFilePath, this.props.fileProperty);
|
||||||
|
let index = newFileList.indexOf(file);
|
||||||
|
if (index !== -1) {
|
||||||
|
newFileList.splice(index,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.model.showAlert(e);
|
||||||
|
}
|
||||||
|
// update state to refelect what we did.
|
||||||
|
if (!this.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newFileList.length === 0 )
|
||||||
|
{
|
||||||
|
this.setState({
|
||||||
|
multiSelect: false,
|
||||||
|
selectedFiles: []
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
multiSelect: true,
|
||||||
|
selectedFiles: newFileList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.requestFiles(this.state.navDirectory);
|
||||||
|
}
|
||||||
private onExecuteMove(newDirectory: string) {
|
private onExecuteMove(newDirectory: string) {
|
||||||
|
if (this.state.multiSelect) {
|
||||||
|
this.onExecuteMoveMulti(newDirectory);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let fileName = pathFileName(this.state.selectedFile);
|
let fileName = pathFileName(this.state.selectedFile);
|
||||||
let oldFilePath = pathConcat(this.state.navDirectory, fileName);
|
let oldFilePath = pathConcat(this.state.navDirectory, fileName);
|
||||||
let newFilePath = pathConcat(newDirectory, fileName);
|
let newFilePath = pathConcat(newDirectory, fileName);
|
||||||
@@ -1369,8 +1617,32 @@ export default withStyles(
|
|||||||
this.setState({ confirmCopyDialogState: null });
|
this.setState({ confirmCopyDialogState: null });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private async onExecuteCopyMulti(newDirectory: string) {
|
||||||
|
|
||||||
|
let files = this.state.selectedFiles.slice();
|
||||||
|
let fileProperty = this.props.fileProperty;
|
||||||
|
try {
|
||||||
|
for (let fileName of files) {
|
||||||
|
let oldFilePath = pathConcat(this.state.navDirectory, fileName);
|
||||||
|
let newFilePath = pathConcat(newDirectory, fileName);
|
||||||
|
|
||||||
|
await this.model.copyFilePropertyFile(oldFilePath, newFilePath, fileProperty, true);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.model.showAlert(e);
|
||||||
|
}
|
||||||
|
// update state to reflect what we did.
|
||||||
|
if (!this.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.requestFiles(this.state.navDirectory);
|
||||||
|
}
|
||||||
private onExecuteCopy(newDirectory: string) {
|
private onExecuteCopy(newDirectory: string) {
|
||||||
this.setState({ copyDialogOpen: false });
|
this.setState({ copyDialogOpen: false });
|
||||||
|
if (this.state.multiSelect) {
|
||||||
|
this.onExecuteCopyMulti(newDirectory);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let fileName = pathFileName(this.state.selectedFile);
|
let fileName = pathFileName(this.state.selectedFile);
|
||||||
let oldFilePath = pathConcat(this.state.navDirectory, fileName);
|
let oldFilePath = pathConcat(this.state.navDirectory, fileName);
|
||||||
let newFilePath = pathConcat(newDirectory, fileName);
|
let newFilePath = pathConcat(newDirectory, fileName);
|
||||||
|
|||||||
@@ -2019,8 +2019,8 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
return this.copyPreset(instanceId, -1);
|
return this.copyPreset(instanceId, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
showAlert(message: string | Error): void {
|
showAlert(message: any): void {
|
||||||
let m = message;
|
let m: string;
|
||||||
if (message instanceof Error) {
|
if (message instanceof Error) {
|
||||||
let e = message as Error;
|
let e = message as Error;
|
||||||
if (e.message) {
|
if (e.message) {
|
||||||
|
|||||||
Reference in New Issue
Block a user