Feat : Save and import side settings

There were requests from multiple users that
they use squoosh for compression but for each
iteration side settings resets to default
causing issues and there is no way to save and
import side settings.

There will be two buttons adjacent to copy-over

save side settings : This will save side encoder
and latest settings to localstorage of browser

import side settings : This will import side encoder
and latest settings from localstorage of browser and
replace the existing settings

Also if there are saved settings in locaStorage then
whenever user loads the app it will take that settings
and populate the side so user do not have to repeatedly
enter same settings for similar compression operation
subject to user has saved side settings

Update:1

Import settings button remains disabled if there
is nothing to import

Whenever the side setting is saved there will be
event fired and eventually listened to enable import
button

All 2 operations show notifications now

Import notification has undo option
This commit is contained in:
Harsh Shah
2023-04-04 15:20:06 +05:30
parent a930e8d928
commit b1df3e1d54
4 changed files with 225 additions and 17 deletions

View File

@@ -17,7 +17,7 @@ import Toggle from './Toggle';
import Select from './Select';
import { Options as QuantOptionsComponent } from 'features/processors/quantize/client';
import { Options as ResizeOptionsComponent } from 'features/processors/resize/client';
import { SwapIcon } from 'client/lazy-app/icons';
import { ImportIcon, SaveIcon, SwapIcon } from 'client/lazy-app/icons';
interface Props {
index: 0 | 1;
@@ -29,10 +29,14 @@ interface Props {
onEncoderOptionsChange(index: 0 | 1, newOptions: EncoderOptions): void;
onProcessorOptionsChange(index: 0 | 1, newOptions: ProcessorState): void;
onCopyToOtherSideClick(index: 0 | 1): void;
onSaveSideSettingsClick(index: 0 | 1): void;
onImportSideSettingsClick(index: 0 | 1): void;
}
interface State {
supportedEncoderMap?: PartialButNotUndefined<typeof encoderMap>;
leftSideSettings?: string | null;
rightSideSettings?: string | null;
}
type PartialButNotUndefined<T> = {
@@ -60,6 +64,8 @@ const supportedEncoderMapP: Promise<PartialButNotUndefined<typeof encoderMap>> =
export default class Options extends Component<Props, State> {
state: State = {
supportedEncoderMap: undefined,
leftSideSettings: localStorage.getItem('leftSideSettings'),
rightSideSettings: localStorage.getItem('rightSideSettings'),
};
constructor() {
@@ -69,6 +75,29 @@ export default class Options extends Component<Props, State> {
);
}
private setLeftSideSettings = () => {
this.setState({
leftSideSettings: localStorage.getItem('leftSideSettings'),
});
};
private setRightSideSettings = () => {
this.setState({
rightSideSettings: localStorage.getItem('rightSideSettings'),
});
};
componentDidMount(): void {
// Changing the state when side setting is stored in localstorage
window.addEventListener('leftSideSettings', this.setLeftSideSettings);
window.addEventListener('rightSideSettings', this.setRightSideSettings);
}
componentWillUnmount(): void {
window.removeEventListener('leftSideSettings', this.setLeftSideSettings);
window.removeEventListener('removeSideSettings', this.setRightSideSettings);
}
private onEncoderTypeChange = (event: Event) => {
const el = event.currentTarget as HTMLSelectElement;
@@ -110,6 +139,14 @@ export default class Options extends Component<Props, State> {
this.props.onCopyToOtherSideClick(this.props.index);
};
private onSaveSideSettingClick = () => {
this.props.onSaveSideSettingsClick(this.props.index);
};
private onImportSideSettingsClick = () => {
this.props.onImportSideSettingsClick(this.props.index);
};
render(
{ source, encoderState, processorState }: Props,
{ supportedEncoderMap }: State,
@@ -139,6 +176,30 @@ export default class Options extends Component<Props, State> {
>
<SwapIcon />
</button>
<button
class={style.saveButton}
title="Save side settings"
onClick={this.onSaveSideSettingClick}
>
<SaveIcon />
</button>
<button
class={
style.importButton +
' ' +
(!this.state.leftSideSettings && this.props.index === 0
? style.buttonOpacity
: '') +
' ' +
(!this.state.rightSideSettings && this.props.index === 1
? style.buttonOpacity
: '')
}
title="Import saved side settings"
onClick={this.onImportSideSettingsClick}
>
<ImportIcon />
</button>
</div>
</h3>
<label class={style.sectionEnabler}>

View File

@@ -119,3 +119,20 @@
fill: var(--header-text-color);
}
}
.save-button,
.import-button {
composes: title-button;
svg {
fill: var(--header-text-color);
}
}
.button-opacity {
pointer-events: none;
cursor: not-allowed;
svg {
opacity: 0.5;
}
}

View File

@@ -281,15 +281,26 @@ export default class Compress extends Component<Props, State> {
source: undefined,
loading: false,
preprocessorState: defaultPreprocessorState,
// Tasking catched side settings if available otherwise taking default settings
sides: [
{
localStorage.getItem('leftSideSettings')
? {
...JSON.parse(localStorage.getItem('leftSideSettings') as string),
loading: false,
}
: {
latestSettings: {
processorState: defaultProcessorState,
encoderState: undefined,
},
loading: false,
},
{
localStorage.getItem('rightSideSettings')
? {
...JSON.parse(localStorage.getItem('rightSideSettings') as string),
loading: false,
}
: {
latestSettings: {
processorState: defaultProcessorState,
encoderState: {
@@ -428,6 +439,99 @@ export default class Compress extends Component<Props, State> {
sides: cleanSet(this.state.sides, otherIndex, oldSettings),
});
};
/**
* This function saves encodedSettings and latestSettings of
* particular side in browser local storage
* @param index : (0|1)
* @returns
*/
private onSaveSideSettingsClick = async (index: 0 | 1) => {
if (index === 0) {
const leftSideSettings = JSON.stringify({
encodedSettings: this.state.sides[index].encodedSettings,
latestSettings: this.state.sides[index].latestSettings,
});
localStorage.setItem('leftSideSettings', leftSideSettings);
// Firing an event when we save side settings in localstorage
window.dispatchEvent(new CustomEvent('leftSideSettings'));
await this.props.showSnack('Left side settings saved', {
timeout: 1500,
actions: ['dismiss'],
});
return;
}
if (index === 1) {
const rightSideSettings = JSON.stringify({
encodedSettings: this.state.sides[index].encodedSettings,
latestSettings: this.state.sides[index].latestSettings,
});
localStorage.setItem('rightSideSettings', rightSideSettings);
// Firing an event when we save side settings in localstorage
window.dispatchEvent(new CustomEvent('rightSideSettings'));
await this.props.showSnack('Right side settings saved', {
timeout: 1500,
actions: ['dismiss'],
});
return;
}
};
/**
* This function sets the side state with catched localstorage
* value as per side index provided
* @param index : (0|1)
* @returns
*/
private onImportSideSettingsClick = async (index: 0 | 1) => {
const leftSideSettingsString = localStorage.getItem('leftSideSettings');
const rightSideSettingsString = localStorage.getItem('rightSideSettings');
if (index === 0 && leftSideSettingsString) {
const oldLeftSideSettings = this.state.sides[index];
const newLeftSideSettings = {
...this.state.sides[index],
...JSON.parse(leftSideSettingsString),
};
this.setState({
sides: cleanSet(this.state.sides, index, newLeftSideSettings),
});
const result = await this.props.showSnack('Left side settings imported', {
timeout: 3000,
actions: ['undo', 'dismiss'],
});
if (result === 'undo') {
this.setState({
sides: cleanSet(this.state.sides, index, oldLeftSideSettings),
});
}
return;
}
if (index === 1 && rightSideSettingsString) {
const oldRightSideSettings = this.state.sides[index];
const newRightSideSettings = {
...this.state.sides[index],
...JSON.parse(rightSideSettingsString),
};
this.setState({
sides: cleanSet(this.state.sides, index, newRightSideSettings),
});
const result = await this.props.showSnack(
'Right side settings imported',
{
timeout: 3000,
actions: ['undo', 'dismiss'],
},
);
if (result === 'undo') {
this.setState({
sides: cleanSet(this.state.sides, index, oldRightSideSettings),
});
}
return;
}
};
private onPreprocessorChange = async (
preprocessorState: PreprocessorState,
@@ -829,6 +933,8 @@ export default class Compress extends Component<Props, State> {
onEncoderOptionsChange={this.onEncoderOptionsChange}
onProcessorOptionsChange={this.onProcessorOptionsChange}
onCopyToOtherSideClick={this.onCopyToOtherClick}
onSaveSideSettingsClick={this.onSaveSideSettingsClick}
onImportSideSettingsClick={this.onImportSideSettingsClick}
/>
));

View File

@@ -97,3 +97,27 @@ export const SwapIcon = () => (
<path d="M5.5 3.6v6.8L2.1 7l3.4-3.4M7 0L0 7l7 7V0zm4 0v14l7-7-7-7z" />
</svg>
);
export const SaveIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img">
<path
data-name="layer2"
d="M41.9 30a16 16 0 1 0 16 16 16 16 0 0 0-16-16zm7.4 15.4a2 2 0 0 1-2.8 0l-2.6-2.6V54a2 2 0 0 1-4 0V42.8l-2.6 2.6a2 2 0 1 1-2.8-2.8l7.4-7.4 7.4 7.4a2 2 0 0 1 0 2.8z"
></path>
<ellipse data-name="layer1" cx="29.1" cy="10" rx="23" ry="8"></ellipse>
<path
data-name="layer1"
d="M29.1 29h2.6a19.7 19.7 0 0 1 14.7-2.4c3.6-1.2 5.6-2.5 5.6-3.5v-6.3C47 20.4 38 22 29 22s-18-1.7-23-5.2V23c.1 2 9.6 6 23.1 6zm-7 17c0-.5 0-.9.1-1.4-6.9-.7-13-2.6-16.1-5.4V46c0 3.7 7.5 6.9 17.6 7.8a19.9 19.9 0 0 1-1.6-7.8zm.8-5.3a19.9 19.9 0 0 1 4.1-7.8c-8.7-.3-17.2-2.2-20.9-5.8V35c0 1.7 6.7 4.7 16.8 5.7z"
></path>
</svg>
);
export const ImportIcon = () => (
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" role="img">
<path
d="M49.189 31.052l-4.378-4.104L35 37.414V2h-6v35.066l-9.751-11.05-4.498 3.969 17.182 19.473 17.256-18.406z"
data-name="layer2"
></path>
<path d="M52 45v11H12V45H6v17h52V45h-6z" data-name="layer1"></path>
</svg>
);