Browse Source

JS constants

binwiederhier 3 years ago
parent
commit
259293f9b3

+ 0 - 1
server/server.go

@@ -43,7 +43,6 @@ import (
 - MEDIUM: Reservation (UI): Ask for confirmation when removing reservation (deadcade)
 - MEDIUM: Reservation table delete button: dialog "keep or delete messages?"
 - LOW: UI: Flickering upgrade banner when logging in
-- LOW: JS constants
 
 */
 

+ 32 - 4
web/src/app/AccountApi.js

@@ -1,21 +1,23 @@
 import {
+    accountBillingPortalUrl,
+    accountBillingSubscriptionUrl,
+    accountPasswordUrl,
     accountReservationSingleUrl,
     accountReservationUrl,
-    accountPasswordUrl,
     accountSettingsUrl,
     accountSubscriptionSingleUrl,
     accountSubscriptionUrl,
     accountTokenUrl,
-    accountUrl, maybeWithAuth, topicUrl,
+    accountUrl,
+    tiersUrl,
     withBasicAuth,
-    withBearerAuth, accountBillingSubscriptionUrl, accountBillingPortalUrl, tiersUrl
+    withBearerAuth
 } from "./utils";
 import session from "./Session";
 import subscriptionManager from "./SubscriptionManager";
 import i18n from "i18next";
 import prefs from "./Prefs";
 import routes from "../components/routes";
-import userManager from "./UserManager";
 
 const delayMillis = 45000; // 45 seconds
 const intervalMillis = 900000; // 15 minutes
@@ -441,6 +443,32 @@ class AccountApi {
     }
 }
 
