diff --git a/source/Background/index.ts b/source/Background/index.ts index 027eb1d..a496759 100644 --- a/source/Background/index.ts +++ b/source/Background/index.ts @@ -18,6 +18,11 @@ export enum StoreLinks { firefox = 'https://addons.mozilla.org/en-US/firefox/addon/kutt/reviews/', } +export type ErrorStateProperties = { + error: boolean | null; + message: string; +}; + type ShortenUrlBodyProperties = { target: string; password?: string; diff --git a/source/Options/Form.tsx b/source/Options/Form.tsx index 44fff1c..072ce09 100644 --- a/source/Options/Form.tsx +++ b/source/Options/Form.tsx @@ -1,11 +1,19 @@ import {useFormState} from 'react-use-form-state'; import React, {useState, useEffect} from 'react'; -import tw, {css} from 'twin.macro'; +import tw, {styled} from 'twin.macro'; import {useExtensionSettings} from '../contexts/extension-settings-context'; import {updateExtensionSettings} from '../util/settings'; +import {CHECK_API_KEY} from '../Background/constants'; +import messageUtil from '../util/mesageUtil'; import {isValidUrl} from '../util/tabs'; -import {Kutt} from '../Background'; +import { + SuccessfulApiKeyCheckProperties, + GetUserSettingsBodyProperties, + ApiErroredProperties, + ErrorStateProperties, + Kutt, +} from '../Background'; import Icon from '../components/Icon'; @@ -16,6 +24,23 @@ type OptionsFormValuesProperties = { host: string; }; +const StyledValidateButton = styled.button` + ${tw`focus:outline-none hover:text-gray-200 inline-flex items-center px-3 py-1 mt-3 mb-1 text-xs font-semibold text-center text-white duration-300 ease-in-out rounded shadow-lg`} + + background: linear-gradient(to right,rgb(126, 87, 194),rgb(98, 0, 234)); + + .validate__icon { + ${tw`inline-flex px-0 bg-transparent`} + + svg { + ${tw`transition-transform duration-300 ease-in-out`} + + stroke: currentColor; + stroke-width: 2.5; + } + } +`; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const onSave = (values: OptionsFormValuesProperties): Promise => { // should always return a Promise @@ -24,7 +49,12 @@ const onSave = (values: OptionsFormValuesProperties): Promise => { const Form: React.FC = () => { const extensionSettingsState = useExtensionSettings()[0]; + const [submitting, setSubmitting] = useState(false); const [showApiKey, setShowApiKey] = useState(false); + const [errored, setErrored] = useState({ + error: null, + message: '', + }); const [ formState, { @@ -102,6 +132,48 @@ const Form: React.FC = () => { } } + async function handleApiKeyVerification(): Promise { + setSubmitting(true); + // request API validation request + const apiKeyValidationBody: GetUserSettingsBodyProperties = { + apikey: formStateValues.apikey.trim(), + hostUrl: + (formStateValues.advanced && + formStateValues.host.trim().length > 0 && + formStateValues.host.trim()) || + Kutt.hostUrl, + }; + const response: + | SuccessfulApiKeyCheckProperties + | ApiErroredProperties = await messageUtil.send( + CHECK_API_KEY, + apiKeyValidationBody + ); + + if (!response.error) { + // set top-level status + setErrored({error: false, message: 'Valid API Key'}); + + // Store user account information + const {domains, email} = response.data; + await updateExtensionSettings({user: {domains, email}}); + } else { + // ---- errored ---- // + setErrored({error: true, message: response.message}); + + // Delete `user` field from settings + await updateExtensionSettings({user: null}); + } + + // enable validate button + setSubmitting(false); + + setTimeout(() => { + // Reset status + setErrored({error: null, message: ''}); + }, 1000); + } + return ( <>
@@ -159,26 +231,24 @@ const Form: React.FC = () => {
- + Validate + + +
diff --git a/source/Popup/Header.tsx b/source/Popup/Header.tsx index 82fb497..5fed697 100644 --- a/source/Popup/Header.tsx +++ b/source/Popup/Header.tsx @@ -4,29 +4,26 @@ import tw, {styled} from 'twin.macro'; import {updateExtensionSettings} from '../util/settings'; import {CHECK_API_KEY} from '../Background/constants'; import { - useExtensionSettings, ExtensionSettingsActionTypes, + useExtensionSettings, } from '../contexts/extension-settings-context'; import {openExtOptionsPage} from '../util/tabs'; import messageUtil from '../util/mesageUtil'; import { SuccessfulApiKeyCheckProperties, - ApiErroredProperties, GetUserSettingsBodyProperties, + ApiErroredProperties, + ErrorStateProperties, } from '../Background'; import Icon from '../components/Icon'; -type ErrorProperties = { - error: boolean | null; - message: string; -}; - const StyledIconsHolder = styled.div` ${tw`flex`} .icon { ${tw`hover:opacity-75 bg-transparent shadow-none`} + height: 34px; width: 34px; } @@ -52,7 +49,7 @@ const Header: React.FC = () => { extensionSettingsDispatch, ] = useExtensionSettings(); const [loading, setLoading] = useState(false); - const [errored, setErrored] = useState({ + const [errored, setErrored] = useState({ error: null, message: '', }); @@ -121,12 +118,11 @@ const Header: React.FC = () => { className="icon refresh__icon" title="Refresh" name={ - // eslint-disable-next-line no-nested-ternary loading ? 'spinner' - : errored && errored.error !== null - ? (errored && !errored.error && 'tick') || 'cross' - : 'refresh' + : (errored.error !== null && + ((!errored.error && 'tick') || 'cross')) || + 'refresh' } onClick={fetchUserDomains} /> diff --git a/source/components/Icon/ArrowLeft.tsx b/source/components/Icon/ArrowLeft.tsx deleted file mode 100644 index 95b5cfa..0000000 --- a/source/components/Icon/ArrowLeft.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -const ArrowLeft: React.FC = () => { - return ( - - - - ); -}; - -export default React.memo(ArrowLeft); diff --git a/source/components/Icon/Cross.tsx b/source/components/Icon/Cross.tsx index ef1da91..6acbd77 100644 --- a/source/components/Icon/Cross.tsx +++ b/source/components/Icon/Cross.tsx @@ -7,10 +7,10 @@ const Cross: React.FC = () => { width="48" height="48" fill="none" - stroke="#000" + stroke="currentColor" + strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" - strokeWidth="2" viewBox="0 0 24 24" > diff --git a/source/components/Icon/Icon.tsx b/source/components/Icon/Icon.tsx index a888923..a8f0507 100644 --- a/source/components/Icon/Icon.tsx +++ b/source/components/Icon/Icon.tsx @@ -1,7 +1,6 @@ import React from 'react'; import StarYellow from './StarYellow'; -import ArrowLeft from './ArrowLeft'; import EyeClosed from './EyeClosed'; import StarWhite from './StarWhite'; import Settings from './Settings'; @@ -15,7 +14,6 @@ import Zap from './Zap'; import Eye from './Eye'; const icons = { - arrowleft: ArrowLeft, copy: Copy, cross: Cross, eye: Eye, diff --git a/source/components/Icon/Settings.tsx b/source/components/Icon/Settings.tsx index fbbbbdf..253e429 100644 --- a/source/components/Icon/Settings.tsx +++ b/source/components/Icon/Settings.tsx @@ -6,7 +6,7 @@ const Settings: React.FC = () => { viewBox="-2 -2 24 24" width={24} height={24} - fill="#b8b8b8" + fill="currentColor" preserveAspectRatio="xMinYMin" className="cog_svg__jam cog_svg__jam-cog" >