@chakra-ui/react#AlertIcon TypeScript Examples
The following examples show how to use
@chakra-ui/react#AlertIcon.
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: PermissionsWalkthrough.tsx From bluebubbles-server with Apache License 2.0 | 6 votes |
PermissionsWalkthrough = (): JSX.Element => {
return (
<SlideFade in={true} offsetY='150px'>
<Box px={5}>
<Text fontSize='4xl'>Permissions</Text>
<Text fontSize='md' mt={5}>
Before setting up BlueBubbles, we need to make sure that the app is given the correct permissions
so that it can operate. The main permission that is required is the <strong>Full Disk Access</strong>
permission. This will allow BlueBubbles to read the iMessage database and provide notifications for
new messages.
</Text>
<Alert status='info' mt={2}>
<AlertIcon />
If you are on macOS Monterey, you will also need to enable <strong>Accessibility</strong> permissions
for BlueBubbles.
</Alert>
<Text fontSize='md' mt={5}>
Here is an evaluation of your current permissions. If Full Disk Access is not enabled, you will not be
able to use BlueBubbles
</Text>
<Box my={3} />
<PermissionRequirements />
<Text fontSize='lg' my={5}>Quick Guide</Text>
<Text fontSize='md' mt={5}>Open System Preferences, then the following:</Text>
<Image src={SystemPreferencesImage} borderRadius='lg' my={2} />
<Image src={FullDiskImage} borderRadius='lg' my={2} />
</Box>
</SlideFade>
);
}
Example #2
Source File: UrlField.tsx From calories-in with MIT License | 6 votes |
function UrlField({ canEdit, food }: Props) {
const { register } = useFormContext<FoodForm>()
return (
<Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
{canEdit && (
<Alert status="info" mb={3}>
<AlertIcon color="teal.400" />
Add a link will open a web page when the food is clicked. This is
useful if you want to show a specific product.
</Alert>
)}
<FormControl id="email">
<Flex alignItems="center">
<FormLabel mb={0} flexShrink={0}>
Link:
</FormLabel>
{canEdit ? (
<Input
{...register('url')}
placeholder="http://example.com"
type="email"
/>
) : (
<Link
href={food?.url}
target="_blank"
noOfLines={1}
color="teal.500"
>
{food?.url}
</Link>
)}
</Flex>
</FormControl>
</Flex>
)
}
Example #3
Source File: index.tsx From calories-in with MIT License | 6 votes |
function Exporter({ onUpdate }: Props) {
const { isLoading, error } = usePdfExport({ onUpdate })
if (isLoading) {
return <Loader label="Exporting..." />
}
return (
<Alert
status={error ? 'error' : 'success'}
variant="subtle"
flexDirection="column"
alignItems="center"
justifyContent="center"
textAlign="center"
height="200px"
bg="white"
>
<AlertIcon color={error ? 'red.400' : 'teal.400'} boxSize="40px" mr={0} />
<AlertTitle mt={4} mb={1} fontSize="lg">
{error
? 'Something went wrong while creating your pdf file'
: 'Your PDF file is ready'}
</AlertTitle>
{!error && (
<AlertDescription maxWidth="sm">
Downloading this plan will allow you to import it later if you need to
update it.
</AlertDescription>
)}
</Alert>
)
}
Example #4
Source File: ErrorAlert.tsx From takeout-app with MIT License | 6 votes |
ErrorAlert: React.FC<Props> = ({error}) => {
return <>
<Alert status="error">
<AlertIcon />
<AlertTitle mr={2}>{error.name}</AlertTitle>
<AlertDescription>{error.message}</AlertDescription>
</Alert>
</>;
}
Example #5
Source File: AppVersionAlert.tsx From takeout-app with MIT License | 6 votes |
AppVersionAlert: React.FC = () => {
const { data: appVersion } = Api.useAppVersion();
return appVersion && appVersion.commit !== COMMIT ? (
<Alert status="info" mt={1}>
<AlertIcon />
New app version available;
<Link textDecoration="underline" onClick={() => window.location.reload()} ml={1}>
Reload?
</Link>
</Alert>
) : null;
}
Example #6
Source File: ApprovePaper.tsx From dope-monorepo with GNU General Public License v3.0 | 5 votes |
ApprovePaper = ({
address,
children,
isApproved,
onApprove,
}: {
address: string;
children: ReactNode;
isApproved: boolean | undefined;
onApprove: (isApproved: boolean) => void;
}) => {
const { account } = useWeb3React();
const paper = usePaper();
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (account) {
paper
.allowance(account, address)
.then((allowance: BigNumber) => onApprove(allowance.gte('12500000000000000000000')));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [account, address, paper]);
if (isApproved === undefined) {
return <Spinner />;
}
if (isApproved) {
return (
<Alert status="success">
<AlertIcon />
$PAPER Spend Approved
</Alert>
);
}
return (
<PanelContainer>
<PanelTitleHeader>Approve $PAPER Spend</PanelTitleHeader>
<PanelBody>
<p>{children}</p>
</PanelBody>
<PanelFooter stacked>
<Button
onClick={async () => {
setIsLoading(true);
try {
const txn = await paper.approve(address, constants.MaxUint256);
await txn.wait(1);
onApprove(true);
} catch (error) {
} finally {
setIsLoading(false);
}
}}
disabled={isLoading}
>
{isLoading ? <Spinner /> : 'Approve $PAPER Spend'}
</Button>
</PanelFooter>
</PanelContainer>
);
}
Example #7
Source File: VolumeFormFields.tsx From calories-in with MIT License | 5 votes |
function VolumeFields({ canEdit, food }: Props) {
const { register } = useFormContext<FoodForm>()
const { portionsById, volumeBasedPortions } = usePortions()
const portion = food?.volume ? portionsById[food.volume.portionId] : undefined
return (
<Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
{canEdit && (
<Alert status="info" mb={3}>
<AlertIcon color="teal.400" />
Enter the food weight in grams per some volume measurement if you want
to convert between weight and volume (for example: between grams and
cups).
</Alert>
)}
<HStack spacing={2}>
{canEdit && <Text fontWeight="medium">1 x</Text>}
{canEdit ? (
<PortionsSelect
width="200px"
portions={volumeBasedPortions}
{...register('volumeForm.portionId')}
/>
) : (
<Text fontWeight="medium">
1 x {`${portion?.singular} (${portion?.millilitersPerAmount} ml)`}
</Text>
)}
<Text fontWeight="medium">=</Text>
<HStack spacing={1} alignItems="center">
{canEdit ? (
<Controller
name="volumeForm.weightInGrams"
render={({ field }) => (
<AmountInput value={field.value} onChange={field.onChange} />
)}
/>
) : (
<Text>{food?.volume?.weightInGrams}g</Text>
)}
{canEdit && <Text textColor="gray.500">g</Text>}
</HStack>
</HStack>
</Flex>
)
}
Example #8
Source File: index.tsx From nextjs-hasura-boilerplate with MIT License | 5 votes |
MyAccountPageComponent = ({ user }) => {
const [name, setName] = useState(user.name);
const [session] = useSession();
const [updateUser, { loading: updateUserFetching, error: updateUserError }] =
useUpdateUserMutation();
const handleSubmit = () => {
updateUser({
variables: {
userId: session.id,
name,
},
});
};
const errorNode = () => {
if (!updateUserError) {
return false;
}
return (
<Alert status="error">
<AlertIcon />
<AlertTitle>{updateUserError}</AlertTitle>
<CloseButton position="absolute" right="8px" top="8px" />
</Alert>
);
};
return (
<Stack spacing={8}>
<Heading>My Account</Heading>
{errorNode()}
<Box shadow="lg" rounded="lg" p={4}>
<Stack spacing={4}>
<FormControl isRequired>
<FormLabel htmlFor="name">Name</FormLabel>
<Input
type="text"
id="name"
value={name}
onChange={(e: FormEvent<HTMLInputElement>) =>
setName(e.currentTarget.value)
}
isDisabled={updateUserFetching}
/>
</FormControl>
<FormControl>
<Button
loadingText="Saving..."
onClick={handleSubmit}
isLoading={updateUserFetching}
isDisabled={!name.trim()}
>
Save
</Button>
</FormControl>
</Stack>
</Box>
</Stack>
);
}
Example #9
Source File: AddNewFeedForm.tsx From nextjs-hasura-boilerplate with MIT License | 5 votes |
AddNewFeedForm = () => {
const [body, setBody] = useState("");
const [session] = useSession();
const [
insertFeed,
{ loading: insertFeedFetching, error: insertFeedError },
] = useInsertFeedMutation();
if (!session) {
return (
<AccessDeniedIndicator message="You need to be signed in to add a new feed!" />
);
}
const handleSubmit = async () => {
await insertFeed({
variables: {
author_id: session.id,
body,
},
});
setBody("");
};
const errorNode = () => {
if (!insertFeedError) {
return false;
}
return (
<Alert status="error">
<AlertIcon />
<AlertTitle>{insertFeedError}</AlertTitle>
<CloseButton position="absolute" right="8px" top="8px" />
</Alert>
);
};
return (
<Stack spacing={4}>
{errorNode()}
<Box p={4} shadow="lg" rounded="lg">
<Stack spacing={4}>
<FormControl isRequired>
<FormLabel htmlFor="body">What's on your mind?</FormLabel>
<Textarea
id="body"
value={body}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) =>
setBody(e.currentTarget.value)
}
isDisabled={insertFeedFetching}
/>
</FormControl>
<FormControl>
<Button
loadingText="Posting..."
onClick={handleSubmit}
isLoading={insertFeedFetching}
isDisabled={!body.trim()}
>
Post
</Button>
</FormControl>
</Stack>
</Box>
</Stack>
);
}
Example #10
Source File: ConnectionWalkthrough.tsx From bluebubbles-server with Apache License 2.0 | 5 votes |
ConnectionWalkthrough = (): JSX.Element => {
const proxyService: string = (useAppSelector(state => state.config.proxy_service) ?? '').toLowerCase().replace(' ', '-');
return (
<SlideFade in={true} offsetY='150px'>
<Box px={5}>
<Text fontSize='4xl'>Connection Setup</Text>
<Text fontSize='md' mt={5}>
In order for you to be able to connect to this BlueBubbles server from the internet, you'll need
to either setup a Dynamic DNS or use one of the integrated proxy services. Proxy services create
a tunnel from your macOS device to your BlueBubbles clients. It does this by routing all communications
from your BlueBubbles server, through the proxy service's servers, and to your BlueBubbles client. Without
this, your BlueBubbles server will only be accessible on your local network.
</Text>
<Text fontSize='md' mt={5}>
Now, we also do not want anyone else to be able to access your BlueBubbles server except you, so we have
setup password-based authentication. All clients will be required to provide the password in order to
interact with the BlueBubbles Server's API.
</Text>
<Text fontSize='md' mt={5}>
Below, you'll be asked to set a password, as well as select the proxy service that you would like to use.
Just note, by
</Text>
<Text fontSize='3xl' mt={5}>Configurations</Text>
<Alert status='info' mt={2}>
<AlertIcon />
You must <i>at minimum</i> set a password and a proxy service
</Alert>
<Stack direction='column' p={5}>
<ServerPasswordField />
<ProxyServiceField />
{(proxyService === 'ngrok') ? (
<>
<NgrokAuthTokenField />
<NgrokRegionField />
</>
): null}
</Stack>
</Box>
</SlideFade>
);
}
Example #11
Source File: InitiationInfo.tsx From dope-monorepo with GNU General Public License v3.0 | 5 votes |
InitiationInfo = () => {
return (
<VerticalPanelStack>
<PanelContainer>
<PanelTitleBar>
<div>Info</div>
</PanelTitleBar>
<PanelBody>
<h3>Initiation</h3>
<p>
Hustlers are the in-game representation of characters inside DOPE WARS. Each Hustler
gains RESPECT based on the amount of time passed since their Initiation. RESPECT will be
useful in the upcoming DOPE WARS game, and provide your Hustler with certain advantages.
</p>
<p>
A Hustler’s appearance is customizable, based on the items in their inventory and
attributes you select. All Hustler artwork is stored on the Ethereum blockchain, with a
next-level technical approach that few projects can match.
</p>
<h3>Claiming Gear</h3>
<p>
Initiating a Hustler will Claim Gear and create 9 new Item NFTs from one DOPE NFT, and
equip them on your Hustler. Because each of these new items become their own separate
NFT, they’re also tradeable on the secondary market.
</p>
<p>
Soon™ you’ll be able to upgrade your Hustler by mixing-and-matching items from multiple
DOPE NFT bundles – with over ONE BILLION possible combinations.
</p>
<p>
Gear from each DOPE NFT can only be claimed once. The DOPE NFT remains in your wallet
and still serves as the governance token for DopeWars DAO. Expect each DOPE NFT to have
more utility developed for it in the future.
</p>
<h3>More Info</h3>
<ul className="normal">
<li>
<Link href="https://dope-wars.notion.site/Hustler-Minting-and-Unbundling-25c6dfb9dca64196aedf8def6297c51a">
<a className="primary">The Dope Wars Hustler Guide</a>
</Link>
</li>
<li>
<Link href="/inventory">
<a className="primary">Gangsta Party</a>
</Link>
</li>
</ul>
</PanelBody>
<div></div>
</PanelContainer>
<Alert
status="info"
css={css`
max-height: 100px;
border: 2px solid black;
`}
>
<AlertIcon />
<div>
All OGs have been Initiated, but Hustlers are an infinite mint! Make as many as you want.
</div>
</Alert>
</VerticalPanelStack>
);
}
Example #12
Source File: InterestRates.tsx From rari-dApp with GNU Affero General Public License v3.0 | 5 votes |
export default function InterestRates() {
const isMobile = useIsSmallScreen();
const { isAuthed } = useRari();
const { t } = useTranslation();
return (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
color="#FFFFFF"
mx="auto"
width="100%"
height="100%"
px={4}
>
<Header isAuthed={isAuthed} />
<Alert colorScheme="green" borderRadius={5} mt="5">
<AlertIcon />
<span style={{ color: "#2F855A" }}>
{t(
"This page is currently in beta. If you notice any issues, please"
)}{" "}
<Link
isExternal={true}
href="https://discord.gg/3uWWeQGq"
textDecoration="underline"
>
{t("let us know!")}
</Link>
</span>
</Alert>
{isMobile ? (
<Alert colorScheme="orange" borderRadius={5} mt={5}>
<AlertIcon />
<span style={{ color: "#C05621" }}>
{t(
"This page is not optimized for use on smaller screens. Sorry for the inconvenience!"
)}
</span>
</Alert>
) : null}
<InterestRatesView />
<Footer />
</Column>
);
}
Example #13
Source File: PrivateApiWalkthrough.tsx From bluebubbles-server with Apache License 2.0 | 5 votes |
PrivateApiWalkthrough = (): JSX.Element => {
return (
<SlideFade in={true} offsetY='150px'>
<Box px={5}>
<Text fontSize='4xl'>Private API Setup (Advanced)</Text>
<Text fontSize='md' mt={5}>
You may already know this, but BlueBubbles is one of the only cross-platform iMessage solution that
supports sending reactions, replies, subjects, and effects. This is because we developed an Objective-C
library that allows us to interface with Apple's "private APIs". Normally, this is not possible, however,
after disabling your macOS device's SIP controls, these private APIs are made accessible.
</Text>
<Text fontSize='md' mt={5}>
If you would like to find out more information, please go to the link below:
</Text>
<LinkBox as='article' maxW='sm' px='5' pb={5} pt={2} mt={5} borderWidth='1px' rounded='xl'>
<Text color='gray'>
https://docs.bluebubbles.app/private-api/
</Text>
<Heading size='md' my={2}>
<LinkOverlay href='https://bluebubbles.app/donate' target='_blank'>
Private API Documentation
</LinkOverlay>
</Heading>
<Text>
This documentation will go over the pros and cons to setting up the Private API. It will speak to
the risks of disabling SIP controls, as well as the full feature set that uses the Private API
</Text>
</LinkBox>
<Text fontSize='3xl' mt={5}>Configurations</Text>
<Alert status='info' mt={2}>
<AlertIcon />
Unless you know what you're doing, please make sure the following Private API Requirements all pass
before enabling the setting. Enabling this will automatically attempt to install the helper bundle
into MacForge or MySIMBL.
</Alert>
<Box mt={4} />
<PrivateApiField
helpText={'Note: If the plugins folder is missing, you may need to manually install the helper bundle'}
/>
</Box>
</SlideFade>
);
}
Example #14
Source File: ApprovePanelOwnedDope.tsx From dope-monorepo with GNU General Public License v3.0 | 4 votes |
ApprovePanelOwnedDope = ({hustlerConfig, setHustlerConfig}: StepsProps) => {
const [mintTo, setMintTo] = useState(hustlerConfig.mintAddress != null);
const [canMint, setCanMint] = useState(false);
const { account } = useWeb3React();
const [hasEnoughPaper, setHasEnoughPaper] = useState<boolean>();
const [isPaperApproved, setIsPaperApproved] = useState<boolean>();
const isContract = useIsContract(account);
const dispatchHustler = useDispatchHustler();
const initiator = useInitiator();
// Set PAPER cost based on contract amount due to "halvening"
const [paperCost, setPaperCost] = useState<BigNumber>();
useEffect(() => {
let isMounted = true;
initiator.cost().then(setPaperCost);
return () => { isMounted = false };
}, [initiator]);
// Has enough paper?
const paper = usePaper();
useEffect(() => {
if (account && paperCost) {
paper
.balanceOf(account)
.then(balance => setHasEnoughPaper(balance.gte(paperCost)));
}
}, [account, paper, paperCost]);
// Can we mint?
useEffect(() => {
if (isPaperApproved && hasEnoughPaper && (!mintTo || (mintTo && hustlerConfig.mintAddress))) {
setCanMint(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isPaperApproved, hasEnoughPaper, isContract, hustlerConfig.mintAddress, hustlerConfig.body]);
const mintHustler = () => {
if (!account) {
return;
}
const config = createConfig(hustlerConfig)
const {
dopeId,
mintAddress,
} = hustlerConfig;
initiator
.mintFromDopeTo(dopeId, mintAddress ? mintAddress : account, config, '0x', 1500000)
.then(() =>
dispatchHustler({
type: 'GO_TO_FINALIZE_STEP',
}),
);
};
const setMintAddress = useCallback(
(value: string) => {
setHustlerConfig({ ...hustlerConfig, mintAddress: value });
},
[hustlerConfig, setHustlerConfig],
);
return(
<Stack>
{!isPaperApproved &&
<ApprovePaper
address={initiator.address}
isApproved={isPaperApproved}
onApprove={approved => setIsPaperApproved(approved)}
>
We need you to allow our Swap Meet to spend <ReceiptItemPaper amount={paperCost} hideUnderline /> to Claim Gear of DOPE NFT #{hustlerConfig.dopeId}.
</ApprovePaper>
}
{isPaperApproved &&
<PanelContainer justifyContent="flex-start">
<PanelTitleHeader>Transaction Details</PanelTitleHeader>
<PanelBody>
<h4>You Use</h4>
<hr className="onColor" />
<ReceiptItemDope dopeId={hustlerConfig.dopeId} hideUnderline />
<br/>
<h4>You Pay</h4>
<hr className="onColor" />
<ReceiptItemPaper amount={paperCost} hideUnderline />
{!hasEnoughPaper && (
<Alert status="error">
<AlertIcon />
Not enough $PAPER
</Alert>
)}
<br/>
<h4>You Receive</h4>
<hr className="onColor" />
<ReceiptItemHustler hustlerConfig={hustlerConfig} />
<ReceiptItemGear hideUnderline />
</PanelBody>
<PanelFooter
css={css`
padding: 1em;
position: relative;
`}
>
<DisconnectAndQuitButton returnToPath='/inventory?section=Dope' />
{/* <MintTo
mintTo={mintTo}
setMintTo={setMintTo}
mintAddress={hustlerConfig.mintAddress}
setMintAddress={setMintAddress}
/> */}
<Button variant="primary" onClick={mintHustler} disabled={!canMint} autoFocus>
✨ Mint Hustler ✨
</Button>
</PanelFooter>
</PanelContainer>
}
</Stack>
);
}
Example #15
Source File: RenderDetail.tsx From ke with MIT License | 4 votes |
RenderDetail = (props: RenderDetailProps): JSX.Element => {
/*
Entry point for displaying components in https://myspa.com/some-url/100500 route format.
Here we fetch data from the backend using the url that we specified in a
admin class.
After that we mounts the widgets of a particular view type. At the moment there are two:
- Detail View (see mountDetailFields for detail)
- Wizard View (see mountWizards for detail)
*/
const [mainDetailObject, setMainDetailObject] = useState<Model>()
const [needRefreshDetailObject, setNeedRefreshDetailObject] = useState<boolean>(true)
const { id } = useParams<{ id: string }>()
const { resourceName, admin, provider, notifier } = props
const toast = useToast()
const detailNotifier = notifier || new ChakraUINotifier(toast)
const [isLoading, setIsLoading] = useState<boolean>(true)
const [loadError, setLoadError] = useState<LoadError | null>(null)
const activeWizardRef = useRef<WizardControl>()
let title = `${admin.verboseName} # ${id}`
if (admin.getPageTitle) {
const pageTitle = admin.getPageTitle(mainDetailObject)
if (pageTitle) {
title = pageTitle
}
}
document.title = title
let favicon = admin.favicon || ''
if (admin.getPageFavicon) {
const favIconSource = admin.getPageFavicon(mainDetailObject)
if (favIconSource) {
favicon = favIconSource
}
}
setFavicon(favicon)
const refreshMainDetailObject = (): void => {
setNeedRefreshDetailObject(true)
}
useEffect(() => {
const backendResourceUrl = admin.getResource(id)
if (needRefreshDetailObject) {
provider
.getObject(backendResourceUrl)
.then(async (res) => {
setNeedRefreshDetailObject(false)
setMainDetailObject(res)
if (admin?.onDetailObjectLoaded !== undefined) {
await admin.onDetailObjectLoaded({
mainDetailObject: res,
provider,
context: containerStore,
setInitialValue,
})
}
})
.catch((er: LoadError) => {
setLoadError(er)
})
.finally(() => setIsLoading(false))
}
}, [id, provider, admin, needRefreshDetailObject, props, mainDetailObject])
const { getDataTestId } = useCreateTestId({ name: admin.name })
useEffect(() => {
admin.onMount()
return () => admin.onUnmount()
}, [admin])
return (
<SaveEventProvider>
<Row>
<Col xs={12} xsOffset={0} md={10} mdOffset={1}>
<Box padding="8px 0px">
<ToListViewLink name={resourceName} />
</Box>
</Col>
</Row>
<Row {...getDataTestId()}>
<Col xs={12} xsOffset={0} md={10} mdOffset={1}>
{isLoading ? <Spinner /> : ''}
{!isLoading && !loadError
? Object.entries(getContainersToMount()).map(([elementsKey, container]: [string, Function]) => {
const elements = admin[elementsKey as keyof typeof admin]
if (!elements) return []
return (
<ErrorBoundary>
{container({
mainDetailObject,
setMainDetailObject,
ViewType,
elements,
elementsKey,
refreshMainDetailObject,
activeWizardRef,
...props,
notifier: detailNotifier,
})}
</ErrorBoundary>
)
})
: ''}
{!isLoading && loadError ? (
<Alert status="error" {...getDataTestId({ postfix: '--loadingError' })}>
<AlertIcon />
<AlertTitle mr={2}>Ошибка при выполнении запроса</AlertTitle>
<AlertDescription>{loadError.response?.data?.message}</AlertDescription>
</Alert>
) : (
''
)}
</Col>
</Row>
</SaveEventProvider>
)
}
Example #16
Source File: create-app-github.tsx From ledokku with MIT License | 4 votes |
CreateAppGithub = () => {
const history = useHistory();
const toast = useToast();
const { user } = useAuth();
const { data: dataApps } = useAppsQuery();
const [isNewWindowClosed, setIsNewWindowClosed] = useState(false);
const [selectedRepo, setSelectedRepo] = useState<Repository>();
const [selectedBranch, setSelectedBranch] = useState('');
const [isProceedModalOpen, setIsProceedModalOpen] = useState(false);
const {
data: installationData,
loading: installationLoading,
} = useGithubInstallationIdQuery({ fetchPolicy: 'network-only' });
const [
getRepos,
{ data: reposData, loading: reposLoading },
] = useRepositoriesLazyQuery({ fetchPolicy: 'network-only' });
const [
getBranches,
{ data: branchesData, loading: branchesLoading },
] = useBranchesLazyQuery({ fetchPolicy: 'network-only' });
const [arrayOfCreateAppLogs, setArrayOfCreateAppLogs] = useState<
RealTimeLog[]
>([]);
const [isTerminalVisible, setIsTerminalVisible] = useState(false);
const [isToastShown, setIsToastShown] = useState(false);
const [createAppGithubMutation, { loading }] = useCreateAppGithubMutation();
const [
isAppCreationSuccess,
setIsAppCreationSuccess,
] = useState<AppCreationStatus>();
useAppCreateLogsSubscription({
onSubscriptionData: (data) => {
const logsExist = data.subscriptionData.data?.appCreateLogs;
if (logsExist) {
setArrayOfCreateAppLogs((currentLogs) => {
return [...currentLogs, logsExist];
});
if (logsExist.type === 'end:success') {
setIsAppCreationSuccess(AppCreationStatus.SUCCESS);
} else if (logsExist.type === 'end:failure') {
setIsAppCreationSuccess(AppCreationStatus.FAILURE);
}
}
},
});
const createAppGithubSchema = yup.object().shape({
name: yup
.string()
.required('App name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name exists',
'App with this name already exists',
(val) => !dataApps?.apps.find((app) => app.name === val)
),
repo: yup.object({
fullName: yup.string().required(),
id: yup.string().required(),
name: yup.string().required(),
}),
installationId: yup.string().required(),
gitBranch: yup.string().optional(),
});
const formik = useFormik<{
name: string;
repo: {
fullName: string;
id: string;
name: string;
};
installationId: string;
gitBranch: string;
}>({
initialValues: {
name: '',
repo: {
fullName: '',
id: '',
name: '',
},
installationId: '',
gitBranch: '',
},
validateOnChange: true,
validationSchema: createAppGithubSchema,
onSubmit: async (values) => {
if (installationData) {
try {
await createAppGithubMutation({
variables: {
input: {
name: values.name,
gitRepoFullName: values.repo.fullName,
branchName: values.gitBranch,
gitRepoId: values.repo.id,
githubInstallationId: values.installationId,
},
},
});
setIsTerminalVisible(true);
} catch (error) {
error.message === 'Not Found'
? toast.error(`Repository : ${values.repo.fullName} not found`)
: toast.error(error.message);
}
}
},
});
const handleNext = () => {
setIsTerminalVisible(false);
const appId = arrayOfCreateAppLogs[arrayOfCreateAppLogs.length - 1].message;
history.push(`app/${appId}`, 'new');
trackGoal(trackingGoals.createAppGithub, 0);
};
const handleOpen = () => {
const newWindow = window.open(
`https://github.com/apps/${config.githubAppName}/installations/new`,
'Install App',
'resizable=1, scrollbars=1, fullscreen=0, height=1000, width=1020,top=' +
window.screen.width +
', left=' +
window.screen.width +
', toolbar=0, menubar=0, status=0'
);
const timer = setInterval(async () => {
if (newWindow && newWindow.closed) {
setIsNewWindowClosed(true);
clearInterval(timer);
}
}, 100);
};
useEffect(() => {
if (!installationLoading && installationData && isNewWindowClosed) {
getRepos({
variables: {
installationId: installationData.githubInstallationId.id,
},
});
setIsNewWindowClosed(false);
}
}, [
installationData,
installationLoading,
isNewWindowClosed,
setIsNewWindowClosed,
getRepos,
]);
useEffect(() => {
if (
!installationLoading &&
installationData &&
!reposLoading &&
reposData &&
selectedRepo
) {
getBranches({
variables: {
installationId: installationData.githubInstallationId.id,
repositoryName: selectedRepo.name,
},
});
}
}, [
installationData,
installationLoading,
reposData,
reposLoading,
getBranches,
selectedRepo?.name,
selectedRepo,
]);
const handleChangeRepo = (active: RepoOption) => {
setSelectedRepo(active.value);
setSelectedBranch('');
if (installationData) {
formik.setValues({
name: active.value.name,
installationId: installationData?.githubInstallationId.id,
repo: {
fullName: active.value.fullName,
name: active.value.name,
id: active.value.id,
},
gitBranch: '',
});
}
};
const handleChangeBranch = (active: BranchOption) => {
setSelectedBranch(active.value.name);
formik.setFieldValue('gitBranch', active.value.name);
};
const repoOptions: RepoOption[] = [];
if (reposData && !reposLoading) {
reposData?.repositories.map((r) =>
repoOptions.push({ value: r, label: r.fullName })
);
}
let branchOptions: BranchOption[] = [];
if (branchesData && !branchesLoading) {
branchesData.branches.map((b) =>
branchOptions.push({ value: b, label: b.name })
);
}
useEffect(() => {
if (installationData && !installationLoading) {
getRepos({
variables: {
installationId: installationData?.githubInstallationId.id,
},
});
}
}, [installationLoading, getRepos, installationData]);
useEffect(() => {
if (selectedRepo && installationData) {
getBranches({
variables: {
installationId: installationData?.githubInstallationId.id,
repositoryName: selectedRepo.name,
},
});
}
}, [selectedRepo, getBranches, installationData]);
// Effect for app creation
useEffect(() => {
isAppCreationSuccess === AppCreationStatus.FAILURE && !isToastShown
? toast.error('Failed to create an app') && setIsToastShown(true)
: isAppCreationSuccess === AppCreationStatus.SUCCESS &&
!isToastShown &&
toast.success('App created successfully') &&
setIsToastShown(true);
}, [isToastShown, isAppCreationSuccess, toast]);
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
{isTerminalVisible ? (
<>
<p className="mb-2 ">
Creating <b>{formik.values.name}</b> app from{' '}
<b>{formik.values.repo.name}</b>
</p>
<p className="text-gray-500 mb-2">
Creating app usually takes a couple of minutes. Breathe in,
breathe out, logs are about to appear below:
</p>
<Terminal className={'w-6/6'}>
{arrayOfCreateAppLogs.map((log) => (
<p
key={arrayOfCreateAppLogs.indexOf(log)}
className={'text-s leading-5'}
>
{log.message?.replaceAll('[1G', '')}
</p>
))}
</Terminal>
{!!isAppCreationSuccess &&
isAppCreationSuccess === AppCreationStatus.SUCCESS ? (
<div className="mt-12 flex justify-end">
<Button
onClick={() => handleNext()}
color="grey"
iconEnd={<FiArrowRight size={20} />}
>
Next
</Button>
</div>
) : !!isAppCreationSuccess &&
isAppCreationSuccess === AppCreationStatus.FAILURE ? (
<div className="mt-12 flex justify-start">
<Button
onClick={() => {
setIsTerminalVisible(false);
formik.resetForm();
}}
color="grey"
iconEnd={<FiArrowLeft size={20} />}
>
Back
</Button>
</div>
) : null}
</>
) : (
<>
<Heading as="h2" size="md">
Create a new GitHub application
</Heading>
{installationData &&
!installationLoading &&
reposData &&
!reposLoading ? (
<>
<Text color="gray.400">
When you push to Git, your application will be redeployed
automatically.
</Text>
<Grid
templateColumns={{
sm: 'repeat(1, 1fr)',
md: 'repeat(3, 1fr)',
}}
>
<GridItem colSpan={2}>
<Flex alignItems="center" mt="12">
<Avatar
size="sm"
name={user?.userName}
src={user?.avatarUrl}
/>
<Text ml="2" fontWeight="bold">
{user?.userName}
</Text>
</Flex>
<form onSubmit={formik.handleSubmit}>
<Box mt="8">
<FormLabel>Repository</FormLabel>
<Select
placeholder="Select repository"
isSearchable={false}
onChange={handleChangeRepo}
options={repoOptions}
/>
</Box>
<Text mt="1" color="gray.400" fontSize="sm">
Can't see your repo in the list?{' '}
<Link
onClick={() => setIsProceedModalOpen(true)}
textDecoration="underline"
>
Configure the GitHub app.
</Link>
</Text>
<Box mt="8">
<FormLabel>Branch to deploy</FormLabel>
<Select
placeholder="Select branch"
isSearchable={false}
disabled={
!branchesData ||
branchesLoading ||
reposLoading ||
!reposData
}
onChange={handleChangeBranch}
options={branchOptions}
/>
</Box>
<Box mt="8" display="flex" justifyContent="flex-end">
<Button
type="submit"
color="grey"
disabled={!selectedBranch || !selectedRepo}
isLoading={loading}
>
Create
</Button>
</Box>
</form>
</GridItem>
</Grid>
</>
) : !reposLoading && !installationLoading && !reposData ? (
<>
<Alert mb="4" mt="4" w="65%" status="info">
<AlertIcon />
<Box flex="1">
<AlertTitle>Set up repository permissions</AlertTitle>
<AlertDescription display="block">
First you will need to set up permissions for repositories
that you would like to use with Ledokku. Once that's done,
it's time to choose repo and branch that you would like to
create app from and off we go.
</AlertDescription>
</Box>
</Alert>
<Button
color="grey"
onClick={() => setIsProceedModalOpen(true)}
>
Set up permissions
</Button>
</>
) : (
<Spinner />
)}
</>
)}
<Modal
isOpen={isProceedModalOpen}
onClose={() => setIsProceedModalOpen(false)}
isCentered
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Github setup info</ModalHeader>
<ModalCloseButton />
<ModalBody>
New window is about to open. After you are done selecting github
repos, close the window and refresh page.
</ModalBody>
<ModalFooter>
<Button
color="grey"
variant="outline"
className="mr-3"
onClick={() => setIsProceedModalOpen(false)}
>
Cancel
</Button>
<Button
color="grey"
onClick={() => {
handleOpen();
setIsProceedModalOpen(false);
}}
>
Proceed
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Container>
</>
);
}
Example #17
Source File: Screen2.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
Screen2 = ({ mode }: { mode: string }) => {
const {
feeTier,
poolOracleModel,
oracleData,
setFeeTier,
activeOracleModel,
tokenAddress,
oracleAddress,
oracleTouched,
uniV3BaseTokenAddress,
setOracleTouched,
activeUniSwapPair,
setOracleAddress,
setActiveOracleModel,
setUniV3BaseTokenAddress,
poolOracleAddress,
setActiveUniSwapPair,
uniV3BaseTokenOracle,
setUniV3BaseTokenOracle,
baseTokenActiveOracleName,
setBaseTokenActiveOracleName,
shouldShowUniV3BaseTokenOracleForm,
// New stuff -skip oracle step with default oracle
hasPriceForAsset,
hasDefaultOracle,
hasCustomOracleForToken,
priceForAsset,
defaultOracle,
customOracleForToken,
} = useAddAssetContext();
if (
poolOracleModel === "MasterOracleV1" ||
poolOracleModel === "ChainlinkPriceOracle"
)
return (
<LegacyOracle
tokenAddress={tokenAddress}
poolOracleModel={poolOracleModel}
setActiveOracleModel={setActiveOracleModel}
poolOracleAddress={poolOracleAddress}
setOracleAddress={setOracleAddress}
/>
);
// If it has a default oracle and the user hasn't edited to be outside the default oracle
const hasDefaultOraclePriceAndHasntEdited =
hasDefaultOracle && hasPriceForAsset && oracleAddress === defaultOracle;
return (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-start"
h="100%"
w="100%"
// bg="aqua"
>
{hasDefaultOraclePriceAndHasntEdited && (
<Row
mainAxisAlignment="center"
crossAxisAlignment="center"
w="100%"
h="30%"
// bg="red"
>
<Alert status="info" width="80%" height="50px" borderRadius={5}>
<AlertIcon />
<Text fontSize="sm" align="center" color="black">
This asset already has a price from the Pool's Default Oracle, but
you can change this asset's oracle if you want.
</Text>
</Alert>
</Row>
)}
<Row
mainAxisAlignment={
mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
? "center"
: "flex-start"
}
crossAxisAlignment={
mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
? "center"
: "flex-start"
}
h="100%"
w="100%"
>
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment={
shouldShowUniV3BaseTokenOracleForm ? "flex-start" : "center"
}
maxHeight="100%"
height="100%"
maxWidth="100%"
width={
mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
? "50%"
: "100%"
}
>
<OracleConfig />
</Column>
{shouldShowUniV3BaseTokenOracleForm ? (
<Column
width="50%"
minW="50%"
height="100%"
mainAxisAlignment="center"
crossAxisAlignment="center"
>
<BaseTokenOracleConfig />
</Column>
) : null}
</Row>
</Column>
);
}
Example #18
Source File: ChatForm.tsx From takeout-app with MIT License | 4 votes |
ChatForm: React.FC<Props> = ({ track, channel }) => {
const chat = useChat();
const { data: session } = Api.useSession();
const isStaff = session?.attendee?.is_staff;
const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
const { register, handleSubmit, reset, setFocus, watch } = useForm<{
message: string;
asAdmin: boolean;
}>({
defaultValues: {
message: "",
asAdmin: false,
},
});
const asAdmin = watch("asAdmin");
const onSubmit = handleSubmit(async (data) => {
if (!chat.session || !channel) return;
if (isRequesting) return;
setIsRequesting(true);
setErrorAlert(null);
try {
if (data.asAdmin && isStaff) {
await Api.sendChatMessage(track.slug, data.message, true);
} else {
// Workaround: aws-sdk-v3 sigv4 fails to generate correct signature for payload containing emoji...
if (/\p{Extended_Pictographic}/u.test(data.message)) {
await Api.sendChatMessage(track.slug, data.message, false);
} else {
await chat.session.postMessage(channel, data.message);
}
}
reset({ message: "", asAdmin: false });
} catch (e) {
setErrorAlert(
<Box my={2}>
<ErrorAlert error={e} />
</Box>,
);
}
setFocus("message");
setIsRequesting(false);
});
const shouldDisable = !session?.attendee || !chat.session || !channel;
// TODO: errorAlert to toast
return (
<Box p="16px" bgColor="#ffffff" borderTop="1px solid" borderColor={Colors.chatBorder}>
{errorAlert}
<form onSubmit={onSubmit}>
<VStack w="100%">
{session && !session.attendee?.is_ready ? (
<Box w="100%">
<Alert status="warning">
<AlertIcon />
<Text as="span">
Set your name at{" "}
<Link as={RouterLink} to="/attendee" textDecoration="underline">
Settings
</Link>{" "}
page
</Text>
</Alert>
</Box>
) : null}
<Box w="100%">
<Textarea
as={TextareaAutoSize}
{...register("message")}
size="sm"
placeholder={asAdmin ? "SAY SOMETHING AS ADMIN..." : "Send a message"}
isRequired
isDisabled={shouldDisable}
autoComplete="off"
rows={1}
minRows={1}
maxRows={4}
onKeyPress={(e) => {
if (e.key == "Enter") {
e.preventDefault();
onSubmit();
}
}}
css={{ resize: "none" }}
/>
</Box>
<Flex w="100%" alignItems="flex-end" direction="row-reverse" justifyContent="space-between">
<IconButton
icon={<SendIcon boxSize="14px" />}
minW="30px"
w="30px"
h="30px"
aria-label="Send"
type="submit"
isLoading={isRequesting}
isDisabled={shouldDisable}
/>
{isStaff ? (
<Tooltip label="Send as an official announcement" aria-label="">
<FormControl display="flex" alignSelf="center" h="30px">
<FormLabel htmlFor="ChatForm__asAdmin" aria-hidden="true" m={0} mr={1}>
<CampaignIcon w="24px" h="24px" />
</FormLabel>
<Switch
aria-label="Send as an official announcement"
id="ChatForm__asAdmin"
size="sm"
isChecked={asAdmin}
isDisabled={shouldDisable}
{...register("asAdmin")}
/>
</FormControl>
</Tooltip>
) : null}
</Flex>
</VStack>
</form>
</Box>
);
}
Example #19
Source File: UniswapV3PriceOracleConfigurator.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
UniswapV3PriceOracleConfigurator = () => {
const { t } = useTranslation();
const {
setFeeTier,
tokenAddress,
setOracleAddress,
setUniV3BaseTokenAddress,
activeUniSwapPair,
setActiveUniSwapPair,
} = useAddAssetContext();
// We get a list of whitelistedPools from uniswap-v3's the graph.
const { data: liquidity, error } = useQuery(
"UniswapV3 pool liquidity for " + tokenAddress,
async () =>
(
await axios.post(
"https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3",
{
query: `{
token(id:"${tokenAddress.toLowerCase()}") {
whitelistPools {
id,
feeTier,
volumeUSD,
totalValueLockedUSD,
token0 {
symbol,
id,
name
},
token1 {
symbol,
id,
name
}
}
}
}`,
}
)
).data,
{ refetchOnMount: false }
);
// When user selects an option this function will be called.
// Active pool, fee Tier, and base token are updated and we set the oracle address to the address of the pool we chose.
const updateBoth = (value: string) => {
const uniPool = liquidity.data.token.whitelistPools[value];
const baseToken: string =
uniPool.token0.id.toLowerCase() === tokenAddress.toLocaleLowerCase()
? uniPool.token1.id
: uniPool.token0.id;
setActiveUniSwapPair(value);
setFeeTier(uniPool.feeTier);
setOracleAddress(uniPool.id);
setUniV3BaseTokenAddress(baseToken);
};
// If liquidity is undefined, theres an error or theres no token found return nothing.
if (liquidity === undefined || liquidity.data === undefined)
return null;
// Sort whitelisted pools by TVL. Greatest to smallest. Greater TVL is safer for users so we show it first.
// Filter out pools where volume is less than $100,000
const liquiditySorted = liquidity.data.token.whitelistPools.sort(
(a: any, b: any): any =>
parseInt(a.totalValueLockedUSD) > parseInt(b.totalValueLockedUSD) ? -1 : 1
);
// .filter((pool: any) => pool.volumeUSD >= 100000);
const selectedOracle = liquidity.data.token.whitelistPools[activeUniSwapPair];
console.log({ selectedOracle });
// const warning = useMemo(() => {
// if (selectedOracle.liquidityProviderCount <=100)
// }, [selectedOracle]);
return (
<>
<Column
crossAxisAlignment="flex-start"
mainAxisAlignment="flex-start"
width={"100%"}
my={2}
px={4}
ml={"auto"}
// bg="aqua"
id="UNIv3COLUMN"
>
<Row
crossAxisAlignment="center"
mainAxisAlignment="space-between"
w="100%"
// bg="green"
>
<SimpleTooltip
label={t(
"This field will determine which pool your oracle reads from. Its safer with more liquidity."
)}
>
<Text fontWeight="bold">
{t("Pool:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Select
{...DASHBOARD_BOX_PROPS}
ml={2}
mb={2}
width="180px"
borderRadius="7px"
value={activeUniSwapPair}
_focus={{ outline: "none" }}
placeholder={
activeUniSwapPair === "" ? t("Choose Pool") : activeUniSwapPair
}
onChange={(event) => {
updateBoth(event.target.value);
}}
>
{typeof liquidity !== undefined
? Object.entries(liquiditySorted).map(([key, value]: any[]) =>
value.totalValueLockedUSD !== null &&
value.totalValueLockedUSD !== undefined &&
value.totalValueLockedUSD >= 100 ? (
<option
className="black-bg-option"
value={key}
key={value.id}
>
{`${value.token0.symbol} / ${
value.token1.symbol
} (${shortUsdFormatter(value.totalValueLockedUSD)})`}
</option>
) : null
)
: null}
</Select>
</Row>
{activeUniSwapPair !== "" ? (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-start"
>
<Row mainAxisAlignment="flex-start" crossAxisAlignment="flex-start">
<Alert
status="warning"
width="100%"
height="70px"
borderRadius={5}
my={1}
>
<AlertIcon />
<Text fontSize="sm" align="center" color="black">
{
"Make sure this Uniswap V3 Pool has full-range liquidity. If not, your pool could be compromised."
}
</Text>
</Alert>
</Row>
<Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
my={2}
w="100%"
// bg="pink"
>
<SimpleTooltip label={t("TVL in pool as of this moment.")}>
<Text fontWeight="bold">
{t("Liquidity:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<h1>
{activeUniSwapPair !== ""
? shortUsdFormatter(
liquidity.data.token.whitelistPools[activeUniSwapPair]
.totalValueLockedUSD
)
: null}
</h1>
</Row>
<Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
my={2}
w="100%"
// bg="pink"
>
<SimpleTooltip label={t("Volume of pool.")}>
<Text fontWeight="bold">
{t("Volume:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<h1>
{activeUniSwapPair !== ""
? shortUsdFormatter(
liquidity.data.token.whitelistPools[activeUniSwapPair]
.volumeUSD
)
: null}
</h1>
</Row>
{/* <Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
my={2}
w="100%"
// bg="pink"
>
<SimpleTooltip
label={t(
"The fee percentage for the pool on Uniswap (0.05%, 0.3%, 1%)"
)}
>
<Text fontWeight="bold">
{t("Fee Tier:")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
<Text>
%
{activeUniSwapPair !== ""
? liquidity.data.token.whitelistPools[activeUniSwapPair]
.feeTier / 10000
: null}
</Text>
</Row> */}
<Row
crossAxisAlignment="center"
mainAxisAlignment="center"
width="260px"
my={0}
>
<Link
href={`https://info.uniswap.org/#/pools/${liquidity.data.token.whitelistPools[activeUniSwapPair].id}`}
isExternal
>
Visit Pool in Uniswap
</Link>
</Row>
</Column>
) : null}
</Column>
</>
);
}
Example #20
Source File: BaseTokenOracleConfig.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
BaseTokenOracleConfig = () => {
const { t } = useTranslation();
const { address } = useRari();
const {
mode,
oracleData,
uniV3BaseTokenAddress,
uniV3BaseTokenOracle,
setUniV3BaseTokenOracle,
baseTokenActiveOracleName,
setBaseTokenActiveOracleName,
} = useAddAssetContext();
const isUserAdmin = address === oracleData?.admin ?? false;
// We get all oracle options.
const options = useGetOracleOptions(oracleData, uniV3BaseTokenAddress);
console.log("helo there", { options });
// If we're editing the asset, show master price oracle as a default.
// Should run only once, when component renders.
useEffect(() => {
if (
mode === "Editing" &&
baseTokenActiveOracleName === "" &&
options &&
options["Current_Price_Oracle"]
)
setBaseTokenActiveOracleName("Current_Price_Oracle");
}, [mode, baseTokenActiveOracleName, options, setBaseTokenActiveOracleName]);
// This will update the oracle address, after user chooses which options they want to use.
// If option is Custom_Oracle oracle address is typed in by user, so we dont trigger this.
useEffect(() => {
if (
!!baseTokenActiveOracleName &&
baseTokenActiveOracleName !== "Custom_Oracle" &&
options
)
setUniV3BaseTokenOracle(options[baseTokenActiveOracleName]);
}, [baseTokenActiveOracleName, options, setUniV3BaseTokenOracle]);
return (
<>
<Row
crossAxisAlignment="center"
mainAxisAlignment="center"
width="100%"
my={2}
>
<Alert status="info" width="80%" height="50px" borderRadius={5} my={1}>
<AlertIcon />
<Text fontSize="sm" align="center" color="black">
{"This Uniswap V3 TWAP Oracle needs an oracle for the BaseToken."}
</Text>
</Alert>
</Row>
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
w="100%"
h="100%"
>
<Column
my={4}
width="100%"
crossAxisAlignment="center"
mainAxisAlignment="space-between"
>
<Column
mainAxisAlignment="center"
crossAxisAlignment="center"
height="50%"
justifyContent="space-around"
>
<CTokenIcon address={uniV3BaseTokenAddress} boxSize={"50px"} />
<SimpleTooltip
label={t("Choose the best price oracle for this BaseToken.")}
>
<Text fontWeight="bold" fontSize="sm" align="center">
{t("BaseToken Price Oracle")} <QuestionIcon ml={1} mb="4px" />
</Text>
</SimpleTooltip>
</Column>
{options ? (
<Box alignItems="center" height="50%">
<Select
{...DASHBOARD_BOX_PROPS}
ml="auto"
my={2}
borderRadius="7px"
_focus={{ outline: "none" }}
width="260px"
placeholder={
baseTokenActiveOracleName.length === 0
? t("Choose Oracle")
: baseTokenActiveOracleName.replaceAll("_", " ")
}
value={baseTokenActiveOracleName.toLowerCase()}
disabled={
!isUserAdmin ||
(!oracleData?.adminOverwrite &&
!options.Current_Price_Oracle === null)
}
onChange={(event) =>
setBaseTokenActiveOracleName(event.target.value)
}
>
{Object.entries(options).map(([key, value]) =>
value !== null &&
value !== undefined &&
key !== "Uniswap_V3_Oracle" &&
key !== "Uniswap_V2_Oracle" ? (
<option className="black-bg-option" value={key} key={key}>
{key.replaceAll("_", " ")}
</option>
) : null
)}
</Select>
{baseTokenActiveOracleName.length > 0 ? (
<Input
width="100%"
textAlign="center"
height="40px"
variant="filled"
size="sm"
mt={2}
mb={2}
value={uniV3BaseTokenOracle}
onChange={(event) => {
const address = event.target.value;
setUniV3BaseTokenOracle(address);
}}
disabled={
baseTokenActiveOracleName === "Custom_Oracle" ? false : true
}
{...DASHBOARD_BOX_PROPS}
_placeholder={{ color: "#e0e0e0" }}
_focus={{ bg: "#121212" }}
_hover={{ bg: "#282727" }}
bg="#282727"
/>
) : null}
</Box>
) : null}
</Column>
</Column>
</>
);
}
Example #21
Source File: NotificationsWalkthrough.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
NotificationsWalkthrough = (): JSX.Element => {
const dispatch = useAppDispatch();
const alertRef = useRef(null);
const serverLoaded = (useAppSelector(state => state.config.fcm_server !== null) ?? false);
const clientLoaded = (useAppSelector(state => state.config.fcm_client !== null) ?? false);
const [isDragging, setDragging] = useBoolean();
const [errors, setErrors] = useState([] as Array<ErrorItem>);
const alertOpen = errors.length > 0;
const onDrop = async (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
dragCounter = 0;
setDragging.off();
// I'm not sure why, but we need to copy the file data _before_ we read it using the file reader.
// If we do not, the data transfer file list gets set to empty after reading the first file.
const listCopy: Array<Blob> = [];
for (let i = 0; i < e.dataTransfer.files.length; i++) {
listCopy.push(e.dataTransfer.files.item(i) as Blob);
}
// Actually read the files
const errors: Array<ErrorItem> = [];
for (let i = 0; i < listCopy.length; i++) {
try {
const fileStr = await readFile(listCopy[i]);
const validClient = isValidClientConfig(fileStr);
const validServer = isValidServerConfig(fileStr);
const jsonData = JSON.parse(fileStr);
if (validClient) {
const test = isValidFirebaseUrl(jsonData);
if (test) {
await saveFcmClient(jsonData);
dispatch(setConfig({ name: 'fcm_client', 'value': jsonData }));
} else {
throw new Error(
'Your Firebase setup does not have a real-time database enabled. ' +
'Please enable the real-time database in your Firebase Console.'
);
}
} else if (validServer) {
await saveFcmServer(jsonData);
dispatch(setConfig({ name: 'fcm_server', 'value': jsonData }));
} else {
throw new Error('Invalid Google FCM File!');
}
} catch (ex: any) {
errors.push({ id: String(i), message: ex?.message ?? String(ex) });
}
}
if (errors.length > 0) {
setErrors(errors);
}
};
const onDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
if (dragCounter === 0) {
setDragging.on();
}
dragCounter += 1;
};
const onDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
};
const onDragLeave = () => {
dragCounter -= 1;
if (dragCounter === 0) {
setDragging.off();
}
};
const closeAlert = () => {
setErrors([]);
};
return (
<SlideFade in={true} offsetY='150px'>
<Box
px={5}
onDragEnter={(e) => onDragEnter(e)}
onDragLeave={() => onDragLeave()}
onDragOver={(e) => onDragOver(e)}
onDrop={(e) => onDrop(e)}
>
<Text fontSize='4xl'>Notifications & Firebase</Text>
<Text fontSize='md' mt={5}>
BlueBubbles utilizes Google FCM (Firebase Cloud Messaging) to deliver notifications to your devices.
We do this so the client do not need to hold a connection to the server at all times. As a result,
BlueBubbles can deliver notifications even when the app is running in the background. It also means
BlueBubbles will use less battery when running in the background.
</Text>
<Alert status='info' mt={5}>
<AlertIcon />
If you do not complete this setup, you will not receive notifications!
</Alert>
<Text fontSize='md' mt={5}>
The setup with Google FCM is a bit tedious, but it is a "set it and forget it" feature. Follow the
instructions here: <Link
as='span'
href='https://bluebubbles.app/install/'
color='brand.primary'
target='_blank'>https://bluebubbles.app/install</Link>
</Text>
<Text fontSize='3xl' mt={5}>Firebase Configurations</Text>
<SimpleGrid columns={2} spacing={5} mt={5}>
<DropZone
text="Drag n' Drop google-services.json"
loadedText="google-services.json Successfully Loaded!"
isDragging={isDragging}
isLoaded={serverLoaded}
/>
<DropZone
text="Drag n' Drop *-firebase-adminsdk-*.json"
loadedText="*-firebase-adminsdk-*.json Successfully Loaded!"
isDragging={isDragging}
isLoaded={clientLoaded}
/>
</SimpleGrid>
</Box>
<ErrorDialog
errors={errors}
modalRef={alertRef}
onClose={() => closeAlert()}
isOpen={alertOpen}
/>
</SlideFade>
);
}