diff --git a/client-tsconfig.json b/client-tsconfig.json index 54dfc176..676e06ee 100644 --- a/client-tsconfig.json +++ b/client-tsconfig.json @@ -13,6 +13,7 @@ // Not really clean, but we need these to access the type of the functions // for comlink "src/features/**/worker/**/*", - "src/features-worker/**/*" + "src/features-worker/**/*", + "src/features/worker-utils/**/*" ] } diff --git a/lib/feature-plugin.js b/lib/feature-plugin.js index b98ab466..0886c732 100644 --- a/lib/feature-plugin.js +++ b/lib/feature-plugin.js @@ -54,7 +54,8 @@ export default function () { ]), `};`, `export type ProcessorWorkerApi = typeof exports;`, - `expose(exports, self);`, + `// 'as any' to work around the way our client code has insight into worker code`, + `expose(exports, self as any);`, ] .flat(Infinity) .join('\n'); diff --git a/package-lock.json b/package-lock.json index ddc8dd01..b8492ce8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,13 +146,13 @@ } }, "@rollup/plugin-replace": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.3.tgz", - "integrity": "sha512-XPmVXZ7IlaoWaJLkSCDaa0Y6uVo5XQYHhiMFzOd5qSv5rE+t/UJToPIOE56flKIxBFQI27ONsxb7dqHnwSsjKQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz", + "integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==", "dev": true, "requires": { - "@rollup/pluginutils": "^3.0.8", - "magic-string": "^0.25.5" + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" } }, "@rollup/pluginutils": { @@ -205,9 +205,9 @@ "dev": true }, "@types/node": { - "version": "14.11.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.5.tgz", - "integrity": "sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==", + "version": "14.14.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", + "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==", "dev": true }, "@types/parse-json": { @@ -860,9 +860,9 @@ "dev": true }, "commander": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", - "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", "dev": true }, "commondir": { @@ -1684,9 +1684,9 @@ "dev": true }, "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -2319,20 +2319,20 @@ "dev": true }, "lint-staged": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.0.tgz", - "integrity": "sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.1.tgz", + "integrity": "sha512-fTkTGFtwFIJJzn/PbUO3RXyEBHIhbfYBE7+rJyLcOXabViaO/h6OslgeK6zpeUtzkDrzkgyAYDTLAwx6JzDTHw==", "dev": true, "requires": { "chalk": "^4.1.0", "cli-truncate": "^2.1.0", - "commander": "^6.0.0", + "commander": "^6.2.0", "cosmiconfig": "^7.0.0", - "debug": "^4.1.1", + "debug": "^4.2.0", "dedent": "^0.7.0", "enquirer": "^2.3.6", - "execa": "^4.0.3", - "listr2": "^2.6.0", + "execa": "^4.1.0", + "listr2": "^3.2.2", "log-symbols": "^4.0.0", "micromatch": "^4.0.2", "normalize-path": "^3.0.0", @@ -2342,9 +2342,9 @@ } }, "listr2": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", - "integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.2.tgz", + "integrity": "sha512-AajqcZEUikF2ioph6PfH3dIuxJclhr3i3kHgTOP0xeXdWQohrvJAAmqVcV43/GI987HFY/vzT73jYXoa4esDHg==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -2353,7 +2353,7 @@ "indent-string": "^4.0.0", "log-update": "^4.0.0", "p-map": "^4.0.0", - "rxjs": "^6.6.2", + "rxjs": "^6.6.3", "through": "^2.3.8" }, "dependencies": { @@ -5843,15 +5843,15 @@ "dev": true }, "preact": { - "version": "10.5.4", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.4.tgz", - "integrity": "sha512-u0LnVtL9WWF61RLzIbEsVFOdsahoTQkQqeRwyf4eWuLMFrxTH/C47tqcnizbUH54E4KG8UzuuZaMc9KarHmpqQ==", + "version": "10.5.5", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.5.tgz", + "integrity": "sha512-5ONLNH1SXMzzbQoExZX4TELemNt+TEDb622xXFNfZngjjM9qtrzseJt+EfiUu4TZ6EJ95X5sE1ES4yqHFSIdhg==", "dev": true }, "preact-render-to-string": { - "version": "5.1.10", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.1.10.tgz", - "integrity": "sha512-40svy7NDe5Qe0ymdsIC11f0hZb05MeTSUqqIaWJ5DEFCh/sF86KcpRW0kN/ymGYDVVUCfv9qFrVuLCXR7aQxgQ==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.1.11.tgz", + "integrity": "sha512-8DXkx8WzeUexYyh9ZjlBSsqcJVOndidw10t1MK1gLx6at4QxQE3RfqaObPgy5WOnw2piyh9kanNB7w7+dmaq4g==", "dev": true, "requires": { "pretty-format": "^3.8.0" @@ -5863,6 +5863,12 @@ "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, + "pretty-bytes": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz", + "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==", + "dev": true + }, "pretty-format": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", @@ -5996,9 +6002,9 @@ } }, "rollup": { - "version": "2.28.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.28.2.tgz", - "integrity": "sha512-8txbsFBFLmm9Xdt4ByTOGa9Muonmc8MfNjnGAR8U8scJlF1ZW7AgNZa7aqBXaKtlvnYP/ab++fQIq9dB9NWUbg==", + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz", + "integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==", "dev": true, "requires": { "fsevents": "~2.1.2" @@ -6643,9 +6649,9 @@ } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "type-fest": { @@ -6655,9 +6661,9 @@ "dev": true }, "typescript": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", - "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", "dev": true }, "uniq": { diff --git a/package.json b/package.json index 90000d7a..b2bb6600 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "devDependencies": { "@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-node-resolve": "^9.0.0", - "@rollup/plugin-replace": "^2.3.3", + "@rollup/plugin-replace": "^2.3.4", "@surma/rollup-plugin-off-main-thread": "^1.4.1", - "@types/node": "^14.11.5", + "@types/node": "^14.14.7", "comlink": "^4.3.0", "cssnano": "^4.1.10", "del": "^5.1.0", @@ -22,7 +22,7 @@ "husky": "^4.3.0", "idb-keyval": "^3.2.0", "linkstate": "^1.1.1", - "lint-staged": "^10.4.0", + "lint-staged": "^10.5.1", "lodash.camelcase": "^4.3.0", "mime-types": "^2.1.27", "pointer-tracker": "^2.4.0", @@ -31,13 +31,14 @@ "postcss-nested": "^4.2.3", "postcss-simple-vars": "^5.0.2", "postcss-url": "^8.0.0", - "preact": "^10.5.4", - "preact-render-to-string": "^5.1.10", + "preact": "^10.5.5", + "preact-render-to-string": "^5.1.11", "prettier": "^2.1.2", - "rollup": "^2.28.2", + "pretty-bytes": "^5.4.1", + "rollup": "^2.33.1", "rollup-plugin-terser": "^7.0.2", "serve": "^11.3.2", - "typescript": "^4.0.3" + "typescript": "^4.0.5" }, "husky": { "hooks": { diff --git a/src/client/lazy-app/Compress/Options/Checkbox/index.tsx b/src/client/lazy-app/Compress/Options/Checkbox/index.tsx index 2b0790d1..e2fd19ec 100644 --- a/src/client/lazy-app/Compress/Options/Checkbox/index.tsx +++ b/src/client/lazy-app/Compress/Options/Checkbox/index.tsx @@ -1,6 +1,6 @@ import { h, Component } from 'preact'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; import { UncheckedIcon, CheckedIcon } from '../../../icons'; interface Props extends preact.JSX.HTMLAttributes {} diff --git a/src/client/lazy-app/Compress/Options/Expander/index.tsx b/src/client/lazy-app/Compress/Options/Expander/index.tsx index cb497c96..3790bf82 100644 --- a/src/client/lazy-app/Compress/Options/Expander/index.tsx +++ b/src/client/lazy-app/Compress/Options/Expander/index.tsx @@ -1,6 +1,6 @@ import { h, Component, ComponentChild, ComponentChildren } from 'preact'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; import { transitionHeight } from '../../../util'; interface Props { @@ -17,41 +17,39 @@ export default class Expander extends Component { private lastElHeight: number = 0; componentWillReceiveProps(nextProps: Props) { - const children = this.props.children as ComponentChild[]; - const nextChildren = nextProps.children as ComponentChild[]; + const children = this.props.children as ComponentChild[] | undefined; + const nextChildren = nextProps.children as ComponentChild[] | undefined; - if (!nextChildren[0] && children[0]) { + if (!nextChildren && children && children[0]) { // Cache the current children for the shrink animation. this.setState({ outgoingChildren: children }); } } componentWillUpdate(nextProps: Props) { - const children = this.props.children as ComponentChild[]; - const nextChildren = nextProps.children as ComponentChild[]; + const children = this.props.children as ComponentChild[] | undefined; + const nextChildren = nextProps.children as ComponentChild[] | undefined; // Only interested if going from empty to not-empty, or not-empty to empty. - if ((children[0] && nextChildren[0]) || (!children[0] && !nextChildren[0])) - return; + if ((children && nextChildren) || (!children && !nextChildren)) return; this.lastElHeight = (this .base as HTMLElement).getBoundingClientRect().height; } async componentDidUpdate(previousProps: Props) { - const children = this.props.children as ComponentChild[]; - const previousChildren = previousProps.children as ComponentChild[]; + const children = this.props.children as ComponentChild[] | undefined; + const previousChildren = previousProps.children as + | ComponentChild[] + | undefined; // Only interested if going from empty to not-empty, or not-empty to empty. - if ( - (children[0] && previousChildren[0]) || - (!children[0] && !previousChildren[0]) - ) + if ((children && previousChildren) || (!children && !previousChildren)) return; // What height do we need to transition to? (this.base as HTMLElement).style.height = ''; (this.base as HTMLElement).style.overflow = 'hidden'; - const newHeight = children[0] + const newHeight = children ? (this.base as HTMLElement).getBoundingClientRect().height : 0; @@ -68,12 +66,12 @@ export default class Expander extends Component { } render(props: Props, { outgoingChildren }: State) { - const children = props.children as ComponentChild[]; - const childrenExiting = !children[0] && outgoingChildren[0]; + const children = props.children as ComponentChild[] | undefined; + const childrenExiting = (!children || !children[0]) && outgoingChildren[0]; return (
- {children[0] ? children : outgoingChildren} + {children && children[0] ? children : outgoingChildren}
); } diff --git a/src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/index.ts b/src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/index.ts index c684e96b..3681decd 100644 --- a/src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/index.ts +++ b/src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/index.ts @@ -1,6 +1,6 @@ import PointerTracker from 'pointer-tracker'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; const RETARGETED_EVENTS = ['focus', 'blur']; const UPDATE_EVENTS = ['input', 'change']; diff --git a/src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/styles.css b/src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/style.css similarity index 100% rename from src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/styles.css rename to src/client/lazy-app/Compress/Options/Range/custom-els/RangeInput/style.css diff --git a/src/client/lazy-app/Compress/Options/Range/index.tsx b/src/client/lazy-app/Compress/Options/Range/index.tsx index 14633411..e4ed66b9 100644 --- a/src/client/lazy-app/Compress/Options/Range/index.tsx +++ b/src/client/lazy-app/Compress/Options/Range/index.tsx @@ -1,8 +1,8 @@ import { h, Component } from 'preact'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; import RangeInputElement from './custom-els/RangeInput'; -import '../../custom-els/RangeInput'; +import './custom-els/RangeInput'; import { linkRef } from 'shared/initial-app/util'; interface Props extends preact.JSX.HTMLAttributes {} diff --git a/src/client/lazy-app/Compress/Options/Range/style.css b/src/client/lazy-app/Compress/Options/Range/style.css index ffe0d54e..b8ed2b69 100644 --- a/src/client/lazy-app/Compress/Options/Range/style.css +++ b/src/client/lazy-app/Compress/Options/Range/style.css @@ -43,7 +43,7 @@ color: #000; } - // Remove the number controls + /* Remove the number controls */ -moz-appearance: textfield; &::-webkit-outer-spin-button, diff --git a/src/client/lazy-app/Compress/Options/Select/index.tsx b/src/client/lazy-app/Compress/Options/Select/index.tsx index 46b28140..d6cbb6d0 100644 --- a/src/client/lazy-app/Compress/Options/Select/index.tsx +++ b/src/client/lazy-app/Compress/Options/Select/index.tsx @@ -1,6 +1,6 @@ import { h, Component } from 'preact'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; interface Props extends preact.JSX.HTMLAttributes { large?: boolean; diff --git a/src/client/lazy-app/Compress/Options/index.tsx b/src/client/lazy-app/Compress/Options/index.tsx index 520da01a..cccb46bc 100644 --- a/src/client/lazy-app/Compress/Options/index.tsx +++ b/src/client/lazy-app/Compress/Options/index.tsx @@ -1,7 +1,7 @@ import { h, Component } from 'preact'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; import { cleanSet, cleanMerge } from '../../util/clean-modify'; import type { SourceImage, OutputType } from '..'; diff --git a/src/client/lazy-app/Compress/Output/index.tsx b/src/client/lazy-app/Compress/Output/index.tsx index ccbdf41a..94a653a9 100644 --- a/src/client/lazy-app/Compress/Output/index.tsx +++ b/src/client/lazy-app/Compress/Output/index.tsx @@ -4,7 +4,7 @@ import type { ScaleToOpts } from './custom-els/PinchZoom'; import './custom-els/PinchZoom'; import './custom-els/TwoUp'; import * as style from './style.css'; -import 'add-css:./styles.css'; +import 'add-css:./style.css'; import { shallowEqual, drawDataToCanvas } from '../../util'; import { ToggleBackgroundIcon, diff --git a/src/client/lazy-app/Compress/Results/FileSize.tsx b/src/client/lazy-app/Compress/Results/FileSize.tsx new file mode 100644 index 00000000..973d1d81 --- /dev/null +++ b/src/client/lazy-app/Compress/Results/FileSize.tsx @@ -0,0 +1,43 @@ +import { h, Component } from 'preact'; +import prettyBytes from 'pretty-bytes'; +import * as style from './style.css'; + +interface Props { + blob: Blob; + compareTo?: Blob; +} + +interface State {} + +export default class FileSize extends Component { + render({ blob, compareTo }: Props) { + let comparison: preact.JSX.Element | undefined; + + if (compareTo) { + const delta = blob.size / compareTo.size; + if (delta > 1) { + const percent = Math.round((delta - 1) * 100) + '%'; + comparison = ( + + {percent === '0%' ? 'slightly' : percent} bigger + + ); + } else if (delta < 1) { + const percent = Math.round((1 - delta) * 100) + '%'; + comparison = ( + + {percent === '0%' ? 'slightly' : percent} smaller + + ); + } else { + comparison = no change; + } + } + + return ( + + {prettyBytes(blob.size)} {comparison} + + ); + } +} diff --git a/src/client/lazy-app/Compress/Results/index.tsx b/src/client/lazy-app/Compress/Results/index.tsx new file mode 100644 index 00000000..eee5a5b7 --- /dev/null +++ b/src/client/lazy-app/Compress/Results/index.tsx @@ -0,0 +1,135 @@ +import { h, Component, ComponentChildren, ComponentChild } from 'preact'; + +import * as style from './style.css'; +import 'add-css:./style.css'; +import FileSize from './FileSize'; +import { + DownloadIcon, + CopyAcrossIcon, + CopyAcrossIconProps, +} from 'client/lazy-app/icons'; +import 'shared/initial-app/custom-els/loading-spinner'; +import { SourceImage } from '../../compress'; + +interface Props { + loading: boolean; + source?: SourceImage; + imageFile?: File; + downloadUrl?: string; + children: ComponentChildren; + copyDirection: CopyAcrossIconProps['copyDirection']; + buttonPosition: keyof typeof buttonPositionClass; + onCopyToOtherClick(): void; +} + +interface State { + showLoadingState: boolean; +} + +const buttonPositionClass = { + 'stack-right': style.stackRight, + 'download-right': style.downloadRight, + 'download-left': style.downloadLeft, +}; + +const loadingReactionDelay = 500; + +export default class Results extends Component { + state: State = { + showLoadingState: false, + }; + + /** The timeout ID between entering the loading state, and changing UI */ + private loadingTimeoutId: number = 0; + + componentDidUpdate(prevProps: Props, prevState: State) { + if (prevProps.loading && !this.props.loading) { + // Just stopped loading + clearTimeout(this.loadingTimeoutId); + this.setState({ showLoadingState: false }); + } else if (!prevProps.loading && this.props.loading) { + // Just started loading + this.loadingTimeoutId = self.setTimeout( + () => this.setState({ showLoadingState: true }), + loadingReactionDelay, + ); + } + } + + private onCopyToOtherClick = (event: Event) => { + event.preventDefault(); + this.props.onCopyToOtherClick(); + }; + + private onDownload = () => { + // GA can’t do floats. So we round to ints. We're deliberately rounding to nearest kilobyte to + // avoid cases where exact image sizes leak something interesting about the user. + const before = Math.round(this.props.source!.file.size / 1024); + const after = Math.round(this.props.imageFile!.size / 1024); + const change = Math.round((after / before) * 1000); + + ga('send', 'event', 'compression', 'download', { + metric1: before, + metric2: after, + metric3: change, + }); + }; + + render( + { + source, + imageFile, + downloadUrl, + children, + copyDirection, + buttonPosition, + }: Props, + { showLoadingState }: State, + ) { + return ( +
+
+ {children ?
{children}
: null} + {!imageFile || showLoadingState ? ( + 'Working…' + ) : ( + + )} +
+ + + +
+ {downloadUrl && imageFile && ( + + + + )} + {showLoadingState && } +
+
+ ); + } +} diff --git a/src/client/lazy-app/Compress/Results/style.css b/src/client/lazy-app/Compress/Results/style.css new file mode 100644 index 00000000..1918c841 --- /dev/null +++ b/src/client/lazy-app/Compress/Results/style.css @@ -0,0 +1,131 @@ +@keyframes action-enter { + from { + transform: rotate(-90deg); + opacity: 0; + animation-timing-function: ease-out; + } +} + +@keyframes action-leave { + from { + transform: rotate(0deg); + opacity: 1; + animation-timing-function: ease-out; + } +} + +.results { + display: grid; + grid-template-columns: [text] 1fr [copy-button] auto [download-button] auto; + background: rgba(0, 0, 0, 0.9); + font-size: 1rem; + + @media (min-width: 400px) { + font-size: 1.2rem; + } + + @media (min-width: 600px) { + font-size: 1.4rem; + } + + &:focus { + outline: none; + } +} + +.result-data { + grid-row: 1; + grid-column: text; + display: flex; + align-items: center; + padding: 0 10px; + white-space: nowrap; + overflow: hidden; +} + +.download-right { + grid-template-columns: [copy-button] auto [text] 1fr [download-button] auto; +} + +.download-left { + grid-template-columns: [download-button] auto [text] 1fr [copy-button] auto; +} + +.stack-right { + & .result-data { + padding: 0 15px; + } +} + +.result-title { + display: flex; + align-items: center; + margin-right: 0.4em; +} + +.size-delta { + font-size: 0.8em; + font-style: italic; + position: relative; + top: -1px; + margin-left: 0.3em; +} + +.size-increase { + color: #e35050; +} + +.size-decrease { + color: #50e3c2; +} + +.download { + grid-row: 1; + grid-column: download-button; + background: #34b9eb; + --size: 38px; + width: var(--size); + height: var(--size); + display: grid; + align-items: center; + justify-items: center; +} + +.download-link { + animation: action-enter 0.2s; + grid-area: 1/1; +} + +.download-link-disable { + pointer-events: none; + opacity: 0; + transform: rotate(90deg); + animation: action-leave 0.2s; +} + +.download-icon, +.copy-icon { + color: #fff; + display: block; + --size: 24px; + width: var(--size); + height: var(--size); + padding: 7px; + filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.7)); +} + +.spinner { + --color: #fff; + --delay: 0; + --size: 22px; + grid-area: 1/1; +} + +.copy-to-other { + grid-row: 1; + grid-column: copy-button; + composes: unbutton from '../../../../shared/initial-app/util.css'; + composes: download; + + background: #656565; +} diff --git a/src/client/lazy-app/Compress/index.tsx b/src/client/lazy-app/Compress/index.tsx index 08dc703a..0c66541b 100644 --- a/src/client/lazy-app/Compress/index.tsx +++ b/src/client/lazy-app/Compress/index.tsx @@ -27,8 +27,7 @@ import Options from './Options'; import ResultCache from './result-cache'; import { cleanMerge, cleanSet } from '../util/clean-modify'; import './custom-els/MultiPanel'; -// TODO: you are here -import Results from '../results'; +import Results from './results'; import WorkerBridge from '../worker-bridge'; import { resize } from 'features/processors/resize/client'; import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar'; @@ -462,7 +461,10 @@ export default class Compress extends Component { preprocessorState: currentState.preprocessorState, }; const sideJobStates: SideJob[] = currentState.sides.map((side) => ({ - processorState: side.latestSettings.processorState, + // If there isn't an encoder selected, we don't process either + processorState: side.latestSettings.encoderState + ? side.latestSettings.processorState + : defaultProcessorState, encoderState: side.latestSettings.encoderState, })); @@ -471,14 +473,18 @@ export default class Compress extends Component { const needsPreprocessing = needsDecoding || latestMainJobState.preprocessorState !== mainJobState.preprocessorState; - const sideWorksNeeded = latestSideJobStates.map((latestSideJob, i) => ({ - processing: + const sideWorksNeeded = latestSideJobStates.map((latestSideJob, i) => { + const needsProcessing = needsPreprocessing || - latestSideJob.processorState !== sideJobStates[i].processorState, - encoding: - needsPreprocessing || - latestSideJob.encoderState !== sideJobStates[i].encoderState, - })); + latestSideJob.processorState !== sideJobStates[i].processorState; + + return { + processing: needsProcessing, + encoding: + needsProcessing || + latestSideJob.encoderState !== sideJobStates[i].encoderState, + }; + }); let jobNeeded = false; @@ -499,14 +505,20 @@ export default class Compress extends Component { } if (!jobNeeded) { - // Clear any loading that may be happening - this.setState((state) => ({ - loading: false, - sides: state.sides.map((side) => ({ ...side, loading: false })) as [ - Side, - Side, - ], - })); + // Clear any loading that may be happening. + // Putting this behind an if to avoid looping. + if ( + currentState.loading || + currentState.sides.some((side) => side.loading) + ) { + this.setState((state) => ({ + loading: false, + sides: state.sides.map((side) => ({ ...side, loading: false })) as [ + Side, + Side, + ], + })); + } return; } @@ -645,7 +657,7 @@ export default class Compress extends Component { // If there's no encoder state, this is "original image", which also // doesn't allow processing. if (!jobState.encoderState) { - file = currentState.source!.file; + file = source.file; data = source.preprocessed; } else { const cacheResult = this.encodeCache.match( @@ -731,10 +743,7 @@ export default class Compress extends Component { loading: false, processed, encodedSettings: { - // If we didn't encode, we didn't preprocess either - processorState: jobState.encoderState - ? jobState.processorState - : defaultProcessorState, + processorState: jobState.processorState, encoderState: jobState.encoderState, }, }; diff --git a/src/client/lazy-app/worker-bridge/index.ts b/src/client/lazy-app/worker-bridge/index.ts index 9209a7d1..1babcef8 100644 --- a/src/client/lazy-app/worker-bridge/index.ts +++ b/src/client/lazy-app/worker-bridge/index.ts @@ -1,6 +1,6 @@ import { wrap } from 'comlink'; import { BridgeMethods, methodNames } from './meta'; -import workerURL from 'omt:../features-worker'; +import workerURL from 'omt:../../../features-worker'; import type { ProcessorWorkerApi } from '../../../features-worker'; import { abortable } from '../util'; diff --git a/src/features/decoders/avif/worker/missing-types.d.ts b/src/features/decoders/avif/worker/missing-types.d.ts deleted file mode 100644 index c729fd74..00000000 --- a/src/features/decoders/avif/worker/missing-types.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright 2020 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/// diff --git a/src/features/decoders/webp/worker/missing-types.d.ts b/src/features/decoders/webp/worker/missing-types.d.ts deleted file mode 100644 index c729fd74..00000000 --- a/src/features/decoders/webp/worker/missing-types.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright 2020 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -///