Translations
parent
3512db1fe7
commit
9be8be49ef
|
@ -49,7 +49,6 @@ import (
|
||||||
- "mute" setting
|
- "mute" setting
|
||||||
- figure out what settings are "web" or "phone"
|
- figure out what settings are "web" or "phone"
|
||||||
UI:
|
UI:
|
||||||
- Subscription dotmenu dropdown: Move to nav bar, or make same as profile dropdown
|
|
||||||
- Translations
|
- Translations
|
||||||
- aria-labels
|
- aria-labels
|
||||||
- Home UI sign-in/login to top right
|
- Home UI sign-in/login to top right
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
"action_bar_unsubscribe": "Unsubscribe",
|
"action_bar_unsubscribe": "Unsubscribe",
|
||||||
"action_bar_toggle_mute": "Mute/unmute notifications",
|
"action_bar_toggle_mute": "Mute/unmute notifications",
|
||||||
"action_bar_toggle_action_menu": "Open/close action menu",
|
"action_bar_toggle_action_menu": "Open/close action menu",
|
||||||
|
"action_bar_profile_title": "Profile",
|
||||||
|
"action_bar_profile_settings": "Settings",
|
||||||
|
"action_bar_profile_logout": "Logout",
|
||||||
|
"action_bar_sign_in": "Sign in",
|
||||||
|
"action_bar_sign_up": "Sign up",
|
||||||
"message_bar_type_message": "Type a message here",
|
"message_bar_type_message": "Type a message here",
|
||||||
"message_bar_error_publishing": "Error publishing notification",
|
"message_bar_error_publishing": "Error publishing notification",
|
||||||
"message_bar_show_dialog": "Show publish dialog",
|
"message_bar_show_dialog": "Show publish dialog",
|
||||||
|
@ -141,12 +146,38 @@
|
||||||
"subscribe_dialog_login_button_login": "Login",
|
"subscribe_dialog_login_button_login": "Login",
|
||||||
"subscribe_dialog_error_user_not_authorized": "User {{username}} not authorized",
|
"subscribe_dialog_error_user_not_authorized": "User {{username}} not authorized",
|
||||||
"subscribe_dialog_error_user_anonymous": "anonymous",
|
"subscribe_dialog_error_user_anonymous": "anonymous",
|
||||||
"account_type_default": "Default",
|
"account_basics_title": "Account",
|
||||||
"account_type_unlimited": "Unlimited",
|
"account_basics_username_title": "Username",
|
||||||
"account_type_none": "None",
|
"account_basics_username_description": "Hey, that's you ❤",
|
||||||
"account_type_pro": "Pro",
|
"account_basics_username_admin_tooltip": "You are Admin",
|
||||||
"account_type_business": "Business",
|
"account_basics_password_title": "Password",
|
||||||
"account_type_business_plus": "Business Plus",
|
"account_basics_password_description": "Change your account password",
|
||||||
|
"account_basics_password_dialog_title": "Change password",
|
||||||
|
"account_basics_password_dialog_new_password_label": "New password",
|
||||||
|
"account_basics_password_dialog_confirm_password_label": "Confirm password",
|
||||||
|
"account_basics_password_dialog_button_cancel": "Cancel",
|
||||||
|
"account_basics_password_dialog_button_submit": "Change password",
|
||||||
|
"account_usage_title": "Usage",
|
||||||
|
"account_usage_of_limit": "of {{limit}}",
|
||||||
|
"account_usage_unlimited": "Unlimited",
|
||||||
|
"account_usage_plan_title": "Account type",
|
||||||
|
"account_usage_plan_code_default": "Default",
|
||||||
|
"account_usage_plan_code_unlimited": "Unlimited",
|
||||||
|
"account_usage_plan_code_none": "None",
|
||||||
|
"account_usage_plan_code_pro": "Pro",
|
||||||
|
"account_usage_plan_code_business": "Business",
|
||||||
|
"account_usage_plan_code_business_plus": "Business Plus",
|
||||||
|
"account_usage_messages_title": "Published messages",
|
||||||
|
"account_usage_emails_title": "Emails sent",
|
||||||
|
"account_usage_attachment_storage_title": "Attachment storage",
|
||||||
|
"account_usage_attachment_storage_subtitle": "{{filesize}} per file",
|
||||||
|
"account_usage_basis_ip_description": "Usage stats and limits for this account are based on your IP address, so they may be shared with other users.",
|
||||||
|
"account_delete_title": "Delete account",
|
||||||
|
"account_delete_description": "Permanently delete your account",
|
||||||
|
"account_delete_dialog_description": "This will permanently delete your account, including all data that is stored on the server. If you really want to proceed, please type '{{username}}' in the text box below.",
|
||||||
|
"account_delete_dialog_label": "Type '{{username}}' to delete account",
|
||||||
|
"account_delete_dialog_button_cancel": "Cancel",
|
||||||
|
"account_delete_dialog_button_submit": "Permanently delete account",
|
||||||
"prefs_notifications_title": "Notifications",
|
"prefs_notifications_title": "Notifications",
|
||||||
"prefs_notifications_sound_title": "Notification sound",
|
"prefs_notifications_sound_title": "Notification sound",
|
||||||
"prefs_notifications_sound_description_none": "Notifications do not play any sound when they arrive",
|
"prefs_notifications_sound_description_none": "Notifications do not play any sound when they arrive",
|
||||||
|
|
|
@ -41,9 +41,9 @@ const Account = () => {
|
||||||
const Basics = () => {
|
const Basics = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Card sx={{p: 3}} aria-label={t("xxxxxxxxx")}>
|
<Card sx={{p: 3}} aria-label={t("account_basics_title")}>
|
||||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
<Typography variant="h5" sx={{marginBottom: 2}}>
|
||||||
Account
|
{t("account_basics_title")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<PrefGroup>
|
<PrefGroup>
|
||||||
<Username/>
|
<Username/>
|
||||||
|
@ -53,80 +53,15 @@ const Basics = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Stats = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { account } = useOutletContext();
|
|
||||||
if (!account) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
const accountType = account.plan.code ?? "none";
|
|
||||||
const normalize = (value, max) => (value / max * 100);
|
|
||||||
return (
|
|
||||||
<Card sx={{p: 3}} aria-label={t("xxxxxxxxx")}>
|
|
||||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
|
||||||
{t("Usage")}
|
|
||||||
</Typography>
|
|
||||||
<PrefGroup>
|
|
||||||
<Pref labelId={"accountType"} title={t("Account type")}>
|
|
||||||
<div>
|
|
||||||
{account?.role === "admin"
|
|
||||||
? <>Unlimited <Tooltip title={"You are Admin"}><span style={{cursor: "default"}}>👑</span></Tooltip></>
|
|
||||||
: t(`account_type_${accountType}`)}
|
|
||||||
</div>
|
|
||||||
</Pref>
|
|
||||||
<Pref labelId={"messages"} title={t("Published messages")}>
|
|
||||||
<div>
|
|
||||||
<Typography variant="body2" sx={{float: "left"}}>{account.stats.messages}</Typography>
|
|
||||||
<Typography variant="body2" sx={{float: "right"}}>{account.limits.messages > 0 ? t("of {{limit}}", { limit: account.limits.messages }) : t("Unlimited")}</Typography>
|
|
||||||
</div>
|
|
||||||
<LinearProgress variant="determinate" value={account.limits.messages > 0 ? normalize(account.stats.messages, account.limits.messages) : 100} />
|
|
||||||
</Pref>
|
|
||||||
<Pref labelId={"emails"} title={t("Emails sent")}>
|
|
||||||
<div>
|
|
||||||
<Typography variant="body2" sx={{float: "left"}}>{account.stats.emails}</Typography>
|
|
||||||
<Typography variant="body2" sx={{float: "right"}}>{account.limits.emails > 0 ? t("of {{limit}}", { limit: account.limits.emails }) : t("Unlimited")}</Typography>
|
|
||||||
</div>
|
|
||||||
<LinearProgress variant="determinate" value={account.limits.emails > 0 ? normalize(account.stats.emails, account.limits.emails) : 100} />
|
|
||||||
</Pref>
|
|
||||||
<Pref labelId={"attachments"} title={t("Attachment storage")} subtitle={t("{{filesize}} per file", { filesize: formatBytes(account.limits.attachment_file_size) })}>
|
|
||||||
<div>
|
|
||||||
<Typography variant="body2" sx={{float: "left"}}>{formatBytes(account.stats.attachment_total_size)}</Typography>
|
|
||||||
<Typography variant="body2" sx={{float: "right"}}>{account.limits.attachment_total_size > 0 ? t("of {{limit}}", { limit: formatBytes(account.limits.attachment_total_size) }) : t("Unlimited")}</Typography>
|
|
||||||
</div>
|
|
||||||
<LinearProgress variant="determinate" value={account.limits.attachment_total_size > 0 ? normalize(account.stats.attachment_total_size, account.limits.attachment_total_size) : 100} />
|
|
||||||
</Pref>
|
|
||||||
</PrefGroup>
|
|
||||||
{account.limits.basis === "ip" && <Typography variant="body1">
|
|
||||||
<em>Usage stats and limits for this account are based on your IP address, so they may be shared
|
|
||||||
with other users.</em>
|
|
||||||
</Typography>}
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Delete = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<Card sx={{p: 3}} aria-label={t("xxxxxxxxx")}>
|
|
||||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
|
||||||
{t("Delete account")}
|
|
||||||
</Typography>
|
|
||||||
<PrefGroup>
|
|
||||||
<DeleteAccount/>
|
|
||||||
</PrefGroup>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Username = () => {
|
const Username = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { account } = useOutletContext();
|
const { account } = useOutletContext();
|
||||||
return (
|
return (
|
||||||
<Pref labelId={"username"} title={t("Username")} description={t("Hey, that's you ❤")}>
|
<Pref title={t("account_basics_username_title")} description={t("account_basics_username_description")}>
|
||||||
<div>
|
<div>
|
||||||
{session.username()}
|
{session.username()}
|
||||||
{account?.role === "admin"
|
{account?.role === "admin"
|
||||||
? <>{" "}<Tooltip title={"You are Admin"}><span style={{cursor: "default"}}>👑</span></Tooltip></>
|
? <>{" "}<Tooltip title={t("account_basics_username_admin_tooltip")}><span style={{cursor: "default"}}>👑</span></Tooltip></>
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</Pref>
|
</Pref>
|
||||||
|
@ -137,14 +72,16 @@ const ChangePassword = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [dialogKey, setDialogKey] = useState(0);
|
const [dialogKey, setDialogKey] = useState(0);
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const labelId = "prefChangePassword";
|
|
||||||
const handleDialogOpen = () => {
|
const handleDialogOpen = () => {
|
||||||
setDialogKey(prev => prev+1);
|
setDialogKey(prev => prev+1);
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDialogCancel = () => {
|
const handleDialogCancel = () => {
|
||||||
setDialogOpen(false);
|
setDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDialogSubmit = async (newPassword) => {
|
const handleDialogSubmit = async (newPassword) => {
|
||||||
try {
|
try {
|
||||||
await accountApi.changePassword(newPassword);
|
await accountApi.changePassword(newPassword);
|
||||||
|
@ -158,11 +95,12 @@ const ChangePassword = () => {
|
||||||
// TODO show error
|
// TODO show error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pref labelId={labelId} title={t("Password")} description={t("Change your account password")}>
|
<Pref title={t("account_basics_password_title")} description={t("account_basics_password_description")}>
|
||||||
<div>
|
<div>
|
||||||
<Typography color="gray" sx={{float: "left", fontSize: "0.7rem", lineHeight: "3.5"}}>⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤</Typography>
|
<Typography color="gray" sx={{float: "left", fontSize: "0.7rem", lineHeight: "3.5"}}>⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤</Typography>
|
||||||
<IconButton onClick={handleDialogOpen} aria-label={t("xxxxxxxx")}>
|
<IconButton onClick={handleDialogOpen} aria-label={t("account_basics_password_description")}>
|
||||||
<EditIcon/>
|
<EditIcon/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -186,13 +124,13 @@ const ChangePasswordDialog = (props) => {
|
||||||
})();
|
})();
|
||||||
return (
|
return (
|
||||||
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
||||||
<DialogTitle>Change password</DialogTitle>
|
<DialogTitle>{t("account_basics_password_dialog_title")}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<TextField
|
<TextField
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="new-password"
|
id="new-password"
|
||||||
label={t("New password")}
|
label={t("account_basics_password_dialog_new_password_label")}
|
||||||
aria-label={t("xxxx")}
|
aria-label={t("account_basics_password_dialog_new_password_label")}
|
||||||
type="password"
|
type="password"
|
||||||
value={newPassword}
|
value={newPassword}
|
||||||
onChange={ev => setNewPassword(ev.target.value)}
|
onChange={ev => setNewPassword(ev.target.value)}
|
||||||
|
@ -202,8 +140,8 @@ const ChangePasswordDialog = (props) => {
|
||||||
<TextField
|
<TextField
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="confirm"
|
id="confirm"
|
||||||
label={t("Confirm password")}
|
label={t("account_basics_password_dialog_confirm_password_label")}
|
||||||
aria-label={t("xxx")}
|
aria-label={t("account_basics_password_dialog_confirm_password_label")}
|
||||||
type="password"
|
type="password"
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={ev => setConfirmPassword(ev.target.value)}
|
onChange={ev => setConfirmPassword(ev.target.value)}
|
||||||
|
@ -212,26 +150,94 @@ const ChangePasswordDialog = (props) => {
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={props.onCancel}>{t("Cancel")}</Button>
|
<Button onClick={props.onCancel}>{t("account_basics_password_dialog_button_cancel")}</Button>
|
||||||
<Button onClick={() => props.onSubmit(newPassword)} disabled={!changeButtonEnabled}>{t("Change password")}</Button>
|
<Button onClick={() => props.onSubmit(newPassword)} disabled={!changeButtonEnabled}>{t("account_basics_password_dialog_button_submit")}</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Stats = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { account } = useOutletContext();
|
||||||
|
if (!account) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
const planCode = account.plan.code ?? "none";
|
||||||
|
const normalize = (value, max) => (value / max * 100);
|
||||||
|
return (
|
||||||
|
<Card sx={{p: 3}} aria-label={t("account_usage_title")}>
|
||||||
|
<Typography variant="h5" sx={{marginBottom: 2}}>
|
||||||
|
{t("account_usage_title")}
|
||||||
|
</Typography>
|
||||||
|
<PrefGroup>
|
||||||
|
<Pref title={t("account_usage_plan_title")}>
|
||||||
|
<div>
|
||||||
|
{account?.role === "admin"
|
||||||
|
? <>{t("account_usage_unlimited")} <Tooltip title={t("account_basics_username_admin_tooltip")}><span style={{cursor: "default"}}>👑</span></Tooltip></>
|
||||||
|
: t(`account_usage_plan_code_${planCode}`)}
|
||||||
|
</div>
|
||||||
|
</Pref>
|
||||||
|
<Pref title={t("account_usage_messages_title")}>
|
||||||
|
<div>
|
||||||
|
<Typography variant="body2" sx={{float: "left"}}>{account.stats.messages}</Typography>
|
||||||
|
<Typography variant="body2" sx={{float: "right"}}>{account.limits.messages > 0 ? t("account_usage_of_limit", { limit: account.limits.messages }) : t("account_usage_unlimited")}</Typography>
|
||||||
|
</div>
|
||||||
|
<LinearProgress variant="determinate" value={account.limits.messages > 0 ? normalize(account.stats.messages, account.limits.messages) : 100} />
|
||||||
|
</Pref>
|
||||||
|
<Pref title={t("account_usage_emails_title")}>
|
||||||
|
<div>
|
||||||
|
<Typography variant="body2" sx={{float: "left"}}>{account.stats.emails}</Typography>
|
||||||
|
<Typography variant="body2" sx={{float: "right"}}>{account.limits.emails > 0 ? t("account_usage_of_limit", { limit: account.limits.emails }) : t("account_usage_unlimited")}</Typography>
|
||||||
|
</div>
|
||||||
|
<LinearProgress variant="determinate" value={account.limits.emails > 0 ? normalize(account.stats.emails, account.limits.emails) : 100} />
|
||||||
|
</Pref>
|
||||||
|
<Pref title={t("account_usage_attachment_storage_title")} subtitle={t("account_usage_attachment_storage_subtitle", { filesize: formatBytes(account.limits.attachment_file_size) })}>
|
||||||
|
<div>
|
||||||
|
<Typography variant="body2" sx={{float: "left"}}>{formatBytes(account.stats.attachment_total_size)}</Typography>
|
||||||
|
<Typography variant="body2" sx={{float: "right"}}>{account.limits.attachment_total_size > 0 ? t("account_usage_of_limit", { limit: formatBytes(account.limits.attachment_total_size) }) : t("account_usage_unlimited")}</Typography>
|
||||||
|
</div>
|
||||||
|
<LinearProgress variant="determinate" value={account.limits.attachment_total_size > 0 ? normalize(account.stats.attachment_total_size, account.limits.attachment_total_size) : 100} />
|
||||||
|
</Pref>
|
||||||
|
</PrefGroup>
|
||||||
|
{account.limits.basis === "ip" &&
|
||||||
|
<Typography variant="body1">
|
||||||
|
<em>{t("account_usage_basis_ip_description")}</em>
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Delete = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Card sx={{p: 3}} aria-label={t("account_delete_title")}>
|
||||||
|
<Typography variant="h5" sx={{marginBottom: 2}}>
|
||||||
|
{t("account_delete_title")}
|
||||||
|
</Typography>
|
||||||
|
<PrefGroup>
|
||||||
|
<DeleteAccount/>
|
||||||
|
</PrefGroup>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const DeleteAccount = () => {
|
const DeleteAccount = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [dialogKey, setDialogKey] = useState(0);
|
const [dialogKey, setDialogKey] = useState(0);
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const labelId = "prefDeleteAccount";
|
|
||||||
const handleDialogOpen = () => {
|
const handleDialogOpen = () => {
|
||||||
setDialogKey(prev => prev+1);
|
setDialogKey(prev => prev+1);
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDialogCancel = () => {
|
const handleDialogCancel = () => {
|
||||||
setDialogOpen(false);
|
setDialogOpen(false);
|
||||||
};
|
};
|
||||||
const handleDialogSubmit = async (newPassword) => {
|
|
||||||
|
const handleDialogSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
await accountApi.delete();
|
await accountApi.delete();
|
||||||
await db.delete();
|
await db.delete();
|
||||||
|
@ -246,11 +252,12 @@ const DeleteAccount = () => {
|
||||||
// TODO show error
|
// TODO show error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pref labelId={labelId} title={t("Delete account")} description={t("Permanently delete your account")}>
|
<Pref title={t("account_delete_title")} description={t("account_delete_description")}>
|
||||||
<div>
|
<div>
|
||||||
<Button fullWidth={false} variant="outlined" color="error" startIcon={<DeleteOutlineIcon />} onClick={handleDialogOpen}>
|
<Button fullWidth={false} variant="outlined" color="error" startIcon={<DeleteOutlineIcon />} onClick={handleDialogOpen}>
|
||||||
Delete account
|
{t("account_delete_title")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<DeleteAccountDialog
|
<DeleteAccountDialog
|
||||||
|
@ -270,16 +277,16 @@ const DeleteAccountDialog = (props) => {
|
||||||
const buttonEnabled = username === session.username();
|
const buttonEnabled = username === session.username();
|
||||||
return (
|
return (
|
||||||
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
||||||
<DialogTitle>{t("Delete account")}</DialogTitle>
|
<DialogTitle>{t("account_delete_title")}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
{t("This will permanently delete your account, including all data that is stored on the server. If you really want to proceed, please type '{{username}}' in the text box below.", { username: session.username()})}
|
{t("account_delete_dialog_description", { username: session.username()})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="account-delete-confirm"
|
id="account-delete-confirm"
|
||||||
label={t("Type '{{username}}' to delete account", { username: session.username()})}
|
label={t("account_delete_dialog_label", { username: session.username()})}
|
||||||
aria-label={t("xxxx")}
|
aria-label={t("account_delete_dialog_label", { username: session.username()})}
|
||||||
type="text"
|
type="text"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={ev => setUsername(ev.target.value)}
|
onChange={ev => setUsername(ev.target.value)}
|
||||||
|
@ -288,8 +295,8 @@ const DeleteAccountDialog = (props) => {
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={props.onCancel}>{t("prefs_users_dialog_button_cancel")}</Button>
|
<Button onClick={props.onCancel}>{t("account_delete_dialog_button_cancel")}</Button>
|
||||||
<Button onClick={props.onSubmit} color="error" disabled={!buttonEnabled}>{t("Permanently delete account")}</Button>
|
<Button onClick={props.onSubmit} color="error" disabled={!buttonEnabled}>{t("account_delete_dialog_button_submit")}</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
@ -319,7 +326,6 @@ const Pref = (props) => {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
role="cell"
|
role="cell"
|
||||||
id={props.labelId}
|
|
||||||
aria-label={props.title}
|
aria-label={props.title}
|
||||||
style={{
|
style={{
|
||||||
flex: '1 0 40%',
|
flex: '1 0 40%',
|
||||||
|
|
|
@ -83,18 +83,17 @@ const ActionBar = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Originally from https://mui.com/components/menus/#MenuListComposition.js
|
|
||||||
const SettingsIcons = (props) => {
|
const SettingsIcons = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [open, setOpen] = useState(false);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [snackOpen, setSnackOpen] = useState(false);
|
const [snackOpen, setSnackOpen] = useState(false);
|
||||||
const [subscriptionSettingsOpen, setSubscriptionSettingsOpen] = useState(false);
|
const [subscriptionSettingsOpen, setSubscriptionSettingsOpen] = useState(false);
|
||||||
const anchorRef = useRef(null);
|
|
||||||
const subscription = props.subscription;
|
const subscription = props.subscription;
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
const handleToggleOpen = () => {
|
const handleToggleOpen = (event) => {
|
||||||
setOpen((prevOpen) => !prevOpen);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleMute = async () => {
|
const handleToggleMute = async () => {
|
||||||
|
@ -102,22 +101,17 @@ const SettingsIcons = (props) => {
|
||||||
await subscriptionManager.setMutedUntil(subscription.id, mutedUntil);
|
await subscriptionManager.setMutedUntil(subscription.id, mutedUntil);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClose = (event) => {
|
const handleClose = () => {
|
||||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
setAnchorEl(null);
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearAll = async (event) => {
|
const handleClearAll = async (event) => {
|
||||||
handleClose(event);
|
|
||||||
console.log(`[ActionBar] Deleting all notifications from ${props.subscription.id}`);
|
console.log(`[ActionBar] Deleting all notifications from ${props.subscription.id}`);
|
||||||
await subscriptionManager.deleteNotifications(props.subscription.id);
|
await subscriptionManager.deleteNotifications(props.subscription.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnsubscribe = async (event) => {
|
const handleUnsubscribe = async (event) => {
|
||||||
console.log(`[ActionBar] Unsubscribing from ${props.subscription.id}`, props.subscription);
|
console.log(`[ActionBar] Unsubscribing from ${props.subscription.id}`, props.subscription);
|
||||||
handleClose(event);
|
|
||||||
await subscriptionManager.remove(props.subscription.id);
|
await subscriptionManager.remove(props.subscription.id);
|
||||||
if (session.exists() && props.subscription.remoteId) {
|
if (session.exists() && props.subscription.remoteId) {
|
||||||
try {
|
try {
|
||||||
|
@ -181,61 +175,26 @@ const SettingsIcons = (props) => {
|
||||||
console.log(`[ActionBar] Error publishing message`, e);
|
console.log(`[ActionBar] Error publishing message`, e);
|
||||||
setSnackOpen(true);
|
setSnackOpen(true);
|
||||||
}
|
}
|
||||||
setOpen(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleListKeyDown = (event) => {
|
|
||||||
if (event.key === 'Tab') {
|
|
||||||
event.preventDefault();
|
|
||||||
setOpen(false);
|
|
||||||
} else if (event.key === 'Escape') {
|
|
||||||
setOpen(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return focus to the button when we transitioned from !open -> open
|
|
||||||
const prevOpen = useRef(open);
|
|
||||||
useEffect(() => {
|
|
||||||
if (prevOpen.current === true && open === false) {
|
|
||||||
anchorRef.current.focus();
|
|
||||||
}
|
|
||||||
prevOpen.current = open;
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IconButton color="inherit" size="large" edge="end" onClick={handleToggleMute} aria-label={t("action_bar_toggle_mute")}>
|
<IconButton color="inherit" size="large" edge="end" onClick={handleToggleMute} aria-label={t("action_bar_toggle_mute")}>
|
||||||
{subscription.mutedUntil ? <NotificationsOffIcon/> : <NotificationsIcon/>}
|
{subscription.mutedUntil ? <NotificationsOffIcon/> : <NotificationsIcon/>}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton color="inherit" size="large" edge="end" ref={anchorRef} onClick={handleToggleOpen} aria-label={t("action_bar_toggle_action_menu")}>
|
<IconButton color="inherit" size="large" edge="end" onClick={handleToggleOpen} aria-label={t("action_bar_toggle_action_menu")}>
|
||||||
<MoreVertIcon/>
|
<MoreVertIcon/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Popper
|
<PopupMenu
|
||||||
|
anchorEl={anchorEl}
|
||||||
open={open}
|
open={open}
|
||||||
anchorEl={anchorRef.current}
|
onClose={handleClose}
|
||||||
role={undefined}
|
|
||||||
placement="bottom-start"
|
|
||||||
transition
|
|
||||||
disablePortal
|
|
||||||
>
|
>
|
||||||
{({TransitionProps, placement}) => (
|
|
||||||
<Grow
|
|
||||||
{...TransitionProps}
|
|
||||||
style={{transformOrigin: placement === 'bottom-start' ? 'left top' : 'left bottom'}}
|
|
||||||
>
|
|
||||||
<Paper>
|
|
||||||
<ClickAwayListener onClickAway={handleClose}>
|
|
||||||
<MenuList autoFocusItem={open} onKeyDown={handleListKeyDown}>
|
|
||||||
<MenuItem onClick={handleSubscriptionSettings}>{t("action_bar_subscription_settings")}</MenuItem>
|
<MenuItem onClick={handleSubscriptionSettings}>{t("action_bar_subscription_settings")}</MenuItem>
|
||||||
<MenuItem onClick={handleSendTestMessage}>{t("action_bar_send_test_notification")}</MenuItem>
|
<MenuItem onClick={handleSendTestMessage}>{t("action_bar_send_test_notification")}</MenuItem>
|
||||||
<MenuItem onClick={handleClearAll}>{t("action_bar_clear_notifications")}</MenuItem>
|
<MenuItem onClick={handleClearAll}>{t("action_bar_clear_notifications")}</MenuItem>
|
||||||
<MenuItem onClick={handleUnsubscribe}>{t("action_bar_unsubscribe")}</MenuItem>
|
<MenuItem onClick={handleUnsubscribe}>{t("action_bar_unsubscribe")}</MenuItem>
|
||||||
</MenuList>
|
</PopupMenu>
|
||||||
</ClickAwayListener>
|
|
||||||
</Paper>
|
|
||||||
</Grow>
|
|
||||||
)}
|
|
||||||
</Popper>
|
|
||||||
<Portal>
|
<Portal>
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={snackOpen}
|
open={snackOpen}
|
||||||
|
@ -256,7 +215,7 @@ const SettingsIcons = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProfileIcon = (props) => {
|
const ProfileIcon = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
@ -265,9 +224,11 @@ const ProfileIcon = (props) => {
|
||||||
const handleClick = (event) => {
|
const handleClick = (event) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
await accountApi.logout();
|
await accountApi.logout();
|
||||||
|
@ -276,25 +237,60 @@ const ProfileIcon = (props) => {
|
||||||
session.resetAndRedirect(routes.app);
|
session.resetAndRedirect(routes.app);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{session.exists() &&
|
{session.exists() &&
|
||||||
<IconButton color="inherit" size="large" edge="end" onClick={handleClick} aria-label={t("xxxxxxx")}>
|
<IconButton color="inherit" size="large" edge="end" onClick={handleClick} aria-label={t("action_bar_profile_title")}>
|
||||||
<AccountCircleIcon/>
|
<AccountCircleIcon/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
{!session.exists() && config.enableLogin &&
|
{!session.exists() && config.enableLogin &&
|
||||||
<Button color="inherit" variant="text" onClick={() => navigate(routes.login)} sx={{m: 1}}>Sign in</Button>
|
<Button color="inherit" variant="text" onClick={() => navigate(routes.login)} sx={{m: 1}} aria-label={t("action_bar_sign_in")}>
|
||||||
|
{t("action_bar_sign_in")}
|
||||||
|
</Button>
|
||||||
}
|
}
|
||||||
{!session.exists() && config.enableSignup &&
|
{!session.exists() && config.enableSignup &&
|
||||||
<Button color="inherit" variant="outlined" onClick={() => navigate(routes.signup)}>Sign up</Button>
|
<Button color="inherit" variant="outlined" onClick={() => navigate(routes.signup)} aria-label={t("action_bar_sign_up")}>
|
||||||
|
{t("action_bar_sign_up")}
|
||||||
|
</Button>
|
||||||
}
|
}
|
||||||
<Menu
|
<PopupMenu
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
id="account-menu"
|
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
onClick={handleClose}
|
>
|
||||||
|
<MenuItem onClick={() => navigate(routes.account)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Person />
|
||||||
|
</ListItemIcon>
|
||||||
|
<b>{session.username()}</b>
|
||||||
|
</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem onClick={() => navigate(routes.settings)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Settings fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
{t("action_bar_profile_settings")}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleLogout}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Logout fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
{t("action_bar_profile_logout")}
|
||||||
|
</MenuItem>
|
||||||
|
</PopupMenu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PopupMenu = (props) => {
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
anchorEl={props.anchorEl}
|
||||||
|
open={props.open}
|
||||||
|
onClose={props.onClose}
|
||||||
|
onClick={props.onClose}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
sx: {
|
sx: {
|
||||||
|
@ -324,29 +320,9 @@ const ProfileIcon = (props) => {
|
||||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={() => navigate(routes.account)}>
|
{props.children}
|
||||||
<ListItemIcon>
|
|
||||||
<Person />
|
|
||||||
</ListItemIcon>
|
|
||||||
<b>{session.username()}</b>
|
|
||||||
</MenuItem>
|
|
||||||
<Divider />
|
|
||||||
<MenuItem onClick={() => navigate(routes.settings)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Settings fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
Settings
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={handleLogout}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Logout fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
Logout
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default ActionBar;
|
export default ActionBar;
|
||||||
|
|
|
@ -127,7 +127,8 @@ const NavList = (props) => {
|
||||||
<ListItemButton onClick={() => navigate(routes.account)} selected={location.pathname === routes.account}>
|
<ListItemButton onClick={() => navigate(routes.account)} selected={location.pathname === routes.account}>
|
||||||
<ListItemIcon><Person/></ListItemIcon>
|
<ListItemIcon><Person/></ListItemIcon>
|
||||||
<ListItemText primary={t("nav_button_account")}/>
|
<ListItemText primary={t("nav_button_account")}/>
|
||||||
</ListItemButton>}
|
</ListItemButton>
|
||||||
|
}
|
||||||
<ListItemButton onClick={() => navigate(routes.settings)} selected={location.pathname === routes.settings}>
|
<ListItemButton onClick={() => navigate(routes.settings)} selected={location.pathname === routes.settings}>
|
||||||
<ListItemIcon><SettingsIcon/></ListItemIcon>
|
<ListItemIcon><SettingsIcon/></ListItemIcon>
|
||||||
<ListItemText primary={t("nav_button_settings")}/>
|
<ListItemText primary={t("nav_button_settings")}/>
|
||||||
|
|
|
@ -45,7 +45,6 @@ const Preferences = () => {
|
||||||
<Notifications/>
|
<Notifications/>
|
||||||
<Appearance/>
|
<Appearance/>
|
||||||
<Users/>
|
<Users/>
|
||||||
<AccessControl/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
@ -507,170 +506,6 @@ const Language = () => {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
const AccessControl = () => {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
const AccessControl = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [dialogKey, setDialogKey] = useState(0);
|
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
|
||||||
const entries = useLiveQuery(() => userManager.all());
|
|
||||||
const handleAddClick = () => {
|
|
||||||
setDialogKey(prev => prev+1);
|
|
||||||
setDialogOpen(true);
|
|
||||||
};
|
|
||||||
const handleDialogCancel = () => {
|
|
||||||
setDialogOpen(false);
|
|
||||||
};
|
|
||||||
const handleDialogSubmit = async (user) => {
|
|
||||||
setDialogOpen(false);
|
|
||||||
try {
|
|
||||||
await userManager.save(user);
|
|
||||||
console.debug(`[Preferences] User ${user.username} for ${user.baseUrl} added`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`[Preferences] Error adding user.`, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Card sx={{ padding: 1 }} aria-label={t("prefs_users_title")}>
|
|
||||||
<CardContent sx={{ paddingBottom: 1 }}>
|
|
||||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
|
||||||
Access control
|
|
||||||
</Typography>
|
|
||||||
<Paragraph>
|
|
||||||
Define read/write access to topics for this server.
|
|
||||||
</Paragraph>
|
|
||||||
{entries?.length > 0 && <AccessControlTable entries={entries}/>}
|
|
||||||
</CardContent>
|
|
||||||
<CardActions>
|
|
||||||
<Button onClick={handleAddClick}>{t("prefs_users_add_button")}</Button>
|
|
||||||
<AccessControlDialog
|
|
||||||
key={`aclDialog${dialogKey}`}
|
|
||||||
open={dialogOpen}
|
|
||||||
user={null}
|
|
||||||
users={entries}
|
|
||||||
onCancel={handleDialogCancel}
|
|
||||||
onSubmit={handleDialogSubmit}
|
|
||||||
/>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AccessControlTable = (props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [dialogKey, setDialogKey] = useState(0);
|
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
|
||||||
const [dialogUser, setDialogUser] = useState(null);
|
|
||||||
const handleEditClick = (user) => {
|
|
||||||
setDialogKey(prev => prev+1);
|
|
||||||
setDialogUser(user);
|
|
||||||
setDialogOpen(true);
|
|
||||||
};
|
|
||||||
const handleDialogCancel = () => {
|
|
||||||
setDialogOpen(false);
|
|
||||||
};
|
|
||||||
const handleDialogSubmit = async (user) => {
|
|
||||||
setDialogOpen(false);
|
|
||||||
try {
|
|
||||||
await userManager.save(user);
|
|
||||||
console.debug(`[Preferences] User ${user.username} for ${user.baseUrl} updated`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`[Preferences] Error updating user.`, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleDeleteClick = async (user) => {
|
|
||||||
try {
|
|
||||||
await userManager.delete(user.baseUrl);
|
|
||||||
console.debug(`[Preferences] User ${user.username} for ${user.baseUrl} deleted`);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[Preferences] Error deleting user for ${user.baseUrl}`, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Table size="small" aria-label={t("prefs_users_table")}>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell sx={{paddingLeft: 0}}>Topic</TableCell>
|
|
||||||
<TableCell>User</TableCell>
|
|
||||||
<TableCell>Access</TableCell>
|
|
||||||
<TableCell/>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{props.entries?.map(user => (
|
|
||||||
<TableRow
|
|
||||||
key={user.baseUrl}
|
|
||||||
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
|
||||||
>
|
|
||||||
<TableCell component="th" scope="row" sx={{paddingLeft: 0}} aria-label={t("prefs_users_table_user_header")}>{user.username}</TableCell>
|
|
||||||
<TableCell aria-label={t("xxxxxxxxxx")}>{user.baseUrl}</TableCell>
|
|
||||||
<TableCell align="right">
|
|
||||||
<IconButton onClick={() => handleEditClick(user)} aria-label={t("prefs_users_edit_button")}>
|
|
||||||
<EditIcon/>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton onClick={() => handleDeleteClick(user)} aria-label={t("prefs_users_delete_button")}>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
<AccessControlDialog
|
|
||||||
key={`userEditDialog${dialogKey}`}
|
|
||||||
open={dialogOpen}
|
|
||||||
user={dialogUser}
|
|
||||||
users={props.entries}
|
|
||||||
onCancel={handleDialogCancel}
|
|
||||||
onSubmit={handleDialogSubmit}
|
|
||||||
/>
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AccessControlDialog = (props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [topic, setTopic] = useState("");
|
|
||||||
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
|
||||||
const addButtonEnabled = (() => {
|
|
||||||
return validTopic(topic);
|
|
||||||
})();
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// TODO
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
|
||||||
<DialogTitle>Add entry</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<TextField
|
|
||||||
autoFocus={editMode}
|
|
||||||
margin="dense"
|
|
||||||
id="topic"
|
|
||||||
label={"Topic"}
|
|
||||||
aria-label={"Topic xx"}
|
|
||||||
value={topic}
|
|
||||||
onChange={ev => setTopic(ev.target.value)}
|
|
||||||
type="text"
|
|
||||||
fullWidth
|
|
||||||
variant="standard"
|
|
||||||
/>
|
|
||||||
<FormControl fullWidth variant="standard" sx={{ margin: 1 }}>
|
|
||||||
<Select value={"read-write"} onChange={() => {}}>
|
|
||||||
<MenuItem value={"private"}>Read/write access only by me</MenuItem>
|
|
||||||
<MenuItem value={"read-only"}>Read/write access by user, anonymous read</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={props.onCancel}>Cancel</Button>
|
|
||||||
<Button onClick={handleSubmit} disabled={!addButtonEnabled}>Add entry</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
const maybeUpdateAccountSettings = async (payload) => {
|
const maybeUpdateAccountSettings = async (payload) => {
|
||||||
if (!session.exists()) {
|
if (!session.exists()) {
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue