Payments webhook test, delete attachments/messages when reservations are removed,

This commit is contained in:
binwiederhier 2023-01-20 22:47:37 -05:00
parent 45b97c7054
commit 31a3bb7cd6
16 changed files with 571 additions and 157 deletions

View file

@ -264,7 +264,6 @@ const AccountType = () => {
const Stats = () => {
const { t } = useTranslation();
const { account } = useContext(AccountContext);
const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false);
if (!account) {
return <></>;
@ -435,6 +434,7 @@ const DeleteAccount = () => {
const DeleteAccountDialog = (props) => {
const { t } = useTranslation();
const { account } = useContext(AccountContext);
const [username, setUsername] = useState("");
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const buttonEnabled = username === session.username();
@ -456,6 +456,9 @@ const DeleteAccountDialog = (props) => {
fullWidth
variant="standard"
/>
{account?.billing?.subscription &&
<Alert severity="warning" sx={{mt: 1}}>{t("account_delete_dialog_billing_warning")}</Alert>
}
</DialogContent>
<DialogActions>
<Button onClick={props.onCancel}>{t("account_delete_dialog_button_cancel")}</Button>

View file

@ -3,7 +3,7 @@ import {useContext, useEffect, useState} from 'react';
import {
Alert,
CardActions,
CardContent,
CardContent, Chip,
FormControl,
Select,
Stack,
@ -20,6 +20,7 @@ import prefs from "../app/Prefs";
import {Paragraph} from "./styles";
import EditIcon from '@mui/icons-material/Edit';
import CloseIcon from "@mui/icons-material/Close";
import WarningIcon from '@mui/icons-material/Warning';
import IconButton from "@mui/material/IconButton";
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import Container from "@mui/material/Container";
@ -41,10 +42,12 @@ import routes from "./routes";
import accountApi, {UnauthorizedError} from "../app/AccountApi";
import {Pref, PrefGroup} from "./Pref";
import LockIcon from "@mui/icons-material/Lock";
import {Public, PublicOff} from "@mui/icons-material";
import {Check, Info, Public, PublicOff} from "@mui/icons-material";
import DialogContentText from "@mui/material/DialogContentText";
import ReserveTopicSelect from "./ReserveTopicSelect";
import {AccountContext} from "./App";
import {useOutletContext} from "react-router-dom";
import subscriptionManager from "../app/SubscriptionManager";
const Preferences = () => {
return (
@ -543,6 +546,12 @@ const ReservationsTable = (props) => {
const [dialogKey, setDialogKey] = useState(0);
const [dialogOpen, setDialogOpen] = useState(false);
const [dialogReservation, setDialogReservation] = useState(null);
const { subscriptions } = useOutletContext();
const localSubscriptions = Object.assign(
...subscriptions
.filter(s => s.baseUrl === config.base_url)
.map(s => ({[s.topic]: s}))
);
const handleEditClick = (reservation) => {
setDialogKey(prev => prev+1);
@ -592,7 +601,9 @@ const ReservationsTable = (props) => {
key={reservation.topic}
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row" sx={{paddingLeft: 0}} aria-label={t("prefs_reservations_table_topic_header")}>{reservation.topic}</TableCell>
<TableCell component="th" scope="row" sx={{paddingLeft: 0}} aria-label={t("prefs_reservations_table_topic_header")}>
{reservation.topic}
</TableCell>
<TableCell aria-label={t("prefs_reservations_table_access_header")}>
{reservation.everyone === "read-write" &&
<>
@ -620,6 +631,9 @@ const ReservationsTable = (props) => {
}
</TableCell>
<TableCell align="right">
{!localSubscriptions[reservation.topic] &&
<Chip icon={<Info/>} label="Not subscribed" color="primary" variant="outlined"/>
}
<IconButton onClick={() => handleEditClick(reservation)} aria-label={t("prefs_reservations_edit_button")}>
<EditIcon/>
</IconButton>

View file

@ -21,13 +21,14 @@ import {Check} from "@mui/icons-material";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Box from "@mui/material/Box";
import {NavLink} from "react-router-dom";
const UpgradeDialog = (props) => {
const { t } = useTranslation();
const { account } = useContext(AccountContext); // May be undefined!
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const [tiers, setTiers] = useState(null);
const [newTier, setNewTier] = useState(account?.tier?.code); // May be undefined
const [newTierCode, setNewTierCode] = useState(account?.tier?.code); // May be undefined
const [loading, setLoading] = useState(false);
const [errorText, setErrorText] = useState("");
@ -41,47 +42,56 @@ const UpgradeDialog = (props) => {
return <></>;
}
const currentTier = account?.tier?.code; // May be undefined
let action, submitButtonLabel, submitButtonEnabled;
const tiersMap = Object.assign(...tiers.map(tier => ({[tier.code]: tier})));
const newTier = tiersMap[newTierCode]; // May be undefined
const currentTier = account?.tier; // May be undefined
const currentTierCode = currentTier?.code; // May be undefined
// Figure out buttons, labels and the submit action
let submitAction, submitButtonLabel, banner;
if (!account) {
submitButtonLabel = t("account_upgrade_dialog_button_redirect_signup");
submitButtonEnabled = true;
action = Action.REDIRECT_SIGNUP;
} else if (currentTier === newTier) {
submitAction = Action.REDIRECT_SIGNUP;
banner = null;
} else if (currentTierCode === newTierCode) {
submitButtonLabel = t("account_upgrade_dialog_button_update_subscription");
submitButtonEnabled = false;
action = null;
} else if (!currentTier) {
submitAction = null;
banner = (currentTierCode) ? Banner.PRORATION_INFO : null;
} else if (!currentTierCode) {
submitButtonLabel = t("account_upgrade_dialog_button_pay_now");
submitButtonEnabled = true;
action = Action.CREATE_SUBSCRIPTION;
} else if (!newTier) {
submitAction = Action.CREATE_SUBSCRIPTION;
banner = null;
} else if (!newTierCode) {
submitButtonLabel = t("account_upgrade_dialog_button_cancel_subscription");
submitButtonEnabled = true;
action = Action.CANCEL_SUBSCRIPTION;
submitAction = Action.CANCEL_SUBSCRIPTION;
banner = Banner.CANCEL_WARNING;
} else {
submitButtonLabel = t("account_upgrade_dialog_button_update_subscription");
submitButtonEnabled = true;
action = Action.UPDATE_SUBSCRIPTION;
submitAction = Action.UPDATE_SUBSCRIPTION;
banner = Banner.PRORATION_INFO;
}
// Exceptional conditions
if (loading) {
submitButtonEnabled = false;
submitAction = null;
} else if (newTier?.code && account?.reservations.length > newTier?.limits.reservations) {
submitAction = null;
banner = Banner.RESERVATIONS_WARNING;
}
const handleSubmit = async () => {
if (action === Action.REDIRECT_SIGNUP) {
if (submitAction === Action.REDIRECT_SIGNUP) {
window.location.href = routes.signup;
return;
}
try {
setLoading(true);
if (action === Action.CREATE_SUBSCRIPTION) {
const response = await accountApi.createBillingSubscription(newTier);
if (submitAction === Action.CREATE_SUBSCRIPTION) {
const response = await accountApi.createBillingSubscription(newTierCode);
window.location.href = response.redirect_url;
} else if (action === Action.UPDATE_SUBSCRIPTION) {
await accountApi.updateBillingSubscription(newTier);
} else if (action === Action.CANCEL_SUBSCRIPTION) {
} else if (submitAction === Action.UPDATE_SUBSCRIPTION) {
await accountApi.updateBillingSubscription(newTierCode);
} else if (submitAction === Action.CANCEL_SUBSCRIPTION) {
await accountApi.deleteBillingSubscription();
}
props.onCancel();
@ -116,27 +126,39 @@ const UpgradeDialog = (props) => {
<TierCard
key={`tierCard${tier.code || '_free'}`}
tier={tier}
selected={newTier === tier.code} // tier.code may be undefined!
onClick={() => setNewTier(tier.code)} // tier.code may be undefined!
current={currentTierCode === tier.code} // tier.code or currentTierCode may be undefined!
selected={newTierCode === tier.code} // tier.code may be undefined!
onClick={() => setNewTierCode(tier.code)} // tier.code may be undefined!
/>
)}
</div>
{action === Action.CANCEL_SUBSCRIPTION &&
{banner === Banner.CANCEL_WARNING &&
<Alert severity="warning">
<Trans
i18nKey="account_upgrade_dialog_cancel_warning"
values={{ date: formatShortDate(account?.billing?.paid_until || 0) }} />
</Alert>
}
{currentTier && (!action || action === Action.UPDATE_SUBSCRIPTION) &&
{banner === Banner.PRORATION_INFO &&
<Alert severity="info">
<Trans i18nKey="account_upgrade_dialog_proration_info" />
</Alert>
}
{banner === Banner.RESERVATIONS_WARNING &&
<Alert severity="warning">
<Trans
i18nKey="account_upgrade_dialog_reservations_warning"
count={account?.reservations.length - newTier?.limits.reservations}
components={{
Link: <NavLink to={routes.settings}/>,
}}
/>
</Alert>
}
</DialogContent>
<DialogFooter status={errorText}>
<Button onClick={props.onCancel}>{t("account_upgrade_dialog_button_cancel")}</Button>
<Button onClick={handleSubmit} disabled={!submitButtonEnabled}>{submitButtonLabel}</Button>
<Button onClick={handleSubmit} disabled={!submitAction}>{submitButtonLabel}</Button>
</DialogFooter>
</Dialog>
);
@ -144,8 +166,19 @@ const UpgradeDialog = (props) => {
const TierCard = (props) => {
const { t } = useTranslation();
const cardStyle = (props.selected) ? { background: "#eee", border: "2px solid #338574" } : { border: "2px solid transparent" };
const tier = props.tier;
let cardStyle, labelStyle, labelText;
if (props.selected) {
cardStyle = { background: "#eee", border: "2px solid #338574" };
labelStyle = { background: "#338574", color: "white" };
labelText = t("account_upgrade_dialog_tier_selected_label");
} else if (props.current) {
cardStyle = { border: "2px solid #eee" };
labelStyle = { background: "#eee", color: "black" };
labelText = t("account_upgrade_dialog_tier_current_label");
} else {
cardStyle = { border: "2px solid transparent" };
}
return (
<Box sx={{
@ -163,16 +196,15 @@ const TierCard = (props) => {
<Card sx={{ height: "100%" }}>
<CardActionArea sx={{ height: "100%" }}>
<CardContent onClick={props.onClick} sx={{ height: "100%" }}>
{props.selected &&
{labelStyle &&
<div style={{
position: "absolute",
top: "0",
right: "15px",
padding: "2px 10px",
background: "#338574",
color: "white",
borderRadius: "3px",
}}>{t("account_upgrade_dialog_tier_selected_label")}</div>
...labelStyle
}}>{labelText}</div>
}
<Typography variant="h5" component="div">
{tier.name || t("account_usage_tier_free")}
@ -217,10 +249,17 @@ const FeatureItem = (props) => {
};
const Action = {
REDIRECT_SIGNUP: 0,
CREATE_SUBSCRIPTION: 1,
UPDATE_SUBSCRIPTION: 2,
CANCEL_SUBSCRIPTION: 3
REDIRECT_SIGNUP: 1,
CREATE_SUBSCRIPTION: 2,
UPDATE_SUBSCRIPTION: 3,
CANCEL_SUBSCRIPTION: 4
};
const Banner = {
CANCEL_WARNING: 1,
PRORATION_INFO: 2,
RESERVATIONS_WARNING: 3
};
export default UpgradeDialog;