Width, again
This commit is contained in:
		
							parent
							
								
									ca5d736a71
								
							
						
					
					
						commit
						c87549e71a
					
				
					 20 changed files with 194 additions and 37 deletions
				
			
		|  | @ -211,7 +211,10 @@ const ChangePasswordDialog = (props) => { | |||
|       </DialogContent> | ||||
|       <DialogFooter status={error}> | ||||
|         <Button onClick={props.onClose}>{t("common_cancel")}</Button> | ||||
|         <Button onClick={handleDialogSubmit} disabled={newPassword.length === 0 || currentPassword.length === 0 || newPassword !== confirmPassword}> | ||||
|         <Button | ||||
|           onClick={handleDialogSubmit} | ||||
|           disabled={newPassword.length === 0 || currentPassword.length === 0 || newPassword !== confirmPassword} | ||||
|         > | ||||
|           {t("account_basics_password_dialog_button_submit")} | ||||
|         </Button> | ||||
|       </DialogFooter> | ||||
|  | @ -288,7 +291,13 @@ const AccountType = () => { | |||
|           </Tooltip> | ||||
|         )} | ||||
|         {config.enable_payments && account.role === Role.USER && !account.billing?.subscription && ( | ||||
|           <Button variant="outlined" size="small" startIcon={<CelebrationIcon sx={{ color: "#55b86e" }} />} onClick={handleUpgradeClick} sx={{ ml: 1 }}> | ||||
|           <Button | ||||
|             variant="outlined" | ||||
|             size="small" | ||||
|             startIcon={<CelebrationIcon sx={{ color: "#55b86e" }} />} | ||||
|             onClick={handleUpgradeClick} | ||||
|             sx={{ ml: 1 }} | ||||
|           > | ||||
|             {t("account_basics_tier_upgrade_button")} | ||||
|           </Button> | ||||
|         )} | ||||
|  | @ -303,7 +312,11 @@ const AccountType = () => { | |||
|           </Button> | ||||
|         )} | ||||
|         {config.enable_payments && ( | ||||
|           <UpgradeDialog key={`upgradeDialogFromAccount${upgradeDialogKey}`} open={upgradeDialogOpen} onCancel={() => setUpgradeDialogOpen(false)} /> | ||||
|           <UpgradeDialog | ||||
|             key={`upgradeDialogFromAccount${upgradeDialogKey}`} | ||||
|             open={upgradeDialogOpen} | ||||
|             onCancel={() => setUpgradeDialogOpen(false)} | ||||
|           /> | ||||
|         )} | ||||
|       </div> | ||||
|       {account.billing?.status === SubscriptionStatus.PAST_DUE && ( | ||||
|  | @ -574,7 +587,11 @@ const Stats = () => { | |||
|             </div> | ||||
|             <LinearProgress | ||||
|               variant="determinate" | ||||
|               value={account.role === Role.USER && account.limits.reservations > 0 ? normalize(account.stats.reservations, account.limits.reservations) : 100} | ||||
|               value={ | ||||
|                 account.role === Role.USER && account.limits.reservations > 0 | ||||
|                   ? normalize(account.stats.reservations, account.limits.reservations) | ||||
|                   : 100 | ||||
|               } | ||||
|             /> | ||||
|           </Pref> | ||||
|         )} | ||||
|  | @ -602,7 +619,10 @@ const Stats = () => { | |||
|                 : t("account_usage_unlimited")} | ||||
|             </Typography> | ||||
|           </div> | ||||
|           <LinearProgress variant="determinate" value={account.role === Role.USER ? normalize(account.stats.messages, account.limits.messages) : 100} /> | ||||
|           <LinearProgress | ||||
|             variant="determinate" | ||||
|             value={account.role === Role.USER ? normalize(account.stats.messages, account.limits.messages) : 100} | ||||
|           /> | ||||
|         </Pref> | ||||
|         {config.enable_emails && ( | ||||
|           <Pref | ||||
|  | @ -629,7 +649,10 @@ const Stats = () => { | |||
|                   : t("account_usage_unlimited")} | ||||
|               </Typography> | ||||
|             </div> | ||||
|             <LinearProgress variant="determinate" value={account.role === Role.USER ? normalize(account.stats.emails, account.limits.emails) : 100} /> | ||||
|             <LinearProgress | ||||
|               variant="determinate" | ||||
|               value={account.role === Role.USER ? normalize(account.stats.emails, account.limits.emails) : 100} | ||||
|             /> | ||||
|           </Pref> | ||||
|         )} | ||||
|         {config.enable_calls && (account.role === Role.ADMIN || account.limits.calls > 0) && ( | ||||
|  | @ -833,7 +856,12 @@ const TokensTable = (props) => { | |||
|       <TableBody> | ||||
|         {tokens.map((token) => ( | ||||
|           <TableRow key={token.token} sx={{ "&:last-child td, &:last-child th": { border: 0 } }}> | ||||
|             <TableCell component="th" scope="row" sx={{ paddingLeft: 0, whiteSpace: "nowrap" }} aria-label={t("account_tokens_table_token_header")}> | ||||
|             <TableCell | ||||
|               component="th" | ||||
|               scope="row" | ||||
|               sx={{ paddingLeft: 0, whiteSpace: "nowrap" }} | ||||
|               aria-label={t("account_tokens_table_token_header")} | ||||
|             > | ||||
|               <span> | ||||
|                 <span style={{ fontFamily: "Monospace", fontSize: "0.9rem" }}>{token.token.slice(0, 12)}</span> | ||||
|                 ... | ||||
|  | @ -893,7 +921,12 @@ const TokensTable = (props) => { | |||
|         ))} | ||||
|       </TableBody> | ||||
|       <Portal> | ||||
|         <Snackbar open={snackOpen} autoHideDuration={3000} onClose={() => setSnackOpen(false)} message={t("account_tokens_table_copied_to_clipboard")} /> | ||||
|         <Snackbar | ||||
|           open={snackOpen} | ||||
|           autoHideDuration={3000} | ||||
|           onClose={() => setSnackOpen(false)} | ||||
|           message={t("account_tokens_table_copied_to_clipboard")} | ||||
|         /> | ||||
|       </Portal> | ||||
|       <TokenDialog key={`tokenDialogEdit${upsertDialogKey}`} open={upsertDialogOpen} token={selectedToken} onClose={handleDialogClose} /> | ||||
|       <TokenDeleteDialog open={deleteDialogOpen} token={selectedToken} onClose={handleDialogClose} /> | ||||
|  | @ -958,7 +991,9 @@ const TokenDialog = (props) => { | |||
|       </DialogContent> | ||||
|       <DialogFooter status={error}> | ||||
|         <Button onClick={props.onClose}>{t("account_tokens_dialog_button_cancel")}</Button> | ||||
|         <Button onClick={handleSubmit}>{editMode ? t("account_tokens_dialog_button_update") : t("account_tokens_dialog_button_create")}</Button> | ||||
|         <Button onClick={handleSubmit}> | ||||
|           {editMode ? t("account_tokens_dialog_button_update") : t("account_tokens_dialog_button_create")} | ||||
|         </Button> | ||||
|       </DialogFooter> | ||||
|     </Dialog> | ||||
|   ); | ||||
|  |  | |||
|  | @ -98,7 +98,13 @@ const SettingsIcons = (props) => { | |||
|       <IconButton color="inherit" size="large" edge="end" onClick={handleToggleMute} aria-label={t("action_bar_toggle_mute")}> | ||||
|         {subscription.mutedUntil ? <NotificationsOffIcon /> : <NotificationsIcon />} | ||||
|       </IconButton> | ||||
|       <IconButton color="inherit" size="large" edge="end" onClick={(ev) => setAnchorEl(ev.currentTarget)} aria-label={t("action_bar_toggle_action_menu")}> | ||||
|       <IconButton | ||||
|         color="inherit" | ||||
|         size="large" | ||||
|         edge="end" | ||||
|         onClick={(ev) => setAnchorEl(ev.currentTarget)} | ||||
|         aria-label={t("action_bar_toggle_action_menu")} | ||||
|       > | ||||
|         <MoreVertIcon /> | ||||
|       </IconButton> | ||||
|       <SubscriptionPopup subscription={subscription} anchor={anchorEl} placement="right" onClose={() => setAnchorEl(null)} /> | ||||
|  |  | |||
|  | @ -99,7 +99,13 @@ const EmojiPicker = (props) => { | |||
|                 }} | ||||
|               > | ||||
|                 {Object.keys(emojisByCategory).map((category) => ( | ||||
|                   <Category key={category} title={category} emojis={emojisByCategory[category]} search={searchFields} onPick={props.onEmojiPick} /> | ||||
|                   <Category | ||||
|                     key={category} | ||||
|                     title={category} | ||||
|                     emojis={emojisByCategory[category]} | ||||
|                     search={searchFields} | ||||
|                     onPick={props.onEmojiPick} | ||||
|                   /> | ||||
|                 ))} | ||||
|               </Box> | ||||
|             </Box> | ||||
|  |  | |||
|  | @ -46,7 +46,9 @@ class ErrorBoundaryImpl extends React.Component { | |||
|     // Fetch additional info and a better stack trace
 | ||||
|     StackTrace.fromError(error).then((stack) => { | ||||
|       console.error("[ErrorBoundary] Stacktrace fetched", stack); | ||||
|       const niceStack = `${error.toString()}\n` + stack.map((el) => `  at ${el.functionName} (${el.fileName}:${el.columnNumber}:${el.lineNumber})`).join("\n"); | ||||
|       const niceStack = | ||||
|         `${error.toString()}\n` + | ||||
|         stack.map((el) => `  at ${el.functionName} (${el.fileName}:${el.columnNumber}:${el.lineNumber})`).join("\n"); | ||||
|       this.setState({ niceStack }); | ||||
|     }); | ||||
|   } | ||||
|  |  | |||
|  | @ -29,7 +29,9 @@ const Messaging = (props) => { | |||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       {subscription && <MessageBar subscription={subscription} message={message} onMessageChange={setMessage} onOpenDialogClick={handleOpenDialogClick} />} | ||||
|       {subscription && ( | ||||
|         <MessageBar subscription={subscription} message={message} onMessageChange={setMessage} onOpenDialogClick={handleOpenDialogClick} /> | ||||
|       )} | ||||
|       <PublishDialog | ||||
|         key={`publishDialog${dialogKey}`} // Resets dialog when canceled/closed
 | ||||
|         openMode={dialogOpenMode} | ||||
|  | @ -95,7 +97,12 @@ const MessageBar = (props) => { | |||
|         <SendIcon /> | ||||
|       </IconButton> | ||||
|       <Portal> | ||||
|         <Snackbar open={snackOpen} autoHideDuration={3000} onClose={() => setSnackOpen(false)} message={t("message_bar_error_publishing")} /> | ||||
|         <Snackbar | ||||
|           open={snackOpen} | ||||
|           autoHideDuration={3000} | ||||
|           onClose={() => setSnackOpen(false)} | ||||
|           message={t("message_bar_error_publishing")} | ||||
|         /> | ||||
|       </Portal> | ||||
|     </Paper> | ||||
|   ); | ||||
|  |  | |||
|  | @ -108,7 +108,8 @@ const NavList = (props) => { | |||
|   const showNotificationBrowserNotSupportedBox = !notifier.browserSupported(); | ||||
|   const showNotificationContextNotSupportedBox = notifier.browserSupported() && !notifier.contextSupported(); // Only show if notifications are generally supported in the browser
 | ||||
|   const showNotificationGrantBox = notifier.supported() && props.subscriptions?.length > 0 && !props.notificationsGranted; | ||||
|   const navListPadding = showNotificationGrantBox || showNotificationBrowserNotSupportedBox || showNotificationContextNotSupportedBox ? "0" : ""; | ||||
|   const navListPadding = | ||||
|     showNotificationGrantBox || showNotificationBrowserNotSupportedBox || showNotificationContextNotSupportedBox ? "0" : ""; | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|  |  | |||
|  | @ -115,7 +115,12 @@ const NotificationList = (props) => { | |||
|           {notifications.slice(0, count).map((notification) => ( | ||||
|             <NotificationItem key={notification.id} notification={notification} onShowSnack={() => setSnackOpen(true)} /> | ||||
|           ))} | ||||
|           <Snackbar open={snackOpen} autoHideDuration={3000} onClose={() => setSnackOpen(false)} message={t("notifications_copied_to_clipboard")} /> | ||||
|           <Snackbar | ||||
|             open={snackOpen} | ||||
|             autoHideDuration={3000} | ||||
|             onClose={() => setSnackOpen(false)} | ||||
|             message={t("notifications_copied_to_clipboard")} | ||||
|           /> | ||||
|         </Stack> | ||||
|       </Container> | ||||
|     </InfiniteScroll> | ||||
|  | @ -156,7 +161,11 @@ const NotificationItem = (props) => { | |||
|         </Tooltip> | ||||
|         {notification.new === 1 && ( | ||||
|           <Tooltip title={t("notifications_mark_read")} enterDelay={500}> | ||||
|             <IconButton onClick={handleMarkRead} sx={{ float: "right", marginRight: -0.5, marginTop: -1 }} aria-label={t("notifications_mark_read")}> | ||||
|             <IconButton | ||||
|               onClick={handleMarkRead} | ||||
|               sx={{ float: "right", marginRight: -0.5, marginTop: -1 }} | ||||
|               aria-label={t("notifications_mark_read")} | ||||
|             > | ||||
|               <CheckIcon /> | ||||
|             </IconButton> | ||||
|           </Tooltip> | ||||
|  |  | |||
|  | @ -251,7 +251,14 @@ const Users = () => { | |||
|       </CardContent> | ||||
|       <CardActions> | ||||
|         <Button onClick={handleAddClick}>{t("prefs_users_add_button")}</Button> | ||||
|         <UserDialog key={`userAddDialog${dialogKey}`} open={dialogOpen} user={null} users={users} onCancel={handleDialogCancel} onSubmit={handleDialogSubmit} /> | ||||
|         <UserDialog | ||||
|           key={`userAddDialog${dialogKey}`} | ||||
|           open={dialogOpen} | ||||
|           user={null} | ||||
|           users={users} | ||||
|           onCancel={handleDialogCancel} | ||||
|           onSubmit={handleDialogSubmit} | ||||
|         /> | ||||
|       </CardActions> | ||||
|     </Card> | ||||
|   ); | ||||
|  | @ -449,7 +456,26 @@ const Language = () => { | |||
| 
 | ||||
|   // Country flags are displayed using emoji. Emoji rendering is handled by platform fonts.
 | ||||
|   // Windows in particular does not yet play nicely with flag emoji so for now, hide flags on Windows.
 | ||||
|   const randomFlags = shuffle(["🇬🇧", "🇺🇸", "🇪🇸", "🇫🇷", "🇧🇬", "🇨🇿", "🇩🇪", "🇵🇱", "🇺🇦", "🇨🇳", "🇮🇹", "🇭🇺", "🇧🇷", "🇳🇱", "🇮🇩", "🇯🇵", "🇷🇺", "🇹🇷"]).slice(0, 3); | ||||
|   const randomFlags = shuffle([ | ||||
|     "🇬🇧", | ||||
|     "🇺🇸", | ||||
|     "🇪🇸", | ||||
|     "🇫🇷", | ||||
|     "🇧🇬", | ||||
|     "🇨🇿", | ||||
|     "🇩🇪", | ||||
|     "🇵🇱", | ||||
|     "🇺🇦", | ||||
|     "🇨🇳", | ||||
|     "🇮🇹", | ||||
|     "🇭🇺", | ||||
|     "🇧🇷", | ||||
|     "🇳🇱", | ||||
|     "🇮🇩", | ||||
|     "🇯🇵", | ||||
|     "🇷🇺", | ||||
|     "🇹🇷", | ||||
|   ]).slice(0, 3); | ||||
|   const showFlags = !navigator.userAgent.includes("Windows"); | ||||
|   let title = t("prefs_appearance_language_title"); | ||||
|   if (showFlags) { | ||||
|  | @ -531,7 +557,12 @@ const Reservations = () => { | |||
|         <Button onClick={handleAddClick} disabled={limitReached}> | ||||
|           {t("prefs_reservations_add_button")} | ||||
|         </Button> | ||||
|         <ReserveAddDialog key={`reservationAddDialog${dialogKey}`} open={dialogOpen} reservations={reservations} onClose={() => setDialogOpen(false)} /> | ||||
|         <ReserveAddDialog | ||||
|           key={`reservationAddDialog${dialogKey}`} | ||||
|           open={dialogOpen} | ||||
|           reservations={reservations} | ||||
|           onClose={() => setDialogOpen(false)} | ||||
|         /> | ||||
|       </CardActions> | ||||
|     </Card> | ||||
|   ); | ||||
|  | @ -545,7 +576,9 @@ const ReservationsTable = (props) => { | |||
|   const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); | ||||
|   const { subscriptions } = useOutletContext(); | ||||
|   const localSubscriptions = | ||||
|     subscriptions?.length > 0 ? Object.assign({}, ...subscriptions.filter((s) => s.baseUrl === config.base_url).map((s) => ({ [s.topic]: s }))) : {}; | ||||
|     subscriptions?.length > 0 | ||||
|       ? Object.assign({}, ...subscriptions.filter((s) => s.baseUrl === config.base_url).map((s) => ({ [s.topic]: s }))) | ||||
|       : {}; | ||||
| 
 | ||||
|   const handleEditClick = (reservation) => { | ||||
|     setDialogKey((prev) => prev + 1); | ||||
|  |  | |||
|  | @ -783,7 +783,12 @@ const AttachmentBox = (props) => { | |||
|             )} | ||||
|           </Typography> | ||||
|         </Box> | ||||
|         <DialogIconButton disabled={props.disabled} onClick={props.onClose} sx={{ marginLeft: "6px" }} aria-label={t("publish_dialog_attached_file_remove")}> | ||||
|         <DialogIconButton | ||||
|           disabled={props.disabled} | ||||
|           onClick={props.onClose} | ||||
|           sx={{ marginLeft: "6px" }} | ||||
|           aria-label={t("publish_dialog_attached_file_remove")} | ||||
|         > | ||||
|           <Close /> | ||||
|         </DialogIconButton> | ||||
|       </Box> | ||||
|  | @ -806,7 +811,13 @@ const ExpandingTextField = (props) => { | |||
|   }, [props.value]); | ||||
|   return ( | ||||
|     <> | ||||
|       <Typography ref={invisibleFieldRef} component="span" variant={props.variant} aria-hidden={true} sx={{ position: "absolute", left: "-200%" }}> | ||||
|       <Typography | ||||
|         ref={invisibleFieldRef} | ||||
|         component="span" | ||||
|         variant={props.variant} | ||||
|         aria-hidden={true} | ||||
|         sx={{ position: "absolute", left: "-200%" }} | ||||
|       > | ||||
|         {props.value} | ||||
|       </Typography> | ||||
|       <TextField | ||||
|  |  | |||
|  | @ -121,7 +121,13 @@ const Signup = () => { | |||
|             ), | ||||
|           }} | ||||
|         /> | ||||
|         <Button type="submit" fullWidth variant="contained" disabled={username === "" || password === "" || password !== confirm} sx={{ mt: 2, mb: 2 }}> | ||||
|         <Button | ||||
|           type="submit" | ||||
|           fullWidth | ||||
|           variant="contained" | ||||
|           disabled={username === "" || password === "" || password !== confirm} | ||||
|           sx={{ mt: 2, mb: 2 }} | ||||
|         > | ||||
|           {t("signup_form_button_submit")} | ||||
|         </Button> | ||||
|         {error && ( | ||||
|  |  | |||
|  | @ -68,7 +68,9 @@ const SubscribePage = (props) => { | |||
|   const baseUrl = anotherServerVisible ? props.baseUrl : config.base_url; | ||||
|   const topic = props.topic; | ||||
|   const existingTopicUrls = props.subscriptions.map((s) => topicUrl(s.baseUrl, s.topic)); | ||||
|   const existingBaseUrls = Array.from(new Set([publicBaseUrl, ...props.subscriptions.map((s) => s.baseUrl)])).filter((s) => s !== config.base_url); | ||||
|   const existingBaseUrls = Array.from(new Set([publicBaseUrl, ...props.subscriptions.map((s) => s.baseUrl)])).filter( | ||||
|     (s) => s !== config.base_url | ||||
|   ); | ||||
|   const showReserveTopicCheckbox = config.enable_reservations && !anotherServerVisible && (config.enable_payments || account); | ||||
|   const reserveTopicEnabled = | ||||
|     session.exists() && (account?.role === Role.ADMIN || (account?.role === Role.USER && (account?.stats.reservations_remaining || 0) > 0)); | ||||
|  | @ -212,7 +214,12 @@ const SubscribePage = (props) => { | |||
|                 inputValue={props.baseUrl} | ||||
|                 onInputChange={updateBaseUrl} | ||||
|                 renderInput={(params) => ( | ||||
|                   <TextField {...params} placeholder={config.base_url} variant="standard" aria-label={t("subscribe_dialog_subscribe_base_url_label")} /> | ||||
|                   <TextField | ||||
|                     {...params} | ||||
|                     placeholder={config.base_url} | ||||
|                     variant="standard" | ||||
|                     aria-label={t("subscribe_dialog_subscribe_base_url_label")} | ||||
|                   /> | ||||
|                 )} | ||||
|               /> | ||||
|             )} | ||||
|  |  | |||
|  | @ -40,7 +40,10 @@ export const SubscriptionPopup = (props) => { | |||
| 
 | ||||
|   const showReservationAdd = config.enable_reservations && !subscription?.reservation && account?.stats.reservations_remaining > 0; | ||||
|   const showReservationAddDisabled = | ||||
|     !showReservationAdd && config.enable_reservations && !subscription?.reservation && (config.enable_payments || account?.stats.reservations_remaining === 0); | ||||
|     !showReservationAdd && | ||||
|     config.enable_reservations && | ||||
|     !subscription?.reservation && | ||||
|     (config.enable_payments || account?.stats.reservations_remaining === 0); | ||||
|   const showReservationEdit = config.enable_reservations && !!subscription?.reservation; | ||||
|   const showReservationDelete = config.enable_reservations && !!subscription?.reservation; | ||||
| 
 | ||||
|  | @ -161,10 +164,20 @@ export const SubscriptionPopup = (props) => { | |||
|         <MenuItem onClick={handleUnsubscribe}>{t("action_bar_unsubscribe")}</MenuItem> | ||||
|       </PopupMenu> | ||||
|       <Portal> | ||||
|         <Snackbar open={showPublishError} autoHideDuration={3000} onClose={() => setShowPublishError(false)} message={t("message_bar_error_publishing")} /> | ||||
|         <Snackbar | ||||
|           open={showPublishError} | ||||
|           autoHideDuration={3000} | ||||
|           onClose={() => setShowPublishError(false)} | ||||
|           message={t("message_bar_error_publishing")} | ||||
|         /> | ||||
|         <DisplayNameDialog open={displayNameDialogOpen} subscription={subscription} onClose={() => setDisplayNameDialogOpen(false)} /> | ||||
|         {showReservationAdd && ( | ||||
|           <ReserveAddDialog open={reserveAddDialogOpen} topic={subscription.topic} reservations={reservations} onClose={() => setReserveAddDialogOpen(false)} /> | ||||
|           <ReserveAddDialog | ||||
|             open={reserveAddDialogOpen} | ||||
|             topic={subscription.topic} | ||||
|             reservations={reservations} | ||||
|             onClose={() => setReserveAddDialogOpen(false)} | ||||
|           /> | ||||
|         )} | ||||
|         {showReservationEdit && ( | ||||
|           <ReserveEditDialog | ||||
|  | @ -175,7 +188,11 @@ export const SubscriptionPopup = (props) => { | |||
|           /> | ||||
|         )} | ||||
|         {showReservationDelete && ( | ||||
|           <ReserveDeleteDialog open={reserveDeleteDialogOpen} topic={subscription.topic} onClose={() => setReserveDeleteDialogOpen(false)} /> | ||||
|           <ReserveDeleteDialog | ||||
|             open={reserveDeleteDialogOpen} | ||||
|             topic={subscription.topic} | ||||
|             onClose={() => setReserveDeleteDialogOpen(false)} | ||||
|           /> | ||||
|         )} | ||||
|       </Portal> | ||||
|     </> | ||||
|  |  | |||
|  | @ -363,7 +363,9 @@ const TierCard = (props) => { | |||
|                 </Feature> | ||||
|               )} | ||||
|               <Feature> | ||||
|                 {t("account_upgrade_dialog_tier_features_attachment_file_size", { filesize: formatBytes(tier.limits.attachment_file_size, 0) })} | ||||
|                 {t("account_upgrade_dialog_tier_features_attachment_file_size", { | ||||
|                   filesize: formatBytes(tier.limits.attachment_file_size, 0), | ||||
|                 })} | ||||
|               </Feature> | ||||
|               {tier.limits.reservations === 0 && <NoFeature>{t("account_upgrade_dialog_tier_features_no_reservations")}</NoFeature>} | ||||
|               {tier.limits.calls === 0 && <NoFeature>{t("account_upgrade_dialog_tier_features_no_calls")}</NoFeature>} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue