Skip to content

Commit

Permalink
feat: image convertaion functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
sitarass committed Dec 22, 2023
1 parent 9c3a7a7 commit 4cb7f67
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 21 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"react-router-dom": "^6.18.0",
"react-scripts": "5.0.1",
"sass": "^1.69.5",
"sharp": "^0.33.1",
"styled-components": "^6.1.0",
"web-vitals": "^2.1.4"
},
Expand Down
20 changes: 14 additions & 6 deletions public/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const fs = require("fs");
const Store = require("electron-store");
var QRCode = require("qrcode");
const { app, BrowserWindow, ipcMain, dialog } = require("electron");
var { imageConverter } = require("./utils");

let mainWindow;
const store = new Store();
Expand Down Expand Up @@ -73,7 +74,7 @@ ipcMain.on("convertUrlsToQRs", (event, urlsArray) => {
});

return {
id: `${url}${Date.now()}`,
id: `${url}-${Date.now()}`,
file: path.resolve(savedPath),
fileName: urlFilename,
extension: extension,
Expand All @@ -86,7 +87,7 @@ ipcMain.on("convertUrlsToQRs", (event, urlsArray) => {
ipcMain.on("saveQRfile", (event, fileData) => {
const options = {
title: "Save QR",
defaultPath: app.getPath("documents"),
defaultPath: app.getPath("documents") + "/" + fileData?.fileName,
filters: [
{
name: fileData?.fileName,
Expand All @@ -99,9 +100,16 @@ ipcMain.on("saveQRfile", (event, fileData) => {
.showSaveDialog(mainWindow, options)
.then(({ filePath }) => {
if (!filePath) return;
const origin = fs.createReadStream(fileData?.file, { flags: "r" });
const destination = fs.createWriteStream(filePath, { flags: "w+" });
origin.pipe(destination);
return imageConverter(
fs.readFileSync(fileData?.file),
fileData?.extension,
fileData?.width,
fileData?.height
).then((data) => {
fs.writeFileSync(filePath, data);
});
})
.catch((err) => {});
.catch((err) => {
console.log(err);
});
});
31 changes: 31 additions & 0 deletions public/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const sharp = require("sharp");

const imageConverter = (imageData, extension, width = 350, height = 350) => {
const imagesFormats = {
svg: "svg",
png: "png",
jpeg: "jpeg",
webp: "webp",
tiff: "tiff",
};

const imageWidth = +width;
const imageHeight = +height;

switch (extension) {
case imagesFormats.png:
return sharp(imageData).resize(imageWidth, imageHeight).png().toBuffer();
case imagesFormats.jpeg:
return sharp(imageData).resize(imageWidth, imageHeight).jpeg().toBuffer();
case imagesFormats.webp:
return sharp(imageData).resize(imageWidth, imageHeight).webp().toBuffer();
case imagesFormats.tiff:
return sharp(imageData).resize(imageWidth, imageHeight).tiff().toBuffer();
default:
return Promise.resolve(imageData);
}
};

module.exports = {
imageConverter,
};
60 changes: 53 additions & 7 deletions src/components/Table/QRContentTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import {
TableRow,
TableBody,
TableContainer,
TextField,
} from "@mui/material";
import { Input } from "@mui/material";
import { IconButton } from "components/Buttons/Buttons";
import UndoIcon from "@mui/icons-material/Undo";
import SaveAsIcon from "@mui/icons-material/SaveAs";
import PreviewIcon from "@mui/icons-material/Preview";
import DialogOverlay from "components/DialogOverlay/DialogOverlay";
import useDialog from "hooks/useDialog";
import { StandardDropDown } from "components/DropDown/DropDowns";
import { hasEqualObjectValues } from "utils/objectUtils";

import styles from "./QRContentTable.module.scss";

Expand All @@ -38,18 +39,36 @@ const QRContentTable = ({ rows, handleRows }) => {
});
};

const transformDimensionsInput = (value) => {
const maximumImageSize = 65535;

if (isNaN(value)) return 0;

if (value > maximumImageSize) return maximumImageSize;

if (value < 0 || value === "-") return 0;

return value;
};

const handleChangeRow = (e, row) => {
if (!previous[row.id]) {
setPrevious((_prev) => ({ ..._prev, [row.id]: row }));
}
const value = e.target.value;

const name = e.target.name;
const value =
name === "width" || name === "height"
? transformDimensionsInput(e.target.value)
: e.target.value;
const { id } = row;

const newRows = rows.map((row) => {
if (row.id === id) {
const hasChanged = previous[row.id]?.[name] !== value;
return { ...row, [name]: value, canBeReverted: hasChanged };
const canBeReverted =
previous[row.id]?.[name] !== value ||
!hasEqualObjectValues(row, previous[row.id]);
return { ...row, [name]: value, canBeReverted };
}
return row;
});
Expand All @@ -65,10 +84,11 @@ const QRContentTable = ({ rows, handleRows }) => {
};

const handleSaveAs = (row) => (e) => {
console.log("save as");
window.api.send("saveQRfile", row);
};

const showDimensionsColumn = rows?.some((row) => row.extension !== "svg");

return (
<>
<TableContainer>
Expand All @@ -78,6 +98,9 @@ const QRContentTable = ({ rows, handleRows }) => {
<TableCell align="left" />
<TableCell align="left">Filename</TableCell>
<TableCell align="left">Extension</TableCell>
{showDimensionsColumn && (
<TableCell align="left">Dimensions</TableCell>
)}
<TableCell align="left">Actions</TableCell>
</TableRow>
</TableHead>
Expand All @@ -95,7 +118,7 @@ const QRContentTable = ({ rows, handleRows }) => {
</IconButton>
</TableCell>
<TableCell align="left">
<Input
<TextField
name="fileName"
variant="standard"
value={row.fileName}
Expand All @@ -113,10 +136,33 @@ const QRContentTable = ({ rows, handleRows }) => {
{ value: "svg", name: "svg" },
{ value: "png", name: "png" },
{ value: "jpeg", name: "jpeg" },
{ value: "webp", name: "webp" },
{ value: "tiff", name: "tiff" },
]}
/>
</TableCell>
{/* add drop down at the above one */}
{showDimensionsColumn && (
<TableCell align="justify">
{row.extension !== "svg" && (
<div className={styles.dimensionsContainer}>
<TextField
name="height"
variant="outlined"
size="small"
value={row?.height}
onChange={handleChangeCell(row)}
/>
<TextField
name="width"
variant="outlined"
size="small"
value={row?.width}
onChange={handleChangeCell(row)}
/>
</div>
)}
</TableCell>
)}
<TableCell align="left">
<IconButton aria-label="saveAs" onClick={() => {}}>
<SaveAsIcon
Expand Down
11 changes: 11 additions & 0 deletions src/components/Table/QRContentTable.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,14 @@
}
}
}

.dimensionsContainer {
display: flex;
flex-direction: column;
gap: rem(8);
max-width: rem(160);

@include tablet-landscape-up {
flex-direction: row;
}
}
1 change: 0 additions & 1 deletion src/utils/materialTheme.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ const theme = createTheme({
alignItems: "start",
},
input: {
height: "100% !important",
overflow: "auto !important",
},
notchedOutline: {
Expand Down
3 changes: 3 additions & 0 deletions src/utils/objectUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const hasEqualObjectValues = (a, b) => {
return JSON.stringify(a) === JSON.stringify(b);
};
2 changes: 2 additions & 0 deletions src/utils/stringUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const tableDataTransformer = (data) => {
fileName: entry?.fileName,
extension: entry?.extension,
canBeReverted: false,
height: 350,
width: 350,
}));
};

Expand Down
12 changes: 8 additions & 4 deletions src/views/QRGenerator.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ const QRGenerator = () => {

const [qrData, setQrData] = useState();

const displaySaveAsAllButton = qrData?.length > 1;

const handleUrlsSubmit = ({ URLs }) => {
const urlsArray = URLs.split("\n");
const urlsArray = [...new Set(URLs.split("\n"))];
window.api.send("convertUrlsToQRs", urlsArray);
};

Expand All @@ -49,9 +51,11 @@ const QRGenerator = () => {
handleRows={setQrData}
tableStyles={styles.tableInnerContainer}
/>
<SecondaryButton className={styles.saveAsButton} onClick={() => {}}>
Save as all
</SecondaryButton>
{displaySaveAsAllButton && (
<SecondaryButton className={styles.saveAsButton} onClick={() => {}}>
Save as all
</SecondaryButton>
)}
</div>
</QRGeneratorLayout>
);
Expand Down
Loading

0 comments on commit 4cb7f67

Please sign in to comment.