diff --git a/package.json b/package.json index 3e2cea8..2b9a84e 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,7 @@ "@types/react": "^17.0.00", "@types/react-dom": "^17.0.0", "@types/react-router-dom": "^5.1.6", + "@types/svgo": "^2.6.0", "@types/universal-analytics": "^0.4.5", "@types/uuid": "^8.3.1", "@types/webpack-env": "^1.15.2", @@ -264,6 +265,7 @@ "react-router-dom": "^5.2.0", "regenerator-runtime": "^0.13.5", "source-map-support": "^0.5.19", + "svgo": "^2.8.0", "universal-analytics": "^0.4.23", "use-query-params": "^1.2.3", "uuid": "^8.3.2" diff --git a/src/components/modules/IconsHome/LeftIconsCollectionsNav/AddIconToCollection/modal.tsx b/src/components/modules/IconsHome/LeftIconsCollectionsNav/AddIconToCollection/modal.tsx index d1325e4..c460dd2 100644 --- a/src/components/modules/IconsHome/LeftIconsCollectionsNav/AddIconToCollection/modal.tsx +++ b/src/components/modules/IconsHome/LeftIconsCollectionsNav/AddIconToCollection/modal.tsx @@ -1,14 +1,11 @@ import { FC, useState, useEffect } from 'react'; -import { Modal, Button } from 'components/ui/atomic-components'; +import { Modal, Button, Checkbox } from 'components/ui/atomic-components'; import { ReactComponent as UploadIcon } from 'assets/icons/upload.svg'; import ImageUploading, { ImageListType } from 'react-images-uploading'; import { ReactComponent as DocumentIcon } from 'assets/icons/document.svg'; import { Collection, CollectionsApi } from 'data/collections'; import { useParams } from 'react-router-dom'; import { ipcRenderer } from 'electron'; -import { useQueryClient } from 'react-query'; -import { Icon } from 'data/icons'; -import { addIconsToDb } from 'data/icons/utils'; import { CollectionsDropdown } from './CollectionsDropdown'; interface Props { @@ -17,7 +14,9 @@ interface Props { } export const AddIconToCollectionModal: FC = ({ show, onClose }) => { - const queryClient = useQueryClient(); + const [optimizeIcon, setOptimizeIcon] = useState( + localStorage.getItem('optimizeIcon') === 'true' + ); const { collectionId }: { collectionId: string } = useParams(); @@ -42,32 +41,10 @@ export const AddIconToCollectionModal: FC = ({ show, onClose }) => { fileName: icon.file?.name, })), folderPath: selectedCollection.folderSrc, + optimizeIcon, }); - // add icons to db - const icons = uploadedIcons - .filter((icon) => !!icon.file) - .map((icon) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const [name, type] = icon!.file!.name.split('.'); - - return { - name, - collectionId, - mime: type, - byteSize: icon.file?.size, - imageSrc: `${selectedCollection.folderSrc.replace(/\/$/, '')}/${icon.file?.name}`, - createdAt: Date.now(), - updatedAt: Date.now(), - } as Icon; - }); - - addIconsToDb(icons, collectionId) - .then(() => { - queryClient.invalidateQueries('icons-list'); - onClose(); - }) - .catch(() => {}); + onClose(); } }; @@ -138,6 +115,29 @@ export const AddIconToCollectionModal: FC = ({ show, onClose }) => { )} + +
+ + SVGO optimize icon.{' '} + + Learn more + + + } + onChange={(val) => { + setOptimizeIcon(val); + localStorage.setItem('optimizeIcon', String(val)); + }} + /> +
); diff --git a/src/components/ui/atomic-components/checkbox/index.tsx b/src/components/ui/atomic-components/checkbox/index.tsx index bd2e139..795fbd6 100644 --- a/src/components/ui/atomic-components/checkbox/index.tsx +++ b/src/components/ui/atomic-components/checkbox/index.tsx @@ -1,11 +1,12 @@ -import { FC, ChangeEvent } from 'react'; +import { FC, ChangeEvent, ReactNode } from 'react'; import './styles.css'; export const Checkbox: FC<{ defaultChecked?: boolean; - label: string; + label: ReactNode; + checked?: boolean; onChange: (val: boolean) => void; -}> = ({ label, defaultChecked, onChange }) => { +}> = ({ checked, label, defaultChecked, onChange }) => { const checkBoxInputChange = (e: ChangeEvent) => { const value = e.target.checked; @@ -17,7 +18,8 @@ export const Checkbox: FC<{ diff --git a/src/main.dev.ts b/src/main.dev.ts index 433f0e3..2d22a3e 100644 --- a/src/main.dev.ts +++ b/src/main.dev.ts @@ -17,8 +17,10 @@ import log from 'electron-log'; import electronDl, { download } from 'electron-dl'; import { Icon } from 'data/icons'; import fs from 'fs'; +import { optimize as svgOptimize } from 'svgo'; import chokidar, { FSWatcher } from 'chokidar'; +import { svgoPluginsConfiguration } from './main/constants/svgoPluginsConfiguration'; import { activateAnalytics } from './main/utils/analytics'; import { getAllFiles } from './main/utils/getAllFiles'; import MenuBuilder from './menu'; @@ -202,9 +204,11 @@ ipcMain.on( { uploadedIcons, folderPath, + optimizeIcon, }: { uploadedIcons: { dataURL: string; fileName: string }[]; folderPath: string; + optimizeIcon: boolean; } ) => { const regex = /^data:.+\/(.+);base64,(.*)$/; @@ -222,9 +226,18 @@ ipcMain.on( } const buffer = Buffer.from(data, 'base64'); + + let fileData: Buffer | string = buffer; + + if (optimizeIcon) { + fileData = svgOptimize(buffer, { + plugins: svgoPluginsConfiguration, + }).data; + } + const formattedPath = path.join(folderPath, filename); - fs.writeFile(formattedPath, buffer, () => {}); + fs.writeFile(formattedPath, fileData, () => {}); } }); } diff --git a/src/main/constants/svgoPluginsConfiguration.ts b/src/main/constants/svgoPluginsConfiguration.ts new file mode 100644 index 0000000..13cb267 --- /dev/null +++ b/src/main/constants/svgoPluginsConfiguration.ts @@ -0,0 +1,144 @@ +import { Plugin } from 'svgo'; + +export const svgoPluginsConfiguration: Plugin[] = [ + { + name: 'cleanupAttrs', + active: true, + }, + { + name: 'cleanupEnableBackground', + active: true, + }, + { + name: 'cleanupIDs', + active: true, + }, + { + name: 'cleanupNumericValues', + active: true, + }, + { + name: 'collapseGroups', + active: true, + }, + { + name: 'convertColors', + active: true, + }, + { + name: 'convertEllipseToCircle', + active: true, + }, + { + name: 'convertPathData', + active: true, + }, + { + name: 'convertShapeToPath', + active: false, + }, + { + name: 'convertTransform', + active: true, + }, + { + name: 'inlineStyles', + active: true, + }, + { + name: 'mergePaths', + active: false, + }, + { + name: 'mergeStyles', + active: true, + }, + { + name: 'moveElemsAttrsToGroup', + active: true, + }, + { + name: 'moveGroupAttrsToElems', + active: true, + }, + { + name: 'removeComments', + active: true, + }, + { + name: 'removeDesc', + active: true, + }, + { + name: 'removeDoctype', + active: true, + }, + { + name: 'removeEditorsNSData', + active: true, + }, + { + name: 'removeEmptyAttrs', + active: true, + }, + { + name: 'removeEmptyContainers', + active: true, + }, + { + name: 'removeEmptyText', + active: true, + }, + { + name: 'removeHiddenElems', + active: true, + }, + { + name: 'removeMetadata', + active: true, + }, + { + name: 'removeNonInheritableGroupAttrs', + active: true, + }, + { + name: 'removeTitle', + active: true, + }, + { + name: 'removeUnknownsAndDefaults', + active: true, + }, + { + name: 'removeUnusedNS', + active: true, + }, + { + name: 'removeUselessDefs', + active: true, + }, + { + name: 'removeUselessStrokeAndFill', + active: true, + }, + { + name: 'removeViewBox', + active: false, + }, + { + name: 'removeXMLProcInst', + active: true, + }, + { + name: 'sortDefsChildren', + active: true, + }, + { + name: 'removeRasterImages', + active: false, + }, + { + name: 'sortAttrs', + active: true, + }, +]; diff --git a/yarn.lock b/yarn.lock index 0a7cf81..6debefe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1836,6 +1836,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.15" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" @@ -2086,6 +2091,13 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/svgo@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@types/svgo/-/svgo-2.6.0.tgz#f0c50eed286422f63b4e3346d8a309b0ea67d58a" + integrity sha512-VSdhb3KTOglle1SLQD4+TB6ezj/MS3rN98gOUkXzbTUhG8VjFKHXN3OVgEFlTnW5fYBxt+lzZlD3PFqkwMj36Q== + dependencies: + "@types/node" "*" + "@types/universal-analytics@^0.4.5": version "0.4.5" resolved "https://registry.yarnpkg.com/@types/universal-analytics/-/universal-analytics-0.4.5.tgz#6a4a88477a70f7ca1ce19caede561ca728149810" @@ -3708,7 +3720,7 @@ commander@^6.0.0, commander@^6.1.0, commander@^6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== -commander@^7.0.0: +commander@^7.0.0, commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== @@ -4015,6 +4027,17 @@ css-select@^2.0.0: domutils "^1.7.0" nth-check "^1.0.2" +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" @@ -4023,7 +4046,7 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" -css-tree@^1.1.2: +css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== @@ -4041,6 +4064,11 @@ css-what@^3.2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== +css-what@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" + integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -4114,7 +4142,7 @@ cssnano@^4.1.10: is-resolvable "^1.0.0" postcss "^7.0.0" -csso@^4.0.2: +csso@^4.0.2, csso@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== @@ -4502,12 +4530,21 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@^2.0.1: +domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== @@ -4526,6 +4563,13 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +domhandler@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" + integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== + dependencies: + domelementtype "^2.2.0" + domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -4534,6 +4578,15 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" +domutils@^2.6.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -8578,6 +8631,13 @@ nth-check@^1.0.2: dependencies: boolbase "~1.0.0" +nth-check@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" + integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== + dependencies: + boolbase "^1.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -9075,6 +9135,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -11194,6 +11259,19 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" +svgo@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"