2022-11-23 13:14:35 +00:00
|
|
|
import {ConfirmButton, FeedbackButton} from '@holllo/preact-components';
|
2022-10-27 13:48:34 +00:00
|
|
|
import {html} from 'htm/preact';
|
|
|
|
import {Component} from 'preact';
|
|
|
|
import browser from 'webextension-polyfill';
|
|
|
|
|
|
|
|
import {
|
|
|
|
matcherTypes,
|
|
|
|
narrowMatcherType,
|
|
|
|
narrowRedirectType,
|
|
|
|
parseRedirect,
|
2022-10-27 19:13:02 +00:00
|
|
|
Redirect,
|
2022-10-27 13:48:34 +00:00
|
|
|
RedirectParameters,
|
|
|
|
redirectTypes,
|
|
|
|
} from '../../redirect/exports.js';
|
2022-10-27 19:13:02 +00:00
|
|
|
import storage from '../../redirect/storage.js';
|
2022-11-23 13:14:35 +00:00
|
|
|
import {share} from '../../utilities/share-redirect.js';
|
2022-10-27 13:48:34 +00:00
|
|
|
|
|
|
|
type Props = {
|
2022-10-27 19:13:02 +00:00
|
|
|
redirect: Redirect;
|
|
|
|
removeRedirect: (id: number) => void;
|
|
|
|
saveRedirect: (redirect: Redirect) => void;
|
2022-10-27 13:48:34 +00:00
|
|
|
};
|
|
|
|
|
2022-10-27 19:33:46 +00:00
|
|
|
type State = {
|
|
|
|
hasBeenEdited: boolean;
|
|
|
|
} & RedirectParameters;
|
2022-10-27 13:48:34 +00:00
|
|
|
|
|
|
|
export default class Editor extends Component<Props, State> {
|
|
|
|
constructor(props: Props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
2022-10-27 19:33:46 +00:00
|
|
|
hasBeenEdited: false,
|
2022-10-27 19:13:02 +00:00
|
|
|
...props.redirect.parameters,
|
2022-10-27 13:48:34 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
onInput = (event: Event, input: 'matcher' | 'redirect') => {
|
|
|
|
const target = event.target as HTMLInputElement;
|
|
|
|
const value = target.value;
|
2022-10-27 19:33:46 +00:00
|
|
|
const newState: Partial<State> = {
|
|
|
|
hasBeenEdited: true,
|
|
|
|
};
|
2022-10-27 13:48:34 +00:00
|
|
|
|
|
|
|
if (input === 'matcher') {
|
2022-10-27 19:33:46 +00:00
|
|
|
newState.matcherValue = value;
|
2022-10-27 13:48:34 +00:00
|
|
|
} else if (input === 'redirect') {
|
2022-10-27 19:33:46 +00:00
|
|
|
newState.redirectValue = value;
|
2022-10-27 13:48:34 +00:00
|
|
|
} else {
|
|
|
|
throw new Error(`Unexpected input changed: ${input as string}`);
|
|
|
|
}
|
2022-10-27 19:33:46 +00:00
|
|
|
|
|
|
|
this.setState(newState);
|
2022-10-27 13:48:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
onSelectChange = (event: Event, select: 'matcher' | 'redirect') => {
|
|
|
|
const target = event.target as HTMLSelectElement;
|
|
|
|
const value = target.value;
|
2022-10-27 19:33:46 +00:00
|
|
|
const newState: Partial<State> = {
|
|
|
|
hasBeenEdited: true,
|
|
|
|
};
|
2022-10-27 13:48:34 +00:00
|
|
|
|
|
|
|
if (select === 'matcher' && narrowMatcherType(value)) {
|
2022-10-27 19:33:46 +00:00
|
|
|
newState.matcherType = value;
|
2022-10-27 13:48:34 +00:00
|
|
|
} else if (select === 'redirect' && narrowRedirectType(value)) {
|
2022-10-27 19:33:46 +00:00
|
|
|
newState.redirectType = value;
|
2022-10-27 13:48:34 +00:00
|
|
|
} else {
|
|
|
|
throw new Error(`${value} is not a valid MatcherType or RedirectType`);
|
|
|
|
}
|
2022-10-27 19:33:46 +00:00
|
|
|
|
|
|
|
this.setState(newState);
|
2022-10-27 13:48:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
onMatcherInput = (event: Event) => {
|
|
|
|
this.onInput(event, 'matcher');
|
|
|
|
};
|
|
|
|
|
|
|
|
onMatcherTypeChange = (event: Event) => {
|
|
|
|
this.onSelectChange(event, 'matcher');
|
|
|
|
};
|
|
|
|
|
|
|
|
onRedirectInput = (event: Event) => {
|
|
|
|
this.onInput(event, 'redirect');
|
|
|
|
};
|
|
|
|
|
|
|
|
onRedirectTypeChange = (event: Event) => {
|
|
|
|
this.onSelectChange(event, 'redirect');
|
|
|
|
};
|
|
|
|
|
2022-10-27 19:13:02 +00:00
|
|
|
parseRedirect = (): Redirect => {
|
2022-10-27 19:30:43 +00:00
|
|
|
const redirect = parseRedirect({
|
|
|
|
enabled: this.state.enabled,
|
|
|
|
id: this.state.id,
|
|
|
|
matcherType: this.state.matcherType,
|
|
|
|
matcherValue: this.state.matcherValue,
|
|
|
|
redirectType: this.state.redirectType,
|
|
|
|
redirectValue: this.state.redirectValue,
|
|
|
|
});
|
2022-10-27 13:48:34 +00:00
|
|
|
if (redirect === undefined) {
|
|
|
|
throw new Error('Failed to parse redirect');
|
|
|
|
}
|
|
|
|
|
|
|
|
return redirect;
|
|
|
|
};
|
|
|
|
|
|
|
|
remove = async () => {
|
2022-10-27 19:13:02 +00:00
|
|
|
const redirect = this.props.redirect;
|
|
|
|
await browser.storage.local.remove(redirect.idString());
|
|
|
|
this.props.removeRedirect(redirect.parameters.id);
|
2022-10-27 13:48:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
save = async () => {
|
|
|
|
const redirect = this.parseRedirect();
|
2022-10-27 19:13:02 +00:00
|
|
|
await storage.save(redirect);
|
2022-10-27 13:48:34 +00:00
|
|
|
this.props.saveRedirect(redirect);
|
2022-10-27 19:33:46 +00:00
|
|
|
this.setState({hasBeenEdited: false});
|
2022-10-27 13:48:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
toggleEnabled = async () => {
|
|
|
|
const enabled = !this.state.enabled;
|
2022-10-27 19:13:02 +00:00
|
|
|
const redirect = this.props.redirect;
|
|
|
|
const prepared = await storage.prepareForStorage(redirect);
|
|
|
|
prepared[redirect.idString()].enabled = enabled;
|
|
|
|
await browser.storage.local.set(prepared);
|
2022-10-27 13:48:34 +00:00
|
|
|
this.setState({enabled});
|
|
|
|
};
|
|
|
|
|
2022-11-23 13:14:35 +00:00
|
|
|
copyShareLink = async () => {
|
|
|
|
const link = share(this.state);
|
|
|
|
await navigator.clipboard.writeText(link);
|
|
|
|
};
|
|
|
|
|
2022-10-27 13:48:34 +00:00
|
|
|
render() {
|
2022-10-27 19:33:46 +00:00
|
|
|
const {
|
|
|
|
enabled,
|
|
|
|
hasBeenEdited,
|
|
|
|
matcherType,
|
|
|
|
matcherValue,
|
|
|
|
redirectType,
|
|
|
|
redirectValue,
|
|
|
|
} = this.state;
|
2022-10-27 13:48:34 +00:00
|
|
|
|
|
|
|
const matcherTypeOptions = matcherTypes.map(
|
|
|
|
(value) => html`<option value=${value}>${value}</option>`,
|
|
|
|
);
|
|
|
|
const redirectTypeOptions = redirectTypes.map(
|
|
|
|
(value) => html`<option value=${value}>${value}</option>`,
|
|
|
|
);
|
|
|
|
|
|
|
|
return html`
|
2022-10-27 19:33:46 +00:00
|
|
|
<div class="editor ${hasBeenEdited ? 'has-been-edited' : ''}">
|
2022-10-27 13:48:34 +00:00
|
|
|
<select
|
|
|
|
class="select"
|
|
|
|
title="Matcher Type"
|
|
|
|
value=${matcherType}
|
|
|
|
onChange=${this.onMatcherTypeChange}
|
|
|
|
>
|
|
|
|
${matcherTypeOptions}
|
|
|
|
</select>
|
|
|
|
|
|
|
|
<input
|
|
|
|
class="input"
|
|
|
|
onInput=${this.onMatcherInput}
|
|
|
|
placeholder="Matcher Value"
|
|
|
|
title="Matcher Value"
|
|
|
|
value=${matcherValue}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<span class="arrow-span">→</span>
|
|
|
|
|
|
|
|
<select
|
|
|
|
class="select"
|
|
|
|
title="Redirect Type"
|
|
|
|
value=${redirectType}
|
|
|
|
onChange=${this.onRedirectTypeChange}
|
|
|
|
>
|
|
|
|
${redirectTypeOptions}
|
|
|
|
</select>
|
|
|
|
|
|
|
|
<input
|
|
|
|
class="input"
|
|
|
|
onInput=${this.onRedirectInput}
|
|
|
|
placeholder="Redirect Value"
|
|
|
|
title="Redirect Value"
|
|
|
|
value=${redirectValue}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<${ConfirmButton}
|
|
|
|
attributes=${{title: 'Remove Redirect'}}
|
|
|
|
class="button destructive"
|
|
|
|
click=${this.remove}
|
|
|
|
confirmClass="confirm"
|
|
|
|
confirmText="✓"
|
|
|
|
text="✗"
|
|
|
|
timeout=${5 * 1000}
|
|
|
|
/>
|
|
|
|
<button class="button" title="Save Redirect" onClick=${this.save}>
|
|
|
|
💾
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
class="button ${enabled ? 'enabled' : 'disabled'}"
|
|
|
|
title="${enabled ? 'Currently Enabled' : 'Currently Disabled'}"
|
|
|
|
onClick=${this.toggleEnabled}
|
|
|
|
>
|
|
|
|
${enabled ? '●' : '○'}
|
|
|
|
</button>
|
2022-11-23 13:14:35 +00:00
|
|
|
<${FeedbackButton}
|
|
|
|
attributes=${{class: 'button share', title: 'Copy share link'}}
|
|
|
|
click=${this.copyShareLink}
|
|
|
|
feedbackText="💖"
|
|
|
|
text="📋"
|
|
|
|
timeout=${5 * 1000}
|
|
|
|
/>
|
2022-10-27 13:48:34 +00:00
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|