Bug fixes

This commit is contained in:
Jake Archibald
2020-11-11 14:49:39 +00:00
parent be4601b93a
commit 56f9d4b8c8
20 changed files with 424 additions and 125 deletions

View File

@@ -13,6 +13,7 @@
// Not really clean, but we need these to access the type of the functions // Not really clean, but we need these to access the type of the functions
// for comlink // for comlink
"src/features/**/worker/**/*", "src/features/**/worker/**/*",
"src/features-worker/**/*" "src/features-worker/**/*",
"src/features/worker-utils/**/*"
] ]
} }

View File

@@ -54,7 +54,8 @@ export default function () {
]), ]),
`};`, `};`,
`export type ProcessorWorkerApi = typeof exports;`, `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) .flat(Infinity)
.join('\n'); .join('\n');

86
package-lock.json generated
View File

@@ -146,13 +146,13 @@
} }
}, },
"@rollup/plugin-replace": { "@rollup/plugin-replace": {
"version": "2.3.3", "version": "2.3.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.3.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz",
"integrity": "sha512-XPmVXZ7IlaoWaJLkSCDaa0Y6uVo5XQYHhiMFzOd5qSv5rE+t/UJToPIOE56flKIxBFQI27ONsxb7dqHnwSsjKQ==", "integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/pluginutils": "^3.0.8", "@rollup/pluginutils": "^3.1.0",
"magic-string": "^0.25.5" "magic-string": "^0.25.7"
} }
}, },
"@rollup/pluginutils": { "@rollup/pluginutils": {
@@ -205,9 +205,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "14.11.5", "version": "14.14.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz",
"integrity": "sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==", "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==",
"dev": true "dev": true
}, },
"@types/parse-json": { "@types/parse-json": {
@@ -860,9 +860,9 @@
"dev": true "dev": true
}, },
"commander": { "commander": {
"version": "6.1.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
"integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
"dev": true "dev": true
}, },
"commondir": { "commondir": {
@@ -1684,9 +1684,9 @@
"dev": true "dev": true
}, },
"execa": { "execa": {
"version": "4.0.3", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
"integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
"dev": true, "dev": true,
"requires": { "requires": {
"cross-spawn": "^7.0.0", "cross-spawn": "^7.0.0",
@@ -2319,20 +2319,20 @@
"dev": true "dev": true
}, },
"lint-staged": { "lint-staged": {
"version": "10.4.0", "version": "10.5.1",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.0.tgz", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.1.tgz",
"integrity": "sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==", "integrity": "sha512-fTkTGFtwFIJJzn/PbUO3RXyEBHIhbfYBE7+rJyLcOXabViaO/h6OslgeK6zpeUtzkDrzkgyAYDTLAwx6JzDTHw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^4.1.0", "chalk": "^4.1.0",
"cli-truncate": "^2.1.0", "cli-truncate": "^2.1.0",
"commander": "^6.0.0", "commander": "^6.2.0",
"cosmiconfig": "^7.0.0", "cosmiconfig": "^7.0.0",
"debug": "^4.1.1", "debug": "^4.2.0",
"dedent": "^0.7.0", "dedent": "^0.7.0",
"enquirer": "^2.3.6", "enquirer": "^2.3.6",
"execa": "^4.0.3", "execa": "^4.1.0",
"listr2": "^2.6.0", "listr2": "^3.2.2",
"log-symbols": "^4.0.0", "log-symbols": "^4.0.0",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
@@ -2342,9 +2342,9 @@
} }
}, },
"listr2": { "listr2": {
"version": "2.6.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.2.tgz",
"integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", "integrity": "sha512-AajqcZEUikF2ioph6PfH3dIuxJclhr3i3kHgTOP0xeXdWQohrvJAAmqVcV43/GI987HFY/vzT73jYXoa4esDHg==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^4.1.0", "chalk": "^4.1.0",
@@ -2353,7 +2353,7 @@
"indent-string": "^4.0.0", "indent-string": "^4.0.0",
"log-update": "^4.0.0", "log-update": "^4.0.0",
"p-map": "^4.0.0", "p-map": "^4.0.0",
"rxjs": "^6.6.2", "rxjs": "^6.6.3",
"through": "^2.3.8" "through": "^2.3.8"
}, },
"dependencies": { "dependencies": {
@@ -5843,15 +5843,15 @@
"dev": true "dev": true
}, },
"preact": { "preact": {
"version": "10.5.4", "version": "10.5.5",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.5.4.tgz", "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.5.tgz",
"integrity": "sha512-u0LnVtL9WWF61RLzIbEsVFOdsahoTQkQqeRwyf4eWuLMFrxTH/C47tqcnizbUH54E4KG8UzuuZaMc9KarHmpqQ==", "integrity": "sha512-5ONLNH1SXMzzbQoExZX4TELemNt+TEDb622xXFNfZngjjM9qtrzseJt+EfiUu4TZ6EJ95X5sE1ES4yqHFSIdhg==",
"dev": true "dev": true
}, },
"preact-render-to-string": { "preact-render-to-string": {
"version": "5.1.10", "version": "5.1.11",
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.1.10.tgz", "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.1.11.tgz",
"integrity": "sha512-40svy7NDe5Qe0ymdsIC11f0hZb05MeTSUqqIaWJ5DEFCh/sF86KcpRW0kN/ymGYDVVUCfv9qFrVuLCXR7aQxgQ==", "integrity": "sha512-8DXkx8WzeUexYyh9ZjlBSsqcJVOndidw10t1MK1gLx6at4QxQE3RfqaObPgy5WOnw2piyh9kanNB7w7+dmaq4g==",
"dev": true, "dev": true,
"requires": { "requires": {
"pretty-format": "^3.8.0" "pretty-format": "^3.8.0"
@@ -5863,6 +5863,12 @@
"integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==",
"dev": true "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": { "pretty-format": {
"version": "3.8.0", "version": "3.8.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
@@ -5996,9 +6002,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.28.2", "version": "2.33.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.28.2.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz",
"integrity": "sha512-8txbsFBFLmm9Xdt4ByTOGa9Muonmc8MfNjnGAR8U8scJlF1ZW7AgNZa7aqBXaKtlvnYP/ab++fQIq9dB9NWUbg==", "integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==",
"dev": true, "dev": true,
"requires": { "requires": {
"fsevents": "~2.1.2" "fsevents": "~2.1.2"
@@ -6643,9 +6649,9 @@
} }
}, },
"tslib": { "tslib": {
"version": "1.13.0", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true "dev": true
}, },
"type-fest": { "type-fest": {
@@ -6655,9 +6661,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "4.0.3", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
"dev": true "dev": true
}, },
"uniq": { "uniq": {

View File

@@ -12,9 +12,9 @@
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.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", "@surma/rollup-plugin-off-main-thread": "^1.4.1",
"@types/node": "^14.11.5", "@types/node": "^14.14.7",
"comlink": "^4.3.0", "comlink": "^4.3.0",
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"del": "^5.1.0", "del": "^5.1.0",
@@ -22,7 +22,7 @@
"husky": "^4.3.0", "husky": "^4.3.0",
"idb-keyval": "^3.2.0", "idb-keyval": "^3.2.0",
"linkstate": "^1.1.1", "linkstate": "^1.1.1",
"lint-staged": "^10.4.0", "lint-staged": "^10.5.1",
"lodash.camelcase": "^4.3.0", "lodash.camelcase": "^4.3.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"pointer-tracker": "^2.4.0", "pointer-tracker": "^2.4.0",
@@ -31,13 +31,14 @@
"postcss-nested": "^4.2.3", "postcss-nested": "^4.2.3",
"postcss-simple-vars": "^5.0.2", "postcss-simple-vars": "^5.0.2",
"postcss-url": "^8.0.0", "postcss-url": "^8.0.0",
"preact": "^10.5.4", "preact": "^10.5.5",
"preact-render-to-string": "^5.1.10", "preact-render-to-string": "^5.1.11",
"prettier": "^2.1.2", "prettier": "^2.1.2",
"rollup": "^2.28.2", "pretty-bytes": "^5.4.1",
"rollup": "^2.33.1",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"serve": "^11.3.2", "serve": "^11.3.2",
"typescript": "^4.0.3" "typescript": "^4.0.5"
}, },
"husky": { "husky": {
"hooks": { "hooks": {

View File

@@ -1,6 +1,6 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
import { UncheckedIcon, CheckedIcon } from '../../../icons'; import { UncheckedIcon, CheckedIcon } from '../../../icons';
interface Props extends preact.JSX.HTMLAttributes {} interface Props extends preact.JSX.HTMLAttributes {}

View File

@@ -1,6 +1,6 @@
import { h, Component, ComponentChild, ComponentChildren } from 'preact'; import { h, Component, ComponentChild, ComponentChildren } from 'preact';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
import { transitionHeight } from '../../../util'; import { transitionHeight } from '../../../util';
interface Props { interface Props {
@@ -17,41 +17,39 @@ export default class Expander extends Component<Props, State> {
private lastElHeight: number = 0; private lastElHeight: number = 0;
componentWillReceiveProps(nextProps: Props) { componentWillReceiveProps(nextProps: Props) {
const children = this.props.children as ComponentChild[]; const children = this.props.children as ComponentChild[] | undefined;
const nextChildren = nextProps.children as ComponentChild[]; 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. // Cache the current children for the shrink animation.
this.setState({ outgoingChildren: children }); this.setState({ outgoingChildren: children });
} }
} }
componentWillUpdate(nextProps: Props) { componentWillUpdate(nextProps: Props) {
const children = this.props.children as ComponentChild[]; const children = this.props.children as ComponentChild[] | undefined;
const nextChildren = nextProps.children as ComponentChild[]; const nextChildren = nextProps.children as ComponentChild[] | undefined;
// Only interested if going from empty to not-empty, or not-empty to empty. // Only interested if going from empty to not-empty, or not-empty to empty.
if ((children[0] && nextChildren[0]) || (!children[0] && !nextChildren[0])) if ((children && nextChildren) || (!children && !nextChildren)) return;
return;
this.lastElHeight = (this this.lastElHeight = (this
.base as HTMLElement).getBoundingClientRect().height; .base as HTMLElement).getBoundingClientRect().height;
} }
async componentDidUpdate(previousProps: Props) { async componentDidUpdate(previousProps: Props) {
const children = this.props.children as ComponentChild[]; const children = this.props.children as ComponentChild[] | undefined;
const previousChildren = previousProps.children as ComponentChild[]; const previousChildren = previousProps.children as
| ComponentChild[]
| undefined;
// Only interested if going from empty to not-empty, or not-empty to empty. // Only interested if going from empty to not-empty, or not-empty to empty.
if ( if ((children && previousChildren) || (!children && !previousChildren))
(children[0] && previousChildren[0]) ||
(!children[0] && !previousChildren[0])
)
return; return;
// What height do we need to transition to? // What height do we need to transition to?
(this.base as HTMLElement).style.height = ''; (this.base as HTMLElement).style.height = '';
(this.base as HTMLElement).style.overflow = 'hidden'; (this.base as HTMLElement).style.overflow = 'hidden';
const newHeight = children[0] const newHeight = children
? (this.base as HTMLElement).getBoundingClientRect().height ? (this.base as HTMLElement).getBoundingClientRect().height
: 0; : 0;
@@ -68,12 +66,12 @@ export default class Expander extends Component<Props, State> {
} }
render(props: Props, { outgoingChildren }: State) { render(props: Props, { outgoingChildren }: State) {
const children = props.children as ComponentChild[]; const children = props.children as ComponentChild[] | undefined;
const childrenExiting = !children[0] && outgoingChildren[0]; const childrenExiting = (!children || !children[0]) && outgoingChildren[0];
return ( return (
<div class={childrenExiting ? style.childrenExiting : ''}> <div class={childrenExiting ? style.childrenExiting : ''}>
{children[0] ? children : outgoingChildren} {children && children[0] ? children : outgoingChildren}
</div> </div>
); );
} }

View File

@@ -1,6 +1,6 @@
import PointerTracker from 'pointer-tracker'; import PointerTracker from 'pointer-tracker';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
const RETARGETED_EVENTS = ['focus', 'blur']; const RETARGETED_EVENTS = ['focus', 'blur'];
const UPDATE_EVENTS = ['input', 'change']; const UPDATE_EVENTS = ['input', 'change'];

View File

@@ -1,8 +1,8 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
import RangeInputElement from './custom-els/RangeInput'; import RangeInputElement from './custom-els/RangeInput';
import '../../custom-els/RangeInput'; import './custom-els/RangeInput';
import { linkRef } from 'shared/initial-app/util'; import { linkRef } from 'shared/initial-app/util';
interface Props extends preact.JSX.HTMLAttributes {} interface Props extends preact.JSX.HTMLAttributes {}

View File

@@ -43,7 +43,7 @@
color: #000; color: #000;
} }
// Remove the number controls /* Remove the number controls */
-moz-appearance: textfield; -moz-appearance: textfield;
&::-webkit-outer-spin-button, &::-webkit-outer-spin-button,

View File

@@ -1,6 +1,6 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
interface Props extends preact.JSX.HTMLAttributes { interface Props extends preact.JSX.HTMLAttributes {
large?: boolean; large?: boolean;

View File

@@ -1,7 +1,7 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
import { cleanSet, cleanMerge } from '../../util/clean-modify'; import { cleanSet, cleanMerge } from '../../util/clean-modify';
import type { SourceImage, OutputType } from '..'; import type { SourceImage, OutputType } from '..';

View File

@@ -4,7 +4,7 @@ import type { ScaleToOpts } from './custom-els/PinchZoom';
import './custom-els/PinchZoom'; import './custom-els/PinchZoom';
import './custom-els/TwoUp'; import './custom-els/TwoUp';
import * as style from './style.css'; import * as style from './style.css';
import 'add-css:./styles.css'; import 'add-css:./style.css';
import { shallowEqual, drawDataToCanvas } from '../../util'; import { shallowEqual, drawDataToCanvas } from '../../util';
import { import {
ToggleBackgroundIcon, ToggleBackgroundIcon,

View File

@@ -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<Props, State> {
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 = (
<span class={`${style.sizeDelta} ${style.sizeIncrease}`}>
{percent === '0%' ? 'slightly' : percent} bigger
</span>
);
} else if (delta < 1) {
const percent = Math.round((1 - delta) * 100) + '%';
comparison = (
<span class={`${style.sizeDelta} ${style.sizeDecrease}`}>
{percent === '0%' ? 'slightly' : percent} smaller
</span>
);
} else {
comparison = <span class={style.sizeDelta}>no change</span>;
}
}
return (
<span>
{prettyBytes(blob.size)} {comparison}
</span>
);
}
}

View File

@@ -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<Props, State> {
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 cant 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 (
<div class={`${style.results} ${buttonPositionClass[buttonPosition]}`}>
<div class={style.resultData}>
{children ? <div class={style.resultTitle}>{children}</div> : null}
{!imageFile || showLoadingState ? (
'Working…'
) : (
<FileSize
blob={imageFile}
compareTo={
source && imageFile !== source.file ? source.file : undefined
}
/>
)}
</div>
<button
class={style.copyToOther}
title="Copy settings to other side"
onClick={this.onCopyToOtherClick}
>
<CopyAcrossIcon
class={style.copyIcon}
copyDirection={copyDirection}
/>
</button>
<div class={style.download}>
{downloadUrl && imageFile && (
<a
class={`${style.downloadLink} ${
showLoadingState ? style.downloadLinkDisable : ''
}`}
href={downloadUrl}
download={imageFile.name}
title="Download"
onClick={this.onDownload}
>
<DownloadIcon class={style.downloadIcon} />
</a>
)}
{showLoadingState && <loading-spinner class={style.spinner} />}
</div>
</div>
);
}
}

View File

@@ -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;
}

View File

@@ -27,8 +27,7 @@ import Options from './Options';
import ResultCache from './result-cache'; import ResultCache from './result-cache';
import { cleanMerge, cleanSet } from '../util/clean-modify'; import { cleanMerge, cleanSet } from '../util/clean-modify';
import './custom-els/MultiPanel'; import './custom-els/MultiPanel';
// TODO: you are here import Results from './results';
import Results from '../results';
import WorkerBridge from '../worker-bridge'; import WorkerBridge from '../worker-bridge';
import { resize } from 'features/processors/resize/client'; import { resize } from 'features/processors/resize/client';
import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar'; import type SnackBarElement from 'shared/initial-app/custom-els/snack-bar';
@@ -462,7 +461,10 @@ export default class Compress extends Component<Props, State> {
preprocessorState: currentState.preprocessorState, preprocessorState: currentState.preprocessorState,
}; };
const sideJobStates: SideJob[] = currentState.sides.map((side) => ({ 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, encoderState: side.latestSettings.encoderState,
})); }));
@@ -471,14 +473,18 @@ export default class Compress extends Component<Props, State> {
const needsPreprocessing = const needsPreprocessing =
needsDecoding || needsDecoding ||
latestMainJobState.preprocessorState !== mainJobState.preprocessorState; latestMainJobState.preprocessorState !== mainJobState.preprocessorState;
const sideWorksNeeded = latestSideJobStates.map((latestSideJob, i) => ({ const sideWorksNeeded = latestSideJobStates.map((latestSideJob, i) => {
processing: const needsProcessing =
needsPreprocessing || needsPreprocessing ||
latestSideJob.processorState !== sideJobStates[i].processorState, latestSideJob.processorState !== sideJobStates[i].processorState;
return {
processing: needsProcessing,
encoding: encoding:
needsPreprocessing || needsProcessing ||
latestSideJob.encoderState !== sideJobStates[i].encoderState, latestSideJob.encoderState !== sideJobStates[i].encoderState,
})); };
});
let jobNeeded = false; let jobNeeded = false;
@@ -499,7 +505,12 @@ export default class Compress extends Component<Props, State> {
} }
if (!jobNeeded) { if (!jobNeeded) {
// Clear any loading that may be happening // 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) => ({ this.setState((state) => ({
loading: false, loading: false,
sides: state.sides.map((side) => ({ ...side, loading: false })) as [ sides: state.sides.map((side) => ({ ...side, loading: false })) as [
@@ -507,6 +518,7 @@ export default class Compress extends Component<Props, State> {
Side, Side,
], ],
})); }));
}
return; return;
} }
@@ -645,7 +657,7 @@ export default class Compress extends Component<Props, State> {
// If there's no encoder state, this is "original image", which also // If there's no encoder state, this is "original image", which also
// doesn't allow processing. // doesn't allow processing.
if (!jobState.encoderState) { if (!jobState.encoderState) {
file = currentState.source!.file; file = source.file;
data = source.preprocessed; data = source.preprocessed;
} else { } else {
const cacheResult = this.encodeCache.match( const cacheResult = this.encodeCache.match(
@@ -731,10 +743,7 @@ export default class Compress extends Component<Props, State> {
loading: false, loading: false,
processed, processed,
encodedSettings: { encodedSettings: {
// If we didn't encode, we didn't preprocess either processorState: jobState.processorState,
processorState: jobState.encoderState
? jobState.processorState
: defaultProcessorState,
encoderState: jobState.encoderState, encoderState: jobState.encoderState,
}, },
}; };

View File

@@ -1,6 +1,6 @@
import { wrap } from 'comlink'; import { wrap } from 'comlink';
import { BridgeMethods, methodNames } from './meta'; import { BridgeMethods, methodNames } from './meta';
import workerURL from 'omt:../features-worker'; import workerURL from 'omt:../../../features-worker';
import type { ProcessorWorkerApi } from '../../../features-worker'; import type { ProcessorWorkerApi } from '../../../features-worker';
import { abortable } from '../util'; import { abortable } from '../util';

View File

@@ -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.
*/
/// <reference path="../../../../../missing-types.d.ts" />

View File

@@ -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.
*/
/// <reference path="../../../../../missing-types.d.ts" />