react-bootstrap#Spinner TypeScript Examples
The following examples show how to use
react-bootstrap#Spinner.
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: App.tsx From devex with GNU General Public License v3.0 | 6 votes |
App: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { inTransition, isValidUrl } = networkContext!
return (
<div className='app-container'>
<Container>
{inTransition || isValidUrl === null
? <div className='center-spinner'><Spinner animation="border" /></div>
: <>
<Switch>
<Route exact path="/labels"><LabelsPage /></Route>
<Route exact path="/networks"><NetworksPage /></Route>
{isValidUrl
? <>
<Switch>
<Route exact path="/"><HomePage /></Route>
<Route exact path="/dsbk"><DSBlocksPage /></Route>
<Route exact path="/txbk"><TxBlocksPage /></Route>
<Route exact path="/tx"><TxnsPage /></Route>
<Route path="/dsbk/:blockNum"><DSBlockDetailsPage /></Route>
<Route path="/txbk/:blockNum"><TxBlockDetailsPage /></Route>
<Route path="/tx/:txnHash"><TxnDetailsPage /></Route>
<Route path="/address/:addr"><AddressDetailsPage /></Route>
<Route><NotFoundPage /></Route>
</Switch>
</>
: <NetworkErrPage />
}
</Switch>
</>
}
</Container>
</div>
)
}
Example #2
Source File: RowTransactions.tsx From devex with GNU General Public License v3.0 | 6 votes |
RowTransactions: React.FC<IProps> = ({ addr }) => {
const ACCOUNT_TRANSACTIONS = gql`
query GetTransactions($addr: String!) {
txnsByAddr(addr: $addr) {
ID
}
}
`;
const { loading, error, data } = useQuery(ACCOUNT_TRANSACTIONS, {
variables: { addr },
});
if (data) {
console.log(data);
}
return loading ? (
<div className="center-spinner">
<Spinner animation="border" />
</div>
) : (
<Row>
<Col>
<div className="address-detail">
<span>Transactions:</span>
<span>{data.txnsByAddr.length}</span>
</div>
</Col>
</Row>
);
}
Example #3
Source File: HomePage.tsx From devex with GNU General Public License v3.0 | 6 votes |
HomePage: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { isIsolatedServer } = networkContext!
return (
<>
{isIsolatedServer !== null // wait for isolated server check to complete
? <div>
<Searchbar isISSearchbar={isIsolatedServer} isHeaderSearchbar={false} />
<Dashboard />
</div>
: <div className='center-spinner'><Spinner animation="border" /></div>
}
</>
)
}
Example #4
Source File: routesTest.tsx From devex with GNU General Public License v3.0 | 6 votes |
describe('react router test', () => {
const history = createMemoryHistory()
it('renders layout', () => {
const homePage = shallow(
<Router history={history}>
<Layout />
</Router>
)
expect(homePage.find(Layout)).toHaveLength(1)
})
it('renders a spinner when in network transition', () => {
const homePage = mount(
<Router history={history}>
<NetworkContext.Provider value={{
isValidUrl: null,
isIsolatedServer: true,
dataService: null,
networkUrl: '',
inTransition: true,
isLoadingNetworks: true
}}>
<App />
</NetworkContext.Provider>
</Router>
)
expect(homePage.find(Spinner)).toHaveLength(1)
})
})
Example #5
Source File: SubmitButton.tsx From bada-frame with GNU General Public License v3.0 | 6 votes |
SubmitButton = ({ loading, buttonText, inline, disabled }: Props) => (
<Button
className="submitButton"
variant="outline-success"
type="submit"
block={!inline}
disabled={loading || disabled}
style={{ padding: '6px 1em' }}>
{loading ? (
<Spinner
as="span"
animation="border"
style={{
width: '22px',
height: '22px',
borderWidth: '0.20em',
color: '#51cd7c',
}}
/>
) : (
buttonText
)}
</Button>
)
Example #6
Source File: EnteSpinner.tsx From bada-frame with GNU General Public License v3.0 | 6 votes |
export default function EnteSpinner(props) {
const { style, ...others } = props ?? {};
return (
<Spinner
animation="border"
style={{
width: '36px',
height: '36px',
borderWidth: '0.20em',
color: '#51cd7c',
...(style && style),
}}
{...others}
role="status"
/>
);
}
Example #7
Source File: AddressDetailsPage.tsx From devex with GNU General Public License v3.0 | 5 votes |
AddressDetailsPage: React.FC = () => {
const { addr } = useParams()
const networkContext = useContext(NetworkContext)
const { dataService } = networkContext!
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [isContract, setIsContract] = useState<boolean | null>(null)
// Fetch data
useEffect(() => {
if (!dataService) return
let isContractRes: boolean
const getData = async () => {
try {
setIsLoading(true)
isContractRes = await dataService.isContractAddr(addr)
setIsContract(isContractRes)
} catch (e) {
console.log(e)
if (isValidAddr(addr))
setIsContract(false)
else
setError(e)
} finally {
setIsLoading(false)
}
}
getData()
return () => {
setIsContract(null)
setError(null)
}
}, [addr, dataService])
return <>
{isLoading ? <div className='center-spinner'><Spinner animation="border" /></div> : null}
{error
? <NotFoundPage />
: <>
{isContract !== null
? isContract
? <ContractDetailsPage addr={addr} />
: <AccountDetailsPage addr={addr} />
: null}
</>
}
</>
}
Example #8
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 5 votes |
CreateProposalButton = ({
className,
isLoading,
proposalThreshold,
hasActiveOrPendingProposal,
hasEnoughVote,
isFormInvalid,
handleCreateProposal,
}: {
className?: string;
isLoading: boolean;
proposalThreshold?: number;
hasActiveOrPendingProposal: boolean;
hasEnoughVote: boolean;
isFormInvalid: boolean;
handleCreateProposal: () => void;
}) => {
const buttonText = () => {
if (hasActiveOrPendingProposal) {
return <Trans>You already have an active or pending proposal</Trans>;
}
if (!hasEnoughVote) {
if (proposalThreshold) {
return (
<Trans>
You must have {i18n.number(proposalThreshold || 0 + 1)} votes to submit a proposal
</Trans>
);
}
return <Trans>You don't have enough votes to submit a proposal</Trans>;
}
return <Trans>Create Proposal</Trans>;
};
return (
<div className="d-grid gap-2">
<Button
className={className}
variant={hasActiveOrPendingProposal || !hasEnoughVote ? 'danger' : 'primary'}
disabled={isFormInvalid || hasActiveOrPendingProposal || !hasEnoughVote}
onClick={handleCreateProposal}
>
{isLoading ? <Spinner animation="border" /> : buttonText()}
</Button>
</div>
);
}
Example #9
Source File: confirm-modal.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 5 votes |
ConfirmModal = (props: any) => {
const { t } = useTranslation();
const [btnOkDisabled, setBtnOkDisabled] = React.useState(true);
const handleEnter = () => {
setBtnOkDisabled(false);
}
const handleOk = () => {
if (props.handleOk) {
setBtnOkDisabled(true);
props.handleOk();
}
}
return (
<Modal
contentClassName='data-modal'
show={props.show}
backdrop="static"
keyboard={false}
onEnter={handleEnter}
centered
>
<Modal.Header id='data-header' className='pb-0' >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h3'} >{props.title}</Card.Title>
</Modal.Header>
<Modal.Body className='bg-light'>
{props.message}
</Modal.Body>
<Modal.Footer id='data-footer'>
<Container className='p-0'>
<Row>
<Col sm='6' lg='4' className='mb-2 mb-sm-0 p-0 pr-sm-2'>
<Button
className='p-0'
block
variant='outline-primary'
onClick={props.onCancel}
>
{t('translation:cancel')}
</Button>
</Col>
<Col sm='6' lg='4' className='p-0 pl-sm-2'>
<Button
className='p-0'
block
onClick={handleOk}
disabled={btnOkDisabled}
>
{t('translation:ok')}
<Spinner
as="span"
className='btn-spinner'
animation="border"
hidden={!btnOkDisabled}
size="sm"
role="status"
aria-hidden="true"
/>
</Button>
</Col>
</Row>
</Container>
</Modal.Footer>
</Modal>
)
}
Example #10
Source File: ISInfo.tsx From devex with GNU General Public License v3.0 | 5 votes |
ISInfo: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { dataService } = networkContext!
const [data, setData] = useState<IISInfo | null>(null)
// Fetch data
useEffect(() => {
if (!dataService) return
let receivedData: IISInfo
const getData = async () => {
try {
receivedData = await dataService.getISInfo()
if (receivedData)
setData(receivedData)
} catch (e) {
console.log(e)
}
}
getData()
}, [dataService])
return <>
<Container className='p-0'>
<Row>
<Col>
<Card className='isinfo-card'>
<Card.Body>
{data
? <div className='isinfo-detail'>
<span>Latest Tx Block:</span>
<QueryPreservingLink to={`/txbk/${data.blockNum}`}>{data.blockNum}</QueryPreservingLink>
</div>
: <div><Spinner animation="border" role="status" /></div>
}
</Card.Body>
</Card>
</Col>
<Col>
<Card className='isinfo-card'>
<Card.Body>
{data
? <div className='isinfo-detail'>
<span>Minimum Gas Price:</span>
<span>{data.minGasPrice}</span>
</div>
: <div><Spinner animation="border" role="status" /></div>
}
</Card.Body>
</Card>
</Col>
</Row>
</Container>
</>
}
Example #11
Source File: user-table.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
UserTable = (props: any) => {
// const context = React.useContext(AppContext);
const { t } = useTranslation();
const { keycloak } = useKeycloak();
const handleSuccess = () => {
setIsUserSuccessfullUpdated(true);
setIsUserCreationError(false);
setTimeout(setShowUserModal, 300, false);
setShowConfirm(false);
}
const [bUsers,
// refreshUsers,
createUser,
readUser,
updateUser,
deleteUser] = useGetUsers(handleSuccess, props.handleError);
const [users, setUsers] = React.useState<IDisplayUser[]>([]);
const [reload, setReload] = React.useState(true);
const [showUserModal, setShowUserModal] = React.useState(false);
const [isUserSuccessfullUpdated, setIsUserSuccessfullUpdated] = React.useState(false);
const [isUserCreationError, setIsUserCreationError] = React.useState(false);
const [editUser, setEditUser] = React.useState<IDisplayUser>(emptyUser);
const [ownUserId, setOwnUserId] = React.useState<string>('');
const [showConfirm, setShowConfirm] = React.useState(false);
const [confirmMessage, setConfirmMessage] = React.useState('');
const [confirmTitle, setConfirmTitle] = React.useState('');
const [confirmHandle, setConfirmHandle] = React.useState<() => void>();
// set user name from keycloak
React.useEffect(() => {
if (keycloak.idTokenParsed) {
setOwnUserId((keycloak.idTokenParsed as any).sub ?? '');
}
}, [keycloak])
React.useEffect(() => {
if (bUsers) {
setUsers(bUsers);
setEditUser({ ...emptyUser });
}
}, [bUsers]);
React.useEffect(() => {
if (props.userReload) {
users.forEach((user => updateDisplayUser(user, false)));
props.setUserReload(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.userReload]);
React.useEffect(() => {
if (props.groupNodes && users && users.length > 0 && reload) {
setReload(false);
users.forEach((user => updateDisplayUser(user, true)));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(props.groupNodes), users, reload]);
const sortUsers = () => {
users.sort((a, b) => {
const nameA = a.username.toUpperCase(); // ignore upper and lowercase
const nameB = b.username.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
});
}
const addDisplayUser = (user: IDisplayUser) => {
setGroupPath(user);
user.displayRole = getDisplayRole(user);
users.push(user);
sortUsers();
setUsers(users);
}
const updateDisplayUser = (user: IDisplayUser, withApi: boolean, onSuccess?: () => void) => {
// set all groupPath for display
setGroupPath(user);
// set all rollDisplay async
if (withApi) {
readUser(user)
.then((response) => {
user.roleCounter = response.data.roleCounter;
user.roleLab = response.data.roleLab;
user.displayRole = getDisplayRole(user);
})
.finally(() => {
updateUsers(user);
if (onSuccess) {
onSuccess();
}
})
}
else {
updateUsers(user);
}
}
const updateUsers = (user: IDisplayUser | IUser) => {
const _users: IDisplayUser[] = [...users];
_users[_users.findIndex(_user => user.id === _user.id)] = { ...user };
setUsers(_users);
}
const removeUsers = (user: IDisplayUser | IUser) => {
users.splice(users.findIndex(_user => user.id === _user.id), 1);
setUsers(users);
}
const userUpdate = (user: IUser) => {
if (editUser && editUser.username) {
const fuser = users.find(u => u.username === user.username);
if (!user.password) {
user.password = undefined;
}
updateUser(user)
.then(() => {
if (
fuser
&& fuser.subGroup !== user.subGroup
&& keycloak.token
&& user.subGroup
) {
addUserToGroup(user.id, user.subGroup, keycloak.token)
.then(() => {
updateDisplayUser(user, true, handleSuccess);
})
.catch(e => {
props.handleError(e);
});
} else {
updateDisplayUser(user, true, handleSuccess);
}
})
} else {
const newUser: any = { ...user };
createUser(newUser)
.then((response) => {
const displayUser: IDisplayUser = { ...response.data };
addDisplayUser(displayUser);
handleSuccess();
})
.catch(e => {
if (e && e.message && (e.message as string).includes('409')) {
setIsUserCreationError(true);
}
else {
props.handleError(e);
}
})
}
}
const startEditUser = (user: IUser) => {
setEditUser({ ...user });
setShowUserModal(true);
}
const handleDeleteUser = (user: IDisplayUser) => {
setConfirmTitle(t('translation:delete-user-title', { userName: user.username }));
setConfirmMessage('');
setShowConfirm(true);
const handle = () => {
if (keycloak.token && user.username) {
deleteUser(user.id)
.then(() => {
removeUsers(user);
handleSuccess();
})
.catch(e => {
props.handleError(e);
})
}
};
// need to wrap a function again because react apply each function passed to hook
setConfirmHandle(() => handle);
}
const getDisplayRole = (user: IUser) => {
let roleString = '';
if (user.roleLab) {
roleString = t('translation:lab');
}
if (user.roleCounter) {
if (roleString) {
roleString += ', ';
}
roleString += t('translation:counter');
}
return roleString;
}
const setGroupPath = (user: IDisplayUser) => {
if (user.subGroup) {
const _groupName = getGroupPath(user.subGroup);
if (_groupName) {
user.displayGroup = _groupName;
}
else {
user.subGroup = '';
user.displayGroup = '';
}
}
else {
user.displayGroup = '';
}
}
const getGroupPath = (groupId: string | null): string => {
let groupName = ''
if (props.groupNodes && groupId) {
const fNode = (props.groupNodes as IGroupNode[]).find(gnode => gnode.group.id === groupId);
if (fNode) {
groupName = fNode.group.path;
}
}
return groupName;
}
return (<>
{
!(users && users.length > 0)
? <CwaSpinner background='#eeeeee' />
: <Collapse appear={true} in={true}>
<Container className='p-0 '>
<Table bordered hover responsive>
<thead>
<tr>
<th>{t('translation:user-name')}</th>
<th>{t('translation:first-name')}</th>
<th>{t('translation:name')}</th>
<th>{t('translation:group')}</th>
<th>{t('translation:permission')}</th>
<th></th>
</tr>
</thead>
<tbody>{
users.map((u, i) =>
<tr key={i}>
<td>{u.subGroup || (ownUserId && u.id === ownUserId)
? <></>
: <OverlayTrigger
placement='top-end'
overlay={
<Tooltip id='no-group-tooltip'>
{t('translation:no-group-tooltip')}
</Tooltip>
}
>
<span className='ff-fa px-1'> </span>
</OverlayTrigger>}
{u.username}</td>
<td>{u.firstName}</td>
<td>{u.lastName}</td>
<td>
{
u.displayGroup
? u.displayGroup
: u.subGroup
? <Spinner
animation="border"
className='d-flex mx-auto'
size="sm"
role="status"
aria-hidden="true"
variant='primary'
/>
: <></>
}
</td>
<td>{
u.displayRole !== undefined
? u.displayRole
: <Spinner
animation="border"
className='d-flex mx-auto'
size="sm"
role="status"
aria-hidden="true"
variant='primary'
/>
}
</td>
<td className='td-btn'>
<Row className='m-0 justify-content-around'>
<Button
className="btn-icon edit-icon"
onClick={() => startEditUser({ ...u })}
>
</Button>
<Button className="btn-icon delete-icon"
onClick={() => handleDeleteUser(u)}
disabled={!(ownUserId && u.id !== ownUserId)}
/>
</Row>
</td>
</tr>
)
}</tbody>
</Table>
<Button
className='btn-add'
size="sm"
variant="light"
onClick={() => { setEditUser({ ...emptyUser }); setShowUserModal(true) }}>
<img className='mr-2' src={imageAdd} alt="Hinzufügen" />
{t('translation:add-user')}
</Button>
</Container>
</Collapse>
}
<UserModal
show={showUserModal}
groups={props.groupNodes}
handleOk={userUpdate}
user={editUser}
onEnter={() => setIsUserSuccessfullUpdated(false)}
isSuccess={isUserSuccessfullUpdated}
isCreationError={isUserCreationError}
resetError={() => setIsUserCreationError(false)}
onCancel={() => setShowUserModal(false)}
onExit={
() => {
setEditUser({ ...emptyUser });
setIsUserSuccessfullUpdated(false);
setIsUserCreationError(false);
}
}
/>
<ConfirmModal
show={showConfirm}
title={confirmTitle}
message={confirmMessage}
onCancel={() => {
setConfirmHandle(undefined);
setShowConfirm(false);
}}
handleOk={() => {
if (confirmHandle) {
confirmHandle();
}
}}
/>
</>
)
}
Example #12
Source File: group-modal.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
GroupModal = (props: any) => { const [btnOkDisabled, setBtnOkDisabled] = React.useState(true); const { t } = useTranslation(); const { keycloak } = useKeycloak(); const [data, setData] = React.useState(''); const [validated, setValidated] = React.useState(false); const [isReady, setIsReady] = React.useState(false); const [isNew, setIsNew] = React.useState(true); const [options, setOptions] = React.useState<JSX.Element[]>(); const [dropdownItems, setDropdownItems] = React.useState<JSX.Element[]>(); const [dropdownList] = React.useState<string[]>(['https://', 'http://']); const [selectedDropdownValue, setSelectedDropdownValue] = React.useState<string>(dropdownList[0]); const [websiteValue, setWebsiteValue] = React.useState(''); const [displayOpeningHours, setDisplayOpeningHours] = React.useState(''); const [errorOpeningHour, setErrorOpeningHour] = React.useState(''); const groupReloaded = (group: IGroupDetails) => { if (group) { setData(unpackData(group.pocDetails)) group.parentGroup = props.parentGroupId; if (group.website) { let website = ''; for (const item of dropdownList) { if (group.website.startsWith(item)) { website = group.website.slice(item.length, group.website.length); setSelectedDropdownValue(item); } } setWebsiteValue(website); } else { setWebsiteValue(''); setSelectedDropdownValue(dropdownList[0]); } } setBtnOkDisabled(false); } const [group, updateGroup, setGroup] = useGetGroupDetails(groupReloaded, props.handleError); React.useEffect(() => { getDropdownItems(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []) React.useEffect(() => { if (group) { setOptions(getOptions()); setDisplayOpeningHours( group.openingHours?.map( (element: string) => element) .join('\n') ); setIsReady(true); } setIsNew(!(group && group.id)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [group]) React.useEffect(() => { setValidated(props.isSuccess) // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.isSuccess]); React.useEffect(() => { if (props.isCreationError) { setBtnOkDisabled(false) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.isCreationError]); const handleCancel = () => { setErrorOpeningHour(''); props.onCancel(); } const unpackData = (data: string) => { if (data) { data = data.replaceAll(',', '\n') } else { data = '' } return data; } const packData = (data: string) => { if (data) { data = data.replaceAll('\n', ',') } return data; } const handleOk = () => { if (props.handleOk) { setBtnOkDisabled(true); group.pocDetails = packData(data); if (websiteValue && ( websiteValue.startsWith('www.') || !( websiteValue.startsWith(dropdownList[0]) || websiteValue.startsWith(dropdownList[1]) ) )) { group.website = selectedDropdownValue + websiteValue; } else { group.website = websiteValue; } props.handleOk(group); } } const handleEnter = () => { if (props.onEnter) { props.onEnter(); } setBtnOkDisabled(false); if (props.groupId) { updateGroup(props.groupId); } else { setGroup({ ...emptyGroup }); setSelectedDropdownValue(dropdownList[0]); setData(''); setWebsiteValue(''); } } const handleExited = () => { setIsReady(false); props.onExit(); } const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { const form = event.currentTarget; event.preventDefault(); event.stopPropagation(); if (errorOpeningHour) { document.getElementById('formPocOpeningHours')?.focus(); return; } if (form.checkValidity()) { handleOk(); } } const updateGroupProp = (name: string, value: any) => { const ngroup = { ...group, [name]: value }; setGroup({ ...ngroup }); } const changeOpeningHoursHandler = (name: string, value: string) => { setDisplayOpeningHours(value); let error = undefined; const openingHours = value.split('\n'); if (openingHours.length > 7) { setErrorOpeningHour('opening-hours-to-much-lines-error'); return; } error = openingHours.find(element => { return !utils.isOpeningHoursValid(element); }); if (error) { setErrorOpeningHour('openening-hours-to-long-error'); } else { setErrorOpeningHour(''); updateGroupProp("openingHours", openingHours); } } const updateSearchPortalConsent = (name: string, value: any) => { const ngroup: IGroupDetails = { ...group, [name]: value }; if (value === false) { ngroup.email = ''; ngroup.website = ''; ngroup.openingHours = []; ngroup.appointmentRequired = false; } setGroup(ngroup); } const collectChildren = (idlist: string[], parentNode: IGroup) => { if (parentNode) { idlist.push(parentNode.id); parentNode.children.forEach(child => collectChildren(idlist, child as IGroup)); } } const getOptions = (): JSX.Element[] => { let result: JSX.Element[] = []; if (group && group.id) { const node = props.groups.find((groupNode: IGroupNode) => groupNode.group.id === group.id); const selfIdOrChildren: string[] = []; if (node) { collectChildren(selfIdOrChildren, node.group); } const fList = props.groups.filter((groupNode: IGroupNode) => selfIdOrChildren.indexOf(groupNode.group.id) < 0) result = fList.map((groupNode: IGroupNode) => <option key={groupNode.group.id} value={groupNode.group.id}>{"\u00A0\u00A0\u00A0\u00A0".repeat(groupNode.level) + groupNode.group.name}</option> ); // result.push(<option key="empty" value="empty">{t('translation:no-parentgroup-option')}</option>); } return result; } const getDropdownItems = () => { setDropdownItems( dropdownList.map( (item: string) => <Dropdown.Item onSelect={(eventKey: any) => setSelectedDropdownValue(eventKey)} eventKey={item} key={item} > {item} </Dropdown.Item>)); } return ( <Modal contentClassName='data-modal' size="lg" show={props.show} backdrop="static" keyboard={false} centered onEnter={handleEnter} onExited={handleExited} > {!isReady ? <CwaSpinner background='#eeeeee' /> : <Fade appear={true} in={true} > <Form className='form-flex' onSubmit={handleSubmit} validated={validated} > <Modal.Header id='data-header' className='pb-0' > <Modal.Title>{isNew ? t('translation:add-group') : t('translation:edit-group')}</Modal.Title> </Modal.Header> <Modal.Body className='bg-light'> {isNew ? <></> : <> <FormGroupSelect controlId='formGroupSelect' title={t('translation:parentgroup')} placeholder={t('translation:no-parentgroup-option')} value={group.parentGroup} onChange={(ent: any) => updateGroupProp('parentGroup', ent.target.value)} options={options} /> <hr /> </> } < FormGroupInput controlId='formFirstName' title={t('translation:name')} value={group ? group.name : ''} required onChange={(evt: any) => { updateGroupProp('name', evt.target.value); props.resetError(); }} maxLength={45} isInvalid={props.isCreationError} InvalidText={t('translation:group-conflict-error')} /> {/* <hr /> */} < FormGroupTextarea controlId='formAdressData' title={t('translation:address-testcenter')} placeholder={t('translation:address-testcenter-placeholder')} value={data} required onChange={(evt: any) => setData(evt.target.value)} type='textarea' maxLength={300} /> {utils.hasRole(keycloak, 'c19_quick_test_poc_nat_admin') ? <FormGroupPermissionCkb controlId='formenablePcr' title={t('translation:enablePcr')} //label={t('translation:for-counter')} onChange={(evt: any) => updateSearchPortalConsent('enablePcr', evt.currentTarget.checked)} type='checkbox' checked={group.enablePcr} /> : <></> } <hr /> {/* < FormGroupInput controlId='formBSNRInput' title={t('translation:bsnr')} placeholder={t('translation:bsnr-placeholder')} value={group ? group.bsnr : ''} onChange={(evt: any) => { updateGroupProp('bsnr', evt.target.value); props.resetError(); }} maxLength={9} prepend='i' tooltip={t('translation:bsnr-tooltip')} pattern={utils.pattern.BSNR} /> */} <FormGroupPermissionCkb controlId='formsearchPortalConsent' title={t('translation:searchPortalConsent')} //label={t('translation:for-counter')} onChange={(evt: any) => updateSearchPortalConsent('searchPortalConsent', evt.currentTarget.checked)} type='checkbox' checked={group.searchPortalConsent} /> <Collapse in={group.searchPortalConsent}> <div> < FormGroupInput controlId='formEmailInput' title={t('translation:email-address')} value={group?.email ? group.email : ''} onChange={(evt: any) => { updateGroupProp('email', evt.target.value); props.resetError(); }} type='email' pattern={utils.pattern.eMail} minLength={5} maxLength={255} /> < FormGroupInput controlId='formPocWebsite' title={t('translation:searchPortalWebsite')} placeholder={t('translation:searchPortalWebsitePlaceholder')} value={websiteValue} dropdown={dropdownItems} dropdownTitle={selectedDropdownValue} prepend='i' tooltip={t('translation:searchPortalWebsiteTooltip')} onChange={(evt: any) => { setWebsiteValue(evt.target.value); props.resetError(); }} maxLength={100} pattern={utils.pattern.url} /> < FormGroupTextarea controlId='formPocOpeningHours' title={t('translation:searchPortalOpeningHours')} value={displayOpeningHours} onChange={(evt: any) => { changeOpeningHoursHandler('openingHours', evt.target.value); props.resetError(); }} type='textarea' rows={7} pattern={utils.pattern.email} isInvalid={errorOpeningHour} invalidText={errorOpeningHour && t('translation:' + errorOpeningHour)} /> <FormGroupPermissionCkb controlId='formAppointmentRequired' title={t('translation:searchPortalAppointmentRequired')} onChange={(evt: any) => updateGroupProp('appointmentRequired', evt.currentTarget.checked)} type='checkbox' checked={group?.appointmentRequired ? group.appointmentRequired : false} /> </div> </Collapse> {!(group && group.pocId) ? <></> : <> <hr /> < FormGroupInput controlId='formPocId' title={t('translation:poc-id')} value={group && group.pocId ? group.pocId : ''} readOnly /> </> } </Modal.Body> <Modal.Footer id='data-footer'> <Container className='p-0'> <Row> <Col sm='6' lg='4' className='mb-2 mb-sm-0 p-0 pr-sm-2'> <Button className='p-0' block variant='outline-primary' onClick={handleCancel} > {t('translation:cancel')} </Button> </Col> <Col sm='6' lg='4' className='p-0 pl-sm-2'> <Button className='p-0' block type='submit' disabled={btnOkDisabled} > {isNew ? t('translation:add') : t('translation:edit')} <Spinner as="span" className='btn-spinner' animation="border" hidden={!btnOkDisabled} size="sm" role="status" aria-hidden="true" /> </Button> </Col> </Row> </Container> </Modal.Footer> </Form> </Fade> } </Modal> ) }
Example #13
Source File: process-input.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
ProcessIdInput = (props: any) => {
const { t } = useTranslation();
const [processNo, setProcessNo] = React.useState('');
const [btnOkDisabled, setBtnOkDisabled] = React.useState(false);
const processIds = useGetPendingProcessIds(undefined, props.handleError);
const handleCancel = () => {
props.onCancel();
// props.onHide();
}
const handleOk = () => {
setBtnOkDisabled(true);
props.onChange(processNo);
// props.onHide();
}
const handleEnter = () => {
setBtnOkDisabled(false);
}
return (
<>
<Modal
contentClassName='data-modal'
show={props.show}
backdrop="static"
keyboard={false}
centered
onEnter={handleEnter}
>
<Modal.Header id='data-header' className='pb-0' >
<Row>
<Col >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >{t('translation:testId-input-header')}</Card.Title>
</Col>
</Row>
</Modal.Header>
{/*
content area with process number input and radios
*/}
<Modal.Body className='py-0 bg-light'>
<hr />
< FormGroupInput controlId='formProcessModalInput' title={t('translation:process-number')}
value={processNo}
onChange={(evt: any) => setProcessNo(evt.currentTarget.value)}
required
min={utils.shortHashLen}
maxLength={utils.shortHashLen}
pattern={utils.pattern.processNo}
datalistId='processId-list'
datalist={processIds ? processIds.map((i: IShortHashedGuid) => <option key={i.shortHashedGuid} value={i.shortHashedGuid} />) : undefined}
/>
<hr />
</Modal.Body>
{/*
footer with cancel and submit button
*/}
<Modal.Footer id='data-footer'>
<Container className='p-0'>
<Row>
<Col xs='6' md='4' className='pl-0'>
<Button
className='py-0'
block
variant='outline-primary'
onClick={handleCancel}
>
{t('translation:cancel')}
</Button>
</Col>
<Col xs='6' md='4' className='pr-0'>
<Button
className='py-0'
block
onClick={handleOk}
disabled={processNo.length !== utils.shortHashLen || btnOkDisabled}
>
{t('translation:ok')}
<Spinner
as="span"
className='btn-spinner'
animation="border"
hidden={!btnOkDisabled}
size="sm"
role="status"
aria-hidden="true"
/>
</Button>
</Col>
</Row>
</Container>
</Modal.Footer>
</Modal>
</>
)
}
Example #14
Source File: user-modal.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
UserModal = (props: any) => { const { t } = useTranslation(); const [user, setUser] = React.useState<IUser>(props.user); const [isNew, setIsNew] = React.useState(true); const [validated, setValidated] = React.useState(false); const [btnOkDisabled, setBtnOkDisabled] = React.useState(true); const [options, setOptions] = React.useState<JSX.Element[]>(); React.useEffect(() => { if (props.user.username !== user.username || !props.user.username) { setUser(props.user); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.user]); React.useEffect(() => { if (user && props.groups) { const options = getOptions(); setOptions(options); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [user, props.groups]); React.useEffect(() => { setValidated(props.isSuccess) // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.isSuccess]); React.useEffect(() => { if (props.isCreationError) { setBtnOkDisabled(false) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.isCreationError]); const handleCancel = () => { props.onCancel(); } const handleExit = () => { props.onExit(); } const updateUserProp = (name: string, value: any) => { const nuser = { ...user, [name]: value }; setUser(nuser); } const handleOk = () => { if (props.handleOk) { setBtnOkDisabled(true); props.resetError(); props.handleOk(user, setUser); } } const handleEnter = () => { if (props.onEnter) { props.onEnter(); } setIsNew(!props.user.username); setBtnOkDisabled(false); } const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { const form = event.currentTarget; event.preventDefault(); event.stopPropagation(); if (form.checkValidity()) { handleOk(); } } const getOptions = () => { let result: JSX.Element[] = []; result = props.groups.map((groupNode: IGroupNode) => <option key={groupNode.group.id} value={groupNode.group.id}>{"\u00A0\u00A0\u00A0\u00A0".repeat(groupNode.level) + groupNode.group.name}</option> ) // if (!user.subGroup || !props.groups.find((groupNode: IGroupNode) => groupNode.group.id === user.subGroup)) { // user.subGroup = null; // result.unshift(<option key={0} value='empty'>{t('translation:no-group-option')}</option>); // } return result; } return ( <Modal contentClassName='data-modal' show={props.show} backdrop="static" keyboard={false} centered onEnter={handleEnter} onExited={handleExit} > <Form className='form-flex' onSubmit={handleSubmit} validated={validated}> <Modal.Header id='data-header' className='pb-0' > <Modal.Title>{isNew ? t('translation:add-user') : t('translation:edit-user')}</Modal.Title> </Modal.Header> <Modal.Body className='bg-light'> < FormGroupInput controlId='formUserNameInput' title={t('translation:user-name')} value={user.username} required readOnly={!isNew} onChange={(evt: any) => { updateUserProp('username', evt.target.value); props.resetError(); }} minLength={3} maxLength={50} isInvalid={props.isCreationError} InvalidText={t('translation:user-conflict-error')} /> < FormGroupInput controlId='formFirstName' title={t('translation:first-name')} value={user.firstName} required onChange={(evt: any) => updateUserProp('firstName', evt.target.value)} maxLength={30} /> < FormGroupInput controlId='formLastName' title={t('translation:name')} value={user.lastName} onChange={(evt: any) => updateUserProp('lastName', evt.target.value)} required maxLength={30} /> < FormGroupInput controlId='formPassword' title={t('translation:password')} value={user.password ? user.password : ''} onChange={(evt: any) => updateUserProp('password', evt.target.value)} required={isNew} type='password' minLength={8} maxLength={64} /> <hr /> <FormGroupPermissionCkb controlId='formRoleLab' title={t('translation:permission')} label={t('translation:for-lab')} onChange={(evt: any) => updateUserProp('roleLab', evt.currentTarget.checked)} type='checkbox' checked={user.roleLab} /> <FormGroupPermissionCkb controlId='formRoleCounter' title={t('translation:permission')} label={t('translation:for-counter')} onChange={(evt: any) => updateUserProp('roleCounter', evt.currentTarget.checked)} type='checkbox' checked={user.roleCounter} /> <hr /> <FormGroupSelect controlId='formGroupSelect' title={t('translation:group')} placeholder={t('translation:no-group-option')} value={user.subGroup ? user.subGroup : ''} onChange={(ent: any) => updateUserProp('subGroup', ent.target.value)} options={options} required /> </Modal.Body> <Modal.Footer id='data-footer'> <Container className='p-0'> <Row> <Col sm='6' lg='4' className='mb-2 mb-sm-0 p-0 pr-sm-2'> <Button className='p-0' block variant='outline-primary' onClick={handleCancel} > {t('translation:cancel')} </Button> </Col> <Col sm='6' lg='4' className='p-0 pl-sm-2'> <Button className='p-0' block type='submit' disabled={btnOkDisabled} > {isNew ? t('translation:add') : t('translation:edit')} <Spinner as="span" className='btn-spinner' animation="border" hidden={!btnOkDisabled} size="sm" role="status" aria-hidden="true" /> </Button> </Col> </Row> </Container> </Modal.Footer> </Form> </Modal> ) }
Example #15
Source File: ValTxnList.tsx From devex with GNU General Public License v3.0 | 4 votes |
ValTxnList: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { dataService, networkUrl } = networkContext!
useEffect(() => { setData(null) }, [networkUrl])
const [data, setData] = useState<TransactionDetails[] | null>(null)
const columns = useMemo(
() => [{
id: 'from-col',
Header: 'From',
accessor: 'txn.senderAddress',
Cell: ({ value }: { value: string }) => (
<QueryPreservingLink to={`/address/${hexAddrToZilAddr(value)}`}>
{hexAddrToZilAddr(value)}
</QueryPreservingLink>)
}, {
id: 'to-col',
Header: 'To',
Cell: ({ row }: { row: Row<TransactionDetails> }) => {
return <ToAddrDisp txnDetails={row.original} />
}
}, {
id: 'hash-col',
Header: 'Hash',
accessor: 'hash',
Cell: ({ row }: { row: Row<TransactionDetails> }) => {
return <QueryPreservingLink to={`/tx/0x${row.original.hash}`}>
<div className='text-right mono'>
{row.original.txn.txParams.receipt && !row.original.txn.txParams.receipt.success
&& <FontAwesomeIcon className='mr-1' icon={faExclamationCircle} color='red' />
}
{'0x' + row.original.hash}
</div>
</QueryPreservingLink>
}
}, {
id: 'amount-col',
Header: 'Amount',
accessor: 'txn.amount',
Cell: ({ value }: { value: string }) => (
<OverlayTrigger placement='right'
overlay={<Tooltip id={'amt-tt'}>{qaToZil(value)}</Tooltip>}>
<div className='text-right sm'>{qaToZil(value, 13)}</div>
</OverlayTrigger>
)
}, {
id: 'fee-col',
Header: 'Fee',
accessor: 'txn',
Cell: ({ value }: { value: Transaction }) => {
const fee = Number(value.txParams.gasPrice) * value.txParams.receipt!.cumulative_gas
return <OverlayTrigger placement='top'
overlay={<Tooltip id={'fee-tt'}>{qaToZil(fee)}</Tooltip>}>
<div className='text-center sm'>{qaToZil(fee, 4)}</div>
</OverlayTrigger>
}
}], []
)
// Fetch Data
useEffect(() => {
let isCancelled = false
if (!dataService) return
let receivedData: TransactionDetails[]
const getData = async () => {
try {
receivedData = await dataService.getLatest5ValidatedTransactions()
if (!isCancelled && receivedData)
setData(receivedData)
} catch (e) {
if (!isCancelled)
console.log(e)
}
}
getData()
const getDataTimer = setInterval(async () => {
await getData()
}, refreshRate)
return () => {
isCancelled = true
clearInterval(getDataTimer)
}
}, [networkUrl, dataService])
return <>
<Card className='valtxlist-card'>
<Card.Header>
<div className='valtxlist-card-header'>
<span>Transactions</span>
<QueryPreservingLink to={'/tx'}>View Recent Transactions</QueryPreservingLink>
</div>
</Card.Header>
<Card.Body>
{data
? <DisplayTable columns={columns} data={data} />
: <Spinner animation="border" role="status" />
}
</Card.Body>
</Card>
</>
}
Example #16
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
Bid: React.FC<{
auction: Auction;
auctionEnded: boolean;
}> = props => {
const activeAccount = useAppSelector(state => state.account.activeAccount);
const { library } = useEthers();
let { auction, auctionEnded } = props;
const activeLocale = useActiveLocale();
const nounsAuctionHouseContract = new NounsAuctionHouseFactory().attach(
config.addresses.nounsAuctionHouseProxy,
);
const account = useAppSelector(state => state.account.activeAccount);
const bidInputRef = useRef<HTMLInputElement>(null);
const [bidInput, setBidInput] = useState('');
const [bidButtonContent, setBidButtonContent] = useState({
loading: false,
content: auctionEnded ? <Trans>Settle</Trans> : <Trans>Place bid</Trans>,
});
const [showConnectModal, setShowConnectModal] = useState(false);
const hideModalHandler = () => {
setShowConnectModal(false);
};
const dispatch = useAppDispatch();
const setModal = useCallback((modal: AlertModal) => dispatch(setAlertModal(modal)), [dispatch]);
const minBidIncPercentage = useAuctionMinBidIncPercentage();
const minBid = computeMinimumNextBid(
auction && new BigNumber(auction.amount.toString()),
minBidIncPercentage,
);
const { send: placeBid, state: placeBidState } = useContractFunction(
nounsAuctionHouseContract,
AuctionHouseContractFunction.createBid,
);
const { send: settleAuction, state: settleAuctionState } = useContractFunction(
nounsAuctionHouseContract,
AuctionHouseContractFunction.settleCurrentAndCreateNewAuction,
);
const bidInputHandler = (event: ChangeEvent<HTMLInputElement>) => {
const input = event.target.value;
// disable more than 2 digits after decimal point
if (input.includes('.') && event.target.value.split('.')[1].length > 2) {
return;
}
setBidInput(event.target.value);
};
const placeBidHandler = async () => {
if (!auction || !bidInputRef.current || !bidInputRef.current.value) {
return;
}
if (currentBid(bidInputRef).isLessThan(minBid)) {
setModal({
show: true,
title: <Trans>Insufficient bid amount ?</Trans>,
message: (
<Trans>
Please place a bid higher than or equal to the minimum bid amount of {minBidEth(minBid)}{' '}
ETH
</Trans>
),
});
setBidInput(minBidEth(minBid));
return;
}
const value = utils.parseEther(bidInputRef.current.value.toString());
const contract = connectContractToSigner(nounsAuctionHouseContract, undefined, library);
const gasLimit = await contract.estimateGas.createBid(auction.nounId, {
value,
});
placeBid(auction.nounId, {
value,
gasLimit: gasLimit.add(10_000), // A 10,000 gas pad is used to avoid 'Out of gas' errors
});
};
const settleAuctionHandler = () => {
settleAuction();
};
const clearBidInput = () => {
if (bidInputRef.current) {
bidInputRef.current.value = '';
}
};
// successful bid using redux store state
useEffect(() => {
if (!account) return;
// tx state is mining
const isMiningUserTx = placeBidState.status === 'Mining';
// allows user to rebid against themselves so long as it is not the same tx
const isCorrectTx = currentBid(bidInputRef).isEqualTo(new BigNumber(auction.amount.toString()));
if (isMiningUserTx && auction.bidder === account && isCorrectTx) {
placeBidState.status = 'Success';
setModal({
title: <Trans>Success</Trans>,
message: <Trans>Bid was placed successfully!</Trans>,
show: true,
});
setBidButtonContent({ loading: false, content: <Trans>Place bid</Trans> });
clearBidInput();
}
}, [auction, placeBidState, account, setModal]);
// placing bid transaction state hook
useEffect(() => {
switch (!auctionEnded && placeBidState.status) {
case 'None':
setBidButtonContent({
loading: false,
content: <Trans>Place bid</Trans>,
});
break;
case 'Mining':
setBidButtonContent({ loading: true, content: <></> });
break;
case 'Fail':
setModal({
title: <Trans>Transaction Failed</Trans>,
message: placeBidState?.errorMessage || <Trans>Please try again.</Trans>,
show: true,
});
setBidButtonContent({ loading: false, content: <Trans>Bid</Trans> });
break;
case 'Exception':
setModal({
title: <Trans>Error</Trans>,
message: placeBidState?.errorMessage || <Trans>Please try again.</Trans>,
show: true,
});
setBidButtonContent({ loading: false, content: <Trans>Bid</Trans> });
break;
}
}, [placeBidState, auctionEnded, setModal]);
// settle auction transaction state hook
useEffect(() => {
switch (auctionEnded && settleAuctionState.status) {
case 'None':
setBidButtonContent({
loading: false,
content: <Trans>Settle Auction</Trans>,
});
break;
case 'Mining':
setBidButtonContent({ loading: true, content: <></> });
break;
case 'Success':
setModal({
title: <Trans>Success</Trans>,
message: <Trans>Settled auction successfully!</Trans>,
show: true,
});
setBidButtonContent({ loading: false, content: <Trans>Settle Auction</Trans> });
break;
case 'Fail':
setModal({
title: <Trans>Transaction Failed</Trans>,
message: settleAuctionState?.errorMessage || <Trans>Please try again.</Trans>,
show: true,
});
setBidButtonContent({ loading: false, content: <Trans>Settle Auction</Trans> });
break;
case 'Exception':
setModal({
title: <Trans>Error</Trans>,
message: settleAuctionState?.errorMessage || <Trans>Please try again.</Trans>,
show: true,
});
setBidButtonContent({ loading: false, content: <Trans>Settle Auction</Trans> });
break;
}
}, [settleAuctionState, auctionEnded, setModal]);
if (!auction) return null;
const isDisabled =
placeBidState.status === 'Mining' || settleAuctionState.status === 'Mining' || !activeAccount;
const fomoNounsBtnOnClickHandler = () => {
// Open Fomo Nouns in a new tab
window.open('https://fomonouns.wtf', '_blank')?.focus();
};
const isWalletConnected = activeAccount !== undefined;
return (
<>
{showConnectModal && activeAccount === undefined && (
<WalletConnectModal onDismiss={hideModalHandler} />
)}
<InputGroup>
{!auctionEnded && (
<>
<span className={classes.customPlaceholderBidAmt}>
{!auctionEnded && !bidInput ? (
<>
Ξ {minBidEth(minBid)}{' '}
<span
className={
activeLocale === 'ja-JP' ? responsiveUiUtilsClasses.disableSmallScreens : ''
}
>
<Trans>or more</Trans>
</span>
</>
) : (
''
)}
</span>
<FormControl
className={classes.bidInput}
type="number"
min="0"
onChange={bidInputHandler}
ref={bidInputRef}
value={bidInput}
/>
</>
)}
{!auctionEnded ? (
<Button
className={auctionEnded ? classes.bidBtnAuctionEnded : classes.bidBtn}
onClick={auctionEnded ? settleAuctionHandler : placeBidHandler}
disabled={isDisabled}
>
{bidButtonContent.loading ? <Spinner animation="border" /> : bidButtonContent.content}
</Button>
) : (
<>
<Col lg={12} className={classes.voteForNextNounBtnWrapper}>
<Button className={classes.bidBtnAuctionEnded} onClick={fomoNounsBtnOnClickHandler}>
<Trans>Vote for the next Noun</Trans> ⌐◧-◧
</Button>
</Col>
{/* Only show force settle button if wallet connected */}
{isWalletConnected && (
<Col lg={12}>
<SettleManuallyBtn settleAuctionHandler={settleAuctionHandler} auction={auction} />
</Col>
)}
</>
)}
</InputGroup>
</>
);
}
Example #17
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
VoteModal = ({ show, onHide, proposalId, availableVotes }: VoteModalProps) => {
const { castVote, castVoteState } = useCastVote();
const { castVoteWithReason, castVoteWithReasonState } = useCastVoteWithReason();
const [vote, setVote] = useState<Vote>();
const [voteReason, setVoteReason] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [isVoteSucessful, setIsVoteSuccessful] = useState(false);
const [isVoteFailed, setIsVoteFailed] = useState(false);
const [failureCopy, setFailureCopy] = useState<ReactNode>('');
const [errorMessage, setErrorMessage] = useState<ReactNode>('');
const getVoteErrorMessage = (error: string | undefined) => {
if (error?.match(/voter already voted/)) {
return <Trans>User Already Voted</Trans>;
}
return error;
};
const handleVoteStateChange = useCallback((state: TransactionStatus) => {
switch (state.status) {
case 'None':
setIsLoading(false);
break;
case 'Mining':
setIsLoading(true);
break;
case 'Success':
setIsLoading(false);
setIsVoteSuccessful(true);
break;
case 'Fail':
setFailureCopy(<Trans>Transaction Failed</Trans>);
setErrorMessage(state?.errorMessage || <Trans>Please try again.</Trans>);
setIsLoading(false);
setIsVoteFailed(true);
break;
case 'Exception':
setFailureCopy(<Trans>Error</Trans>);
setErrorMessage(
getVoteErrorMessage(state?.errorMessage) || <Trans>Please try again.</Trans>,
);
setIsLoading(false);
setIsVoteFailed(true);
break;
}
}, []);
// Cast vote transaction state hook
useEffect(() => {
handleVoteStateChange(castVoteState);
}, [castVoteState, handleVoteStateChange]);
// Cast vote with reason transaction state hook
useEffect(() => {
handleVoteStateChange(castVoteWithReasonState);
}, [castVoteWithReasonState, handleVoteStateChange]);
// Auto close the modal after a transaction completes succesfully
// Leave failed transaction up until user closes manually to allow for debugging
useEffect(() => {
if (isVoteSucessful) {
setTimeout(onHide, POST_SUCESSFUL_VOTE_MODAL_CLOSE_TIME_MS);
}
}, [isVoteSucessful, onHide]);
// If show is false (i.e. on hide) reset failure related state variables
useEffect(() => {
if (show) {
return;
}
setIsVoteFailed(false);
}, [show]);
const voteModalContent = (
<>
{isVoteSucessful && (
<div className={classes.transactionStatus}>
<p>
<Trans>
You've successfully voted on on prop {i18n.number(parseInt(proposalId || '0'))}
</Trans>
</p>
<div className={classes.voteSuccessBody}>
<Trans>Thank you for voting.</Trans>
</div>
</div>
)}
{isVoteFailed && (
<div className={classes.transactionStatus}>
<p className={classes.voteFailureTitle}>
<Trans>There was an error voting for your account.</Trans>
</p>
<div className={classes.voteFailureBody}>
{failureCopy}: <span className={classes.voteFailureErrorMessage}>{errorMessage}</span>
</div>
</div>
)}
{!isVoteFailed && !isVoteSucessful && (
<div className={clsx(classes.votingButtonsWrapper, isLoading ? classes.disabled : '')}>
<div onClick={() => setVote(Vote.FOR)}>
<NavBarButton
buttonText={
availableVotes > 1 ? (
<Trans>
Cast {i18n.number(availableVotes)} votes for Prop{' '}
{i18n.number(parseInt(proposalId || '0'))}
</Trans>
) : (
<Trans>Cast 1 vote for Prop {i18n.number(parseInt(proposalId || '0'))}</Trans>
)
}
buttonIcon={<></>}
buttonStyle={
vote === Vote.FOR
? NavBarButtonStyle.WHITE_ACTIVE_VOTE_SUBMIT
: NavBarButtonStyle.WHITE_INFO
}
/>
</div>
<br />
<div onClick={() => setVote(Vote.AGAINST)}>
<NavBarButton
buttonText={
availableVotes > 1 ? (
<Trans>
Cast {i18n.number(availableVotes)} votes against Prop{' '}
{i18n.number(parseInt(proposalId || '0'))}
</Trans>
) : (
<Trans>Cast 1 vote against Prop {i18n.number(parseInt(proposalId || '0'))}</Trans>
)
}
buttonIcon={<></>}
buttonStyle={
vote === Vote.AGAINST
? NavBarButtonStyle.WHITE_ACTIVE_VOTE_SUBMIT
: NavBarButtonStyle.WHITE_INFO
}
/>
</div>
<br />
<div onClick={() => setVote(Vote.ABSTAIN)}>
<NavBarButton
buttonText={
<Trans>
Abstain from voting on Prop {i18n.number(parseInt(proposalId || '0'))}
</Trans>
}
buttonIcon={<></>}
buttonStyle={
vote === Vote.ABSTAIN
? NavBarButtonStyle.WHITE_ACTIVE_VOTE_SUBMIT
: NavBarButtonStyle.WHITE_INFO
}
/>
</div>
<br />
<FloatingLabel controlId="reasonTextarea" label={<Trans>Reason (Optional)</Trans>}>
<FormControl
as="textarea"
placeholder={
i18n.locale === 'en' ? `Reason for voting ${Vote[vote ?? Vote.FOR]}` : ''
}
value={voteReason}
onChange={e => setVoteReason(e.target.value)}
className={classes.voteReasonTextarea}
/>
</FloatingLabel>
<br />
<Button
onClick={() => {
if (vote === undefined || !proposalId || isLoading) {
return;
}
setIsLoading(true);
if (voteReason.trim() === '') {
castVote(proposalId, vote);
} else {
castVoteWithReason(proposalId, vote, voteReason);
}
}}
className={vote === undefined ? classes.submitBtnDisabled : classes.submitBtn}
>
{isLoading ? <Spinner animation="border" /> : <Trans>Submit Vote</Trans>}
</Button>
</div>
)}
</>
);
// On modal dismiss, reset non-success state
const resetNonSuccessStateAndHideModal = () => {
setIsLoading(false);
setIsVoteFailed(false);
setErrorMessage('');
setFailureCopy('');
onHide();
};
return (
<>
{show && (
<Modal
onDismiss={resetNonSuccessStateAndHideModal}
title={<Trans>Vote on Prop {i18n.number(parseInt(proposalId || '0'))}</Trans>}
content={voteModalContent}
/>
)}
</>
);
}
Example #18
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
VotePage = ({
match: {
params: { id },
},
}: RouteComponentProps<{ id: string }>) => {
const proposal = useProposal(id);
const [showVoteModal, setShowVoteModal] = useState<boolean>(false);
const [isQueuePending, setQueuePending] = useState<boolean>(false);
const [isExecutePending, setExecutePending] = useState<boolean>(false);
const dispatch = useAppDispatch();
const setModal = useCallback((modal: AlertModal) => dispatch(setAlertModal(modal)), [dispatch]);
const { queueProposal, queueProposalState } = useQueueProposal();
const { executeProposal, executeProposalState } = useExecuteProposal();
// Get and format date from data
const timestamp = Date.now();
const currentBlock = useBlockNumber();
const startDate =
proposal && timestamp && currentBlock
? dayjs(timestamp).add(
AVERAGE_BLOCK_TIME_IN_SECS * (proposal.startBlock - currentBlock),
'seconds',
)
: undefined;
const endDate =
proposal && timestamp && currentBlock
? dayjs(timestamp).add(
AVERAGE_BLOCK_TIME_IN_SECS * (proposal.endBlock - currentBlock),
'seconds',
)
: undefined;
const now = dayjs();
// Get total votes and format percentages for UI
const totalVotes = proposal
? proposal.forCount + proposal.againstCount + proposal.abstainCount
: undefined;
const forPercentage = proposal && totalVotes ? (proposal.forCount * 100) / totalVotes : 0;
const againstPercentage = proposal && totalVotes ? (proposal.againstCount * 100) / totalVotes : 0;
const abstainPercentage = proposal && totalVotes ? (proposal.abstainCount * 100) / totalVotes : 0;
// Only count available votes as of the proposal created block
const availableVotes = useUserVotesAsOfBlock(proposal?.createdBlock ?? undefined);
const hasSucceeded = proposal?.status === ProposalState.SUCCEEDED;
const isAwaitingStateChange = () => {
if (hasSucceeded) {
return true;
}
if (proposal?.status === ProposalState.QUEUED) {
return new Date() >= (proposal?.eta ?? Number.MAX_SAFE_INTEGER);
}
return false;
};
const startOrEndTimeCopy = () => {
if (startDate?.isBefore(now) && endDate?.isAfter(now)) {
return <Trans>Ends</Trans>;
}
if (endDate?.isBefore(now)) {
return <Trans>Ended</Trans>;
}
return <Trans>Starts</Trans>;
};
const startOrEndTimeTime = () => {
if (!startDate?.isBefore(now)) {
return startDate;
}
return endDate;
};
const moveStateButtonAction = hasSucceeded ? <Trans>Queue</Trans> : <Trans>Execute</Trans>;
const moveStateAction = (() => {
if (hasSucceeded) {
return () => {
if (proposal?.id) {
return queueProposal(proposal.id);
}
};
}
return () => {
if (proposal?.id) {
return executeProposal(proposal.id);
}
};
})();
const onTransactionStateChange = useCallback(
(
tx: TransactionStatus,
successMessage?: ReactNode,
setPending?: (isPending: boolean) => void,
getErrorMessage?: (error?: string) => ReactNode | undefined,
onFinalState?: () => void,
) => {
switch (tx.status) {
case 'None':
setPending?.(false);
break;
case 'Mining':
setPending?.(true);
break;
case 'Success':
setModal({
title: <Trans>Success</Trans>,
message: successMessage || <Trans>Transaction Successful!</Trans>,
show: true,
});
setPending?.(false);
onFinalState?.();
break;
case 'Fail':
setModal({
title: <Trans>Transaction Failed</Trans>,
message: tx?.errorMessage || <Trans>Please try again.</Trans>,
show: true,
});
setPending?.(false);
onFinalState?.();
break;
case 'Exception':
setModal({
title: <Trans>Error</Trans>,
message: getErrorMessage?.(tx?.errorMessage) || <Trans>Please try again.</Trans>,
show: true,
});
setPending?.(false);
onFinalState?.();
break;
}
},
[setModal],
);
useEffect(
() =>
onTransactionStateChange(
queueProposalState,
<Trans>Proposal Queued!</Trans>,
setQueuePending,
),
[queueProposalState, onTransactionStateChange, setModal],
);
useEffect(
() =>
onTransactionStateChange(
executeProposalState,
<Trans>Proposal Executed!</Trans>,
setExecutePending,
),
[executeProposalState, onTransactionStateChange, setModal],
);
const activeAccount = useAppSelector(state => state.account.activeAccount);
const {
loading,
error,
data: voters,
} = useQuery<ProposalVotes>(proposalVotesQuery(proposal?.id ?? '0'), {
skip: !proposal,
});
const voterIds = voters?.votes?.map(v => v.voter.id);
const { data: delegateSnapshot } = useQuery<Delegates>(
delegateNounsAtBlockQuery(voterIds ?? [], proposal?.createdBlock ?? 0),
{
skip: !voters?.votes?.length,
},
);
const { delegates } = delegateSnapshot || {};
const delegateToNounIds = delegates?.reduce<Record<string, string[]>>((acc, curr) => {
acc[curr.id] = curr?.nounsRepresented?.map(nr => nr.id) ?? [];
return acc;
}, {});
const data = voters?.votes?.map(v => ({
supportDetailed: v.supportDetailed,
nounsRepresented: delegateToNounIds?.[v.voter.id] ?? [],
}));
const [showToast, setShowToast] = useState(true);
useEffect(() => {
if (showToast) {
setTimeout(() => {
setShowToast(false);
}, 5000);
}
}, [showToast]);
if (!proposal || loading || !data) {
return (
<div className={classes.spinner}>
<Spinner animation="border" />
</div>
);
}
if (error) {
return <Trans>Failed to fetch</Trans>;
}
const isWalletConnected = !(activeAccount === undefined);
const isActiveForVoting = startDate?.isBefore(now) && endDate?.isAfter(now);
const forNouns = getNounVotes(data, 1);
const againstNouns = getNounVotes(data, 0);
const abstainNouns = getNounVotes(data, 2);
return (
<Section fullWidth={false} className={classes.votePage}>
<VoteModal
show={showVoteModal}
onHide={() => setShowVoteModal(false)}
proposalId={proposal?.id}
availableVotes={availableVotes || 0}
/>
<Col lg={10} className={classes.wrapper}>
{proposal && (
<ProposalHeader
proposal={proposal}
isActiveForVoting={isActiveForVoting}
isWalletConnected={isWalletConnected}
submitButtonClickHandler={() => setShowVoteModal(true)}
/>
)}
</Col>
<Col lg={10} className={clsx(classes.proposal, classes.wrapper)}>
{isAwaitingStateChange() && (
<Row className={clsx(classes.section, classes.transitionStateButtonSection)}>
<Col className="d-grid">
<Button
onClick={moveStateAction}
disabled={isQueuePending || isExecutePending}
variant="dark"
className={classes.transitionStateButton}
>
{isQueuePending || isExecutePending ? (
<Spinner animation="border" />
) : (
<Trans>{moveStateButtonAction} Proposal ⌐◧-◧</Trans>
)}
</Button>
</Col>
</Row>
)}
<Row>
<VoteCard
proposal={proposal}
percentage={forPercentage}
nounIds={forNouns}
variant={VoteCardVariant.FOR}
/>
<VoteCard
proposal={proposal}
percentage={againstPercentage}
nounIds={againstNouns}
variant={VoteCardVariant.AGAINST}
/>
<VoteCard
proposal={proposal}
percentage={abstainPercentage}
nounIds={abstainNouns}
variant={VoteCardVariant.ABSTAIN}
/>
</Row>
{/* TODO abstract this into a component */}
<Row>
<Col xl={4} lg={12}>
<Card className={classes.voteInfoCard}>
<Card.Body className="p-2">
<div className={classes.voteMetadataRow}>
<div className={classes.voteMetadataRowTitle}>
<h1>
<Trans>Threshold</Trans>
</h1>
</div>
<div className={classes.thresholdInfo}>
<span>
<Trans>Quorum</Trans>
</span>
<h3>
<Trans>{i18n.number(proposal.quorumVotes)} votes</Trans>
</h3>
</div>
</div>
</Card.Body>
</Card>
</Col>
<Col xl={4} lg={12}>
<Card className={classes.voteInfoCard}>
<Card.Body className="p-2">
<div className={classes.voteMetadataRow}>
<div className={classes.voteMetadataRowTitle}>
<h1>{startOrEndTimeCopy()}</h1>
</div>
<div className={classes.voteMetadataTime}>
<span>
{startOrEndTimeTime() &&
i18n.date(new Date(startOrEndTimeTime()?.toISOString() || 0), {
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short',
})}
</span>
<h3>
{startOrEndTimeTime() &&
i18n.date(new Date(startOrEndTimeTime()?.toISOString() || 0), {
dateStyle: 'long',
})}
</h3>
</div>
</div>
</Card.Body>
</Card>
</Col>
<Col xl={4} lg={12}>
<Card className={classes.voteInfoCard}>
<Card.Body className="p-2">
<div className={classes.voteMetadataRow}>
<div className={classes.voteMetadataRowTitle}>
<h1>Snapshot</h1>
</div>
<div className={classes.snapshotBlock}>
<span>
<Trans>Taken at block</Trans>
</span>
<h3>{proposal.createdBlock}</h3>
</div>
</div>
</Card.Body>
</Card>
</Col>
</Row>
<ProposalContent proposal={proposal} />
</Col>
</Section>
);
}
Example #19
Source File: ViewAllTable.tsx From devex with GNU General Public License v3.0 | 4 votes |
ViewAllTable: React.FC<IViewAllTableParams<DsBlockObj | TxBlockObj | TransactionDetails>> =
({ columns, data, isLoading, fetchData, pageCount: controlledPageCount }) => {
const { getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageCount,
gotoPage,
nextPage,
previousPage,
// Get the state from the instance
state: { pageIndex } } = useTable<DsBlockObj | TxBlockObj | TransactionDetails>({
columns,
data,
initialState: { pageIndex: 0 },
manualPagination: true,
pageCount: controlledPageCount,
}, usePagination)
const fetchDataDebounce = useAsyncDebounce(fetchData, 300)
useEffect(() => {
fetchDataDebounce({ pageIndex })
// fetchDataDebounce changes when fetchData function changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageIndex, fetchData])
const generatePagination = useCallback((currentPage: number, pageCount: number, delta = 2) => {
const separate = (a: number, b: number, isLower: boolean) => {
const temp = b - a
if (temp === 0)
return [a]
else if (temp === 1)
return [a, b]
else if (temp === 2)
return [a, a + 1, b]
else
return [a, isLower ? -1 : -2, b]
}
return Array(delta * 2 + 1)
.fill(0)
.map((_, index) => currentPage - delta + index)
.filter(page => 0 < page && page <= pageCount)
.flatMap((page, index, { length }) => {
if (!index) {
return separate(1, page, true)
}
if (index === length - 1) {
return separate(page, pageCount, false)
}
return [page]
})
}, [])
return (
<>
<BRow>
<BCol className='align-self-center pl-3'>
{data.length === 0
? null
: <span className='subtext'>Items Per Page: <strong>10</strong></span>}
</BCol>
<BCol>
<Pagination className='justify-content-end'>
<Pagination.Prev onClick={() => previousPage()} disabled={!canPreviousPage} />
{generatePagination(pageIndex + 1, pageCount).map((page) => {
if (page === -1)
return <Pagination.Ellipsis key={page} onClick={() => gotoPage(pageIndex - 5)} />
else if (page === -2)
return <Pagination.Ellipsis key={page} onClick={() => gotoPage(pageIndex + 5)} />
else if (page === pageIndex + 1)
return <Pagination.Item key={page} active>{page}</Pagination.Item>
else
return <Pagination.Item key={page} onClick={() => gotoPage(Number(page) - 1)}>{page}</Pagination.Item>
})}
<Pagination.Next onClick={() => nextPage()} disabled={!canNextPage} />
</Pagination>
</BCol>
</BRow>
<div className='viewall-table table'>
{isLoading ? <div className='center-spinner mt-4'><Spinner animation="border" /></div> : null}
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup: HeaderGroup<DsBlockObj | TxBlockObj | TransactionDetails>) => (
<tr {...headerGroup.getHeaderGroupProps()} key={headerGroup.getHeaderGroupProps().key} >
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()} key={column.getHeaderProps().key} id={column.id}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody style={isLoading ? { opacity: '0.5' } : {}}{...getTableBodyProps()}>
{page.map((row: Row<DsBlockObj | TxBlockObj | TransactionDetails>) => {
prepareRow(row)
return (
<tr {...row.getRowProps()} key={row.getRowProps().key}>
{row.cells.map((cell: Cell<DsBlockObj | TxBlockObj | TransactionDetails>) => {
return (
<td {...cell.getCellProps()}
key={cell.getCellProps().key}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
</>
)
}
Example #20
Source File: TxBlockList.tsx From devex with GNU General Public License v3.0 | 4 votes |
TxBlockList: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { dataService, networkUrl } = networkContext!
useEffect(() => { setData(null) }, [networkUrl]) // Unset data on url change
const [data, setData] = useState<TxBlockObj[] | null>(null)
const columns = useMemo(
() => [{
id: 'txheight-col',
Header: 'Height',
accessor: 'header.BlockNum',
Cell: ({ value }: { value: string }) => (
<QueryPreservingLink to={`/txbk/${value}`}>
{value}
</QueryPreservingLink>
)
},
{
id: 'numTxns-col',
Header: 'Txns',
accessor: 'header.NumTxns',
Cell: ({ value }: { value: string }) => (
<div className='text-center'>{value}</div>
),
},
{
id: 'total-txn-fees-col',
Header: 'Txn Fees',
accessor: 'header.TxnFees',
Cell: ({ value }: { value: string }) => (
<div className='text-right'>
<OverlayTrigger placement='top'
overlay={<Tooltip id={'amt-tt'}> {qaToZil(value)} </Tooltip>}>
<span>{qaToZil(value, 5)}</span>
</OverlayTrigger>
</div>)
},
{
id: 'rewards-col',
Header: 'Rewards',
accessor: 'header.Rewards',
Cell: ({ value }: { value: string }) => (
<div className='text-right'>
<OverlayTrigger placement='top'
overlay={<Tooltip id={'amt-tt'}> {qaToZil(value)} </Tooltip>}>
<span>{qaToZil(value, 5)}</span>
</OverlayTrigger>
</div>)
},
{
id: 'ds-block-col',
Header: 'DS Block',
accessor: 'header.DSBlockNum',
Cell: ({ value }: { value: string }) => (
<QueryPreservingLink to={`/dsbk/${value}`}>
<div className='text-center'>{value}</div>
</QueryPreservingLink>
)
},
{
id: 'age-col',
Header: 'Age',
accessor: 'header.Timestamp',
Cell: ({ value }: { value: string }) => (
<div className='text-right'>{
timestampToTimeago(value)}
</div>
),
}], []
)
// Fetch Data
useEffect(() => {
let isCancelled = false
if (!dataService) return
let receivedData: TxBlockObj[]
const getData = async () => {
try {
receivedData = await dataService.getLatest5TxBlocks()
if (!isCancelled && receivedData)
setData(receivedData)
} catch (e) {
if (!isCancelled)
console.log(e)
}
}
getData()
const getDataTimer = setInterval(async () => {
await getData()
}, refreshRate)
return () => {
isCancelled = true
clearInterval(getDataTimer)
}
}, [dataService])
return <>
<Card className='txblock-card'>
<Card.Header>
<div className='dsblock-card-header'>
<span>Transaction Blocks</span>
<QueryPreservingLink to={'/txbk'}>View All</QueryPreservingLink>
</div>
</Card.Header>
<Card.Body>
{data
? <DisplayTable columns={columns} data={data} />
: <Spinner animation="border" role="status" />
}
</Card.Body>
</Card>
</>
}
Example #21
Source File: PendTxnList.tsx From devex with GNU General Public License v3.0 | 4 votes |
PendTxnList: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { dataService, networkUrl } = networkContext!
useEffect(() => { setData(null) }, [networkUrl]) // Unset data on url change
const [data, setData] = useState<TransactionStatus[] | null>(null)
const columns = useMemo(
() => [{
id: 'pend-hash-col',
Header: 'Hash',
accessor: 'TxnHash',
Cell: ({ value }: { value: string }) => (
<div className='mono'>{'0x' + value}</div>
)
},
{
id: 'code-col',
Header: 'Code',
accessor: 'code',
Cell: ({ value }: { value: number }) => (
<div className='text-center'>{value}</div>
)
},
{
id: 'description-col',
Header: 'Description',
accessor: 'info',
Cell: ({ value }: { value: string }) => (
<div style={{ whiteSpace: 'pre-wrap' }}>{value}</div>
)
}], []
)
// Fetch Data
useEffect(() => {
let isCancelled = false
if (!dataService) return
let receivedData: TransactionStatus[]
const getData = async () => {
try {
receivedData = await dataService.getLatest5PendingTransactions()
if (!isCancelled && receivedData)
setData(receivedData)
} catch (e) {
if (!isCancelled)
console.log(e)
}
}
getData()
const getDataTimer = setInterval(async () => {
await getData()
}, refreshRate)
return () => {
isCancelled = true
clearInterval(getDataTimer)
}
}, [dataService])
return <>
<Card className='pendtxlist-card'>
<Card.Header>
<div className='pendtxlist-card-header'>
<span>Pending Transactions</span>
</div>
</Card.Header>
<Card.Body>
{data
? data.length > 0
? <div className='custom-scroll-div'>
<CustomScroll allowOuterScroll={true}>
<div className='pendtxlist-table'>
<DisplayTable columns={columns} data={data.sort((a, b) => a.code - b.code)} />
</div>
</CustomScroll>
</div>
: <div className='ml-1'>
No Pending Transactions
</div>
: <Spinner animation="border" role="status" />
}
</Card.Body>
</Card>
</>
}
Example #22
Source File: DSBlockList.tsx From devex with GNU General Public License v3.0 | 4 votes |
DSBlockList: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { dataService, networkUrl } = networkContext!
useEffect(() => { setData(null) }, [networkUrl]) // Unset data on url change
const [data, setData] = useState<DsBlockObj[] | null>(null)
const columns = useMemo(
() => [{
id: 'dsheight-col',
Header: 'Height',
accessor: 'header.BlockNum',
Cell: ({ value }: { value: string }) => (
<QueryPreservingLink to={`/dsbk/${value}`}>
{value}
</QueryPreservingLink>
),
},
{
id: 'difficulty-col',
Header: 'Difficulty',
accessor: 'header.Difficulty',
Cell: ({ value }: { value: string }) => (
<div className='text-center'>{value}</div>
),
},
{
id: 'ds-difficulty-col',
Header: 'DS Difficulty',
accessor: 'header.DifficultyDS',
Cell: ({ value }: { value: string }) => (
<div className='text-center'>{value}</div>
),
},
{
id: 'age-col',
Header: 'Age',
accessor: 'header.Timestamp',
Cell: ({ value }: { value: string }) => (
<div className='text-right'>{timestampToTimeago(value)}</div>
),
}], []
)
// Fetch data
useEffect(() => {
let isCancelled = false
if (!dataService) return
let receivedData: DsBlockObj[]
const getData = async () => {
try {
receivedData = await dataService.getLatest5DSBlocks()
if (!isCancelled && receivedData)
setData(receivedData)
} catch (e) {
if (!isCancelled)
console.log(e)
}
}
getData()
const getDataTimer = setInterval(async () => {
await getData()
}, refreshRate)
return () => {
isCancelled = true
clearInterval(getDataTimer)
}
}, [dataService])
return <>
<Card className='dsblock-card'>
<Card.Header>
<div className='dsblock-card-header'>
<span>DS Blocks</span>
<QueryPreservingLink to={'/dsbk'}>View All</QueryPreservingLink>
</div>
</Card.Header>
<Card.Body>
{data
? <DisplayTable columns={columns} data={data} />
: <Spinner animation="border" role="status" />
}
</Card.Body>
</Card>
</>
}
Example #23
Source File: BCInfo.tsx From devex with GNU General Public License v3.0 | 4 votes |
BCInfo: React.FC = () => {
const networkContext = useContext(NetworkContext)
const { dataService, networkUrl } = networkContext!
const [data, setData] = useState<BlockchainInfo | null>(null)
const [state, setState] = useState<BCInfoState>(defaultBCInfoState)
useEffect(() => { setData(null); setState(defaultBCInfoState) }, [networkUrl]) // Unset data on url change
useEffect(() => {
if (!data) return
setState((prevState: BCInfoState) => {
const newState: BCInfoState = { ...prevState }
if (!prevState.startTxBlock)
newState.startTxBlock = parseInt(data.NumTxBlocks, 10) - 1
if (!prevState.maxTPS || prevState.maxTPS <= data.TransactionRate) {
newState.maxTPS = data.TransactionRate
newState.maxTPSTxBlockNum = parseInt(data.NumTxBlocks, 10) - 1
}
if (!prevState.maxTxnCount || prevState.maxTxnCount <= parseInt(data.NumTxnsTxEpoch, 10)) {
newState.maxTxnCount = parseInt(data.NumTxnsTxEpoch, 10)
newState.maxTxnCountTxBlockNum = parseInt(data.NumTxBlocks, 10) - 1
}
return newState
})
}, [data])
// Fetch data
useEffect(() => {
let isCancelled = false
if (!dataService) return
let receivedData: BlockchainInfo
const getData = async () => {
try {
receivedData = await dataService.getBlockchainInfo()
if (!isCancelled && receivedData)
setData(receivedData)
} catch (e) {
if (!isCancelled)
console.log(e)
}
}
getData()
const getDataTimer = setInterval(async () => {
await getData()
}, refreshRate)
return () => {
isCancelled = true
clearInterval(getDataTimer)
}
}, [dataService])
return <>
<Card className='bcstats-card'>
<Card.Body>
{data
? <Container>
<Row className='mb-3'>
<Col>
<span className='subtext'>Current Tx Epoch:</span>
<br />
<span>{parseInt(data.NumTxBlocks).toLocaleString('en')}</span>
</Col>
<Col>
<span className='subtext'>Number of Transactions:</span>
<br />
<span>{parseInt(data.NumTransactions).toLocaleString('en')}</span>
</Col>
<Col>
<span className='subtext'>Peers:</span>
<br />
<span>{data.NumPeers.toLocaleString('en')}</span>
</Col>
<Col>
<span className='subtext'>Sharding Structure:</span>
<br />
<span>[{data.ShardingStructure && data.ShardingStructure.NumPeers
? data.ShardingStructure.NumPeers.toString()
: "no shards"}]</span>
</Col>
</Row>
<Row className='mb-3'>
<Col>
<span className='subtext'>Current DS Epoch:</span>
<br />
<span>{parseInt(data.CurrentDSEpoch).toLocaleString('en')}</span>
</Col>
<Col>
<span className='subtext'>DS Block Rate:</span>
<br />
<span>{data.DSBlockRate.toFixed(5)}</span>
</Col>
<Col>
<span className='subtext'>Tx Block Rate:</span>
<br />
<span>{data.TxBlockRate.toFixed(5)}</span>
</Col>
<Col>
<span className='subtext'>TPS:</span>
<br />
<span>{data.TransactionRate.toFixed(5)}</span>
</Col>
</Row>
<Row>
<Col>
<span className='subtext'>Number of Txns in DS Epoch:</span>
<br />
<span>{parseInt(data.NumTxnsDSEpoch).toLocaleString('en')}</span>
</Col>
<Col>
<span className='subtext'>Number of Txns in Txn Epoch:</span>
<br />
<span>{parseInt(data.NumTxnsTxEpoch).toLocaleString('en')}</span>
</Col>
<Col>
<OverlayTrigger placement='left'
overlay={<Tooltip id={'tt'}>This statistic is accurate from TxBlock {state.startTxBlock}. Requires user to stay on the Home Page</Tooltip>}>
<FontAwesomeIcon className='info-icon' icon={faInfoCircle} />
</OverlayTrigger>
{' '}
<span className='subtext'>Recent Max Observed TPS:</span>
<br />
<span>{state.maxTPS && state.maxTPS.toFixed(5)}</span>
<span>
{' '}
<small className='text-nowrap subtext'>
(on TxBlock <QueryPreservingLink to={`/txbk/${state.maxTPSTxBlockNum}`}>{state.maxTPSTxBlockNum}</QueryPreservingLink>)
</small>
</span>
</Col>
<Col>
<OverlayTrigger placement='left'
overlay={<Tooltip id={'tt'}>This statistic is accurate from TxBlock {state.startTxBlock}. Requires user to stay on the Home Page</Tooltip>}>
<FontAwesomeIcon className='info-icon' icon={faInfoCircle} />
</OverlayTrigger>
{' '}
<span className='subtext'>Recent Max Observed Txn Count:</span>
<br />
<span>{state.maxTxnCount}
{' '}
<small className='text-nowrap subtext'>
(on TxBlock <QueryPreservingLink to={`/txbk/${state.maxTxnCountTxBlockNum}`}>{state.maxTxnCountTxBlockNum}</QueryPreservingLink>)
</small>
</span>
</Col>
</Row>
</Container>
: <Spinner animation="border" role="status" />
}
</Card.Body>
</Card>
</>
}
Example #24
Source File: TxnDetailsPage.tsx From devex with GNU General Public License v3.0 | 4 votes |
TxnDetailsPage: React.FC = () => {
const { txnHash } = useParams();
const networkContext = useContext(NetworkContext);
const { dataService, networkUrl } = networkContext!;
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<TransactionDetails | null>(null);
// Fetch data
useEffect(() => {
if (!dataService) return;
let receivedData: TransactionDetails;
const getData = async () => {
try {
setIsLoading(true);
receivedData = await dataService.getTransactionDetails(txnHash);
if (receivedData) {
setData(receivedData);
}
} catch (e) {
console.log(e);
setError(e);
} finally {
setIsLoading(false);
}
};
getData();
return () => {
setData(null);
setError(null);
};
}, [dataService, txnHash]);
return (
<>
{isLoading ? (
<div className="center-spinner">
<Spinner animation="border" />
</div>
) : null}
{error ? (
<NotFoundPage />
) : (
data &&
data.txn.txParams.receipt && (
<>
<div className="transaction-header">
<h3 className="mb-1">
<span className="mr-1">
{data.txn.txParams.receipt.success === undefined ||
data.txn.txParams.receipt.success ? (
<FontAwesomeIcon color="green" icon={faExchangeAlt} />
) : (
<FontAwesomeIcon color="red" icon={faExclamationCircle} />
)}
</span>
<span className="ml-2">Transaction</span>
<LabelStar type="Transaction" />
</h3>
<ViewBlockLink
network={networkUrl}
type="tx"
identifier={data.hash}
/>
</div>
<div className="subtext">
<HashDisp hash={"0x" + data.hash} />
</div>
<Card className="txn-details-card">
<Card.Body>
<Container>
<Row>
<Col>
<div className="txn-detail">
<span>From:</span>
<span>
<QueryPreservingLink
to={`/address/${hexAddrToZilAddr(
data.txn.senderAddress
)}`}
>
{hexAddrToZilAddr(data.txn.senderAddress)}
</QueryPreservingLink>
</span>
</div>
</Col>
<Col>
<div className="txn-detail">
<span>To:</span>
<span>
{data.contractAddr ? (
data.txn.txParams.receipt.success ? (
<QueryPreservingLink
to={`/address/${hexAddrToZilAddr(
data.contractAddr
)}`}
>
<FontAwesomeIcon
color="darkturquoise"
icon={faFileContract}
/>{" "}
{hexAddrToZilAddr(data.contractAddr)}
</QueryPreservingLink>
) : (
<QueryPreservingLink
to={`/address/${hexAddrToZilAddr(
data.txn.txParams.toAddr
)}`}
>
{hexAddrToZilAddr(data.txn.txParams.toAddr)}
</QueryPreservingLink>
)
) : (
<QueryPreservingLink
to={`/address/${hexAddrToZilAddr(
data.txn.txParams.toAddr
)}`}
>
{hexAddrToZilAddr(data.txn.txParams.toAddr)}
</QueryPreservingLink>
)}
</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className="txn-detail">
<span>Amount:</span>
<span>
{qaToZil(data.txn.txParams.amount.toString())}
</span>
</div>
</Col>
<Col>
<div className="txn-detail">
<span>Nonce:</span>
<span>{data.txn.txParams.nonce}</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className="txn-detail">
<span>Gas Limit:</span>
<span>{data.txn.txParams.gasLimit.toString()}</span>
</div>
</Col>
<Col>
<div className="txn-detail">
<span>Gas Price:</span>
<span>
{qaToZil(data.txn.txParams.gasPrice.toString())}
</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className="txn-detail">
<span>Transaction Fee:</span>
<span>
{qaToZil(
Number(data.txn.txParams.gasPrice) *
data.txn.txParams.receipt!.cumulative_gas
)}
</span>
</div>
</Col>
<Col>
<div className="txn-detail">
<span>Transaction Block:</span>
<span>
<QueryPreservingLink
to={`/txbk/${data.txn.txParams.receipt.epoch_num}`}
>
{data.txn.txParams.receipt.epoch_num}
</QueryPreservingLink>
</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className="txn-detail">
<span>Success:</span>
<span>{`${data.txn.txParams.receipt.success}`}</span>
</div>
</Col>
{data.txn.txParams.receipt.accepted !== undefined && (
<Col>
<div className="txn-detail">
<span>Accepts $ZIL:</span>
<span>{`${data.txn.txParams.receipt.accepted}`}</span>
</div>
</Col>
)}
</Row>
</Container>
</Card.Body>
</Card>
<TransactionFlow hash={txnHash} txn={data.txn} />
<InfoTabs tabs={generateTabsFromTxnDetails(data)} />
</>
)
)}
</>
);
}
Example #25
Source File: TransactionFlowNew.tsx From devex with GNU General Public License v3.0 | 4 votes |
TransactionFlow: React.FC<IProps> = ({ hash, txn }) => {
const [transaction, setTransaction] = useState<any>(undefined);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState<Error | false>(false);
const [modalDisplay, setModalDisplay] = useState(false);
const [modalData, setModalData] = useState(undefined);
const [nodes, setNodes] = useState([]);
const [links, setLinks] = useState([]);
const networkContext = useContext(NetworkContext);
const { dataService, isIsolatedServer, apolloUrl } = networkContext!;
const ref: any = useRef<SVGElement | null>();
const TRANSACTION_QUERY = gql`
query GetTransaction($customId: String!) {
txFindByCustomId(customId: $customId) {
fromAddr
toAddr
amount
receipt {
event_logs {
address
_eventname
params {
vname
type
value
}
}
transitions {
accepted
addr
depth
msg {
_tag
_amount
_recipient
params {
vname
type
value
}
}
}
}
}
}
`;
const { loading, error, data } = useQuery(TRANSACTION_QUERY, {
variables: { customId: stripHexPrefix(hash) },
context: {
uri: apolloUrl,
},
fetchPolicy: "cache-and-network",
});
useEffect(() => {
setIsLoading(false);
if (data) {
if (
data.txFindByCustomId.length &&
data.txFindByCustomId[0] !== transaction
) {
setTransaction(data.txFindByCustomId[0]);
} else {
setIsError(
new Error("transaction was not found on the apollo-server.")
);
}
}
}, [data]);
const getNodeColor = async (node: {
id: string;
type?: string;
color?: string;
}) => {
if (!dataService || isIsolatedServer === null) return "#035992";
const colors = [
{
type: "caller",
color: "#666",
},
{
type: "contract",
color: "orange",
},
{
type: "user",
color: "#035992",
},
];
try {
if (node.type === undefined) {
const targetContract = await dataService?.isContractAddr(node.id);
node.type = targetContract ? "contract" : "user";
}
const exists = colors.find(
(item: { type: string; color: string }) => item.type === node.type
);
return exists ? exists.color : "#035992";
} catch (error) {
return "#035992";
}
};
const openModal = (d: any, link: any) => {
document.body.classList.add("has-modal-open");
console.log(link);
setModalData(link);
setModalDisplay(true);
};
const openNode = (d: any, node: any) => {
window.location.href = `/address/${node.id}${window.location.search}`;
};
useEffect(() => {
if (transaction !== undefined) {
const links: any = [
{
source: hexAddrToZilAddr(transaction.fromAddr),
target: hexAddrToZilAddr(transaction.toAddr),
amount: transaction.amount,
index: 0,
txData: transaction,
receipt: transaction.receipt,
},
];
const nodes: any = [
{
id: hexAddrToZilAddr(transaction.fromAddr),
type: "caller",
},
{
id: hexAddrToZilAddr(transaction.toAddr),
type: "contract",
},
];
console.log("nodes", nodes);
console.log("links", links);
if (transaction.receipt.transitions.length) {
let ioo = 0;
transaction.receipt.transitions.forEach(async (tr: any) => {
ioo++;
if (
!nodes.find((node: any) => node.id === hexAddrToZilAddr(tr.addr))
) {
nodes.push({
id: hexAddrToZilAddr(tr.addr),
});
}
if (
!nodes.find(
(node: any) => node.id === hexAddrToZilAddr(tr.msg._recipient)
)
) {
nodes.push({
id: hexAddrToZilAddr(tr.msg._recipient),
});
}
//console.log(transaction.receipt.event_logs, tr.msg._recipient);
const events = transaction.receipt.event_logs.filter((evv: any) => {
return evv.address === tr.msg._recipient;
});
links.push({
source: hexAddrToZilAddr(tr.addr),
target: hexAddrToZilAddr(tr.msg._recipient),
data: { ...tr },
txData: transaction,
index: ioo,
events: events,
});
});
}
Promise.all(
nodes.map(
async (element: { id: string; type?: string; color?: string }) => {
element.color = await getNodeColor(element);
return element;
}
)
).then(() => {
setNodes(nodes);
setLinks(links);
});
}
}, [transaction]);
useEffect(() => {
setIsLoading(true);
if (!dataService || isIsolatedServer === null) return;
d3.select(ref.current).selectAll("*").remove();
if (nodes.length) {
const nodeWidth = 362;
const nodeHeight = 40;
// set the dimensions and margins of the graph
const width = 1200,
height = 570;
// append the svg object to the body of the page
const svg = d3
.select(ref.current)
.append("svg")
.attr("width", width)
.attr("height", height);
const link = svg
.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.selectAll("path")
.data(links);
const linkLine = link
.join("path")
.attr("stroke", "#aaa")
.attr("id", (d: any, index: number) => {
return `linkLine-${index}`;
})
.attr("marker-end", (d: any) => {
return `url(${new URL(`#arrow-${d.target}`, window.location.href)})`;
})
.on("click", openModal);
const linkTextContainer = link
.enter()
.append("text")
.attr("transform", "translate(0,0)")
.style("cursor", "pointer")
.on("click", openModal);
linkTextContainer.append("title").text(function (d: any) {
if (d.index === 0) {
return d.index + 1;
}
if (d.data && d.data.msg && d.data.msg._tag) {
return d.data.msg._tag.length > 15
? `${d.index + 1}.${d.data.msg._tag.substring(0, 15)}...`
: `${d.index + 1}.${d.data.msg._tag}`;
}
return d.index + 1;
});
linkTextContainer
.append("textPath")
.attr("class", "linkText")
.attr("transform", "translate(56,0)")
.text(function (d: any, i) {
if (d.index === 0) {
return d.index + 1;
}
if (d.data && d.data.msg && d.data.msg._tag) {
return d.data.msg._tag.length > 15
? `${d.index + 1}.${d.data.msg._tag.substring(0, 15)}...`
: `${d.index + 1}.${d.data.msg._tag}`;
}
return d.index + 1;
})
.style("font-size", "16px")
.style("font-family", "monospace")
.attr("fill", "#fff")
.attr("xlink:xlink:href", (d: any, index: number) => {
return `#linkLine-${index}`;
});
// Initialize the nodes
const nodeg = svg
.selectAll("rect")
.data(nodes)
.enter()
.append<Element>("g");
const node = nodeg
.append<Element>("rect")
.attr("class", "node-rect")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("stroke-width", 2)
.attr("fill", (d: any) => d.color)
.attr("x", -500)
.attr("y", -500);
node.on("click", openNode);
const label = nodeg
.append("text")
.text(function (d: any) {
return d.id;
})
.style("font-size", "14px")
.style("font-family", "monospace")
.style("cursor", "pointer")
.attr("fill", "#fff")
.attr("x", -500)
.attr("y", -500)
.on("click", openNode);
node.append("title").text(function (d: any) {
return d.id;
});
// Per-type markers, as they don't inherit styles.
svg
.append("defs")
.selectAll("marker")
.data(nodes)
.join("marker")
.attr("id", (d: any) => `arrow-${d.id}`)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 10)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.append("path")
.attr("fill", "#fff")
.attr("d", "M0,-5L10,0L0,5");
const arcs: string[] = [];
const linkArc = (d: any) => {
const fromx = d.source.x + nodeWidth / 2;
const fromy =
d.target.y > d.source.y ? d.source.y + nodeHeight : d.source.y;
let tox = d.target.x + nodeWidth / 2;
const toy =
d.target.y > d.source.y
? d.target.y - 3
: d.target.y + nodeHeight + 3;
if (
arcs.includes(
`${d.source.x}-${d.source.y}-${d.target.x}-${d.target.y}`
)
) {
tox = tox + 70;
}
arcs.push(`${d.source.x}-${d.source.y}-${d.target.x}-${d.target.y}`);
return `
M${fromx},${fromy}
A0,0 0 0,1 ${tox},${toy}
`;
};
// This function is run at each iteration of the force algorithm, updating the nodes position.
const ticked = () => {
node
.attr("x", function (d: any) {
return d.x;
})
.attr("y", function (d: any) {
return d.y;
});
linkLine.attr("d", linkArc);
label
.attr("x", function (d: any) {
return d.x + 4;
})
.attr("y", function (d: any) {
return d.y + nodeHeight / 2 + 5;
});
linkTextContainer.attr("dx", function (d: any) {
return 50;
});
linkTextContainer.attr("dy", function (d: any) {
return -5;
});
setIsLoading(false);
};
// Let's list the force we wanna apply on the network
d3.forceSimulation(nodes) // Force algorithm is applied to data.nodes
.force(
"link",
d3
.forceLink() // This force provides links between nodes
.id(function (d: any) {
return d.id;
}) // This provide the id of a node
.links(links) // and this the list of links
)
//.force("charge", d3.forceManyBody().strength(-300)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength
.force("center", d3.forceCenter(width / 3, height / 2)) // This force attracts nodes to the center of the svg area
//.force("x", d3.forceX())
//.force("y", d3.forceY())
.force("collide", d3.forceCollide(160))
.on("end", ticked);
}
}, [nodes, links]);
const closeModal = () => {
document.body.classList.remove("has-modal-open");
setModalDisplay(false);
};
return (
<div>
{loading || isLoading ? (
<div className="center-spinner">
<Spinner animation="border" />
</div>
) : null}
{error || isError ? (
<div className="alert alert-info">
Transaction Flow error:{" "}
{error ? error.message : isError ? isError.message : null}
</div>
) : (
transaction && (
<div className="mt-4">
<h3 className="mb-4">Transaction call-graph</h3>
<div className="transaction-flow d-flex p-0">
<TransitionModal
modalData={modalData}
display={modalDisplay}
closeModal={closeModal}
/>
<div id="d3-viewer" ref={ref}></div>
</div>
<TransitionFlowDetails links={links} txn={txn} />
</div>
)
)}
</div>
);
}
Example #26
Source File: TxBlockDetailsPage.tsx From devex with GNU General Public License v3.0 | 4 votes |
TxBlockDetailsPage: React.FC = () => {
const { blockNum } = useParams()
const networkContext = useContext(NetworkContext)
const { dataService, isIsolatedServer } = networkContext!
const [error, setError] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [isLoadingTrans, setIsLoadingTrans] = useState(false)
const [txBlockObj, setTxBlockObj] = useState<TxBlockObj | null>(null)
const [txBlockTxns, setTxBlockTxns] = useState<string[] | null>(null)
const [latestTxBlockNum, setLatestTxBlockNum] = useState<number | null>(null)
const [transactionData, setTransactionData] = useState<TransactionDetails[] | null>(null)
// Fetch data
useEffect(() => {
setIsLoading(true)
if (!dataService || isIsolatedServer === null) return
let latestTxBlockNum: number
let txBlockObj: TxBlockObj
let txBlockTxns: string[]
const getData = async () => {
try {
if (isNaN(blockNum))
throw new Error('Not a valid block number')
if (isIsolatedServer) {
txBlockTxns = await dataService.getISTransactionsForTxBlock(parseInt(blockNum))
latestTxBlockNum = await dataService.getISBlockNum()
} else {
txBlockObj = await dataService.getTxBlockObj(parseInt(blockNum))
latestTxBlockNum = await dataService.getNumTxBlocks()
try {
txBlockTxns = await dataService.getTransactionsForTxBlock(parseInt(blockNum))
} catch (e) { console.log(e) }
}
if (txBlockObj)
setTxBlockObj(txBlockObj)
if (txBlockTxns)
setTxBlockTxns(txBlockTxns)
if (latestTxBlockNum)
setLatestTxBlockNum(latestTxBlockNum)
} catch (e) {
console.log(e)
setError(e)
} finally {
setIsLoading(false)
}
}
getData()
return () => {
setTxBlockObj(null)
setTxBlockTxns(null)
setLatestTxBlockNum(null)
setError(null)
}
}, [blockNum, dataService, isIsolatedServer])
const columns = useMemo(
() => [{
id: 'from-col',
Header: 'From',
accessor: 'txn.senderAddress',
Cell: ({ value }: { value: string }) => (
<QueryPreservingLink to={`/address/${hexAddrToZilAddr(value)}`}>
{hexAddrToZilAddr(value)}
</QueryPreservingLink>
)
}, {
id: 'to-col',
Header: 'To',
Cell: ({ row }: { row: Row<TransactionDetails> }) => {
return <ToAddrDisp txnDetails={row.original} />
}
}, {
id: 'hash-col',
Header: 'Hash',
Cell: ({ row }: { row: Row<TransactionDetails> }) => {
console.log(row)
return <QueryPreservingLink to={`/tx/0x${row.original.hash}`}>
<div className='text-right mono'>
{row.original.txn.txParams.receipt && !row.original.txn.txParams.receipt.success
&& <FontAwesomeIcon className='mr-1' icon={faExclamationCircle} color='red' />
}
{'0x' + row.original.hash}
</div>
</QueryPreservingLink>
}
}, {
id: 'amount-col',
Header: 'Amount',
accessor: 'txn.amount',
Cell: ({ value }: { value: string }) => (
<OverlayTrigger placement='right'
overlay={<Tooltip id={'amt-tt'}>{qaToZil(value)}</Tooltip>}>
<div className='text-right'>{qaToZil(value, 10)}</div>
</OverlayTrigger>
)
}, {
id: 'fee-col',
Header: 'Fee',
accessor: 'txn',
Cell: ({ value }: { value: Transaction }) => {
const fee = Number(value.txParams.gasPrice) * value.txParams.receipt!.cumulative_gas
return <OverlayTrigger placement='top'
overlay={<Tooltip id={'fee-tt'}>{qaToZil(fee)}</Tooltip>}>
<div className='text-center'>{qaToZil(fee, 4)}</div>
</OverlayTrigger>
}
}], []
)
const fetchData = useCallback(({ pageIndex }) => {
if (!txBlockTxns || !dataService) return
let receivedData: TransactionDetails[]
const getData = async () => {
try {
setIsLoadingTrans(true)
receivedData = await dataService.getTransactionsDetails(txBlockTxns.slice(pageIndex * 10, pageIndex * 10 + 10))
if (receivedData)
setTransactionData(receivedData)
} catch (e) {
console.log(e)
} finally {
setIsLoadingTrans(false)
}
}
getData()
}, [dataService, txBlockTxns])
return <>
{isLoading ? <div className='center-spinner'><Spinner animation="border" /></div> : null}
{error
? <NotFoundPage />
: <>
{latestTxBlockNum &&
<div className={isIsolatedServer ? 'txblock-header mb-3' : 'txblock-header'}>
<h3 className='mb-1'>
<span className='mr-1'>
<FontAwesomeIcon className='fa-icon' icon={faCubes} />
</span>
<span className='ml-2'>
Tx Block
</span>
{' '}
<span className='subtext'>#{blockNum}</span>
<LabelStar type='Tx Block' />
</h3>
<span>
<QueryPreservingLink
className={
isIsolatedServer
? parseInt(blockNum, 10) === 1 ? 'disabled mr-3' : 'mr-3'
: parseInt(blockNum, 10) === 0 ? 'disabled mr-3' : 'mr-3'}
to={`/txbk/${parseInt(blockNum, 10) - 1}`}>
<FontAwesomeIcon size='2x' className='fa-icon' icon={faCaretSquareLeft} />
</QueryPreservingLink>
<QueryPreservingLink
className={
isIsolatedServer
? parseInt(blockNum, 10) === latestTxBlockNum ? 'disabled' : ''
: parseInt(blockNum, 10) === latestTxBlockNum - 1 ? 'disabled' : ''}
to={`/txbk/${parseInt(blockNum, 10) + 1}`}>
<FontAwesomeIcon size='2x' className='fa-icon' icon={faCaretSquareRight} />
</QueryPreservingLink>
</span>
</div>
}
{txBlockObj && (
<>
<div className='subtext'>
<HashDisp hash={'0x' + txBlockObj.body.BlockHash} />
</div>
<Card className='txblock-details-card'>
<Card.Body>
<Container>
<BRow>
<BCol>
<div className='txblock-detail'>
<span>Date:</span>
<span>
{timestampToDisplay(txBlockObj.header.Timestamp)}
{' '}
({timestampToTimeago(txBlockObj.header.Timestamp)})
</span>
</div>
</BCol>
<BCol>
<div className='txblock-detail'>
<span>Transactions:</span>
<span>{txBlockObj.header.NumTxns}</span>
</div>
</BCol>
</BRow>
<BRow>
<BCol>
<div className='txblock-detail'>
<span>Gas Limit:</span>
<span>{txBlockObj.header.GasLimit}</span>
</div>
</BCol>
<BCol>
<div className='txblock-detail'>
<span>Gas Used:</span>
<span>{txBlockObj.header.GasUsed}</span>
</div>
</BCol>
</BRow>
<BRow>
<BCol>
<div className='txblock-detail'>
<span>Txn Fees:</span>
<span>{qaToZil(txBlockObj.header.TxnFees)}</span>
</div>
</BCol>
<BCol>
<div className='txblock-detail'>
<span>Rewards Fees:</span>
<span>{qaToZil(txBlockObj.header.Rewards)}</span>
</div>
</BCol>
</BRow>
<BRow>
<BCol>
<div className='txblock-detail'>
<span>DS Block:</span>
<span><QueryPreservingLink to={`/dsbk/${txBlockObj.header.DSBlockNum}`}>{txBlockObj.header.DSBlockNum}</QueryPreservingLink></span>
</div>
</BCol>
<BCol>
<div className='txblock-detail'>
<span>DS Leader:</span>
<span><QueryPreservingLink to={`/address/${pubKeyToZilAddr(txBlockObj.header.MinerPubKey)}`}>{pubKeyToZilAddr(txBlockObj.header.MinerPubKey)}</QueryPreservingLink></span>
</div>
</BCol>
</BRow>
</Container>
</Card.Body>
</Card>
{txBlockObj.body.MicroBlockInfos.length > 0 && (
<Card className='txblock-details-card mono'>
<Card.Body>
<Container>
<span>Micro Blocks</span>
{txBlockObj.body.MicroBlockInfos
.map((x) => (
<div key={x.MicroBlockHash}>[{x.MicroBlockShardId}] {x.MicroBlockHash}</div>
))}
</Container>
</Card.Body>
</Card>
)}
</>
)}
{txBlockTxns && txBlockTxns.length > 0 && (
<>
<h4>Transactions</h4>
<ViewAllTable
isLoading={isLoadingTrans}
fetchData={fetchData}
pageCount={Math.ceil(txBlockTxns.length / 10)}
columns={columns}
data={transactionData ? transactionData : []} />
</>
)}
</>
}
</>
}
Example #27
Source File: DSBlockDetailsPage.tsx From devex with GNU General Public License v3.0 | 4 votes |
DSBlockDetailsPage: React.FC = () => {
const { blockNum } = useParams()
const networkContext = useContext(NetworkContext)
const { dataService } = networkContext!
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [data, setData] = useState<DsBlockObj | null>(null)
const [latestDSBlockNum, setLatestDSBlockNum] = useState<number | null>(null)
const [minerInfo, setMinerInfo] = useState<MinerInfo | null>(null)
const [currShardIdx, setCurrShardIdx] = useState<number>(0)
const [showMore, setShowMore] = useState<boolean>(false)
// Fetch data
useEffect(() => {
setIsLoading(true)
if (!dataService) return
let latestDSBlockNum: number
let receivedData: DsBlockObj
let minerInfo: MinerInfo
const getData = async () => {
try {
if (isNaN(blockNum))
throw new Error('Not a valid block number')
receivedData = await dataService.getDSBlockDetails(blockNum)
latestDSBlockNum = await dataService.getNumDSBlocks()
try { // wrapped in another try catch because it is optional
minerInfo = await dataService.getMinerInfo(blockNum)
} catch (e) { console.log(e) }
if (receivedData)
setData(receivedData)
if (latestDSBlockNum)
setLatestDSBlockNum(latestDSBlockNum)
if (minerInfo)
setMinerInfo(minerInfo)
} catch (e) {
console.log(e)
setError(e)
} finally {
setIsLoading(false)
}
}
getData()
return () => {
setData(null)
setLatestDSBlockNum(null)
setMinerInfo(null)
setError(null)
setCurrShardIdx(0)
setShowMore(false)
}
}, [blockNum, dataService])
return <>
{isLoading ? <div className='center-spinner'><Spinner animation="border" /></div> : null}
{error
? <NotFoundPage />
: data && (
<>
<div className='dsblock-header'>
<h3 className='mb-1'>
<span className='mr-1'>
<FontAwesomeIcon className='fa-icon' icon={faCubes} />
</span>
<span className='ml-2'>
DS Block
</span>
{' '}
<span className='subtext'>#{data.header.BlockNum}</span>
<LabelStar type='DS Block' />
</h3>
<span>
<QueryPreservingLink
className={parseInt(data.header.BlockNum) === 0
? 'disabled mr-3' : 'mr-3'}
to={`/dsbk/${parseInt(data.header.BlockNum) - 1}`}>
<FontAwesomeIcon size='2x' className='fa-icon' icon={faCaretSquareLeft} />
</QueryPreservingLink>
<QueryPreservingLink
className={latestDSBlockNum && parseInt(data.header.BlockNum) === latestDSBlockNum - 1 ? 'disabled' : ''}
to={`/dsbk/${parseInt(data.header.BlockNum) + 1}`}>
<FontAwesomeIcon size='2x' className='fa-icon' icon={faCaretSquareRight} />
</QueryPreservingLink>
</span>
</div>
<Card className='dsblock-details-card'>
<Card.Body>
<Container>
<Row>
<Col>
<div className='dsblock-detail'>
<span>Date:</span>
<span>
{timestampToDisplay(data.header.Timestamp)}
{' '}
({timestampToTimeago(data.header.Timestamp)})
</span>
</div>
</Col>
<Col>
<div className='dsblock-detail'>
<span>Difficulty:</span>
<span>{data.header.Difficulty}</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className='dsblock-detail'>
<span>DS Difficulty:</span>
<span>{data.header.DifficultyDS}</span>
</div>
</Col>
<Col>
<div className='dsblock-detail'>
<span>Gas Price:</span>
<span>{qaToZil(data.header.GasPrice)}</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className='dsblock-detail'>
<span>DS Leader:</span>
<span>
<QueryPreservingLink to={`/address/${pubKeyToZilAddr(data.header.LeaderPubKey)}`}>
{pubKeyToZilAddr(data.header.LeaderPubKey)}
</QueryPreservingLink>
</span>
</div>
</Col>
</Row>
<Row>
<Col>
<div className='dsblock-detail'>
<span>Signature:</span>
<span className='dsblock-signature'>{data.signature}</span>
</div>
</Col>
</Row>
</Container>
</Card.Body>
</Card>
{data.header.PoWWinners.length > 0 && (
<Card className='dsblock-details-card'>
<Card.Body>
<Container className='mono'>
<h6 className='mb-2'>PoW Winners</h6>
{data.header.PoWWinners.map((x, index) => <div key={index}>[{index}]
{' '}
<QueryPreservingLink to={`/address/${pubKeyToZilAddr(x)}`}>{pubKeyToZilAddr(x)}</QueryPreservingLink></div>)}
</Container>
</Card.Body>
</Card>
)}
{minerInfo &&
<>
<Collapse in={showMore}>
<Row>
<Col>
<Card className='miner-card'>
<Card.Body>
<Container className='mono'>
<Row className='justify-content-between'>
<span>DS Committee</span>
<span>Total: <strong>{minerInfo.dscommittee.length}</strong></span>
</Row>
<Row className='justify-content-center'>
{minerInfo.dscommittee.length > 0
? <MinerTable addresses={minerInfo.dscommittee} />
: <span className='my-3'>No addresses to show</span>
}
</Row>
</Container>
</Card.Body>
</Card>
</Col>
<Col>
<Card className='miner-card ml-auto'>
<Card.Body>
<Container className='mono'>
<Row className='justify-content-between'>
<Col>
<span>Shard {currShardIdx + 1} of {minerInfo.shards.length}</span>
</Col>
<Col className='text-center shard-toggle'>
<span>
<FontAwesomeIcon size='2x'
cursor='pointer'
onClick={currShardIdx === 0 ? undefined : () => (setCurrShardIdx(currShardIdx - 1))}
className={currShardIdx === 0 ? 'disabled' : ''} icon={faAngleLeft} />
<FontAwesomeIcon size='2x'
cursor='pointer'
onClick={currShardIdx === minerInfo.shards.length - 1 ? undefined : () => (setCurrShardIdx(currShardIdx + 1))}
className={currShardIdx === minerInfo.shards.length - 1 ? 'disabled ml-3' : 'ml-3'} icon={faAngleRight} />
</span>
</Col>
<Col className='text-right'>
<span>
Total:
{' '}
<strong>{minerInfo.shards[currShardIdx].nodes.length}</strong>
</span>
</Col>
</Row>
<Row className='justify-content-center'>
{minerInfo.shards[currShardIdx].nodes.length > 0
? <MinerTable addresses={minerInfo.shards[currShardIdx].nodes} />
: <span className='my-3'>No addresses to show</span>
}
</Row>
</Container>
</Card.Body>
</Card>
</Col>
</Row>
</Collapse>
</>
}
<Container className='showmore-container' onClick={() => setShowMore(!showMore)}>
<Row>
<FontAwesomeIcon icon={showMore ? faAngleUp : faAngleDown} size='2x' className='fa-icon' />
</Row>
</Container>
</>
)}
</>
}
Example #28
Source File: ContractTransactionsTab.tsx From devex with GNU General Public License v3.0 | 4 votes |
ContractTransactionsTab: React.FC<IProps> = ({
contractAddr,
onTransactionsCount,
fungibleToken,
}) => {
const [transactionsCount, setTransactionsCount] = useState<number>(0);
const networkContext = useContext(NetworkContext);
const { apolloUrl } = networkContext!;
const generatePagination = useCallback(
(currentPage: number, pageCount: number, delta = 2) => {
const separate = (a: number, b: number, isLower: boolean) => {
const temp = b - a;
if (temp === 0) return [a];
else if (temp === 1) return [a, b];
else if (temp === 2) return [a, a + 1, b];
else return [a, isLower ? -1 : -2, b];
};
return Array(delta * 2 + 1)
.fill(0)
.map((_, index) => currentPage - delta + index)
.filter((page) => 0 < page && page <= pageCount)
.flatMap((page, index, { length }) => {
if (!index) {
return separate(1, page, true);
}
if (index === length - 1) {
return separate(page, pageCount, false);
}
return [page];
});
},
[]
);
const ACCOUNT_TRANSACTIONS = gql`
query GetTransactions($addr: String!, $page: Int) {
txPagination(
page: $page
filter: {
OR: [
{ fromAddr: $addr }
{ toAddr: $addr }
{ receipt: { transitions: { addr: $addr } } }
{ receipt: { transitions: { msg: { _recipient: $addr } } } }
]
}
sort: TIMESTAMP_DESC
) {
count
items {
ID
receipt {
success
cumulative_gas
transitions {
addr
msg {
_recipient
}
}
}
gasPrice
gasLimit
fromAddr
toAddr
amount
timestamp
type
}
pageInfo {
currentPage
perPage
pageCount
itemCount
hasNextPage
hasPreviousPage
}
}
}
`;
const hexAddr = zilAddrToHexAddr(contractAddr);
const {
loading: transactionsLoading,
error,
data: txData,
fetchMore,
} = useQuery(ACCOUNT_TRANSACTIONS, {
variables: { addr: hexAddr, page: 1 },
context: {
uri: apolloUrl,
},
fetchPolicy: "cache-and-network",
});
if (txData && transactionsCount !== txData.txPagination.count) {
setTransactionsCount(txData.txPagination.count);
onTransactionsCount(txData.txPagination.count);
}
const localFetch = (page: Number) => {
return fetchMore({
variables: {
page,
},
updateQuery: (prev: any, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev;
return fetchMoreResult;
},
});
};
return (
<div>
{transactionsLoading ? (
<div className="center-spinner">
<Spinner animation="border" />
</div>
) : null}
{txData && txData.txPagination ? (
<>
<div className="row align-items-center mb-0">
<div className="col subtext">
Items Per Page: <strong>20</strong>
</div>
<div className="col">
{txData && txData.txPagination ? (
<Pagination className="justify-content-end">
<Pagination.Prev
onClick={() =>
localFetch(txData.txPagination.pageInfo.currentPage - 1)
}
disabled={!txData.txPagination.pageInfo.hasPreviousPage}
/>
{generatePagination(
txData.txPagination.pageInfo.currentPage + 1,
txData.txPagination.pageInfo.pageCount
).map((page) => {
if (page === -1)
return (
<Pagination.Ellipsis
key={page}
onClick={() =>
localFetch(
txData.txPagination.pageInfo.currentPage - 5
)
}
/>
);
else if (page === -2)
return (
<Pagination.Ellipsis
key={page}
onClick={() =>
localFetch(
txData.txPagination.pageInfo.currentPage + 5
)
}
/>
);
else if (page === txData.txPagination.pageInfo.currentPage)
return (
<Pagination.Item key={page} active>
{page}
</Pagination.Item>
);
else
return (
<Pagination.Item
key={page}
onClick={() => localFetch(Number(page))}
>
{page}
</Pagination.Item>
);
})}
<Pagination.Next
onClick={() =>
localFetch(txData.txPagination.pageInfo.currentPage + 1)
}
disabled={!txData.txPagination.pageInfo.hasNextPage}
/>
</Pagination>
) : null}
</div>
</div>
<TransactionsCard
transactions={txData.txPagination.items}
fungibleToken={fungibleToken}
addr={contractAddr}
/>
</>
) : null}
{error ? "Error while loading transactions" : null}
</div>
);
}
Example #29
Source File: ContractDetailsPage.tsx From devex with GNU General Public License v3.0 | 4 votes |
ContractDetailsPage: React.FC<IProps> = ({ addr }) => {
const networkContext = useContext(NetworkContext);
const { dataService, isIsolatedServer, networkUrl } = networkContext!;
const addrRef = useRef(addr);
const [contractData, setContractData] = useState<ContractData | null>(null);
const [creationTxnHash, setCreationTxnHash] = useState<string | null>(null);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [owner, setOwner] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [transactionsCount, setTransactionsCount] = useState<number>(0);
const isFungibleToken = (contractData: ContractData) => {
const symbol = contractData.initParams.find(
(item) => item.vname === "symbol"
);
const name = contractData.initParams.find(
(item) => item.vname === "name"
);
const init_supply = contractData.initParams.find(
(item) => item.vname === "init_supply"
);
const decimals = contractData.initParams.find(
(item) => item.vname === "decimals"
);
if (symbol && name && init_supply && decimals) {
const holders = Object.keys(contractData.state.balances).length;
/* const holds = Object.values(contractData.state.balances);
const init_value = typeof (init_supply.value === "string")
? parseFloat(init_supply.value)
: 0;
console.log(holds);
const supply =
init_value -
(holds.length
? holds.reduce(
(prev: number, current: any) => prev + parseFloat(current),
0
)
: 0); */
return {
symbol,
name,
init_supply,
decimals,
holders,
};
}
return false;
};
const fungibleToken = contractData ? isFungibleToken(contractData) : false;
// Fetch data
useEffect(() => {
setIsLoading(true);
if (!dataService || isIsolatedServer === null) return;
let contractData: ContractData;
let creationTxnHash: string;
let owner: string;
const getData = async () => {
try {
if (isIsolatedServer) {
contractData = await dataService.getContractData(addrRef.current);
} else {
contractData = await dataService.getContractData(addrRef.current);
creationTxnHash = await dataService.getTxnIdFromContractData(
contractData
);
owner = await dataService.getTransactionOwner(creationTxnHash);
}
if (contractData) setContractData(contractData);
if (creationTxnHash) setCreationTxnHash(creationTxnHash);
if (owner) setOwner(owner);
} catch (e) {
console.log(e);
} finally {
setIsLoading(false);
}
};
getData();
}, [dataService, isIsolatedServer]);
const generateTabsObj = () => {
const tabs: {
tabHeaders: string[];
tabTitles: string[];
tabContents: React.ReactNode[];
} = {
tabHeaders: [],
tabTitles: [],
tabContents: [],
};
if (!contractData) return tabs;
tabs.tabHeaders.push("transactions");
tabs.tabTitles.push("Transactions");
tabs.tabContents.push(
<ContractTransactionsTab
initParams={contractData.initParams}
contractAddr={addrRef.current}
fungibleToken={fungibleToken}
onTransactionsCount={setTransactionsCount}
/>
);
tabs.tabHeaders.push("initParams");
tabs.tabTitles.push("Init Parameters");
tabs.tabContents.push(
<InitParamsTab initParams={contractData.initParams} />
);
tabs.tabHeaders.push("State");
tabs.tabTitles.push("State");
tabs.tabContents.push(<DefaultTab content={contractData.state} />);
tabs.tabHeaders.push("code");
tabs.tabTitles.push("Code");
tabs.tabContents.push(<CodeTab code={contractData.code} />);
return tabs;
};
return (
<>
{isLoading ? (
<div className="center-spinner">
<Spinner animation="border" />
</div>
) : null}
{contractData && (
<>
<div className="address-header">
<h3 className="mb-1">
<span className="mr-1">
<FontAwesomeIcon className="fa-icon" icon={faFileContract} />
</span>
<span className="ml-2">Contract</span>
<LabelStar type="Contract" />
</h3>
<ViewBlockLink
network={networkUrl}
type="address"
identifier={addrRef.current}
/>
</div>
<div className="subtext">
<AddressDisp isLinked={false} addr={addrRef.current} />
</div>
<Card className="address-details-card">
<Card.Body>
<Container>
<Row className="mb-4">
<Col>
<div className="subtext text-small">Balance</div>
<div>{qaToZil(contractData.state["_balance"])}</div>
</Col>
<Col>
<div className="subtext text-small">Transactions</div>
<div>{transactionsCount}</div>
</Col>
{fungibleToken ? (
<>
<Col>
<div className="subtext text-small">Token Info</div>
<div>
{fungibleToken.name.value} <br />
{fungibleToken.symbol.value}
</div>
</Col>
<Col>
<div className="subtext text-small">Token Holders</div>
<div>{fungibleToken.holders}</div>
</Col>
<Col>
<div className="subtext text-small">
Token Transfers
</div>
<div></div>
</Col>
<Col>
<div className="subtext text-small">Token Supply</div>
<div>{fungibleToken.init_supply.value}</div>
</Col>
</>
) : null}
</Row>
{creationTxnHash && (
<>
<Row className="mb-0">
<Col>
<div>
<span className="mr-2">Contract Creation:</span>
<span className="pl-2">
<QueryPreservingLink to={`/tx/${creationTxnHash}`}>
{creationTxnHash}
</QueryPreservingLink>
</span>
</div>
</Col>
</Row>
</>
)}
</Container>
</Card.Body>
</Card>
<InfoTabs tabs={generateTabsObj()} />
</>
)}
</>
);
}