mirror of
https://github.com/thedevs-network/kutt-extension.git
synced 2026-02-03 05:43:24 +00:00
fix: linting issues
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
node_modules/
|
||||
dist/
|
||||
extension/
|
||||
.yarn/
|
||||
.pnp.js
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"webextensions": true
|
||||
},
|
||||
"extends": [
|
||||
"@abhijithvijayan/eslint-config/typescript",
|
||||
"@abhijithvijayan/eslint-config/node",
|
||||
"@abhijithvijayan/eslint-config/react"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"no-shadow": ["error", {
|
||||
"builtinGlobals": false,
|
||||
"hoist": "functions",
|
||||
"allow": []
|
||||
}],
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"jsx-a11y/label-has-associated-control": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"react/no-array-index-key": "warn",
|
||||
"node/no-unsupported-features/es-syntax": ["error", {
|
||||
"ignores": ["modules"]
|
||||
}]
|
||||
},
|
||||
"settings": {
|
||||
"node": {
|
||||
"tryExtensions": [".tsx"] // append tsx to the list as well
|
||||
}
|
||||
}
|
||||
}
|
||||
48
eslint.config.mjs
Normal file
48
eslint.config.mjs
Normal file
@@ -0,0 +1,48 @@
|
||||
import nodeConfig from '@abhijithvijayan/eslint-config/node';
|
||||
import tsConfig from '@abhijithvijayan/eslint-config/typescript';
|
||||
import reactConfig from '@abhijithvijayan/eslint-config/react';
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
'node_modules/**',
|
||||
'dist/**',
|
||||
'extension/**',
|
||||
'.yarn/**',
|
||||
'.pnp.js',
|
||||
'*.js',
|
||||
'*.mjs',
|
||||
'vite.config.ts',
|
||||
],
|
||||
},
|
||||
...nodeConfig({
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
}),
|
||||
...tsConfig({
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
}),
|
||||
...reactConfig({
|
||||
files: ['**/*.tsx'],
|
||||
}),
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
// Disable due to resolver issues in ESM
|
||||
'import-x/no-duplicates': 'off',
|
||||
// Browser extension code uses browser APIs, not Node.js
|
||||
'n/no-unsupported-features/node-builtins': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.tsx'],
|
||||
rules: {
|
||||
'react/jsx-props-no-spreading': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/no-array-index-key': 'warn',
|
||||
'jsx-a11y/label-has-associated-control': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -340,8 +340,7 @@ type MessageRequest = {
|
||||
browser.runtime.onMessage.addListener(
|
||||
(message: unknown, _sender: Runtime.MessageSender): void | Promise<any> => {
|
||||
const request = message as MessageRequest;
|
||||
// eslint-disable-next-line consistent-return
|
||||
// eslint-disable-next-line default-case
|
||||
|
||||
switch (request.action) {
|
||||
case constants.CHECK_API_KEY: {
|
||||
return checkApiKey(request.params);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type {JSX} from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
import {
|
||||
@@ -31,7 +32,7 @@ import Table from './Table';
|
||||
|
||||
import styles from './History.module.scss';
|
||||
|
||||
function History() {
|
||||
function History(): JSX.Element {
|
||||
const [, shortenedLinksDispatch] = useShortenedLinks();
|
||||
const [, extensionSettingsDispatch] = useExtensionSettings();
|
||||
const [requestStatusState, requestStatusDispatch] = useRequestStatus();
|
||||
@@ -54,7 +55,8 @@ function History() {
|
||||
(advancedSettings &&
|
||||
(settings?.host as string) &&
|
||||
isValidUrl(settings.host as string) && {
|
||||
hostDomain: (settings.host as string)
|
||||
hostDomain:
|
||||
(settings.host as string)
|
||||
.replace('http://', '')
|
||||
.replace('https://', '')
|
||||
.replace('www.', '')
|
||||
@@ -137,7 +139,7 @@ function History() {
|
||||
<div className={styles.historyContent}>
|
||||
<Header subtitle="Recent Links" hostUrl={hostUrl} />
|
||||
|
||||
{/* eslint-disable-next-line no-nested-ternary */}
|
||||
{}
|
||||
{!requestStatusState.loading ? (
|
||||
!errored.error ? (
|
||||
<Table />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {QRCodeSVG} from 'qrcode.react';
|
||||
import type {JSX} from 'react';
|
||||
import {Dispatch, SetStateAction} from 'react';
|
||||
|
||||
import styles from './Modal.module.scss';
|
||||
@@ -8,16 +9,24 @@ type Props = {
|
||||
setModalView: Dispatch<SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
function Modal({link, setModalView}: Props) {
|
||||
function Modal({link, setModalView}: Props): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={styles.modalOverlay}
|
||||
onClick={(): void => setModalView(false)}
|
||||
onKeyDown={(e): void => {
|
||||
if (e.key === 'Escape') setModalView(false);
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<div
|
||||
className={styles.modalContent}
|
||||
onClick={(e): void => e.stopPropagation()}
|
||||
onKeyDown={(e): void => e.stopPropagation()}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className={styles.qrCodeWrapper}>
|
||||
<QRCodeSVG size={196} value={link} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import type {JSX} from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
@@ -13,7 +14,7 @@ import Modal from './Modal';
|
||||
|
||||
import styles from './Table.module.scss';
|
||||
|
||||
function Table() {
|
||||
function Table(): JSX.Element {
|
||||
const [shortenedLinksState, shortenedLinksDispatch] = useShortenedLinks();
|
||||
const [QRView, setQRView] = useState<boolean>(false);
|
||||
const [copied, setCopied] = useState<boolean>(false);
|
||||
@@ -72,8 +73,7 @@ function Table() {
|
||||
</thead>
|
||||
<tbody className={styles.tbody}>
|
||||
{!(shortenedLinksState.total === 0) ? (
|
||||
shortenedLinksState.items.map((item) => {
|
||||
return (
|
||||
shortenedLinksState.items.map((item) => (
|
||||
<tr key={item.id} className={styles.tr}>
|
||||
<td className={clsx(styles.td, styles.tdOriginal)}>
|
||||
<a
|
||||
@@ -116,18 +116,14 @@ function Table() {
|
||||
) : (
|
||||
<CopyToClipboard
|
||||
text={item.link}
|
||||
onCopy={(): void => {
|
||||
return handleCopyToClipboard(item.id);
|
||||
}}
|
||||
onCopy={(): void => handleCopyToClipboard(item.id)}
|
||||
>
|
||||
<Icon name="copy" className={styles.actionIcon} />
|
||||
</CopyToClipboard>
|
||||
)}
|
||||
|
||||
<Icon
|
||||
onClick={(): void =>
|
||||
handleQRCodeViewToggle(item.id)
|
||||
}
|
||||
onClick={(): void => handleQRCodeViewToggle(item.id)}
|
||||
className={styles.actionIcon}
|
||||
name="qrcode"
|
||||
/>
|
||||
@@ -140,8 +136,7 @@ function Table() {
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td className={styles.emptyRow}>No URLs History</td>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type {JSX} from 'react';
|
||||
import {memo} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
@@ -8,7 +9,7 @@ import Icon from '../components/Icon';
|
||||
|
||||
import styles from './Footer.module.scss';
|
||||
|
||||
function Footer() {
|
||||
function Footer(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<footer className={styles.footer}>
|
||||
@@ -25,11 +26,26 @@ function Footer() {
|
||||
className={styles.ratingLink}
|
||||
>
|
||||
<div className={styles.starsContainer}>
|
||||
<Icon className={clsx(styles.starIcon, styles.gray)} name="star-white" />
|
||||
<Icon className={clsx(styles.starIcon, styles.gray)} name="star-white" />
|
||||
<Icon className={clsx(styles.starIcon, styles.gray)} name="star-white" />
|
||||
<Icon className={clsx(styles.starIcon, styles.gray)} name="star-white" />
|
||||
<Icon className={clsx(styles.starIcon, styles.gray)} name="star-white" />
|
||||
<Icon
|
||||
className={clsx(styles.starIcon, styles.gray)}
|
||||
name="star-white"
|
||||
/>
|
||||
<Icon
|
||||
className={clsx(styles.starIcon, styles.gray)}
|
||||
name="star-white"
|
||||
/>
|
||||
<Icon
|
||||
className={clsx(styles.starIcon, styles.gray)}
|
||||
name="star-white"
|
||||
/>
|
||||
<Icon
|
||||
className={clsx(styles.starIcon, styles.gray)}
|
||||
name="star-white"
|
||||
/>
|
||||
<Icon
|
||||
className={clsx(styles.starIcon, styles.gray)}
|
||||
name="star-white"
|
||||
/>
|
||||
</div>
|
||||
<p className={styles.ratingText}>Rate on Store</p>
|
||||
</a>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import {isNull, isUndefined} from '@abhijithvijayan/ts-utils';
|
||||
import type {JSX} from 'react';
|
||||
import {useState, useEffect, useRef, ChangeEvent} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import {useExtensionSettings} from '../contexts/extension-settings-context';
|
||||
import {updateExtensionSettings, clearExtensionSettings} from '../util/settings';
|
||||
import {
|
||||
updateExtensionSettings,
|
||||
clearExtensionSettings,
|
||||
} from '../util/settings';
|
||||
import {CHECK_API_KEY} from '../Background/constants';
|
||||
import messageUtil from '../util/mesageUtil';
|
||||
import {isValidUrl} from '../util/link';
|
||||
@@ -38,12 +42,10 @@ type FormValidity = {
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const onSave = (values: OptionsFormValuesProperties): Promise<any> => {
|
||||
const onSave = (values: OptionsFormValuesProperties): Promise<any> =>
|
||||
// should always return a Promise
|
||||
return updateExtensionSettings(values); // update local settings
|
||||
};
|
||||
|
||||
function Form() {
|
||||
updateExtensionSettings(values); // update local settings
|
||||
function Form(): JSX.Element {
|
||||
const extensionSettingsState = useExtensionSettings()[0];
|
||||
const hostInputRef = useRef<HTMLInputElement>(null);
|
||||
const [submitting, setSubmitting] = useState<boolean>(false);
|
||||
@@ -86,51 +88,83 @@ function Form() {
|
||||
}, [formValues]);
|
||||
|
||||
function handleApiKeyInputChange(apikey: string): void {
|
||||
setFormValues((prev) => ({...prev, apikey}));
|
||||
setFormValues((prev) => {
|
||||
return {...prev, apikey};
|
||||
});
|
||||
// ToDo: Remove special symbols
|
||||
|
||||
if (!(apikey.trim().length > 0)) {
|
||||
setFormErrors((prev) => ({...prev, apikey: 'API key missing'}));
|
||||
setFormValidity((prev) => ({...prev, apikey: false}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, apikey: 'API key missing'};
|
||||
});
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, apikey: false};
|
||||
});
|
||||
} else if (apikey && apikey.trim().length < 40) {
|
||||
setFormErrors((prev) => ({...prev, apikey: 'API key must be 40 characters'}));
|
||||
setFormValidity((prev) => ({...prev, apikey: false}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, apikey: 'API key must be 40 characters'};
|
||||
});
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, apikey: false};
|
||||
});
|
||||
} else if (apikey && apikey.trim().length > 40) {
|
||||
setFormErrors((prev) => ({...prev, apikey: 'API key cannot exceed 40 characters'}));
|
||||
setFormValidity((prev) => ({...prev, apikey: false}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, apikey: 'API key cannot exceed 40 characters'};
|
||||
});
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, apikey: false};
|
||||
});
|
||||
} else {
|
||||
setFormErrors((prev) => {
|
||||
const {apikey: _, ...rest} = prev;
|
||||
return rest;
|
||||
});
|
||||
setFormValidity((prev) => ({...prev, apikey: true}));
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, apikey: true};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleHostUrlInputChange(host: string): void {
|
||||
if (!formValues.advanced) {
|
||||
setFormErrors((prev) => ({...prev, host: 'Enable Advanced Options first'}));
|
||||
setFormValidity((prev) => ({...prev, host: false}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, host: 'Enable Advanced Options first'};
|
||||
});
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, host: false};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setFormValues((prev) => ({...prev, host}));
|
||||
setFormValues((prev) => {
|
||||
return {...prev, host};
|
||||
});
|
||||
|
||||
if (!(host.trim().length > 0)) {
|
||||
setFormErrors((prev) => ({...prev, host: 'Custom URL cannot be empty'}));
|
||||
setFormValidity((prev) => ({...prev, host: false}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, host: 'Custom URL cannot be empty'};
|
||||
});
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, host: false};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValidUrl(host.trim()) || host.trim().length < 10) {
|
||||
setFormErrors((prev) => ({...prev, host: 'Please enter a valid url'}));
|
||||
setFormValidity((prev) => ({...prev, host: false}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, host: 'Please enter a valid url'};
|
||||
});
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, host: false};
|
||||
});
|
||||
} else {
|
||||
setFormErrors((prev) => {
|
||||
const {host: _, ...rest} = prev;
|
||||
return rest;
|
||||
});
|
||||
setFormValidity((prev) => ({...prev, host: true}));
|
||||
setFormValidity((prev) => {
|
||||
return {...prev, host: true};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,8 +224,7 @@ function Form() {
|
||||
<span className={styles.labelLinkWrapper}>
|
||||
<a
|
||||
href={`${
|
||||
(formValues.advanced && formValues.host) ||
|
||||
Kutt.hostUrl
|
||||
(formValues.advanced && formValues.host) || Kutt.hostUrl
|
||||
}/login`}
|
||||
target="blank"
|
||||
rel="nofollow noopener noreferrer"
|
||||
@@ -258,7 +291,12 @@ function Form() {
|
||||
</button>
|
||||
|
||||
{!isNull(errored.error) && (
|
||||
<div className={clsx(styles.validationFeedback, errored.error ? styles.error : styles.success)}>
|
||||
<div
|
||||
className={clsx(
|
||||
styles.validationFeedback,
|
||||
errored.error ? styles.error : styles.success
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
className={styles.feedbackIcon}
|
||||
name={errored.error ? 'cross' : 'tick'}
|
||||
@@ -294,7 +332,9 @@ function Form() {
|
||||
type="checkbox"
|
||||
checked={formValues.history}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setFormValues((prev) => ({...prev, history: e.target.checked}));
|
||||
setFormValues((prev) => {
|
||||
return {...prev, history: e.target.checked};
|
||||
});
|
||||
}}
|
||||
className={styles.toggleInput}
|
||||
/>
|
||||
@@ -308,7 +348,8 @@ function Form() {
|
||||
<span className={styles.infoIcon}>
|
||||
<Icon name="info" />
|
||||
<span className={styles.tooltip}>
|
||||
Returns the existing short link if the same URL was shortened before
|
||||
Returns the existing short link if the same URL was shortened
|
||||
before
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@@ -327,7 +368,9 @@ function Form() {
|
||||
type="checkbox"
|
||||
checked={formValues.reuse}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setFormValues((prev) => ({...prev, reuse: e.target.checked}));
|
||||
setFormValues((prev) => {
|
||||
return {...prev, reuse: e.target.checked};
|
||||
});
|
||||
}}
|
||||
className={styles.toggleInput}
|
||||
/>
|
||||
@@ -360,7 +403,9 @@ function Form() {
|
||||
type="checkbox"
|
||||
checked={formValues.advanced}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setFormValues((prev) => ({...prev, advanced: e.target.checked}));
|
||||
setFormValues((prev) => {
|
||||
return {...prev, advanced: e.target.checked};
|
||||
});
|
||||
if (e.target.checked) {
|
||||
setTimeout(() => hostInputRef.current?.focus(), 350);
|
||||
}
|
||||
@@ -371,7 +416,12 @@ function Form() {
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div className={clsx(styles.advancedSection, !formValues.advanced && styles.hidden)}>
|
||||
<div
|
||||
className={clsx(
|
||||
styles.advancedSection,
|
||||
!formValues.advanced && styles.hidden
|
||||
)}
|
||||
>
|
||||
<div className={styles.inputGroup}>
|
||||
<label htmlFor="host" className={styles.label}>
|
||||
<span className={styles.labelWithInfo}>
|
||||
@@ -379,7 +429,8 @@ function Form() {
|
||||
<span className={styles.infoIcon}>
|
||||
<Icon name="info" />
|
||||
<span className={styles.tooltip}>
|
||||
URL of your self-hosted Kutt instance (e.g., https://kutt.example.com)
|
||||
URL of your self-hosted Kutt instance (e.g.,
|
||||
https://kutt.example.com)
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@@ -424,14 +475,29 @@ function Form() {
|
||||
</div>
|
||||
|
||||
{showResetConfirm && (
|
||||
<div className={styles.modalOverlay} onClick={() => setShowResetConfirm(false)}>
|
||||
<div className={styles.modal} onClick={(e) => e.stopPropagation()}>
|
||||
<div
|
||||
className={styles.modalOverlay}
|
||||
onClick={() => setShowResetConfirm(false)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape') setShowResetConfirm(false);
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<div
|
||||
className={styles.modal}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className={styles.modalHeader}>
|
||||
<Icon name="info" className={styles.modalIcon} />
|
||||
<span className={styles.modalTitle}>Reset Settings?</span>
|
||||
</div>
|
||||
<p className={styles.modalText}>
|
||||
This will permanently delete your API key and all preferences. You will need to reconfigure the extension.
|
||||
This will permanently delete your API key and all preferences. You
|
||||
will need to reconfigure the extension.
|
||||
</p>
|
||||
<div className={styles.modalActions}>
|
||||
<button
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type {JSX} from 'react';
|
||||
import {memo} from 'react';
|
||||
|
||||
import {Kutt} from '../Background';
|
||||
@@ -9,7 +10,10 @@ type Props = {
|
||||
hostUrl?: string;
|
||||
};
|
||||
|
||||
function Header({subtitle = 'Extension Settings', hostUrl = Kutt.hostUrl}: Props) {
|
||||
function Header({
|
||||
subtitle = 'Extension Settings',
|
||||
hostUrl = Kutt.hostUrl,
|
||||
}: Props): JSX.Element {
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<a
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type {JSX} from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
import {getExtensionSettings} from '../util/settings';
|
||||
@@ -21,7 +22,7 @@ import Form from './Form';
|
||||
|
||||
import styles from './Options.module.scss';
|
||||
|
||||
function Options() {
|
||||
function Options(): JSX.Element {
|
||||
const [, extensionSettingsDispatch] = useExtensionSettings();
|
||||
const [requestStatusState, requestStatusDispatch] = useRequestStatus();
|
||||
const [hostUrl, setHostUrl] = useState<string>(Kutt.hostUrl);
|
||||
@@ -36,7 +37,8 @@ function Options() {
|
||||
(advancedSettings &&
|
||||
(settings?.host as string) &&
|
||||
isValidUrl(settings.host as string) && {
|
||||
hostDomain: (settings.host as string)
|
||||
hostDomain:
|
||||
(settings.host as string)
|
||||
.replace('http://', '')
|
||||
.replace('https://', '')
|
||||
.replace('www.', '')
|
||||
@@ -49,7 +51,10 @@ function Options() {
|
||||
|
||||
// inject existing keys (if field doesn't exist, use default)
|
||||
// For history: default to true for new users, but respect existing user preference
|
||||
const historyEnabled = Object.prototype.hasOwnProperty.call(settings, 'history')
|
||||
const historyEnabled = Object.prototype.hasOwnProperty.call(
|
||||
settings,
|
||||
'history'
|
||||
)
|
||||
? (settings.history as boolean)
|
||||
: true;
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type {JSX} from 'react';
|
||||
import {useState, useRef, useEffect, type ChangeEvent} from 'react';
|
||||
import {
|
||||
EMPTY_STRING,
|
||||
isEmpty,
|
||||
isNull,
|
||||
get,
|
||||
} from '@abhijithvijayan/ts-utils';
|
||||
import {EMPTY_STRING, isEmpty, isNull, get} from '@abhijithvijayan/ts-utils';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import {useExtensionSettings} from '../contexts/extension-settings-context';
|
||||
@@ -30,7 +26,7 @@ export enum CONSTANTS {
|
||||
DefaultDomainId = 'default',
|
||||
}
|
||||
|
||||
function Form() {
|
||||
function Form(): JSX.Element {
|
||||
const extensionSettingsState = useExtensionSettings()[0];
|
||||
const requestStatusDispatch = useRequestStatus()[1];
|
||||
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||
@@ -45,13 +41,17 @@ function Form() {
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent): void {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsDropdownOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
return (): void =>
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const [formState, setFormState] = useState({
|
||||
@@ -68,9 +68,7 @@ function Form() {
|
||||
}>({});
|
||||
|
||||
const isFormValid: boolean =
|
||||
!formErrors.customurl &&
|
||||
!formErrors.password &&
|
||||
true;
|
||||
!formErrors.customurl && !formErrors.password && true;
|
||||
|
||||
async function handleFormSubmit(): Promise<void> {
|
||||
// enable loading screen
|
||||
@@ -131,7 +129,9 @@ function Form() {
|
||||
},
|
||||
});
|
||||
// reset form fields (keep domain selection)
|
||||
setFormState((prev) => ({...prev, customurl: '', password: ''}));
|
||||
setFormState((prev) => {
|
||||
return {...prev, customurl: '', password: ''};
|
||||
});
|
||||
setFormErrors({});
|
||||
} else {
|
||||
// errored
|
||||
@@ -146,51 +146,76 @@ function Form() {
|
||||
}
|
||||
|
||||
function handleCustomUrlInputChange(url: string): void {
|
||||
setFormState((prev) => ({...prev, customurl: url}));
|
||||
setFormState((prev) => {
|
||||
return {...prev, customurl: url};
|
||||
});
|
||||
// ToDo: Remove special symbols
|
||||
|
||||
if (url.length > 0 && url.length < 3) {
|
||||
setFormErrors((prev) => ({
|
||||
setFormErrors((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
customurl: 'Custom URL must be at-least 3 characters',
|
||||
}));
|
||||
};
|
||||
});
|
||||
} else {
|
||||
setFormErrors((prev) => ({...prev, customurl: undefined}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, customurl: undefined};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handlePasswordInputChange(password: string): void {
|
||||
setFormState((prev) => ({...prev, password}));
|
||||
setFormState((prev) => {
|
||||
return {...prev, password};
|
||||
});
|
||||
// ToDo: Remove special symbols
|
||||
|
||||
if (password.length > 0 && password.length < 3) {
|
||||
setFormErrors((prev) => ({
|
||||
setFormErrors((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
password: 'Password must be at-least 3 characters',
|
||||
}));
|
||||
};
|
||||
});
|
||||
} else {
|
||||
setFormErrors((prev) => ({...prev, password: undefined}));
|
||||
setFormErrors((prev) => {
|
||||
return {...prev, password: undefined};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.formContainer}>
|
||||
<div className={styles.formGroup}>
|
||||
<label className={styles.label}>
|
||||
Domain
|
||||
</label>
|
||||
<label className={styles.label}>Domain</label>
|
||||
|
||||
<div className={styles.dropdown} ref={dropdownRef}>
|
||||
<button
|
||||
type="button"
|
||||
className={clsx(styles.dropdownTrigger, isDropdownOpen && styles.open)}
|
||||
className={clsx(
|
||||
styles.dropdownTrigger,
|
||||
isDropdownOpen && styles.open
|
||||
)}
|
||||
onClick={() => !isSubmitting && setIsDropdownOpen(!isDropdownOpen)}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
<span className={clsx(styles.dropdownValue, formState.domain && styles.hasValue)}>
|
||||
{domainOptions.find(({value}) => value === formState.domain)?.option || 'Select domain'}
|
||||
<span
|
||||
className={clsx(
|
||||
styles.dropdownValue,
|
||||
formState.domain && styles.hasValue
|
||||
)}
|
||||
>
|
||||
{domainOptions.find(({value}) => value === formState.domain)
|
||||
?.option || 'Select domain'}
|
||||
</span>
|
||||
<Icon name="chevron-down" className={clsx(styles.dropdownIcon, isDropdownOpen && styles.open)} />
|
||||
<Icon
|
||||
name="chevron-down"
|
||||
className={clsx(
|
||||
styles.dropdownIcon,
|
||||
isDropdownOpen && styles.open
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{isDropdownOpen && (
|
||||
@@ -206,7 +231,9 @@ function Form() {
|
||||
formState.domain === value && styles.selected
|
||||
)}
|
||||
onClick={() => {
|
||||
setFormState((prev) => ({...prev, domain: value}));
|
||||
setFormState((prev) => {
|
||||
return {...prev, domain: value};
|
||||
});
|
||||
setIsDropdownOpen(false);
|
||||
}}
|
||||
>
|
||||
@@ -233,7 +260,10 @@ function Form() {
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
spellCheck="false"
|
||||
className={clsx(styles.input, formErrors.customurl && styles.inputError)}
|
||||
className={clsx(
|
||||
styles.input,
|
||||
formErrors.customurl && styles.inputError
|
||||
)}
|
||||
/>
|
||||
|
||||
<span className={styles.errorText}>{formErrors.customurl}</span>
|
||||
@@ -267,7 +297,10 @@ function Form() {
|
||||
handlePasswordInputChange(e.target.value);
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
className={clsx(styles.input, formErrors.password && styles.inputError)}
|
||||
className={clsx(
|
||||
styles.input,
|
||||
formErrors.password && styles.inputError
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {isNull, EMPTY_STRING} from '@abhijithvijayan/ts-utils';
|
||||
import type {JSX} from 'react';
|
||||
import {useState} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
import Icon from '../components/Icon';
|
||||
import styles from './Header.module.scss';
|
||||
|
||||
function Header() {
|
||||
function Header(): JSX.Element {
|
||||
const [extensionSettingsState, extensionSettingsDispatch] =
|
||||
useExtensionSettings();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {isNull, EMPTY_STRING} from '@abhijithvijayan/ts-utils';
|
||||
import type {JSX} from 'react';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
import {Kutt, UserSettingsResponseProperties} from '../Background';
|
||||
@@ -25,7 +26,7 @@ import Form, { CONSTANTS } from './Form';
|
||||
|
||||
import styles from './Popup.module.scss';
|
||||
|
||||
function Popup() {
|
||||
function Popup(): JSX.Element {
|
||||
const [extensionSettingsState, extensionSettingsDispatch] =
|
||||
useExtensionSettings();
|
||||
const [requestStatusState, requestStatusDispatch] = useRequestStatus();
|
||||
@@ -54,9 +55,7 @@ function Popup() {
|
||||
});
|
||||
|
||||
// Open options page
|
||||
setTimeout(() => {
|
||||
return openExtOptionsPage();
|
||||
}, 1300);
|
||||
setTimeout(() => openExtOptionsPage(), 1300);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -75,7 +74,8 @@ function Popup() {
|
||||
isValidUrl(settings.host as string)
|
||||
) {
|
||||
defaultHost = {
|
||||
hostDomain: (settings.host as string)
|
||||
hostDomain:
|
||||
(settings.host as string)
|
||||
.replace('http://', EMPTY_STRING)
|
||||
.replace('https://', EMPTY_STRING)
|
||||
.replace('www.', EMPTY_STRING)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import type {JSX} from 'react';
|
||||
import {useState, useEffect} from 'react';
|
||||
import {QRCodeSVG} from 'qrcode.react';
|
||||
import clsx from 'clsx';
|
||||
@@ -14,7 +15,7 @@ export type ProcessedRequestProperties = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
function ResponseBody() {
|
||||
function ResponseBody(): JSX.Element {
|
||||
const [{error, message}] = useRequestStatus();
|
||||
const [copied, setCopied] = useState<boolean>(false);
|
||||
const [QRView, setQRView] = useState<boolean>(false);
|
||||
@@ -42,29 +43,29 @@ function ResponseBody() {
|
||||
<Icon
|
||||
className={clsx(styles.icon, styles.qrIcon)}
|
||||
name="qrcode"
|
||||
onClick={(): void => {
|
||||
return setQRView(!QRView);
|
||||
}}
|
||||
onClick={(): void => setQRView(!QRView)}
|
||||
/>
|
||||
|
||||
{!copied ? (
|
||||
<CopyToClipboard
|
||||
text={message}
|
||||
onCopy={(): void => {
|
||||
return setCopied(true);
|
||||
}}
|
||||
onCopy={(): void => setCopied(true)}
|
||||
>
|
||||
<Icon className={clsx(styles.icon, styles.copyIcon)} name="copy" />
|
||||
<Icon
|
||||
className={clsx(styles.icon, styles.copyIcon)}
|
||||
name="copy"
|
||||
/>
|
||||
</CopyToClipboard>
|
||||
) : (
|
||||
<Icon className={clsx(styles.icon, styles.copyIcon)} name="tick" />
|
||||
<Icon
|
||||
className={clsx(styles.icon, styles.copyIcon)}
|
||||
name="tick"
|
||||
/>
|
||||
)}
|
||||
|
||||
<CopyToClipboard
|
||||
text={message}
|
||||
onCopy={(): void => {
|
||||
return setCopied(true);
|
||||
}}
|
||||
onCopy={(): void => setCopied(true)}
|
||||
>
|
||||
<h1 className={styles.link}>{removeProtocol(message)}</h1>
|
||||
</CopyToClipboard>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type {ReactNode} from 'react';
|
||||
import type {JSX, ReactNode} from 'react';
|
||||
import styles from './BodyWrapper.module.scss';
|
||||
|
||||
type WrapperProperties = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
function BodyWrapper({children}: WrapperProperties) {
|
||||
function BodyWrapper({children}: WrapperProperties): JSX.Element {
|
||||
return <div className={styles.wrapper}>{children}</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const ChevronDown: React.FC = () => {
|
||||
return (
|
||||
const ChevronDown: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -15,6 +14,5 @@ const ChevronDown: React.FC = () => {
|
||||
<path d="M6 9l6 6 6-6" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ChevronDown);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Clock: React.FC = () => {
|
||||
return (
|
||||
const Clock: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -17,6 +16,5 @@ const Clock: React.FC = () => {
|
||||
<path d="M12 6v6l4 2" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Clock);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Copy: React.FC = () => {
|
||||
return (
|
||||
const Copy: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -17,6 +16,5 @@ const Copy: React.FC = () => {
|
||||
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Copy);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Cross: React.FC = () => {
|
||||
return (
|
||||
const Cross: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const Cross: React.FC = () => {
|
||||
<path d="M18 6L6 18M6 6l12 12" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Cross);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Eye: React.FC = () => {
|
||||
return (
|
||||
const Eye: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -17,6 +16,5 @@ const Eye: React.FC = () => {
|
||||
<circle cx={12} cy={12} r={3} />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Eye);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const EyeClosed: React.FC = () => {
|
||||
return (
|
||||
const EyeClosed: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const EyeClosed: React.FC = () => {
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24M1 1l22 22" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(EyeClosed);
|
||||
|
||||
@@ -48,8 +48,8 @@ type Props = {
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const Icon: React.FC<Props> = ({name, ...rest}) => {
|
||||
return <div {...rest}>{React.createElement(icons[name])}</div>;
|
||||
};
|
||||
const Icon: React.FC<Props> = ({name, ...rest}) => (
|
||||
<div {...rest}>{React.createElement(icons[name])}</div>
|
||||
);
|
||||
|
||||
export default Icon;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Info: React.FC = () => {
|
||||
return (
|
||||
const Info: React.FC = () => (
|
||||
<svg
|
||||
width={14}
|
||||
height={14}
|
||||
@@ -17,6 +16,5 @@ const Info: React.FC = () => {
|
||||
<line x1="12" y1="8" x2="12.01" y2="8" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Info);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const QRCode: React.FC = () => {
|
||||
return (
|
||||
const QRCode: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const QRCode: React.FC = () => {
|
||||
<path d="M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(QRCode);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Refresh: React.FC = () => {
|
||||
return (
|
||||
const Refresh: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -17,6 +16,5 @@ const Refresh: React.FC = () => {
|
||||
<path d="M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Refresh);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
return (
|
||||
const Settings: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -17,6 +16,5 @@ const Settings: React.FC = () => {
|
||||
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Settings);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Spinner: React.FC = () => {
|
||||
return (
|
||||
const Spinner: React.FC = () => (
|
||||
<>
|
||||
<svg
|
||||
id="spinner"
|
||||
@@ -27,6 +26,5 @@ const Spinner: React.FC = () => {
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Spinner);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const StarWhite: React.FC = () => {
|
||||
return (
|
||||
const StarWhite: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const StarWhite: React.FC = () => {
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(StarWhite);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const StarYellow: React.FC = () => {
|
||||
return (
|
||||
const StarYellow: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const StarYellow: React.FC = () => {
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(StarYellow);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Tick: React.FC = () => {
|
||||
return (
|
||||
const Tick: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const Tick: React.FC = () => {
|
||||
<path d="M20 6L9 17l-5-5" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Tick);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
const Zap: React.FC = () => {
|
||||
return (
|
||||
const Zap: React.FC = () => (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -16,6 +15,5 @@ const Zap: React.FC = () => {
|
||||
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Zap);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type {JSX} from 'react';
|
||||
import Icon from './Icon';
|
||||
import styles from './Loader.module.scss';
|
||||
|
||||
function Loader() {
|
||||
function Loader(): JSX.Element {
|
||||
return (
|
||||
<div className={styles.loader}>
|
||||
<Icon name="spinner" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type {JSX} from 'react';
|
||||
import {createContext, useReducer, useContext, type ReactNode} from 'react';
|
||||
|
||||
import {Kutt} from '../Background';
|
||||
@@ -126,7 +126,7 @@ type ExtensionSettingsProviderProps = {
|
||||
|
||||
function ExtensionSettingsProvider({
|
||||
children,
|
||||
}: ExtensionSettingsProviderProps) {
|
||||
}: ExtensionSettingsProviderProps): JSX.Element {
|
||||
const [state, dispatch] = useReducer(extensionSettingsReducer, initialValues);
|
||||
|
||||
return (
|
||||
@@ -138,6 +138,6 @@ function ExtensionSettingsProvider({
|
||||
</ExtensionSettingsStateContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export {ExtensionSettingsProvider, useExtensionSettings};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type {JSX} from 'react';
|
||||
import {createContext, useReducer, useContext, type ReactNode} from 'react';
|
||||
|
||||
export enum RequestStatusActionTypes {
|
||||
@@ -89,7 +89,9 @@ type RequestStatusProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
function RequestStatusProvider({children}: RequestStatusProviderProps) {
|
||||
function RequestStatusProvider({
|
||||
children,
|
||||
}: RequestStatusProviderProps): JSX.Element {
|
||||
const [state, dispatch] = useReducer(requestStatusReducer, initialValues);
|
||||
|
||||
return (
|
||||
@@ -101,6 +103,6 @@ function RequestStatusProvider({children}: RequestStatusProviderProps) {
|
||||
</RequestStatusStateContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export {RequestStatusProvider, useRequestStatus};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type {JSX} from 'react';
|
||||
import {createContext, useContext, useReducer, type ReactNode} from 'react';
|
||||
|
||||
import {UserShortenedLinkStats} from '../Background';
|
||||
@@ -96,7 +96,9 @@ type ShortenedLinksProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
function ShortenedLinksProvider({children}: ShortenedLinksProviderProps) {
|
||||
function ShortenedLinksProvider({
|
||||
children,
|
||||
}: ShortenedLinksProviderProps): JSX.Element {
|
||||
const [state, dispatch] = useReducer(shortenedLinksReducer, initialValues);
|
||||
|
||||
return (
|
||||
@@ -108,6 +110,6 @@ function ShortenedLinksProvider({children}: ShortenedLinksProviderProps) {
|
||||
</ShortenedLinksStateContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export {useShortenedLinks, ShortenedLinksProvider};
|
||||
|
||||
@@ -37,7 +37,7 @@ export function detectBrowser(): Browser | null {
|
||||
}
|
||||
|
||||
const [name, match] = matchedRule;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
let versionParts = match[1] && match[1].split(/[._]/).slice(0, 3);
|
||||
if (!versionParts) {
|
||||
versionParts = [];
|
||||
|
||||
@@ -24,7 +24,6 @@ export function getExtensionSettings(): Promise<{[s: string]: any}> {
|
||||
return browser.storage.local.get('settings');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export async function updateExtensionSettings(newFields?: {
|
||||
[s: string]: any;
|
||||
}): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user