hooks#useAlert TypeScript Examples
The following examples show how to use
hooks#useAlert.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: useProgram.ts From gear-js with GNU General Public License v3.0 | 6 votes |
useProgram = (id?: string): [ProgramModel?, Metadata?] => {
const alert = useAlert();
const [program, setProgram] = useState<ProgramModel>();
const metadata = useMemo(() => {
const meta = program?.meta?.meta;
if (meta) {
return JSON.parse(meta) as Metadata;
}
}, [program]);
useEffect(() => {
if (id) {
getProgram(id).then(({ result }) => setProgram(result)).catch((err: RPCResponseError) => alert.error(err.message));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]);
return [program, metadata];
}
Example #2
Source File: Hint.tsx From gear-js with GNU General Public License v3.0 | 6 votes |
Hint: FC<Params> = ({ children }) => {
const alert = useAlert();
const handleClick = () => {
alert.error(`${children}`);
};
return (
<div className={styles.hint}>
<HelpCircle className={styles.icon} size="16" onClick={handleClick} />
</div>
);
}
Example #3
Source File: Message.tsx From gear-js with GNU General Public License v3.0 | 6 votes |
Message = ({ message }: Props) => {
const { id } = message;
const { api } = useApi();
const { account } = useAccount();
const alert = useAlert();
const showErrorAlert = (error: string) => {
alert.error(error);
};
const showSuccessAlert = (data: ISubmittableResult) => {
if (!data.status.isBroadcast) {
alert.success(`Status: ${data.status}`);
}
};
const handleClaimButtonClick = () => {
if (account) {
const { address, meta } = account;
api.claimValueFromMailbox.submit(id);
web3FromSource(meta.source)
.then(({ signer }) => api.claimValueFromMailbox.signAndSend(address, { signer }, showSuccessAlert))
.catch((error: Error) => showErrorAlert(error.message));
}
};
return (
<div className={styles.message}>
<pre className={styles.pre}>{getPreformattedText(message)}</pre>
<div>
<ReplyLink to={id} />
<Button text="Claim value" icon={claimIcon} color="secondary" size="small" onClick={handleClaimButtonClick} />
</div>
</div>
);
}
Example #4
Source File: CopiedInfo.tsx From gear-js with GNU General Public License v3.0 | 6 votes |
CopiedInfo = ({ title, info }: Props) => {
const alert = useAlert();
const handleClick = () => copyToClipboard(info, alert);
return (
<div>
<p>{title}:</p>
<p className={styles.info}>{info}</p>
<Button icon={copySVG} color="transparent" className={styles.copyButton} onClick={handleClick} />
</div>
);
}
Example #5
Source File: MetaFile.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
MetaFile = (props: Props) => {
const alert = useAlert();
const metaFieldRef = useRef<HTMLInputElement>(null);
const { file, className, onUpload, onDelete } = props;
const uploadMetaFile = () => {
metaFieldRef.current?.click();
};
const handleChangeMetaFile = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files.length) {
const isCorrectFormat = checkFileFormat(event.target.files[0]);
if (isCorrectFormat) {
onUpload(event.target.files[0]);
} else {
alert.error('Wrong file format');
}
event.target.value = '';
}
};
return (
<div className={clsx(styles.upload, className)}>
<label htmlFor="meta" className={styles.caption}>
Metadata file:
</label>
<div className={styles.block}>
<Field
id="meta"
name="meta"
className={styles.hidden}
type="file"
innerRef={metaFieldRef}
onChange={handleChangeMetaFile}
/>
{file ? (
<div className={clsx(styles.value, styles.filename)}>
{file.name}
<button type="button" onClick={onDelete}>
<Trash2 color="#ffffff" size="20" strokeWidth="1" />
</button>
</div>
) : (
<Button
text="Select file"
type="button"
color="secondary"
className={styles.button}
onClick={uploadMetaFile}
/>
)}
</div>
</div>
);
}
Example #6
Source File: Provider.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
useEditor = () => {
const alert = useAlert();
const [isBuildDone, setIsBuildDone] = useState(false);
useEffect(() => {
let timerId: any;
if (localStorage.getItem(LOCAL_STORAGE.PROGRAM_COMPILE_ID)) {
const id = localStorage.getItem(LOCAL_STORAGE.PROGRAM_COMPILE_ID);
timerId = setInterval(() => {
fetch(WASM_COMPILER_GET, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify({ id }),
})
.then((data) => data.json())
.then((json) => {
const zip = new JSZip();
zip.loadAsync(json.file.data).then((data) => {
data.generateAsync({ type: 'blob' }).then((val) => {
saveAs(val, `program.zip`);
setIsBuildDone(false);
localStorage.removeItem(LOCAL_STORAGE.PROGRAM_COMPILE_ID);
clearInterval(timerId);
alert.success('Program is ready!');
});
});
})
.catch((err) => console.error(err));
}, 20000);
}
return () => {
clearInterval(timerId);
};
}, [isBuildDone, alert]);
return { isBuildDone, setIsBuildDone };
}
Example #7
Source File: UserProgram.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
UserProgram = (props: Props) => {
const alert = useAlert();
const { program, isMetaLinkActive = true } = props;
return (
<div className={styles.programsListItem} key={program.id}>
<div className={styles.programWrapper}>
<span
className={clsx(
styles.programsListIndicator,
program.initStatus === ProgramStatus.Success && styles['program-item-success'],
program.initStatus === ProgramStatus.Failed && styles['program-item-failure'],
program.initStatus === ProgramStatus.InProgress && styles['program-item-loading']
)}
/>
<div className={styles.programWrapperName}>
<div className={styles.programsListName}>
<Link className={styles.programLink} to={`/program/${program.id}`}>
{program.name && fileNameHandler(program.name)}
</Link>
</div>
</div>
<div className={styles.programsCopyId}>
<button type="button" onClick={() => copyToClipboard(program.id, alert, 'Program ID copied')}>
<img src={Copy} alt="copy program ID" />
</button>
</div>
</div>
<div className={styles.programWrapperData}>
<div className={styles.programsListInfo}>
Timestamp:
<span className={styles.programsListInfoData}>{program.timestamp && formatDate(program.timestamp)}</span>
</div>
</div>
<div className={styles.programsListBtns}>
<Link to={`/send/message/${program.id}`} className={styles.allProgramsItemSendMessage}>
<img src={MessageIllustration} alt="Send message to program" />
</Link>
<Link
to={generatePath(routes.meta, { programId: program.id })}
tabIndex={Number(isMetaLinkActive)}
className={clsx(styles.allProgramsItemUpload, !isMetaLinkActive && styles.linkInactive)}
>
<img src={UploadIcon} alt="Upload metadata" />
</Link>
</div>
</div>
);
}
Example #8
Source File: App.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
Component = () => {
globalStyles();
const alert = useAlert();
const events = useEvents();
const { isApiReady } = useApi();
const [searchParams, setSearchParams] = useSearchParams();
useLoggedInAccount();
useEffect(() => {
if (isApiReady) {
subscribeToEvents(alert);
}
}, [isApiReady, alert]);
useEffect(() => {
const urlNodeAddress = searchParams.get(NODE_ADRESS_URL_PARAM);
if (!urlNodeAddress) {
searchParams.set(NODE_ADRESS_URL_PARAM, nodeApi.address);
setSearchParams(searchParams, { replace: true });
}
}, [searchParams, setSearchParams]);
const isFooterHidden = () => {
const locationPath = window.location.pathname.replaceAll('/', '');
const privacyPath = routes.privacyPolicy.replaceAll('/', '');
const termsOfUsePath = routes.termsOfUse.replaceAll('/', '');
return locationPath === privacyPath || locationPath === termsOfUsePath;
};
// we'll get rid of multiple paths in one route anyway, so temp solution
const getMultipleRoutes = (paths: string[], element: JSX.Element) =>
paths.map((path) => <Route key={path} path={path} element={element} />);
return (
<div className="app">
<Header />
<Main>
{isApiReady ? (
<Routes>
{getMultipleRoutes(mainRoutes, <Programs />)}
{getMultipleRoutes(utilRoutes, <Document />)}
{/* temp solution since in react-router v6 optional parameters are gone */}
<Route path={routes.explorer}>
<Route path="" element={<Explorer events={events} />} />
<Route path=":blockId" element={<Explorer events={events} />} />
</Route>
<Route path={routes.program} element={<Program />} />
<Route path={routes.message} element={<Message />} />
<Route path={routes.state} element={<State />} />
<Route path={routes.send}>
<Route path={routes.sendMessage} element={<Send />} />
<Route path={routes.reply} element={<Send />} />
</Route>
<Route path={routes.meta} element={<Meta />} />
<Route path={routes.editor} element={<EditorPage />} />
<Route path={routes.mailbox} element={<Mailbox />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
) : (
<Loader />
)}
</Main>
{isFooterHidden() || <Footer />}
</div>
);
}
Example #9
Source File: SelectAccountModal.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
SelectAccountModal = (props: Props) => {
const alert = useAlert();
const { logout, switchAccount } = useAccount();
const { isOpen, accounts, onClose } = props;
const selectAccount = (account: InjectedAccountWithMeta) => {
switchAccount(account);
localStorage.setItem(LOCAL_STORAGE.ACCOUNT, account.address);
localStorage.setItem(LOCAL_STORAGE.PUBLIC_KEY_RAW, GearKeyring.decodeAddress(account.address));
onClose();
alert.success('Account successfully changed');
};
const handleLogout = () => {
logout()
localStorage.removeItem(LOCAL_STORAGE.ACCOUNT);
localStorage.removeItem(LOCAL_STORAGE.PUBLIC_KEY_RAW);
onClose();
};
return (
<Modal
open={isOpen}
title="Connect"
content={
accounts ? (
<>
<AccountList list={accounts} toggleAccount={selectAccount} />
<Button
aria-label="Logout"
icon={logoutSVG}
color="transparent"
className={styles.logoutButton}
onClick={handleLogout}
/>
</>
) : (
<p className={styles.message}>
Polkadot extension was not found or disabled. Please{' '}
<a href="https://polkadot.js.org/extension/" target="_blank" rel="noreferrer">
install it
</a>
</p>
)
}
handleClose={onClose}
/>
);
}
Example #10
Source File: MessagesList.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
MessagesList: VFC<Props> = ({ messages }) => {
const alert = useAlert();
return (
<div className="messages__list">
<div className={clsx('messages__list-block', 'messages__list-block__header')}>
<div className="messages__list-item">
<img className="messages__list-icon" src={codeIcon} alt="program name" />
<p className="messages__list-caption">Program name</p>
</div>
<div className="messages__list-item">
<img className="messages__list-icon" src={idIcon} alt="program id" />
<p>Message Id</p>
</div>
<div className="messages__list-item">
<img className="messages__list-icon" src={timestampIcon} alt="program date" />
<p>Timestamp</p>
</div>
</div>
{messages &&
messages.length > 0 &&
messages.map((message: MessageModel) => (
<div
key={message.id}
className={clsx(
'messages__list-block',
message.replyError === '0' || message.replyError === null
? 'messages__list-block_success'
: 'messages__list-block_error'
)}
>
<div className="messages__list-item">
<span className="messages__list-status" />
<p className="messages__list-caption">{message.destination && fileNameHandler(message.destination)}</p>
</div>
<div className="messages__list-item">
<Link className="messages__list-link" to={`/message/${message.id}`}>
{message.id}
</Link>
<div className="programsCopyId">
<button type="button" onClick={() => copyToClipboard(message.id, alert, 'Message ID copied')}>
<img src={copyIcon} alt="copy message ID" />
</button>
</div>
</div>
<div className="messages__list-item">
<p>{message.timestamp && formatDate(message.timestamp)}</p>
</div>
</div>
))}
</div>
);
}
Example #11
Source File: Node.tsx From gear-js with GNU General Public License v3.0 | 5 votes |
Node = ({ address, isCustom, setLocalNodes, selectedNode, setSelectedNode }: Props) => {
const alert = useAlert();
const handleChange = () => {
setSelectedNode(address);
};
const handleCopy = () => {
copyToClipboard(address, alert, 'Node address copied');
};
const removeNode = () => {
setLocalNodes((prevNodes) => prevNodes.filter((prevNode) => prevNode.address !== address));
if (selectedNode === address) {
setSelectedNode(nodeApi.address);
}
};
return (
<li className={styles.node}>
<Radio
label={address}
name="node"
className={styles.radio}
checked={selectedNode === address}
onChange={handleChange}
/>
<div className={styles.buttons}>
<Button aria-label="Copy node address" icon={copy} color="transparent" onClick={handleCopy} />
{isCustom && (
<Button
aria-label="Remove node address"
icon={trash}
color="transparent"
onClick={removeNode}
disabled={address === nodeApi.address}
/>
)}
</div>
</li>
);
}
Example #12
Source File: FormPayload.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
FormPayload = (props: Props) => {
const { name, values } = props;
const alert = useAlert();
const [field, meta, { setValue }] = useField<PayloadValue>(name);
const jsonManualPayload = useRef<string>();
const [isManualView, setIsManualView] = useState(!values);
const [manualPayloadFile, setManualPayloadFile] = useState<File>();
const handleViewChange = () => setIsManualView((prevState) => !prevState);
const resetFileData = () => {
setManualPayloadFile(void 0);
jsonManualPayload.current = void 0;
};
const dropManualPayloadFile = () => {
resetFileData();
if (values) {
setValue(values.manualPayload, false);
}
};
const handleUploadManualPayload = async (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) {
return dropManualPayloadFile();
}
try {
if (!checkFileFormat(file, FILE_TYPES.JSON)) {
throw new Error('Wrong file format');
}
setManualPayloadFile(file);
const fileText = (await readTextFileAsync(file)) ?? '';
setValue(fileText);
jsonManualPayload.current = fileText;
} catch (error: unknown) {
alert.error((error as Error).message);
}
};
useEffect(() => {
if (!values) {
return;
}
const payloadValue = isManualView ? jsonManualPayload.current ?? values.manualPayload : values.payload;
setValue(payloadValue, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isManualView]);
useChangeEffect(() => {
if (!values && manualPayloadFile) {
resetFileData();
}
setIsManualView(!values);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values]);
return (
<>
<div className={styles.formPayload}>
{values && (
<Checkbox
type="switch"
label="Manual input"
checked={isManualView}
className={styles.viewCheckbox}
onChange={handleViewChange}
/>
)}
{!isManualView && values ? (
<PayloadStructure levelName={name} typeStructure={values.typeStructure} />
) : (
<>
<Textarea
{...field}
id={name}
rows={15}
value={field.value as string}
placeholder="// Enter your payload here"
/>
{values && (
<FileInput
value={manualPayloadFile}
accept={FILE_TYPES.JSON}
className={styles.fileInput}
onChange={handleUploadManualPayload}
/>
)}
</>
)}
</div>
{meta.error && <div className={styles.error}>{meta.error}</div>}
</>
);
}
Example #13
Source File: UploadMetaForm.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
UploadMetaForm = ({ programId, programName }: Props) => {
const alert = useAlert();
const { account } = useAccount();
const [isFileUpload, setFileUpload] = useState(true);
const [meta, setMeta] = useState<Metadata | null>(null);
const [metaFile, setMetaFile] = useState<File | null>(null);
const [metaBuffer, setMetaBuffer] = useState<string | null>(null);
const [fieldsFromFile, setFieldFromFile] = useState<string[] | null>(null);
const [initialValues, setInitialValues] = useState<FormValues>({
name: programName,
...INITIAL_VALUES,
});
const handleUploadMetaFile = async (file: File) => {
try {
const fileBuffer = (await readFileAsync(file)) as Buffer;
const currentMetaWasm = await getWasmMetadata(fileBuffer);
if (!currentMetaWasm) {
return;
}
const valuesFromFile = getMetaValues(currentMetaWasm);
const currentMetaBuffer = Buffer.from(new Uint8Array(fileBuffer)).toString('base64');
setMeta(currentMetaWasm);
setMetaBuffer(currentMetaBuffer);
setFieldFromFile(Object.keys(valuesFromFile));
setInitialValues({
...INITIAL_VALUES,
...valuesFromFile,
name: currentMetaWasm.title ?? programName,
});
} catch (error) {
alert.error(`${error}`);
} finally {
setMetaFile(file);
}
};
const resetForm = () => {
setMetaFile(null);
setMeta(null);
setMetaBuffer(null);
setFieldFromFile(null);
setInitialValues({
name: programName,
...INITIAL_VALUES,
});
};
const handleSubmit = (values: FormValues, actions: FormikHelpers<FormValues>) => {
if (!account) {
alert.error(`WALLET NOT CONNECTED`);
return;
}
const { name, ...formMeta } = values;
if (isFileUpload) {
if (meta) {
addMetadata(meta, metaBuffer, account, programId, name, alert);
} else {
alert.error(`ERROR: metadata not loaded`);
}
} else {
addMetadata(formMeta, null, account, programId, name, alert);
}
actions.setSubmitting(false);
resetForm();
};
const fields = isFileUpload ? fieldsFromFile : META_FIELDS;
return (
<Formik
initialValues={initialValues}
validateOnBlur
validationSchema={Schema}
enableReinitialize
onSubmit={handleSubmit}
>
{({ isValid, isSubmitting }: FormikProps<FormValues>) => {
const emptyFile = isFileUpload && !meta;
const disabledBtn = emptyFile || !isValid || isSubmitting;
return (
<Form className={styles.uploadMetaForm}>
<MetaSwitch isMetaFromFile={isFileUpload} onChange={setFileUpload} className={styles.formField} />
<FormInput name="name" label="Program name:" className={styles.formField} />
{fields?.map((field) => {
const MetaField = field === 'types' ? FormTextarea : FormInput;
return (
<MetaField
key={field}
name={field}
label={`${field}:`}
disabled={isFileUpload}
className={styles.formField}
/>
);
})}
{isFileUpload && (
<MetaFile
file={metaFile}
className={styles.formField}
onUpload={handleUploadMetaFile}
onDelete={resetForm}
/>
)}
<div className={styles.formBtnWrapper}>
<Button type="submit" text="Upload metadata" className={styles.formSubmitBtn} disabled={disabledBtn} />
</div>
</Form>
);
}}
</Formik>
);
}
Example #14
Source File: DropTarget.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
DropTarget = ({ type, setDroppedFile }: Props) => {
const alert = useAlert();
const [wrongFormat, setWrongFormat] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
if (wrongFormat) {
setTimeout(() => setWrongFormat(false), 3000);
}
const checkFileFormat = useCallback((files: any) => {
if (typeof files[0]?.name === 'string') {
const fileExt: string = files[0].name.split('.').pop().toLowerCase();
return fileExt !== 'wasm';
}
return true;
}, []);
const handleFilesUpload = useCallback(
(file: File) => {
setDroppedFile({ file, type });
},
[setDroppedFile, type]
);
const emulateInputClick = () => {
inputRef.current?.click();
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const {
target: { files },
} = event;
if (files?.length) {
const isCorrectFormat = checkFileFormat(files);
setWrongFormat(isCorrectFormat);
if (!isCorrectFormat) {
handleFilesUpload(files[0]);
// since type='file' input can't be controlled,
// reset it's value to trigger onChange again in case the same file selected twice
event.target.value = '';
} else {
alert.error('Wrong file format');
setWrongFormat(false);
}
}
};
const handleFileDrop = useCallback(
(item) => {
if (item) {
const { files } = item;
const isCorrectFormat = checkFileFormat(files);
setWrongFormat(isCorrectFormat);
if (!isCorrectFormat) {
handleFilesUpload(files[0]);
} else {
alert.error('Wrong file format');
setWrongFormat(false);
}
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[checkFileFormat, handleFilesUpload]
);
const [{ canDrop, isOver }, drop] = useDrop(
() => ({
accept: [NativeTypes.FILE],
drop(item: { files: any[] }) {
if (handleFileDrop) {
handleFileDrop(item);
}
},
collect: (monitor: DropTargetMonitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
}),
[handleFileDrop]
);
const isActive = canDrop && isOver;
const className = clsx(styles.drop, isActive && styles.active);
const isProgramUpload = type === UploadTypes.PROGRAM;
const buttonText = `Upload ${type}`;
return (
<div className={className} ref={drop}>
{isActive ? (
<div className={styles.file}>
<span className={styles.text}>Drop your .wasm files here to upload</span>
</div>
) : (
<div className={styles.noFile}>
<input className={styles.input} ref={inputRef} type="file" onChange={handleChange} />
<Button
text={buttonText}
icon={isProgramUpload ? upload : editor}
color={isProgramUpload ? 'primary' : 'secondary'}
onClick={emulateInputClick}
/>
<div className={styles.text}>{`Click “${buttonText}” to browse or drag and drop your .wasm files here`}</div>
</div>
)}
</div>
);
}
Example #15
Source File: UploadForm.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
UploadForm: VFC<Props> = ({ setDroppedFile, droppedFile }) => {
const { api } = useApi();
const alert = useAlert();
const { account } = useAccount();
const [fieldFromFile, setFieldFromFile] = useState<string[] | null>(null);
const [meta, setMeta] = useState<Metadata | null>(null);
const [metaFile, setMetaFile] = useState<string | null>(null);
const [droppedMetaFile, setDroppedMetaFile] = useState<File>();
const [isMetaFromFile, setIsMetaFromFile] = useState<boolean>(true);
const handleResetForm = () => {
setDroppedFile(null);
};
const handleResetMetaForm = (setValues: SetValues) => {
setMeta(null);
setMetaFile(null);
setDroppedMetaFile(void 0);
setFieldFromFile(null);
setValues(INITIAL_VALUES, false);
};
const handleUploadMetaFile = (setValues: SetValues) => async (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) {
return handleResetMetaForm(setValues);
}
try {
if (!checkFileFormat(file)) {
throw new Error('Wrong file format');
}
setDroppedMetaFile(file);
const readedFile = (await readFileAsync(file)) as Buffer;
const metadata: Metadata = await getWasmMetadata(readedFile);
if (!metadata) {
throw new Error('Failed to load metadata');
}
const metaBufferString = Buffer.from(new Uint8Array(readedFile)).toString('base64');
const valuesFromFile = getMetaValues(metadata);
setMeta(metadata);
setMetaFile(metaBufferString);
setFieldFromFile(Object.keys(valuesFromFile));
setValues(
({ programValues }) => ({
metaValues: valuesFromFile,
programValues: {
...programValues,
programName: metadata?.title || '',
},
}),
false
);
} catch (error) {
alert.error(`${error}`);
}
};
const handleSubmitForm = (values: FormValues) => {
if (!account) {
alert.error(`Wallet not connected`);
return;
}
const { value, payload, gasLimit, programName } = values.programValues;
const programOptions: UploadProgramModel = {
meta: void 0,
value,
title: '',
gasLimit,
programName,
initPayload: meta ? getSubmitPayload(payload) : payload,
};
if (meta) {
programOptions.meta = isMetaFromFile ? meta : values.metaValues;
}
UploadProgram(api, account, droppedFile, programOptions, metaFile, alert, handleResetForm).catch(() => {
alert.error(`Invalid JSON format`);
});
};
const handleCalculateGas = async (values: ProgramValues, setFieldValue: SetFieldValue) => {
const fileBuffer = (await readFileAsync(droppedFile)) as ArrayBuffer;
const code = Buffer.from(new Uint8Array(fileBuffer));
calculateGas('init', api, values, alert, meta, code).then((gasLimit) =>
setFieldValue('programValues.gasLimit', gasLimit)
);
};
const payloadFormValues = useMemo(() => getPayloadFormValues(meta?.types, meta?.init_input), [meta]);
const metaFields = isMetaFromFile ? fieldFromFile : META_FIELDS;
const isUploadAvailable = !(account && parseInt(account.balance.value, 10) > 0);
return (
<Box className={styles.uploadFormWrapper}>
<h3 className={styles.heading}>UPLOAD NEW PROGRAM</h3>
<Formik initialValues={INITIAL_VALUES} validateOnBlur validationSchema={Schema} onSubmit={handleSubmitForm}>
{({ values, setFieldValue, setValues }) => (
<Form className={styles.uploadForm}>
<div className={styles.formContent}>
<div className={styles.program}>
<div className={styles.fieldWrapper}>
<span className={styles.caption}>File:</span>
<span className={styles.fileName}>{droppedFile.name}</span>
</div>
<FormInput
name="programValues.programName"
label="Name:"
placeholder="Name"
className={styles.formField}
/>
<FormNumberFormat
name="programValues.gasLimit"
label="Gas limit:"
placeholder="20,000,000"
thousandSeparator
allowNegative={false}
className={styles.formField}
/>
<FormInput
type="number"
name="programValues.value"
label="Initial value:"
placeholder="0"
className={styles.formField}
/>
<div className={styles.fieldWrapper}>
<label htmlFor="programValues.payload" className={clsx(styles.caption, styles.top)}>
Initial payload:
</label>
<FormPayload name="programValues.payload" values={payloadFormValues} />
</div>
</div>
<fieldset className={styles.meta}>
<legend className={styles.metaLegend}>Metadata:</legend>
<MetaSwitch isMetaFromFile={isMetaFromFile} onChange={setIsMetaFromFile} className={styles.formField} />
{isMetaFromFile && (
<div className={styles.fieldWrapper}>
<FileInput
label="Metadata file:"
value={droppedMetaFile}
className={clsx(styles.formField, styles.fileInput)}
onChange={handleUploadMetaFile(setValues)}
/>
</div>
)}
{metaFields?.map((field) => {
const FormField = field === 'types' ? FormTextarea : FormInput;
return (
<FormField
key={field}
name={`metaValues.${field}`}
label={`${field}:`}
disabled={isMetaFromFile}
className={styles.formField}
/>
);
})}
</fieldset>
</div>
<div className={styles.buttons}>
<Button type="submit" text="Upload program" disabled={isUploadAvailable} />
<Button
text="Calculate Gas"
onClick={() => {
handleCalculateGas(values.programValues, setFieldValue);
}}
/>
<Button
type="submit"
text="Cancel upload"
color="transparent"
aria-label="closeUploadForm"
onClick={handleResetForm}
/>
</div>
</Form>
)}
</Formik>
</Box>
);
}
Example #16
Source File: ProgramSwitch.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
ProgramSwitch: VFC<Props> = ({ pageType }) => {
const { api } = useApi();
const alert = useAlert();
const { account: currentAccount } = useAccount();
const [captchaToken, setCaptchaToken] = useState('');
const captchaRef = useRef<HCaptcha>(null);
const handleTransferBalance = async () => {
try {
if (!currentAccount) {
throw new Error(`WALLET NOT CONNECTED`);
}
const apiRequest = new ServerRPCRequestService();
const response = await apiRequest.callRPC(RPC_METHODS.GET_TEST_BALANCE, {
address: `${currentAccount.address}`,
token: captchaToken,
});
if (response.error) {
alert.error(`${response.error.message}`);
}
} catch (error) {
alert.error(`${error}`);
}
};
useEffect(() => {
if (captchaToken) {
handleTransferBalance();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [captchaToken]);
const handleTestBalanceClick = () => {
if (captchaToken) {
handleTransferBalance();
} else {
captchaRef.current?.execute();
}
};
const handleTransferBalanceFromAlice = () => {
try {
if (!currentAccount) {
throw new Error(`WALLET NOT CONNECTED`);
}
if (api) {
api.balance.transferFromAlice(currentAccount.address, GEAR_BALANCE_TRANSFER_VALUE);
}
} catch (error) {
alert.error(`${error}`);
}
};
return (
<div className="switch-block">
<div className="switch-block--wrapper">
<div className="switch-buttons">
<Link
to={routes.main}
className={clsx(
'switch-buttons__item',
pageType === SWITCH_PAGE_TYPES.UPLOAD_PROGRAM && 'switch-buttons__item--active'
)}
>
Upload program
</Link>
<Link
to={routes.uploadedPrograms}
className={clsx(
'switch-buttons__item',
pageType === SWITCH_PAGE_TYPES.UPLOADED_PROGRAMS && 'switch-buttons__item--active'
)}
>
My programs
</Link>
<Link
to={routes.allPrograms}
className={clsx(
'switch-buttons__item',
pageType === SWITCH_PAGE_TYPES.ALL_PROGRAMS && 'switch-buttons__item--active'
)}
>
All programs
</Link>
<Link
to={routes.messages}
className={clsx(
'switch-buttons__item',
pageType === SWITCH_PAGE_TYPES.ALL_MESSAGES && 'switch-buttons__item--active'
)}
>
Messages
</Link>
</div>
{currentAccount && (
<>
<Button
className="test-balance-button"
text="Get test balance"
onClick={isDevChain() ? handleTransferBalanceFromAlice : handleTestBalanceClick}
/>
<HCaptcha
sitekey={HCAPTCHA_SITE_KEY}
onVerify={setCaptchaToken}
onExpire={() => setCaptchaToken('')}
ref={captchaRef}
theme="dark"
size="invisible"
/>
</>
)}
</div>
<BlocksSummary />
</div>
);
}
Example #17
Source File: MessageForm.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
MessageForm: VFC<Props> = ({ id, metadata, replyErrorCode }) => {
const { api } = useApi();
const alert = useAlert();
const { account: currentAccount } = useAccount();
const initialValues = useRef<FormValues>({
value: 0,
payload: '',
gasLimit: 20000000,
payloadType: 'Bytes',
destination: id,
});
const isReply = !!replyErrorCode;
const isMeta = useMemo(() => metadata && Object.keys(metadata).length > 0, [metadata]);
const handleSubmit = (values: FormValues, { resetForm }: FormikHelpers<FormValues>) => {
if (!currentAccount) {
alert.error(`WALLET NOT CONNECTED`);
return;
}
const payload = getSubmitPayload(values.payload);
const apiMethod = isReply ? api.reply : api.message;
const payloadType = isMeta ? void 0 : values.payloadType;
const message = {
value: values.value.toString(),
payload,
gasLimit: values.gasLimit.toString(),
replyToId: values.destination,
destination: values.destination,
};
sendMessage(apiMethod, currentAccount, message, alert, resetForm, metadata, payloadType);
};
const handleCalculateGas = (values: FormValues, setFieldValue: SetFieldValue) => () => {
const method = isReply ? 'reply' : 'handle';
calculateGas(method, api, values, alert, metadata, null, id, replyErrorCode).then((gasLimit) =>
setFieldValue('gasLimit', gasLimit)
);
};
const payloadFormValues = useMemo(() => getPayloadFormValues(metadata?.types, metadata?.handle_input), [metadata]);
return (
<Formik initialValues={initialValues.current} validateOnBlur validationSchema={Schema} onSubmit={handleSubmit}>
{({ errors, touched, values, setFieldValue }) => (
<Form id="message-form">
<div className="message-form--wrapper">
<div className="message-form--col">
<div className="message-form--info">
<label htmlFor="destination" className="message-form__field">
{isReply ? 'Message Id:' : 'Destination:'}
</label>
<div className="message-form__field-wrapper">
<Field
id="destination"
name="destination"
type="text"
className={clsx(
'inputField',
errors.destination && touched.destination && 'message-form__input-error'
)}
/>
{errors.destination && touched.destination && (
<div className="message-form__error">{errors.destination}</div>
)}
</div>
</div>
<div className="message-form--info">
<label htmlFor="payload" className="message-form__field">
Payload:
</label>
<FormPayload name="payload" values={payloadFormValues} />
</div>
{!isMeta && (
<div className="message-form--info">
<label htmlFor="payloadType" className="message-form__field">
Payload type:
</label>
<PayloadType />
</div>
)}
<div className="message-form--info">
<label htmlFor="gasLimit" className="message-form__field">
Gas limit:
</label>
<div className="message-form__field-wrapper">
<NumberFormat
name="gasLimit"
placeholder="20,000,000"
value={values.gasLimit}
thousandSeparator
allowNegative={false}
className={clsx('inputField', errors.gasLimit && touched.gasLimit && 'message-form__input-error')}
onValueChange={(val) => {
const { floatValue } = val;
setFieldValue('gasLimit', floatValue);
}}
/>
{errors.gasLimit && touched.gasLimit ? (
<div className="message-form__error">{errors.gasLimit}</div>
) : null}
</div>
</div>
<div className="message-form--info">
<label htmlFor="value" className="message-form__field">
Value:
</label>
<div className="message-form__field-wrapper">
<Field
id="value"
name="value"
placeholder="20000"
type="number"
className={clsx('inputField', errors.value && touched.value && 'message-form__input-error')}
/>
{errors.value && touched.value ? <div className="message-form__error">{errors.value}</div> : null}
</div>
</div>
<div className="message-form--btns">
<button
className="message-form__button"
type="button"
onClick={handleCalculateGas(values, setFieldValue)}
>
Calculate Gas
</button>
<button className="message-form__button" type="submit">
<img src={MessageIllustration} alt="message" />
{isReply ? 'Send reply' : 'Send message'}
</button>
</div>
</div>
</div>
</Form>
)}
</Formik>
);
}
Example #18
Source File: EditorPage.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
EditorPage = () => {
const navigate = useNavigate();
const alert = useAlert();
const { isBuildDone, setIsBuildDone } = useEditor();
const [state, dispatch] = useReducer(reducer, { tree: null });
const [currentFile, setCurrentFile] = useState<string[] | null>(null);
const [programName, setProgramName] = useState('');
const [isProgramNameError, setIsProgramNameError] = useState(false);
const options = {
selectOnLineNumbers: true,
fontSize: 14,
fontFamily: 'SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
theme: 'vs-dark',
language: 'rust',
};
useEffect(() => {
dispatch({ type: 'SET_DATA', payload: addParentToNode(SimpleExample) });
}, []);
function createStructure(zip: any, path: string | null, filesList: any) {
for (const key in filesList) {
if (Object.prototype.hasOwnProperty.call(filesList, key)) {
const file = filesList[key];
let newPath = '';
if (file.type === 'file') {
if (path) {
zip.folder(path).file(`${file.name}`, file.value);
} else {
zip.file(`${file.name}`, file.value);
}
} else {
if (path) {
newPath = `${path}/${file.name}`;
zip.folder(newPath);
} else {
newPath = file.name;
zip.folder(file.name);
}
createStructure(zip, newPath, file.children);
}
}
}
}
async function createArchive() {
const zip = new JSZip();
if (state.tree) {
createStructure(zip, null, state.tree.root.children);
}
return zip.generateAsync({ type: 'blob' });
}
function buildWasmProgram(val: any) {
const formData = new FormData();
formData.append('file', val);
fetch(WASM_COMPILER_BUILD, {
method: 'POST',
body: formData,
})
.then((data) => data.json())
.then((json) => {
localStorage.setItem(LOCAL_STORAGE.PROGRAM_COMPILE_ID, json.id);
setIsBuildDone(true);
alert.success('Compiling, please wait!');
});
}
function handleDownload() {
createArchive()
.then((val: any) => {
saveAs(val, `${programName.trim()}.zip`);
})
.catch((err) => {
console.error(err);
});
}
function handleBuild() {
createArchive()
.then((val: any) => {
buildWasmProgram(val);
})
.catch((err) => {
console.error(err);
});
}
function handleClose() {
navigate(-1);
}
function onNodeClick(node: EditorItem) {
setCurrentFile(node.path);
}
function handleProgramNameChange(event: ChangeEvent) {
const target = event.target as HTMLInputElement;
const { value } = target;
if ((value.trim().length && isProgramNameError) || (!value.trim().length && !isProgramNameError)) {
setIsProgramNameError(!isProgramNameError);
}
setProgramName(target.value);
}
function handleEditorChange(value: string | undefined) {
if (currentFile) {
const file = get(state.tree, currentFile);
dispatch({ type: 'UPDATE_VALUE', payload: { nodeId: file.id, value } });
}
}
function handlePanelBtnClick(type: string) {
if (!programName.trim().length) {
setIsProgramNameError(true);
return;
}
if (type === EDITOR_BTNS.DOWNLOAD) {
handleDownload();
} else if (type === EDITOR_BTNS.BUILD) {
handleBuild();
}
}
function getCurrFileName() {
let value = '';
if (currentFile) {
value = get(state.tree, currentFile).value;
}
return value;
}
function getCurrFileLang() {
let lang = '';
if (currentFile) {
lang = get(state.tree, currentFile).lang;
}
return lang;
}
// @ts-ignore
/* eslint-disable react/jsx-no-bind */
return (
<EditorTreeContext.Provider
value={{
state,
dispatch,
onNodeClick,
setCurrentFile,
}}
>
<div className="editor-page">
<PageHeader programName={programName} pageType={PAGE_TYPES.EDITOR_PAGE} handleClose={handleClose} />
<div className="editor-content">
<div className="editor-panel">
<div className="editor-panel--form">
<span className="editor-panel--form__label">Program name:</span>
<input
type="text"
className={clsx('editor-panel--form__input', isProgramNameError && 'error')}
value={programName}
onChange={handleProgramNameChange}
/>
</div>
{isBuildDone && <div className="editor-panel--text">Compiling ...</div>}
<div className="editor-panel--actions">
<button
className="editor-panel--actions__btn"
type="button"
onClick={() => handlePanelBtnClick(EDITOR_BTNS.DOWNLOAD)}
>
<img src={EditorDownload} alt="editor-download" />
Download
</button>
<button
className="editor-panel--actions__btn"
type="button"
onClick={() => handlePanelBtnClick(EDITOR_BTNS.BUILD)}
disabled={isBuildDone}
>
<img src={EditorBuild} alt="editor-build" />
Compile
</button>
</div>
</div>
<div className="editor-container">
<EditorTree />
<div className="editor-container__editor">
{currentFile ? (
<>
<Editor
theme="vs-dark"
options={options}
value={getCurrFileName()}
language={getCurrFileLang()}
onChange={handleEditorChange}
/>
</>
) : (
<div className="editor-empty">Please select at least one file</div>
)}
</div>
</div>
</div>
<span className="editor-page__footer-text">2022. All rights reserved.</span>
</div>
</EditorTreeContext.Provider>
);
}
Example #19
Source File: useCodeUpload.tsx From gear-js with GNU General Public License v3.0 | 4 votes |
useCodeUpload = () => {
const { api } = useApi();
const alert = useAlert();
const { account } = useAccount();
const submit = async (file: File) => {
const arrayBuffer = (await readFileAsync(file)) as ArrayBuffer;
const buffer = Buffer.from(arrayBuffer);
return api.code.submit(buffer);
};
const getErrorMessage = (event: Event) => {
const { docs, method: errorMethod } = api.getExtrinsicFailedError(event);
const formattedDocs = docs.filter(Boolean).join('. ');
return `${errorMethod}: ${formattedDocs}`;
};
const uploadCode = async (file: File) => {
if (!account) {
alert.error('Wallet not connected');
return;
}
const { address, meta } = account;
const alertTitle = 'gear.submitCode';
const alertId = alert.loading('SignIn', { title: alertTitle });
try {
const { signer } = await web3FromSource(meta.source);
const { codeHash } = await submit(file);
await api.code.signAndSend(address, { signer }, ({ events, status }) => {
if (status.isReady) {
alert.update(alertId, 'Ready');
return;
}
if (status.isInBlock) {
alert.update(alertId, 'InBlock');
events.forEach(({ event }) => {
const { method, section } = event;
if (method === 'CodeSaved') {
alert.success(<CopiedInfo title="Code hash" info={codeHash} />, {
title: `${section}.CodeSaved`,
timeout: 0,
});
return;
}
if (method === 'ExtrinsicFailed') {
alert.error(getErrorMessage(event), { title: `${section}.ExtrinsicFailed` });
return;
}
});
return;
}
if (status.isFinalized) {
alert.update(alertId, 'Finalized', DEFAULT_SUCCESS_OPTIONS);
return;
}
if (status.isInvalid) {
alert.update(alertId, PROGRAM_ERRORS.INVALID_TRANSACTION, DEFAULT_ERROR_OPTIONS);
}
});
} catch (error) {
alert.update(alertId, `${error}`, DEFAULT_ERROR_OPTIONS);
console.error(error);
}
};
return uploadCode;
}