@chakra-ui/react#Menu TypeScript Examples
The following examples show how to use
@chakra-ui/react#Menu.
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: Header.tsx From rari-dApp with GNU Affero General Public License v3.0 | 6 votes |
UtilsLink = ({
isAuthed,
ml,
}: {
isAuthed: boolean;
ml?: number | string;
}) => {
const { t } = useTranslation();
return (
<Box ml={ml ?? 0}>
<Menu autoSelect={false} placement="bottom">
<MenuButton>
<SubMenuText text={t("Utilities")} parentLink="/utils" />
</MenuButton>
<Portal>
<MenuList {...DASHBOARD_BOX_PROPS} color="#FFF" minWidth="110px">
{isAuthed && (
<SubMenuItem name={t("Positions")} link="/utils/positions" />
)}
<SubMenuItem
name={t("Interest Rates")}
link="/utils/interest-rates"
/>
</MenuList>
</Portal>
</Menu>
</Box>
);
}
Example #2
Source File: Header.tsx From rari-dApp with GNU Affero General Public License v3.0 | 6 votes |
PoolsLink = ({ ml }: { ml?: number | string }) => {
const { t } = useTranslation();
return (
<Box ml={ml ?? 0}>
<Menu autoSelect={false} placement="bottom">
<MenuButton>
<SubMenuText text={t("Pools")} parentLink="/pools" />
</MenuButton>
<Portal>
<MenuList {...DASHBOARD_BOX_PROPS} color="#FFF" minWidth="110px">
<SubMenuItem name={t("USDC Pool")} link="/pools/usdc" />
<SubMenuItem name={t("DAI Pool")} link="/pools/dai" />
<SubMenuItem name={t("Yield Pool")} link="/pools/yield" />
<SubMenuItem name={t("ETH Pool")} link="/pools/eth" />
</MenuList>
</Portal>
</Menu>
</Box>
);
}
Example #3
Source File: ChatMessageView.tsx From takeout-app with MIT License | 6 votes |
ChatMessageMenu: React.FC<ChatMessageMenuProps> = ({ track, message, pinned, onOpen, onClose }) => {
const chat = useChat();
const real = !pinned && !message.adminControl?.promo;
return (
<Menu onOpen={onOpen} onClose={onClose}>
<MenuButton>Menu</MenuButton>
<Portal>
<MenuList zIndex="1501">
<MenuItem
onClick={() => window.open(`/control/chime_users/${encodeURIComponent(message.sender.handle)}`, "_blank")}
>
Lookup Attendee
</MenuItem>
{pinned ? (
<MenuItem onClick={() => Api.pinChatMessage(track.slug, null)}>Unpin</MenuItem>
) : (
<MenuItem onClick={() => Api.pinChatMessage(track.slug, message)}>Pin</MenuItem>
)}
{real ? (
<MenuItem onClick={() => chat.session!.redactMessage(message.channel, message.id)}>Redact</MenuItem>
) : null}
</MenuList>
</Portal>
</Menu>
);
}
Example #4
Source File: Navigation.tsx From bluebubbles-server with Apache License 2.0 | 5 votes |
Navigation = (): JSX.Element => {
const { isOpen, onOpen, onClose } = useDisclosure();
const {
isOpen: isNotificationsOpen,
onOpen: onNotificationOpen,
onClose: onNotificationClose
} = useDisclosure();
const notifications: Array<NotificationItem> = useAppSelector(state => state.notificationStore.notifications);
const unreadCount = notifications.filter(e => !e.read).length;
const dispatch = useAppDispatch();
return (
<Box minH="100vh">
<Router>
<SidebarContent onClose={() => onClose} display={{ base: 'none', md: 'block' }} />
<Drawer
autoFocus={false}
isOpen={isOpen}
placement="left"
onClose={onClose}
returnFocusOnClose={false}
onOverlayClick={onClose}
size="full"
>
<DrawerContent>
<SidebarContent onClose={onClose} />
</DrawerContent>
</Drawer>
{/* mobilenav */}
<MobileNav onOpen={onOpen} onNotificationOpen={onNotificationOpen} unreadCount={unreadCount} />
<Box ml={{ base: 0, md: 60 }} p="2">
<Routes>
<Route path="/settings" element={<SettingsLayout />} />
<Route path="/logs" element={<LogsLayout />} />
<Route path="/contacts" element={<ContactsLayout />} />
<Route path="/fcm" element={<FcmLayout />} />
<Route path="/devices" element={<DevicesLayout />} />
<Route path="/webhooks" element={<ApiLayout />} />
<Route path="/guides" element={<GuidesLayout />} />
<Route path="/" element={<HomeLayout />} />
</Routes>
</Box>
</Router>
<Drawer onClose={() => closeNotification(onNotificationClose, dispatch)} isOpen={isNotificationsOpen} size="lg">
<DrawerOverlay />
<DrawerContent>
<DrawerHeader>Notifications / Alerts ({unreadCount})</DrawerHeader>
<DrawerBody>
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"mr={5}
mb={4}
>
Manage
</MenuButton>
<MenuList>
<MenuItem icon={<FiTrash />} onClick={() => {
dispatch(clearAlerts());
}}>
Clear Alerts
</MenuItem>
<MenuItem icon={<BsCheckAll />} onClick={() => {
dispatch(readAll());
}}>
Mark All as Read
</MenuItem>
</MenuList>
</Menu>
<NotificationsTable notifications={notifications} />
</DrawerBody>
</DrawerContent>
</Drawer>
</Box>
);
}
Example #5
Source File: Header.tsx From ledokku with MIT License | 5 votes |
Header = () => {
const { user, logout } = useAuth();
return (
<nav>
<Container maxW="5xl">
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
h={16}
>
<Box display="flex" alignItems="center">
<Heading as="h3" fontSize="medium">
<Link to="/">Ledokku</Link>
</Heading>
</Box>
<div>
<Menu placement="bottom-end">
<MenuButton>
{user?.avatarUrl && (
<Image
h={8}
w={8}
borderRadius="full"
src={user.avatarUrl}
alt="Avatar"
/>
)}
</MenuButton>
<MenuList fontSize="sm" color="gray.700">
<MenuItem as={Link} to="/dashboard">
Dashboard
</MenuItem>
<MenuDivider />
<MenuItem
as="a"
href="https://github.com/ledokku/ledokku"
target="_blank"
rel="noopener noreferrer"
>
Github
</MenuItem>
<MenuDivider />
<MenuItem onClick={() => logout()}>Logout</MenuItem>
</MenuList>
</Menu>
</div>
</Box>
</Container>
</nav>
);
}
Example #6
Source File: HeaderMenu.tsx From openchakra with MIT License | 5 votes |
HeaderMenu = () => {
return (
<Menu placement="bottom">
<CustomMenuButton
rightIcon={<ChevronDownIcon path="" />}
size="xs"
variant="ghost"
colorScheme="gray"
>
Editor
</CustomMenuButton>
<Portal>
<LightMode>
<MenuList bg="white" zIndex={999}>
{process.env.NEXT_PUBLIC_IS_V1 && (
<MenuItemLink isExternal href="https://v0.openchakra.app">
<Box mr={2} as={GoArchive} />
Chakra v0 Editor
</MenuItemLink>
)}
<ExportMenuItem />
<ImportMenuItem />
<MenuDivider />
<MenuItemLink
isExternal
href="https://chakra-ui.com/getting-started"
>
<Box mr={2} as={GoRepo} />
Chakra UI Docs
</MenuItemLink>
<MenuItemLink href="https://github.com/premieroctet/openchakra/issues">
<Box mr={2} as={FaBomb} />
Report issue
</MenuItemLink>
</MenuList>
</LightMode>
</Portal>
</Menu>
)
}
Example #7
Source File: Navbar.tsx From coindrop with GNU General Public License v3.0 | 5 votes |
UserMenu = () => {
const { colorMode, toggleColorMode } = useColorMode();
const { logout } = useUser();
return (
<Menu placement="bottom-end">
<MenuButton as={Button} variant="ghost">
<HamburgerMenuIcon />
</MenuButton>
<MenuList>
<NextLink href="/account">
<MenuItem>
<Flex
align="center"
>
<SettingsIcon mr={2} />
My Account
</Flex>
</MenuItem>
</NextLink>
{/* This is an external link to ensure Ecwid scripts run on page changes */}
{/* Should figure out a way to trigger the scripts manually within /shop */}
{/* <Link href="/shop" style={{textDecoration: "none"}}>
<MenuItem>
<Flex
align="center"
>
<Icon mr={2} as={AiOutlineShopping} />
Shop
</Flex>
</MenuItem>
</Link> */}
<MenuItem
onClick={toggleColorMode}
>
<Flex
align="center"
>
{colorMode === 'dark' ? <SunIcon mr={2} /> : <MoonIcon mr={2} />}
{colorMode === 'dark' ? 'Light mode' : 'Dark mode'}
</Flex>
</MenuItem>
<MenuItem
onClick={() => {
logout();
}}
>
<Flex
align="center"
>
<LogoutIcon mr={2} />
Log out
</Flex>
</MenuItem>
</MenuList>
</Menu>
);
}
Example #8
Source File: ApiLayout.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
ApiLayout = (): JSX.Element => {
const dialogRef = useRef(null);
const [dialogOpen, setDialogOpen] = useBoolean();
const webhooks = useAppSelector(state => state.webhookStore.webhooks);
return (
<Box p={3} borderRadius={10}>
<Flex flexDirection="column">
<Stack direction='column' p={5}>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Text fontSize='2xl'>API</Text>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Information</PopoverHeader>
<PopoverBody>
<Text>
Learn how you can interact with the API to automate and orchestrate iMessage-related
actions. Our REST API gives you access to the underlying iMessage API in a
more succinct and easy to digest way. We also offer webhooks so you can receive
callbacks from the server.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Divider orientation='horizontal' />
<Text>
BlueBubbles offers a high-level REST API to interact with the server, as well as iMessage itself.
With the API, you'll be able to send messages, fetch messages, filter chats, and more! To see what
else you can do in the API, please see the documentation below:
</Text>
<Spacer />
<LinkBox as='article' maxW='sm' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
<Text color='gray'>
https://documenter.getpostman.com
</Text>
<Heading size='sm' mt={2}>
<LinkOverlay href='https://documenter.getpostman.com/view/765844/UV5RnfwM' target='_blank'>
Click to view API documentation
</LinkOverlay>
</Heading>
</LinkBox>
</Stack>
<Stack direction='column' p={5}>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Text fontSize='2xl'>Webhooks</Text>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Information</PopoverHeader>
<PopoverBody>
<Text>
Any webhooks registered here will receive a POST request whenever an iMessage event
occurs. The body of the POST request will be a JSON payload containing the type of
event and the event data.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Divider orientation='horizontal' />
<Spacer />
<Box>
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"
>
Manage
</MenuButton>
<MenuList>
<MenuItem icon={<AiOutlinePlus />} onClick={setDialogOpen.on}>
Add Webhook
</MenuItem>
</MenuList>
</Menu>
</Box>
<Spacer />
<WebhooksTable webhooks={webhooks} />
</Stack>
</Flex>
<AddWebhookDialog
modalRef={dialogRef}
isOpen={dialogOpen}
onClose={() => setDialogOpen.off()}
/>
</Box>
);
}
Example #9
Source File: ContactsLayout.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
ContactsLayout = (): JSX.Element => {
const [search, setSearch] = useState('' as string);
const [isLoading, setIsLoading] = useBoolean(true);
const [contacts, setContacts] = useState([] as any[]);
const [permission, setPermission] = useState((): string | null => {
return null;
});
const dialogRef = useRef(null);
const inputFile = useRef(null);
const [dialogOpen, setDialogOpen] = useBoolean();
const alertRef = useRef(null);
const [requiresConfirmation, confirm] = useState((): string | null => {
return null;
});
let filteredContacts = contacts;
if (search && search.length > 0) {
filteredContacts = filteredContacts.filter((c) => buildIdentifier(c).includes(search.toLowerCase()));
}
const {
currentPage,
setCurrentPage,
pagesCount,
pages
} = usePagination({
pagesCount: Math.ceil(filteredContacts.length / perPage),
initialState: { currentPage: 1 },
});
const refreshPermissionStatus = async (): Promise<void> => {
setPermission(null);
await waitMs(500);
ipcRenderer.invoke('contact-permission-status').then((status: string) => {
setPermission(status);
}).catch(() => {
setPermission('Unknown');
});
};
const requestContactPermission = async (): Promise<void> => {
setPermission(null);
ipcRenderer.invoke('request-contact-permission').then((status: string) => {
setPermission(status);
}).catch(() => {
setPermission('Unknown');
});
};
const loadContacts = (showToast = false) => {
ipcRenderer.invoke('get-contacts').then((contactList: any[]) => {
setContacts(contactList.map((e: any) => {
// Patch the ID as a string
e.id = String(e.id);
return e;
}));
setIsLoading.off();
}).catch(() => {
setIsLoading.off();
});
if (showToast) {
showSuccessToast({
id: 'contacts',
description: 'Successfully refreshed Contacts!'
});
}
};
useEffect(() => {
loadContacts();
refreshPermissionStatus();
}, []);
const getEmptyContent = () => {
const wrap = (child: JSX.Element) => {
return (
<section style={{marginTop: 20}}>
{child}
</section>
);
};
if (isLoading) {
return wrap(<CircularProgress isIndeterminate />);
}
if (contacts.length === 0) {
return wrap(<Text fontSize="md">BlueBubbles found no contacts in your Mac's Address Book!</Text>);
}
return null;
};
const filterContacts = () => {
return filteredContacts.slice((currentPage - 1) * perPage, currentPage * perPage);
};
const onCreate = async (contact: ContactItem) => {
const newContact = await createContact(
contact.firstName,
contact.lastName,
{
emails: contact.emails.map((e: NodeJS.Dict<any>) => e.address),
phoneNumbers: contact.phoneNumbers.map((e: NodeJS.Dict<any>) => e.address)
}
);
if (newContact) {
// Patch the contact using a string ID & source type
newContact.id = String(newContact.id);
newContact.sourceType = 'db';
// Patch the addresses
(newContact as any).phoneNumbers = (newContact as any).addresses.filter((e: any) => e.type === 'phone');
(newContact as any).emails = (newContact as any).addresses.filter((e: any) => e.type === 'email');
setContacts([newContact, ...contacts]);
}
};
const onUpdate = async (contact: NodeJS.Dict<any>) => {
const cId = typeof(contact.id) === 'string' ? Number.parseInt(contact.id) : contact.id as number;
const newContact = await updateContact(
cId,
{
firstName: contact.firstName,
lastName: contact.lastName,
displayName: contact.displayName
}
);
const copiedContacts = [...contacts];
let updated = false;
for (let i = 0; i < copiedContacts.length; i++) {
if (copiedContacts[i].id === String(cId)) {
copiedContacts[i].firstName = newContact.firstName;
copiedContacts[i].lastName = newContact.lastName;
copiedContacts[i].displayName = newContact.displayName;
updated = true;
}
}
if (updated) {
setContacts(copiedContacts);
}
};
const onDelete = async (contactId: number | string) => {
await deleteContact(typeof(contactId) === 'string' ? Number.parseInt(contactId as string) : contactId);
setContacts(contacts.filter((e: ContactItem) => {
return e.id !== String(contactId);
}));
};
const onAddAddress = async (contactId: number | string, address: string) => {
const cId = typeof(contactId) === 'string' ? Number.parseInt(contactId as string) : contactId;
const addr = await addAddressToContact(cId, address, address.includes('@') ? 'email' : 'phone');
if (addr) {
setContacts(contacts.map((e: ContactItem) => {
if (e.id !== String(contactId)) return e;
if (address.includes('@')) {
e.emails = [...e.emails, addr];
} else {
e.phoneNumbers = [...e.phoneNumbers, addr];
}
return e;
}));
}
};
const onDeleteAddress = async (contactAddressId: number) => {
await deleteContactAddress(contactAddressId);
setContacts(contacts.map((e: ContactItem) => {
e.emails = e.emails.filter((e: ContactAddress) => e.id !== contactAddressId);
e.phoneNumbers = e.phoneNumbers.filter((e: ContactAddress) => e.id !== contactAddressId);
return e;
}));
};
const clearLocalContacts = async () => {
// Delete the contacts, then filter out the DB items
await deleteLocalContacts();
setContacts(contacts.filter(e => e.sourceType !== 'db'));
};
const confirmationActions: ConfirmationItems = {
clearLocalContacts: {
message: (
'Are you sure you want to clear/delete all local Contacts?<br /><br />' +
'This will remove any Contacts added manually, via the API, or via the import process'
),
func: clearLocalContacts
}
};
return (
<Box p={3} borderRadius={10}>
<Stack direction='column' p={5}>
<Text fontSize='2xl'>Controls</Text>
<Divider orientation='horizontal' />
<Box>
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"mr={5}
>
Manage
</MenuButton>
<MenuList>
<MenuItem icon={<BsPersonPlus />} onClick={() => setDialogOpen.on()}>
Add Contact
</MenuItem>
<MenuItem icon={<BiRefresh />} onClick={() => loadContacts(true)}>
Refresh Contacts
</MenuItem>
<MenuItem
icon={<BiImport />}
onClick={() => {
if (inputFile && inputFile.current) {
(inputFile.current as HTMLElement).click();
}
}}
>
Import VCF
<input
type='file'
id='file'
ref={inputFile}
accept=".vcf"
style={{display: 'none'}}
onChange={(e) => {
const files = e?.target?.files ?? [];
for (const i of files) {
ipcRenderer.invoke('import-vcf', i.webkitRelativePath);
}
}}
/>
</MenuItem>
<MenuDivider />
<MenuItem icon={<FiTrash />} onClick={() => confirm('clearLocalContacts')}>
Clear Local Contacts
</MenuItem>
</MenuList>
</Menu>
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"
mr={5}
>
Permissions
</MenuButton>
<MenuList>
<MenuItem icon={<BiRefresh />} onClick={() => refreshPermissionStatus()}>
Refresh Permission Status
</MenuItem>
{(permission !== null && permission !== 'Authorized') ? (
<MenuItem icon={<BsUnlockFill />} onClick={() => requestContactPermission()}>
Request Permission
</MenuItem>
) : null}
</MenuList>
</Menu>
<Text as="span" verticalAlign="middle">
Status: <Text as="span" color={getPermissionColor(permission)}>
{permission ? permission : 'Checking...'}
</Text>
</Text>
</Box>
</Stack>
<Stack direction='column' p={5}>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Text fontSize='2xl'>Contacts ({filteredContacts.length})</Text>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Information</PopoverHeader>
<PopoverBody>
<Text>
Here are the contacts on your macOS device that BlueBubbles knows about,
and will serve to any clients that want to know about them. These include
contacts from this Mac's Address Book, as well as contacts from uploads/imports
or manual entry.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Divider orientation='horizontal' />
<Flex flexDirection='row' justifyContent='flex-end' alignItems='center' pt={3}>
<InputGroup width="xxs">
<InputLeftElement pointerEvents='none'>
<AiOutlineSearch color='gray.300' />
</InputLeftElement>
<Input
placeholder='Search Contacts'
onChange={(e) => {
if (currentPage > 1) {
setCurrentPage(1);
}
setSearch(e.target.value);
}}
value={search}
/>
</InputGroup>
</Flex>
<Flex justifyContent="center" alignItems="center">
{getEmptyContent()}
</Flex>
{(contacts.length > 0) ? (
<ContactsTable
contacts={filterContacts()}
onCreate={onCreate}
onDelete={onDelete}
onUpdate={onUpdate}
onAddressAdd={onAddAddress}
onAddressDelete={onDeleteAddress}
/>
) : null}
<Pagination
pagesCount={pagesCount}
currentPage={currentPage}
onPageChange={setCurrentPage}
>
<PaginationContainer
align="center"
justify="space-between"
w="full"
pt={2}
>
<PaginationPrevious minWidth={'75px'}>Previous</PaginationPrevious>
<Box ml={1}></Box>
<PaginationPageGroup flexWrap="wrap" justifyContent="center">
{pages.map((page: number) => (
<PaginationPage
key={`pagination_page_${page}`}
page={page}
my={1}
px={3}
fontSize={14}
/>
))}
</PaginationPageGroup>
<Box ml={1}></Box>
<PaginationNext minWidth={'50px'}>Next</PaginationNext>
</PaginationContainer>
</Pagination>
</Stack>
<ContactDialog
modalRef={dialogRef}
isOpen={dialogOpen}
onCreate={onCreate}
onDelete={onDelete}
onAddressAdd={onAddAddress}
onAddressDelete={onDeleteAddress}
onClose={() => setDialogOpen.off()}
/>
<ConfirmationDialog
modalRef={alertRef}
onClose={() => confirm(null)}
body={confirmationActions[requiresConfirmation as string]?.message}
onAccept={() => {
confirmationActions[requiresConfirmation as string].func();
}}
isOpen={requiresConfirmation !== null}
/>
</Box>
);
}
Example #10
Source File: DevicesLayout.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
DevicesLayout = (): JSX.Element => {
const [requiresConfirmation, confirm] = useState((): string | null => {
return null;
});
const alertRef = useRef(null);
const devices = useAppSelector(state => state.deviceStore.devices);
const dispatch = useAppDispatch();
useEffect(() => {
refreshDevices(false);
// Refresh devices every 60 seconds
const refresher = setInterval(() => {
refreshDevices(false);
}, 60000);
// Return a function to clear the interval on unmount
return () => clearInterval(refresher);
}, []);
return (
<Box p={3} borderRadius={10}>
<Stack direction='column' p={5}>
<Text fontSize='2xl'>Controls</Text>
<Divider orientation='horizontal' />
<Box>
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"mr={5}
>
Manage
</MenuButton>
<MenuList>
<MenuItem icon={<BiRefresh />} onClick={() => refreshDevices()}>
Refresh Devices
</MenuItem>
<MenuItem icon={<FiTrash />} onClick={() => confirm('clearDevices')}>
Clear Devices
</MenuItem>
</MenuList>
</Menu>
</Box>
</Stack>
<Stack direction='column' p={5}>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Text fontSize='2xl'>Devices</Text>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Information</PopoverHeader>
<PopoverBody>
<Text>
Here is where you'll find any devices that are registered with your BlueBubbles
server to receive notifications and other messages. If you do not see your device
here after setting up your app, please contact us for assistance.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Divider orientation='horizontal' />
{(devices.length === 0) ? (
<Flex justifyContent="center" alignItems="center">
<section style={{marginTop: 20}}>
<Text fontSize="md">You have no devices registered with the server!</Text>
</section>
</Flex>
) : null}
{(devices.length > 0) ? (
<DevicesTable devices={devices} />
) : null}
</Stack>
<ConfirmationDialog
modalRef={alertRef}
onClose={() => confirm(null)}
body={confirmationActions[requiresConfirmation as string]?.message}
onAccept={() => {
if (hasKey(confirmationActions, requiresConfirmation as string)) {
if (confirmationActions[requiresConfirmation as string].shouldDispatch ?? false) {
dispatch(confirmationActions[requiresConfirmation as string].func() as AnyAction);
} else {
confirmationActions[requiresConfirmation as string].func();
}
}
}}
isOpen={requiresConfirmation !== null}
/>
</Box>
);
}
Example #11
Source File: FcmLayout.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
FcmLayout = (): JSX.Element => {
const dispatch = useAppDispatch();
const alertRef = useRef(null);
const confirmationActions: NodeJS.Dict<any> = {
clearConfiguration: {
message: (
'Are you sure you want to clear your FCM Configuration?<br /><br />' +
'Doing so will prevent notifications from being delivered until ' +
'your configuration is re-loaded'
),
func: async () => {
const success = await clearFcmConfiguration();
if (success) {
dispatch(setConfig({ name: 'fcm_client', 'value': null }));
dispatch(setConfig({ name: 'fcm_server', 'value': 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 [requiresConfirmation, setRequiresConfirmation] = useState(null as string | null);
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([]);
};
const confirm = (confirmationType: string | null) => {
setRequiresConfirmation(confirmationType);
};
return (
<Box
p={3}
borderRadius={10}
onDragEnter={(e) => onDragEnter(e)}
onDragLeave={() => onDragLeave()}
onDragOver={(e) => onDragOver(e)}
onDrop={(e) => onDrop(e)}
>
<Stack direction='column' p={5}>
<Text fontSize='2xl'>Controls</Text>
<Divider orientation='horizontal' />
<Flex flexDirection="row" justifyContent="flex-start">
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"
mr={5}
>
Manage
</MenuButton>
<MenuList>
<MenuItem icon={<FiTrash />} onClick={() => confirm('clearConfiguration')}>
Clear Configuration
</MenuItem>
</MenuList>
</Menu>
</Flex>
</Stack>
<Stack direction='column' p={5}>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Text fontSize='2xl'>Configuration</Text>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Information</PopoverHeader>
<PopoverBody>
<Text>
Drag and drop your JSON configuration files from your Google Firebase Console. If you
do not have these configuration files. Please go to
<span style={{ color: baseTheme.colors.brand.primary }}>
<Link href='https://bluebubbles.app/install' color='brand.primary' target='_blank'> Our Website </Link>
</span>
to learn how.
</Text>
<Text>
These configurations enable the BlueBubbles server to send notifications and other
messages to all of the clients via Google FCM. Google Play Services is required
for Android Devices.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Divider orientation='horizontal' />
<Spacer />
<SimpleGrid columns={2} spacing={5}>
<DropZone
text="Drag n' Drop Google Services JSON"
loadedText="Google Services JSON Successfully Loaded!"
isDragging={isDragging}
isLoaded={clientLoaded}
/>
<DropZone
text="Drag n' Drop Admin SDK JSON"
loadedText="Admin SDK JSON Successfully Loaded!"
isDragging={isDragging}
isLoaded={serverLoaded}
/>
</SimpleGrid>
</Stack>
<ErrorDialog
errors={errors}
modalRef={alertRef}
onClose={() => closeAlert()}
isOpen={alertOpen}
/>
<ConfirmationDialog
modalRef={alertRef}
onClose={() => confirm(null)}
body={confirmationActions[requiresConfirmation as string]?.message}
onAccept={() => {
if (hasKey(confirmationActions, requiresConfirmation as string)) {
confirmationActions[requiresConfirmation as string].func();
}
}}
isOpen={requiresConfirmation !== null}
/>
</Box>
);
}
Example #12
Source File: LogsLayout.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
LogsLayout = (): JSX.Element => {
const dispatch = useAppDispatch();
const [requiresConfirmation, confirm] = useState((): string | null => {
return null;
});
const alertRef = useRef(null);
let logs = useAppSelector(state => state.logStore.logs);
const showDebug = useAppSelector(state => state.logStore.debug);
// If we don't want to show debug logs, filter them out
if (!showDebug) {
logs = logs.filter(e => e.type !== 'debug');
}
const toggleDebugMode = (e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setDebug(e.target.checked));
};
return (
<Box p={3} borderRadius={10}>
<Flex flexDirection="column">
<Stack direction='column' p={5}>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Text fontSize='2xl'>Controls</Text>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Information</PopoverHeader>
<PopoverBody>
<Text>
This page will allow you to perform debugging actions on your BlueBubbles server.
As many of you know, software is not perfect, and there will always be edge cases
depending on the environment. These controls allow us to get the information needed, or
take the required actions to solve an issue. It also allows you to "see" into what
the server is doing in the background.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Divider orientation='horizontal' />
<Flex flexDirection="row" justifyContent="flex-start">
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"
mr={5}
>
Manage
</MenuButton>
<MenuList>
<MenuItem icon={<VscDebugRestart />} onClick={() => confirm('restartServices')}>
Restart Services
</MenuItem>
<MenuItem icon={<BsBootstrapReboot />} onClick={() => confirm('fullRestart')}>
Full Restart
</MenuItem>
<MenuItem icon={<FiExternalLink />} onClick={() => openLogLocation()}>
Open Log Location
</MenuItem>
<MenuItem icon={<GoFileSubmodule />} onClick={() => openAppLocation()}>
Open App Location
</MenuItem>
<MenuItem icon={<AiOutlineClear />} onClick={() => clearLogs()}>
Clear Logs
</MenuItem>
</MenuList>
</Menu>
<Menu>
<MenuButton
as={Button}
rightIcon={<BsChevronDown />}
width="12em"
mr={5}
>
Debug Actions
</MenuButton>
<MenuList>
<MenuItem icon={<BsTerminal />} onClick={() => confirm('restartViaTerminal')}>
Restart via Terminal
</MenuItem>
<MenuItem icon={<AiOutlineClear />} onClick={() => confirm('clearEventCache')}>
Clear Event Cache
</MenuItem>
</MenuList>
</Menu>
</Flex>
</Stack>
<Stack direction='column' p={5}>
<Text fontSize='2xl'>Debug Logs</Text>
<Divider orientation='horizontal' />
<Spacer />
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Checkbox onChange={(e) => toggleDebugMode(e)} isChecked={showDebug}>Show Debug Logs</Checkbox>
<Popover trigger='hover'>
<PopoverTrigger>
<Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
<AiOutlineInfoCircle />
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Inforamation</PopoverHeader>
<PopoverBody>
<Text>
Enabling this option will show DEBUG level logs. Leaving
this disabled will only INFO, WARN, and ERROR level logs.
</Text>
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
<Spacer />
<LogsTable logs={logs} />
</Stack>
</Flex>
<ConfirmationDialog
modalRef={alertRef}
onClose={() => confirm(null)}
body={confirmationActions[requiresConfirmation as string]?.message}
onAccept={() => {
if (hasKey(confirmationActions, requiresConfirmation as string)) {
if (confirmationActions[requiresConfirmation as string].shouldDispatch ?? false) {
dispatch(confirmationActions[requiresConfirmation as string].func() as AnyAction);
} else {
confirmationActions[requiresConfirmation as string].func();
}
}
}}
isOpen={requiresConfirmation !== null}
/>
</Box>
);
}
Example #13
Source File: top-nav.tsx From notebook with MIT License | 4 votes |
TopNav: React.SFC<TopNavProps> = ({ handleNoteCreate }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<Flex mb={"30px"} align="center">
<HStack>
<Box p="2" as={Link} to="/">
<motion.div whileHover={{ scale: 1.1 }}>
<Heading
as="h1"
size="xl"
bgGradient="linear(to-l, #7928CA,#FF0080)"
bgClip="text"
_focus={{ boxShadow: "none", outline: "none" }}
_hover={{
textDecoration: "none",
bgGradient: "linear(to-r, red.500, yellow.500)"
}}
>
Notebook App
</Heading>
</motion.div>
</Box>
</HStack>
<Spacer />
<Box>
<HStack>
<HStack d={["none", "none", "block"]}>
<Button
leftIcon={<AddIcon />}
bgGradient="linear(to-l, #7928CA,#FF0080)"
_hover={{ bgGradient: "linear(to-r, red.500, yellow.500)" }}
variant="solid"
size="sm"
onClick={onOpen}
>
Add new note
</Button>
<Button
leftIcon={<ArrowRightIcon />}
bgGradient="linear(to-l, #7928CA,#FF0080)"
_hover={{ bgGradient: "linear(to-r, red.500, yellow.500)" }}
variant="solid"
size="sm"
as={Link}
to="/projects"
>
Open source
</Button>
</HStack>
<Box d={["block", "block", "none"]}>
<Menu>
<MenuButton
as={IconButton}
aria-label="Options"
icon={<HamburgerIcon />}
transition="all 0.2s"
size="md"
variant="outline"
_hover={{ bg: "gray.400" }}
_focus={{ boxShadow: "outline" }}
/>
<MenuList fontSize="sm" zIndex={5}>
<MenuItem icon={<AddIcon />} onClick={onOpen}>
{" "}
<Text textShadow="1px 1px #9c1786">Add new note</Text>
</MenuItem>
<MenuDivider />
<MenuItem icon={<ArrowRightIcon />} as={Link} to="/projects">
{" "}
<Text textShadow="1px 1px #9c1786">
Open source repositories
</Text>
</MenuItem>
</MenuList>
</Menu>
</Box>
<ColorModeSwitcher justifySelf="flex-end" />
</HStack>
</Box>
</Flex>
<NoteForm
isOpen={isOpen}
onClose={onClose}
handleNoteCreate={handleNoteCreate}
/>
</>
);
}
Example #14
Source File: top-nav.tsx From portfolio with MIT License | 4 votes |
export default function TopNav() {
const { isOpen, onOpen, onClose } = useDisclosure();
const menuProps = {
bg: useColorModeValue("gray.200", "gray.900"),
color: useColorModeValue("blue.500", "blue.200")
};
return (
<>
<Box bg={useColorModeValue("white", "gray.700")} px={4} boxShadow={"lg"}>
<Flex
h={16}
alignItems={"center"}
justifyContent={"space-between"}
w={["90%", "85%", "80%"]}
maxW={800}
mx="auto"
>
<IconButton
size={"md"}
icon={isOpen ? <AiOutlineClose /> : <GiHamburgerMenu />}
aria-label={"Open Menu"}
display={["inherit", "inherit", "none"]}
onClick={isOpen ? onClose : onOpen}
/>
<HStack spacing={8} alignItems={"center"}>
<Box>
<Avatar
as={Link}
size={"sm"}
href={"/portfolio"}
src={UserIcon}
// src={"https://avatars2.githubusercontent.com/u/37842853?v=4"}
/>
</Box>
<HStack
as={"nav"}
spacing={4}
display={{ base: "none", md: "flex" }}
>
{webLinks.map((link, index) => (
<NavLink
key={index}
name={link.name}
path={link.path}
onClose={onClose}
/>
))}
<Menu isLazy>
<MenuButton
as={Button}
variant="ghost"
size="sm"
px={2}
py={1.5}
fontSize={"1em"}
rounded={"md"}
height={"auto "}
_hover={menuProps}
_expanded={menuProps}
_focus={{ boxShadow: "outline" }}
rightIcon={<BiChevronDown size={18} />}
>
Links
</MenuButton>
<MenuList zIndex={5}>
<Link as={RouterNavLink} to="/tech-stack">
<MenuItem>
<HStack>
<Icon
as={AiTwotoneThunderbolt}
size={18}
color={useColorModeValue("blue.500", "blue.200")}
/>
<Text>Tech Stack</Text>
</HStack>
</MenuItem>
</Link>
<Link as={RouterNavLink} to="/open-source">
<MenuItem>
<HStack>
<Icon
as={BsBook}
size={18}
color={useColorModeValue("blue.500", "blue.200")}
/>
<Text>Open Source</Text>
</HStack>
</MenuItem>
</Link>
<Link as={RouterNavLink} to="/story-timeline">
<MenuItem>
<HStack>
<Icon
as={MdTimeline}
size={18}
color={useColorModeValue("blue.500", "blue.200")}
/>
<Text>Developer Story</Text>
</HStack>
</MenuItem>
</Link>
<Link as={RouterNavLink} to="/achievements">
<MenuItem>
<HStack>
<Icon
as={BsCheckCircle}
size={18}
color={useColorModeValue("blue.500", "blue.200")}
/>
<Text>Achievements</Text>
</HStack>
</MenuItem>
</Link>
</MenuList>
</Menu>
</HStack>
</HStack>
<Flex alignItems={"center"}>
<IconButton
as={Link}
href={"https://github.com/MA-Ahmad"}
size={"md"}
icon={<FaGithub />}
aria-label={"Github account"}
bg={useColorModeValue("white", "gray.700")}
_hover={{
textDecoration: "none",
bg: useColorModeValue("gray.200", "gray.900")
}}
/>
<ColorModeSwitcher justifySelf="flex-end" />
</Flex>
</Flex>
{isOpen ? (
<Box
pb={4}
w={["100%", "100%", "80%"]}
maxW={800}
display={["inherit", "inherit", "none"]}
>
<Stack as={"nav"} spacing={4}>
{mobileLinks.map((link, index) => (
<NavLink
index={index}
name={link.name}
path={link.path}
onClose={onClose}
/>
))}
</Stack>
</Box>
) : null}
</Box>
</>
);
}
Example #15
Source File: Header.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
Header = ({
isAuthed,
isPool,
isFuse,
padding,
}: {
isAuthed: boolean;
isFuse?: boolean;
isPool?: boolean;
padding?: boolean;
}) => {
const { t } = useTranslation();
return (
<Row
color="#FFFFFF"
px={padding ? 4 : 0}
height="38px"
my={4}
mainAxisAlignment="space-between"
crossAxisAlignment="center"
overflowX="visible"
overflowY="visible"
width="100%"
>
{isAuthed ? (
isPool ? (
<AnimatedPoolLogo />
) : isFuse ? (
<AnimatedFuseSmallLogo />
) : (
<AnimatedSmallLogo />
)
) : isPool ? (
<PoolLogo />
) : isFuse ? (
<FuseSmallLogo />
) : (
<AnimatedSmallLogo />
)}
<Row
mx={4}
expand
mainAxisAlignment={{ md: "space-around", base: "space-between" }}
crossAxisAlignment="flex-start"
overflowX="auto"
overflowY="hidden"
transform="translate(0px, 7px)"
>
<HeaderLink name={t("Overview")} route="/" />
<HeaderLink ml={4} name={t("Fuse")} route="/fuse" />
<PoolsLink ml={3} />
<HeaderLink ml={4} name={t("Pool2")} route="/pool2" />
<HeaderLink ml={4} name={t("Tranches")} route="/tranches" />
<Box ml={4}>
<Menu autoSelect={false} placement="bottom">
<MenuButton>
<SubMenuText text={t("Governance")} />
</MenuButton>
<Portal>
<MenuList {...DASHBOARD_BOX_PROPS} color="#FFF" minWidth="110px">
<SubMenuItem
name={t("Snapshot")}
link="https://vote.rari.capital/"
/>
<SubMenuItem
name={t("Forums")}
link="https://forums.rari.capital/"
/>
</MenuList>
</Portal>
</Menu>
</Box>
<UtilsLink ml={4} isAuthed={isAuthed} />
</Row>
<AccountButton />
</Row>
);
}