+// Maps to user.Role in user/types.go
+export const Role = {
+    ADMIN: "admin",
+    USER: "user"
+};
+
+// Maps to server.visitorLimitBasis in server/visitor.go
+export const LimitBasis = {
+    IP: "ip",
+    TIER: "tier"
+};
+
+// Maps to stripe.SubscriptionStatus
+export const SubscriptionStatus = {
+    ACTIVE: "active",
+    PAST_DUE: "past_due"
+};
+
+// Maps to user.Permission in user/types.go
+export const Permission = {
+    READ_WRITE: "read-write",
+    READ_ONLY: "read-only",
+    WRITE_ONLY: "write-only",
+    DENY_ALL: "deny-all"
+};
+
 export class UsernameTakenError extends Error {
     constructor(username) {
         super("Username taken");

+ 23 - 17
web/src/components/Account.js

@@ -28,7 +28,13 @@ import TextField from "@mui/material/TextField";
 import routes from "./routes";
 import IconButton from "@mui/material/IconButton";
 import {formatBytes, formatShortDate, formatShortDateTime, openUrl, truncateString, validUrl} from "../app/utils";
-import accountApi, {IncorrectPasswordError, UnauthorizedError} from "../app/AccountApi";
+import accountApi, {
+    IncorrectPasswordError,
+    LimitBasis,
+    Role,
+    SubscriptionStatus,
+    UnauthorizedError
+} from "../app/AccountApi";
 import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
 import {Pref, PrefGroup} from "./Pref";
 import db from "../app/db";
@@ -92,7 +98,7 @@ const Username = () => {
         <Pref labelId={labelId} title={t("account_basics_username_title")} description={t("account_basics_username_description")}>
             <div aria-labelledby={labelId}>
                 {session.username()}
-                {account?.role === "admin"
+                {account?.role === Role.ADMIN
                     ? <>{" "}<Tooltip title={t("account_basics_username_admin_tooltip")}><span style={{cursor: "default"}}>👑</span></Tooltip></>
                     : ""}
             </div>
@@ -237,7 +243,7 @@ const AccountType = () => {
     };
 
     let accountType;
-    if (account.role === "admin") {
+    if (account.role === Role.ADMIN) {
         const tierSuffix = (account.tier) ? `(with ${account.tier.name} tier)` : `(no tier)`;
         accountType = `${t("account_usage_tier_admin")} ${tierSuffix}`;
     } else if (!account.tier) {
@@ -248,7 +254,7 @@ const AccountType = () => {
 
     return (
         <Pref
-            alignTop={account.billing?.status === "past_due" || account.billing?.cancel_at > 0}
+            alignTop={account.billing?.status === SubscriptionStatus.PAST_DUE || account.billing?.cancel_at > 0}
             title={t("account_usage_tier_title")}
             description={t("account_usage_tier_description")}
         >
@@ -259,7 +265,7 @@ const AccountType = () => {
                         <span><InfoIcon/></span>
                     </Tooltip>
                 }
-                {config.enable_payments && account.role === "user" && !account.billing?.subscription &&
+                {config.enable_payments && account.role === Role.USER && !account.billing?.subscription &&
                     <Button
                         variant="outlined"
                         size="small"
@@ -268,7 +274,7 @@ const AccountType = () => {
                         sx={{ml: 1}}
                     >{t("account_usage_tier_upgrade_button")}</Button>
                 }
-                {config.enable_payments && account.role === "user" && account.billing?.subscription &&
+                {config.enable_payments && account.role === Role.USER && account.billing?.subscription &&
                     <Button
                         variant="outlined"
                         size="small"
@@ -276,7 +282,7 @@ const AccountType = () => {
                         sx={{ml: 1}}
                     >{t("account_usage_tier_change_button")}</Button>
                 }
-                {config.enable_payments && account.role === "user" && account.billing?.customer &&
+                {config.enable_payments && account.role === Role.USER && account.billing?.customer &&
                     <Button
                         variant="outlined"
                         size="small"
@@ -290,7 +296,7 @@ const AccountType = () => {
                     onCancel={() => setUpgradeDialogOpen(false)}
                 />
             </div>
-            {account.billing?.status === "past_due" &&
+            {account.billing?.status === SubscriptionStatus.PAST_DUE &&
                 <Alert severity="error" sx={{mt: 1}}>{t("account_usage_tier_payment_overdue")}</Alert>
             }
             {account.billing?.cancel_at > 0 &&
@@ -318,7 +324,7 @@ const Stats = () => {
                 {t("account_usage_title")}
             </Typography>
             <PrefGroup>
-                {account.role !== "admin" &&
+                {account.role === Role.USER &&
                     <Pref title={t("account_usage_reservations_title")}>
                         {account.limits.reservations > 0 &&
                             <>
@@ -326,7 +332,7 @@ const Stats = () => {
                                     <Typography variant="body2"
                                                 sx={{float: "left"}}>{account.stats.reservations}</Typography>
                                     <Typography variant="body2"
-                                                sx={{float: "right"}}>{account.role === "user" ? t("account_usage_of_limit", {limit: account.limits.reservations}) : t("account_usage_unlimited")}</Typography>
+                                                sx={{float: "right"}}>{account.role === Role.USER ? t("account_usage_of_limit", {limit: account.limits.reservations}) : t("account_usage_unlimited")}</Typography>
                                 </div>
                                 <LinearProgress
                                     variant="determinate"
@@ -347,11 +353,11 @@ const Stats = () => {
                 }>
                     <div>
                         <Typography variant="body2" sx={{float: "left"}}>{account.stats.messages}</Typography>
-                        <Typography variant="body2" sx={{float: "right"}}>{account.role === "user" ? t("account_usage_of_limit", { limit: account.limits.messages }) : t("account_usage_unlimited")}</Typography>
+                        <Typography variant="body2" sx={{float: "right"}}>{account.role === Role.USER ? t("account_usage_of_limit", { limit: account.limits.messages }) : t("account_usage_unlimited")}</Typography>
                     </div>
                     <LinearProgress
                         variant="determinate"
-                        value={account.role === "user" ? normalize(account.stats.messages, account.limits.messages) : 100}
+                        value={account.role === Role.USER ? normalize(account.stats.messages, account.limits.messages) : 100}
                     />
                 </Pref>
                 <Pref title={
@@ -362,11 +368,11 @@ const Stats = () => {
                 }>
                     <div>
                         <Typography variant="body2" sx={{float: "left"}}>{account.stats.emails}</Typography>
-                        <Typography variant="body2" sx={{float: "right"}}>{account.role === "user" ? t("account_usage_of_limit", { limit: account.limits.emails }) : t("account_usage_unlimited")}</Typography>
+                        <Typography variant="body2" sx={{float: "right"}}>{account.role === Role.USER ? t("account_usage_of_limit", { limit: account.limits.emails }) : t("account_usage_unlimited")}</Typography>
                     </div>
                     <LinearProgress
                         variant="determinate"
-                        value={account.role === "user" ? normalize(account.stats.emails, account.limits.emails) : 100}
+                        value={account.role === Role.USER ? normalize(account.stats.emails, account.limits.emails) : 100}
                     />
                 </Pref>
                 <Pref
@@ -382,15 +388,15 @@ const Stats = () => {
                 >
                     <div>
                         <Typography variant="body2" sx={{float: "left"}}>{formatBytes(account.stats.attachment_total_size)}</Typography>
-                        <Typography variant="body2" sx={{float: "right"}}>{account.role === "user" ? t("account_usage_of_limit", { limit: formatBytes(account.limits.attachment_total_size) }) : t("account_usage_unlimited")}</Typography>
+                        <Typography variant="body2" sx={{float: "right"}}>{account.role === Role.USER ? t("account_usage_of_limit", { limit: formatBytes(account.limits.attachment_total_size) }) : t("account_usage_unlimited")}</Typography>
                     </div>
                     <LinearProgress
                         variant="determinate"
-                        value={account.role === "user" ? normalize(account.stats.attachment_total_size, account.limits.attachment_total_size) : 100}
+                        value={account.role === Role.USER ? normalize(account.stats.attachment_total_size, account.limits.attachment_total_size) : 100}
                     />
                 </Pref>
             </PrefGroup>
-            {account.role === "user" && account.limits.basis === "ip" &&
+            {account.role === Role.USER && account.limits.basis === LimitBasis.IP &&
                 <Typography variant="body1">
                     {t("account_usage_basis_ip_description")}
                 </Typography>

+ 6 - 6
web/src/components/Navigation.js

@@ -28,7 +28,7 @@ import config from "../app/config";
 import ArticleIcon from '@mui/icons-material/Article';
 import {Trans, useTranslation} from "react-i18next";
 import session from "../app/Session";
-import accountApi from "../app/AccountApi";
+import accountApi, {Permission, Role} from "../app/AccountApi";
 import CelebrationIcon from '@mui/icons-material/Celebration';
 import UpgradeDialog from "./UpgradeDialog";
 import {AccountContext} from "./App";
@@ -104,7 +104,7 @@ const NavList = (props) => {
         navigate(routes.account);
     };
 
-    const isAdmin = account?.role === "admin";
+    const isAdmin = account?.role === Role.ADMIN;
     const isPaid = account?.billing?.subscription;
     const showUpgradeBanner = config.enable_payments && !isAdmin && !isPaid;
     const showSubscriptionsList = props.subscriptions?.length > 0;
@@ -264,16 +264,16 @@ const SubscriptionItem = (props) => {
             <ListItemText primary={displayName} primaryTypographyProps={{ style: { overflow: "hidden", textOverflow: "ellipsis" } }}/>
             {subscription.reservation?.everyone &&
                 <ListItemIcon edge="end" sx={{ minWidth: "26px" }}>
-                    {subscription.reservation?.everyone === "read-write" &&
+                    {subscription.reservation?.everyone === Permission.READ_WRITE &&
                         <Tooltip title={t("prefs_reservations_table_everyone_read_write")}><PermissionReadWrite size="small"/></Tooltip>
                     }
-                    {subscription.reservation?.everyone === "read-only" &&
+                    {subscription.reservation?.everyone === Permission.READ_ONLY &&
                         <Tooltip title={t("prefs_reservations_table_everyone_read_only")}><PermissionRead size="small"/></Tooltip>
                     }
-                    {subscription.reservation?.everyone === "write-only" &&
+                    {subscription.reservation?.everyone === Permission.WRITE_ONLY &&
                         <Tooltip title={t("prefs_reservations_table_everyone_write_only")}><PermissionWrite size="small"/></Tooltip>
                     }
-                    {subscription.reservation?.everyone === "deny-all" &&
+                    {subscription.reservation?.everyone === Permission.DENY_ALL &&
                         <Tooltip title={t("prefs_reservations_table_everyone_deny_all")}><PermissionDenyAll size="small"/></Tooltip>
                     }
                 </ListItemIcon>

+ 7 - 7
web/src/components/Preferences.js

@@ -39,7 +39,7 @@ import {playSound, shuffle, sounds, validTopic, validUrl} from "../app/utils";
 import {useTranslation} from "react-i18next";
 import session from "../app/Session";
 import routes from "./routes";
-import accountApi, {UnauthorizedError} from "../app/AccountApi";
+import accountApi, {Permission, Role, UnauthorizedError} from "../app/AccountApi";
 import {Pref, PrefGroup} from "./Pref";
 import LockIcon from "@mui/icons-material/Lock";
 import {Info, Public, PublicOff} from "@mui/icons-material";
@@ -485,11 +485,11 @@ const Reservations = () => {
     const [dialogKey, setDialogKey] = useState(0);
     const [dialogOpen, setDialogOpen] = useState(false);
 
-    if (!config.enable_reservations || !session.exists() || !account || account.role === "admin") {
+    if (!config.enable_reservations || !session.exists() || !account || account.role === Role.ADMIN) {
         return <></>;
     }
     const reservations = account.reservations || [];
-    const limitReached = account.role === "user" && account.stats.reservations_remaining === 0;
+    const limitReached = account.role === Role.USER && account.stats.reservations_remaining === 0;
 
     const handleAddClick = () => {
         setDialogKey(prev => prev+1);
@@ -602,25 +602,25 @@ const ReservationsTable = (props) => {
                             {reservation.topic}
                         </TableCell>
                         <TableCell aria-label={t("prefs_reservations_table_access_header")}>
-                            {reservation.everyone === "read-write" &&
+                            {reservation.everyone === Permission.READ_WRITE &&
                                 <>
                                     <Public fontSize="small" sx={{color: "grey", verticalAlign: "bottom", mr: 0.5}}/>
                                     {t("prefs_reservations_table_everyone_read_write")}
                                 </>
                             }
-                            {reservation.everyone === "read-only" &&
+                            {reservation.everyone === Permission.READ_ONLY &&
                                 <>
                                     <PublicOff fontSize="small" sx={{color: "grey", verticalAlign: "bottom", mr: 0.5}}/>
                                     {t("prefs_reservations_table_everyone_read_only")}
                                 </>
                             }
-                            {reservation.everyone === "write-only" &&
+                            {reservation.everyone === Permission.WRITE_ONLY &&
                                 <>
                                     <PublicOff fontSize="small" sx={{color: "grey", verticalAlign: "bottom", mr: 0.5}}/>
                                     {t("prefs_reservations_table_everyone_write_only")}
                                 </>
                             }
-                            {reservation.everyone === "deny-all" &&
+                            {reservation.everyone === Permission.DENY_ALL &&
                                 <>
                                     <LockIcon fontSize="small" sx={{color: "grey", verticalAlign: "bottom", mr: 0.5}}/>
                                     {t("prefs_reservations_table_everyone_deny_all")}

+ 5 - 4
web/src/components/ReserveTopicSelect.js

@@ -5,6 +5,7 @@ import MenuItem from "@mui/material/MenuItem";
 import ListItemIcon from "@mui/material/ListItemIcon";
 import ListItemText from "@mui/material/ListItemText";
 import {PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite} from "./ReserveIcons";
+import {Permission} from "../app/AccountApi";
 
 const ReserveTopicSelect = (props) => {
     const { t } = useTranslation();
@@ -24,19 +25,19 @@ const ReserveTopicSelect = (props) => {
                     }
                 }}
             >
-                <MenuItem value="deny-all">
+                <MenuItem value={Permission.DENY_ALL}>
                     <ListItemIcon><PermissionDenyAll/></ListItemIcon>
                     <ListItemText primary={t("prefs_reservations_table_everyone_deny_all")}/>
                 </MenuItem>
-                <MenuItem value="read-only">
+                <MenuItem value={Permission.READ_ONLY}>
                     <ListItemIcon><PermissionRead/></ListItemIcon>
                     <ListItemText primary={t("prefs_reservations_table_everyone_read_only")}/>
                 </MenuItem>
-                <MenuItem value="write-only">
+                <MenuItem value={Permission.WRITE_ONLY}>
                     <ListItemIcon><PermissionWrite/></ListItemIcon>
                     <ListItemText primary={t("prefs_reservations_table_everyone_write_only")}/>
                 </MenuItem>
-                <MenuItem value="read-write">
+                <MenuItem value={Permission.READ_WRITE}>
                     <ListItemIcon><PermissionReadWrite/></ListItemIcon>
                     <ListItemText primary={t("prefs_reservations_table_everyone_read_write")}/>
                 </MenuItem>

+ 2 - 2
web/src/components/SubscribeDialog.js

@@ -17,7 +17,7 @@ import DialogFooter from "./DialogFooter";
 import {useTranslation} from "react-i18next";
 import session from "../app/Session";
 import routes from "./routes";
-import accountApi, {TopicReservedError, UnauthorizedError} from "../app/AccountApi";
+import accountApi, {Role, TopicReservedError, UnauthorizedError} from "../app/AccountApi";
 import ReserveTopicSelect from "./ReserveTopicSelect";
 import {AccountContext} from "./App";
 
@@ -87,7 +87,7 @@ const SubscribePage = (props) => {
     const existingBaseUrls = Array
         .from(new Set([publicBaseUrl, ...props.subscriptions.map(s => s.baseUrl)]))
         .filter(s => s !== config.base_url);
-    const reserveTopicEnabled = session.exists() && account?.role === "user" && (account?.stats.reservations_remaining || 0) > 0;
+    const reserveTopicEnabled = session.exists() && account?.role === Role.USER && (account?.stats.reservations_remaining || 0) > 0;
 
     const handleSubscribe = async () => {
         const user = await userManager.get(baseUrl); // May be undefined

+ 2 - 3
web/src/components/UpgradeDialog.js

@@ -1,4 +1,5 @@
 import * as React from 'react';
+import {useContext, useEffect, useState} from 'react';
 import Dialog from '@mui/material/Dialog';
 import DialogContent from '@mui/material/DialogContent';
 import DialogTitle from '@mui/material/DialogTitle';
@@ -6,16 +7,14 @@ import {Alert, CardActionArea, CardContent, ListItem, useMediaQuery} from "@mui/
 import theme from "./theme";
 import DialogFooter from "./DialogFooter";
 import Button from "@mui/material/Button";
-import accountApi, {TopicReservedError, UnauthorizedError} from "../app/AccountApi";
+import accountApi, {UnauthorizedError} from "../app/AccountApi";
 import session from "../app/Session";
 import routes from "./routes";
-import {useContext, useEffect, useState} from "react";
 import Card from "@mui/material/Card";
 import Typography from "@mui/material/Typography";
 import {AccountContext} from "./App";
 import {formatBytes, formatNumber, formatShortDate} from "../app/utils";
 import {Trans, useTranslation} from "react-i18next";
-import subscriptionManager from "../app/SubscriptionManager";
 import List from "@mui/material/List";
 import {Check} from "@mui/icons-material";
 import ListItemIcon from "@mui/material/ListItemIcon";