diff options
| author | 2024-05-07 19:48:12 +0200 | |
|---|---|---|
| committer | 2024-05-07 19:48:12 +0200 | |
| commit | 578a4e0cf520c781af932f37be6fada706a3fe3c (patch) | |
| tree | d0b4f13a2828a03d946e8138820f32f958aaa4fb /web/source/settings/components | |
| parent | bump modernc.org/sqlite v1.29.8 -> v1.29.9 (concurrency workaround) (#2906) (diff) | |
| download | gotosocial-578a4e0cf520c781af932f37be6fada706a3fe3c.tar.xz | |
[bugfix] Reset emoji fields on upload error (#2905)
Diffstat (limited to 'web/source/settings/components')
| -rw-r--r-- | web/source/settings/components/error.tsx | 91 | ||||
| -rw-r--r-- | web/source/settings/components/form/inputs.tsx | 26 | ||||
| -rw-r--r-- | web/source/settings/components/form/mutation-button.tsx | 4 | 
3 files changed, 79 insertions, 42 deletions
diff --git a/web/source/settings/components/error.tsx b/web/source/settings/components/error.tsx index 15c3bccd4..a2b4772dc 100644 --- a/web/source/settings/components/error.tsx +++ b/web/source/settings/components/error.tsx @@ -17,7 +17,9 @@  	along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ -import React from "react"; +import { SerializedError } from "@reduxjs/toolkit"; +import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; +import React, { ReactNode } from "react";  function ErrorFallback({ error, resetErrorBoundary }) {  	return ( @@ -44,39 +46,70 @@ function ErrorFallback({ error, resetErrorBoundary }) {  	);  } -function Error({ error }) { -	/* eslint-disable-next-line no-console */ -	console.error("Rendering error:", error); -	let message; +interface GtsError { +	/** +	 * Error message returned from the API. +	 */ +	error: string; + +	/** +	 * For OAuth errors: description of the error. +	 */ +	error_description?: string; +} + +interface ErrorProps { +	error: FetchBaseQueryError | SerializedError | Error | undefined; +	 +	/** +	 * Optional function to clear the error. +	 * If provided, rendered error will have +	 * a "dismiss" button. +	 */ +	reset?: () => void; +} -	if (error.data != undefined) { // RTK Query error with data -		if (error.status) { -			message = (<> -				<b>{error.status}:</b> {error.data.error} -				{error.data.error_description && -					<p> -						{error.data.error_description} -					</p> -				} -			</>); -		} else { -			message = error.data.error; -		} -	} else if (error.name != undefined || error.type != undefined) { // JS error -		message = (<> -			<b>{error.type && error.name}:</b> {error.message} -		</>); -	} else if (error.status && typeof error.error == "string") { -		message = (<> -			<b>{error.status}:</b> {error.error} -		</>); +function Error({ error, reset }: ErrorProps) { +	if (error === undefined) { +		return null; +	} +	 +	/* eslint-disable-next-line no-console */ +	console.error("caught error: ", error); +	 +	let message: ReactNode; +	if ("status" in error) { +		// RTK Query error with data. +		const gtsError = error.data as GtsError; +		const errMsg = gtsError.error_description ?? gtsError.error; +		message = <>Code {error.status} {errMsg}</>;  	} else { -		message = error.message ?? error; +		// SerializedError or Error. +		const errMsg = error.message ?? JSON.stringify(error); +		message = ( +			<>{error.name && `${error.name}: `}{errMsg}</> +		); +	} + +	let className = "error"; +	if (reset) { +		className += " with-dismiss";  	}  	return ( -		<div className="error"> -			{message} +		<div className={className}> +			<span>{message}</span> +			{ reset &&  +				<span  +					className="dismiss" +					onClick={reset} +					role="button" +					tabIndex={0} +				> +					<span>Dismiss</span> +					<i className="fa fa-fw fa-close" aria-hidden="true" /> +				</span> +			}  		</div>  	);  } diff --git a/web/source/settings/components/form/inputs.tsx b/web/source/settings/components/form/inputs.tsx index f82937fc1..c68095d95 100644 --- a/web/source/settings/components/form/inputs.tsx +++ b/web/source/settings/components/form/inputs.tsx @@ -29,6 +29,7 @@ import type {  	RadioFormInputHook,  	TextFormInputHook,  } from "../../lib/form/types"; +import { nanoid } from "nanoid";  export interface TextInputProps extends React.DetailedHTMLProps<  	React.InputHTMLAttributes<HTMLInputElement>, @@ -92,22 +93,25 @@ export interface FileInputProps extends React.DetailedHTMLProps<  export function FileInput({ label, field, ...props }: FileInputProps) {  	const { onChange, ref, infoComponent } = field; +	const id = nanoid();  	return (  		<div className="form-field file"> -			<label> -				<div className="label">{label}</div> +			<label className="label-label" htmlFor={id}> +				{label} +			</label> +			<label className="label-button" htmlFor={id}>  				<div className="file-input button">Browse</div> -				{infoComponent} -				{/* <a onClick={removeFile("header")}>remove</a> */} -				<input -					type="file" -					className="hidden" -					onChange={onChange} -					ref={ref ? ref as RefObject<HTMLInputElement> : undefined} -					{...props} -				/>  			</label> +			<input +				id={id} +				type="file" +				className="hidden" +				onChange={onChange} +				ref={ref ? ref as RefObject<HTMLInputElement> : undefined} +				{...props} +			/> +			{infoComponent}  		</div>  	);  } diff --git a/web/source/settings/components/form/mutation-button.tsx b/web/source/settings/components/form/mutation-button.tsx index 1e6d8c968..5d831cd24 100644 --- a/web/source/settings/components/form/mutation-button.tsx +++ b/web/source/settings/components/form/mutation-button.tsx @@ -51,9 +51,9 @@ export default function MutationButton({  	}  	return ( -		<div className={wrapperClassName}> +		<div className={wrapperClassName ? wrapperClassName : "mutation-button"}>  			{(showError && targetsThisButton && result.error) && -				<Error error={result.error} /> +				<Error error={result.error} reset={result.reset} />  			}  			<button  				type="submit"  | 
