forked from mirrors/ntfy
WIP Access control UI
This commit is contained in:
parent
e650f813c5
commit
7487b0da58
2 changed files with 142 additions and 6 deletions
|
@ -183,7 +183,7 @@
|
|||
"account_usage_plan_code_business_plus": "Business Plus",
|
||||
"account_usage_messages_title": "Published messages",
|
||||
"account_usage_emails_title": "Emails sent",
|
||||
"account_usage_topics_title": "Topics reserved",
|
||||
"account_usage_topics_title": "Reserved topics",
|
||||
"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.",
|
||||
|
@ -239,6 +239,17 @@
|
|||
"prefs_users_dialog_button_save": "Save",
|
||||
"prefs_appearance_title": "Appearance",
|
||||
"prefs_appearance_language_title": "Language",
|
||||
"prefs_access_title": "Reserved topics",
|
||||
"prefs_access_description": "You may reserve topic names for personal use here, and define access to a topic for other users.",
|
||||
"prefs_access_add_button": "Add reserved topic",
|
||||
"prefs_access_edit_button": "Edit topic access",
|
||||
"prefs_access_delete_button": "Reset topic access",
|
||||
"prefs_access_table": "Reserved topics table",
|
||||
"prefs_access_table_topic_header": "Topic",
|
||||
"prefs_access_table_access_header": "Access",
|
||||
"prefs_access_table_perms_private": "Only I can publish and subscribe",
|
||||
"prefs_access_table_perms_public_read": "I can publish, everyone can subscribe",
|
||||
"prefs_access_table_perms_public": "Everyone can publish and subscribe",
|
||||
"priority_min": "min",
|
||||
"priority_low": "low",
|
||||
"priority_default": "default",
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow, Tooltip,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
useMediaQuery
|
||||
} from "@mui/material";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
@ -32,22 +33,23 @@ import DialogTitle from "@mui/material/DialogTitle";
|
|||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import userManager from "../app/UserManager";
|
||||
import {playSound, shuffle, sounds, validTopic, validUrl} from "../app/utils";
|
||||
import {playSound, shuffle, sounds, 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 {Pref, PrefGroup} from "./Pref";
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {useOutletContext} from "react-router-dom";
|
||||
import LockIcon from "@mui/icons-material/Lock";
|
||||
|
||||
const Preferences = () => {
|
||||
return (
|
||||
<Container maxWidth="md" sx={{marginTop: 3, marginBottom: 3}}>
|
||||
<Stack spacing={3}>
|
||||
<Notifications/>
|
||||
<Appearance/>
|
||||
<Access/>
|
||||
<Users/>
|
||||
<Appearance/>
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
|
@ -471,6 +473,129 @@ const Language = () => {
|
|||
)
|
||||
};
|
||||
|
||||
const Access = () => {
|
||||
const { t } = useTranslation();
|
||||
const { account } = useOutletContext();
|
||||
const [dialogKey, setDialogKey] = useState(0);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
|
||||
const handleAddClick = () => {
|
||||
setDialogKey(prev => prev+1);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleDialogCancel = () => {
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleDialogSubmit = async (entry) => {
|
||||
setDialogOpen(false);
|
||||
try {
|
||||
await accountApi.addAccessEntry();
|
||||
console.debug(`[Preferences] Added entry ${entry.topic}`);
|
||||
} catch (e) {
|
||||
console.log(`[Preferences] Error adding access entry.`, e);
|
||||
}
|
||||
};
|
||||
|
||||
if (!session.exists() || !account) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card sx={{ padding: 1 }} aria-label={t("prefs_access_title")}>
|
||||
<CardContent sx={{ paddingBottom: 1 }}>
|
||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
||||
{t("prefs_access_title")}
|
||||
</Typography>
|
||||
<Paragraph>
|
||||
{t("prefs_access_description")}
|
||||
</Paragraph>
|
||||
{account.access.length > 0 && <AccessTable entries={account.access}/>}
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button onClick={handleAddClick}>{t("prefs_access_add_button")}</Button>
|
||||
{/*<UserDialog
|
||||
key={`userEditDialog${dialogKey}`}
|
||||
open={dialogOpen}
|
||||
user={dialogUser}
|
||||
users={props.users}
|
||||
onCancel={handleDialogCancel}
|
||||
onSubmit={handleDialogSubmit}
|
||||
/>*/}
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const AccessTable = (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);
|
||||
// FIXME
|
||||
};
|
||||
|
||||
const handleDeleteClick = async (user) => {
|
||||
// FIXME
|
||||
};
|
||||
|
||||
return (
|
||||
<Table size="small" aria-label={t("prefs_access_table")}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{paddingLeft: 0}}>{t("prefs_access_table_topic_header")}</TableCell>
|
||||
<TableCell>{t("prefs_access_table_access_header")}</TableCell>
|
||||
<TableCell/>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{props.entries.map(entry => (
|
||||
<TableRow
|
||||
key={entry.topic}
|
||||
sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
||||
>
|
||||
<TableCell component="th" scope="row" sx={{paddingLeft: 0}} aria-label={t("prefs_access_table_topic_header")}>{entry.topic}</TableCell>
|
||||
<TableCell aria-label={t("prefs_access_table_access_header")}>
|
||||
<LockIcon fontSize="small" sx={{verticalAlign: "bottom", mr: 0.5}}/>
|
||||
{t("prefs_access_table_perms_private")}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton onClick={() => handleEditClick(entry)} aria-label={t("prefs_access_edit_button")}>
|
||||
<EditIcon/>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => handleDeleteClick(entry)} aria-label={t("prefs_access_delete_button")}>
|
||||
<CloseIcon/>
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
{/*<UserDialog
|
||||
key={`userEditDialog${dialogKey}`}
|
||||
open={dialogOpen}
|
||||
user={dialogUser}
|
||||
users={props.users}
|
||||
onCancel={handleDialogCancel}
|
||||
onSubmit={handleDialogSubmit}
|
||||
/>*/}
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const maybeUpdateAccountSettings = async (payload) => {
|
||||
if (!session.exists()) {
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue