Skip to content

Commit

Permalink
Fixes #232 - Problems deleting/replacing user files
Browse files Browse the repository at this point in the history
  • Loading branch information
replaysMike committed Apr 22, 2024
1 parent 859cee7 commit ade9e07
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 26 deletions.
50 changes: 40 additions & 10 deletions Binner/Binner.Web/ClientApp/src/components/PartMediaMemoized.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
productImages.unshift({
name: data[i].originalFileName,
value: `/api/storedFile/preview?fileName=${data[i].fileName}&token=${getImagesToken()}`,
url: `/api/storedFile/local?fileName=${data[i].fileName}&token=${getImagesToken()}`,
id: data[i].storedFileId
});
}
Expand All @@ -115,7 +116,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
description: data[i].originalFileName,
imageUrl: `/api/storedFile/preview?fileName=${data[i].fileName}&token=${getImagesToken()}`,
manufacturer: "",
title: data[i].originalFileName
title: data[i].originalFileName,
},
id: data[i].storedFileId
};
Expand All @@ -130,7 +131,8 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
pinoutImages.unshift({
name: data[i].originalFileName,
value: `/api/storedFile/preview?fileName=${data[i].fileName}&token=${getImagesToken()}`,
id: data[i].storedFileId
url: `/api/storedFile/local?fileName=${data[i].fileName}&token=${getImagesToken()}`,
id: data[i].storedFileId,
});
}
setMetadata({ ...metadata, pinoutImages });
Expand All @@ -141,7 +143,8 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
circuitImages.unshift({
name: data[i].originalFileName,
value: `/api/storedFile/preview?fileName=${data[i].fileName}&token=${getImagesToken()}`,
id: data[i].storedFileId
url: `/api/storedFile/local?fileName=${data[i].fileName}&token=${getImagesToken()}`,
id: data[i].storedFileId,
});
}
setMetadata({ ...metadata, circuitImages });
Expand Down Expand Up @@ -246,10 +249,37 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
);
};

const handleVisitLink = (e, url) => {
const handleVisitLink = async (e, data) => {
e.preventDefault();
e.stopPropagation();
window.open(url, "_blank");

if (data && data.localfile) {
// check if the stored file exists first
await fetchApi(`api/storedfile/exists?fileName=${data.localfile}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
},
}).then(() => {
// OK, file exists
if (data.value.datasheetUrl)
window.open(data.value.datasheetUrl, "_blank");
else
window.open(data.url || data.value, "_blank");
}).catch(err => {
// file not found, or file read error
if (err.data.status === 404)
toast.error(`User file '${data.localfile}' does not exist on disk!`)
else
toast.error(`Failed to read local file '${data.localfile}'!`)
});
} else {
// open the external link
if (data.value.datasheetUrl)
window.open(data.value.datasheetUrl, "_blank");
else
window.open(data.url || data.value, "_blank");
}
};

const confirmDeleteLocalFileClose = (e) => {
Expand Down Expand Up @@ -285,7 +315,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
{metadata.productImages
?.filter((x) => x.value.length > 0)
?.map((productImage, imageKey) => (
<Carousel.Item key={imageKey}>
<Carousel.Item key={imageKey} onClick={(e) => handleVisitLink(e, productImage)}>
<Image src={productImage.value} size="large" />
{productImage.id && (
<Popup
Expand Down Expand Up @@ -334,7 +364,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
<div>
<Carousel variant="dark" interval={null} onSelect={onCurrentDatasheetChanged} className="datasheets">
{metadata.datasheets.map((datasheet, datasheetKey) => (
<Carousel.Item key={datasheetKey} onClick={(e) => handleVisitLink(e, datasheet.value.datasheetUrl)} {...getDatasheetAttributes(datasheet)}>
<Carousel.Item key={datasheetKey} onClick={(e) => handleVisitLink(e, datasheet)} {...getDatasheetAttributes(datasheet)}>
<Image src={datasheet.value.imageUrl} size="large" />
{datasheet.id && (
<Popup
Expand Down Expand Up @@ -392,7 +422,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
<div>
<Carousel variant="dark" interval={null} className="pinout-images">
{metadata.pinoutImages.map((pinout, pinoutKey) => (
<Carousel.Item key={pinoutKey}>
<Carousel.Item key={pinoutKey} onClick={(e) => handleVisitLink(e, pinout)}>
<Image src={pinout.value} size="large" />
{pinout.id && (
<Popup
Expand Down Expand Up @@ -441,7 +471,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
<div>
<Carousel variant="dark" interval={null} className="circuit-images">
{metadata.circuitImages.map((circuit, circuitKey) => (
<Carousel.Item key={circuitKey}>
<Carousel.Item key={circuitKey} onClick={(e) => handleVisitLink(e, circuit)}>
<Image src={circuit.value} size="large" />
{circuit.id && (
<Popup
Expand Down Expand Up @@ -482,7 +512,7 @@ export function PartMediaMemoized({ infoResponse, datasheet, part, loadingPartMe
</Dropzone>
</>
);
}, [metadata, datasheetTitle, datasheetPartName, datasheetDescription, datasheetManufacturer, thePart, loadingPartMetadata, uploading]);
}, [metadata, datasheetTitle, datasheetPartName, datasheetDescription, datasheetManufacturer, thePart, loadingPartMetadata, uploading, setConfirmDeleteLocalFileIsOpen, confirmDeleteLocalFileIsOpen, confirmLocalFileDeleteContent, handleDeleteLocalFile]);

return (
<>
Expand Down
39 changes: 23 additions & 16 deletions Binner/Binner.Web/ClientApp/src/pages/Inventory.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ export function Inventory(props) {
const [metadataParts, setMetadataParts] = useState([]);
const [duplicateParts, setDuplicateParts] = useState([]);
const [duplicatePartModalOpen, setDuplicatePartModalOpen] = useState(false);
const [confirmDeleteIsOpen, setConfirmDeleteIsOpen] = useState(false);
const [confirmPartDeleteContent, setConfirmPartDeleteContent] = useState(null);
const [confirmDeletePartIsOpen, setConfirmDeletePartIsOpen] = useState(false);
const [confirmDeletePartContent, setConfirmDeletePartContent] = useState(null);
const [partTypes, setPartTypes] = useState([]);
const [allPartTypes, setAllPartTypes] = useState([]);
const [loadingPart, setLoadingPart] = useState(false);
Expand Down Expand Up @@ -699,7 +699,9 @@ export function Inventory(props) {
...storedProductImages.map((pi) => ({
name: pi.originalFileName,
value: `/api/storedFile/preview?fileName=${pi.fileName}&token=${getImagesToken()}`,
id: pi.storedFileId
url: `/api/storedFile/local?fileName=${pi.fileName}&token=${getImagesToken()}`,
id: pi.storedFileId,
localfile: pi.fileName,
}))
);
if (storedDatasheets && storedDatasheets.length > 0)
Expand All @@ -713,23 +715,28 @@ export function Inventory(props) {
manufacturer: "",
title: pi.originalFileName
},
id: pi.storedFileId
id: pi.storedFileId,
localfile: pi.fileName,
}))
);
if (storedPinouts && storedPinouts.length > 0)
infoResponse.pinoutImages.unshift(
...storedPinouts.map((pi) => ({
name: pi.originalFileName,
value: `/api/storedFile/preview?fileName=${pi.fileName}&token=${getImagesToken()}`,
id: pi.storedFileId
url: `/api/storedFile/local?fileName=${pi.fileName}&token=${getImagesToken()}`,
id: pi.storedFileId,
localfile: pi.fileName,
}))
);
if (storedReferenceDesigns && storedReferenceDesigns.length > 0)
infoResponse.circuitImages.unshift(
...storedReferenceDesigns.map((pi) => ({
name: pi.originalFileName,
value: `/api/storedFile/preview?fileName=${pi.fileName}&token=${getImagesToken()}`,
id: pi.storedFileId
url: `/api/storedFile/local?fileName=${pi.fileName}&token=${getImagesToken()}`,
id: pi.storedFileId,
localfile: pi.fileName,
}))
);
return infoResponse;
Expand Down Expand Up @@ -1041,18 +1048,18 @@ export function Inventory(props) {
body: JSON.stringify({ partId: selectedPart.partId })
});
const partsDeleted = _.without(parts, _.findWhere(parts, { partId: selectedPart.partId }));
setConfirmDeleteIsOpen(false);
setConfirmDeletePartIsOpen(false);
setParts(partsDeleted);
setSelectedPart(null);
props.history(`/inventory`);
};

const confirmDeleteOpen = (e, part) => {
const openDeletePart = (e, part) => {
e.preventDefault();
e.stopPropagation();
setConfirmDeleteIsOpen(true);
setConfirmDeletePartIsOpen(true);
setSelectedPart(part);
setConfirmPartDeleteContent(
setConfirmDeletePartContent(
<p>
<Trans i18nKey="confirm.deletePart" name={inputPartNumber}>
Are you sure you want to delete part <b>{{ name: inputPartNumber }}</b>?
Expand All @@ -1066,10 +1073,10 @@ export function Inventory(props) {
);
};

const confirmDeleteClose = (e) => {
const closeDeletePart = (e) => {
e.preventDefault();
e.stopPropagation();
setConfirmDeleteIsOpen(false);
setConfirmDeletePartIsOpen(false);
setSelectedPart(null);
};

Expand Down Expand Up @@ -1301,7 +1308,7 @@ export function Inventory(props) {
/>
</Button.Group>
{part && part.partId > 0 && (
<Button type="button" title="Delete" onClick={(e) => confirmDeleteOpen(e, part)} style={{ marginLeft: "10px" }}>
<Button type="button" title="Delete" onClick={(e) => openDeletePart(e, part)} style={{ marginLeft: "10px" }}>
<Icon name="delete" />
{t('button.delete', "Delete")}
</Button>
Expand Down Expand Up @@ -1560,10 +1567,10 @@ export function Inventory(props) {
<Confirm
className="confirm"
header={t('confirm.header.deletePart', "Delete Part")}
open={confirmDeleteIsOpen}
onCancel={confirmDeleteClose}
open={confirmDeletePartIsOpen}
onCancel={closeDeletePart}
onConfirm={handleDeletePart}
content={confirmPartDeleteContent}
content={confirmDeletePartContent}
/>
<Confirm
className="confirm"
Expand Down
23 changes: 23 additions & 0 deletions Binner/Binner.Web/Controllers/StoredFileController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ public async Task<IActionResult> GetAsync(GetStoredFileRequest request)
return Ok(storedFile);
}

/// <summary>
/// Check if an existing stored file exists on disk
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
[HttpGet("exists")]
public async Task<IActionResult> GetStoredFileExistsAsync([FromQuery] string fileName)
{
if (string.IsNullOrEmpty(fileName)) return NotFound();
var storedFile = await _storedFileService.GetStoredFileAsync(fileName);
if (storedFile == null) return NotFound();

// read the file contents
new FileExtensionContentTypeProvider().TryGetContentType(fileName, out var contentType);
if (string.IsNullOrEmpty(contentType))
return BadRequest($"Could not determine content type for file '{fileName}'");
var path = _storedFileService.GetStoredFilePath(storedFile.StoredFileType);
var pathToFile = Path.Combine(path, fileName);
if (System.IO.File.Exists(pathToFile))
return Ok();
return NotFound();
}

/// <summary>
/// Get an existing user uploaded file
/// </summary>
Expand Down

0 comments on commit ade9e07

Please sign in to comment.