Basic user access endpoint
This commit is contained in:
		
							parent
							
								
									b131d676c4
								
							
						
					
					
						commit
						bd86e3d951
					
				
					 9 changed files with 95 additions and 23 deletions
				
			
		|  | @ -18,7 +18,7 @@ class UserManager { | |||
|     } | ||||
| 
 | ||||
|     async save(user) { | ||||
|         if (user.baseUrl === config.baseUrl) { | ||||
|         if (session.exists() && user.baseUrl === config.baseUrl) { | ||||
|             return; | ||||
|         } | ||||
|         await db.users.put(user); | ||||
|  |  | |||
|  | @ -11,12 +11,17 @@ import {NavLink} from "react-router-dom"; | |||
| import AvatarBox from "./AvatarBox"; | ||||
| import {useTranslation} from "react-i18next"; | ||||
| import accountApi, {UnauthorizedError} from "../app/AccountApi"; | ||||
| import IconButton from "@mui/material/IconButton"; | ||||
| import {InputAdornment} from "@mui/material"; | ||||
| import {Visibility, VisibilityOff} from "@mui/icons-material"; | ||||
| 
 | ||||
| const Login = () => { | ||||
|     const { t } = useTranslation(); | ||||
|     const [error, setError] = useState(""); | ||||
|     const [username, setUsername] = useState(""); | ||||
|     const [password, setPassword] = useState(""); | ||||
|     const [showPassword, setShowPassword] = useState(false); | ||||
| 
 | ||||
|     const handleSubmit = async (event) => { | ||||
|         event.preventDefault(); | ||||
|         const user = { username, password }; | ||||
|  | @ -66,11 +71,25 @@ const Login = () => { | |||
|                     fullWidth | ||||
|                     name="password" | ||||
|                     label={t("signup_form_password")} | ||||
|                     type="password" | ||||
|                     type={showPassword ? "text" : "password"} | ||||
|                     id="password" | ||||
|                     value={password} | ||||
|                     onChange={ev => setPassword(ev.target.value.trim())} | ||||
|                     autoComplete="current-password" | ||||
|                     InputProps={{ | ||||
|                         endAdornment: ( | ||||
|                             <InputAdornment position="end"> | ||||
|                                 <IconButton | ||||
|                                     aria-label={t("signup_form_toggle_password_visibility")} | ||||
|                                     onClick={() => setShowPassword(!showPassword)} | ||||
|                                     onMouseDown={(ev) => ev.preventDefault()} | ||||
|                                     edge="end" | ||||
|                                 > | ||||
|                                     {showPassword ? <VisibilityOff /> : <Visibility />} | ||||
|                                 </IconButton> | ||||
|                             </InputAdornment> | ||||
|                         ) | ||||
|                     }} | ||||
|                 /> | ||||
|                 <Button | ||||
|                     type="submit" | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import { | |||
|     TableBody, | ||||
|     TableCell, | ||||
|     TableHead, | ||||
|     TableRow, | ||||
|     TableRow, Tooltip, | ||||
|     useMediaQuery | ||||
| } from "@mui/material"; | ||||
| import Typography from "@mui/material/Typography"; | ||||
|  | @ -38,6 +38,8 @@ 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"; | ||||
| 
 | ||||
| const Preferences = () => { | ||||
|     return ( | ||||
|  | @ -245,14 +247,17 @@ const UserTable = (props) => { | |||
|     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 { | ||||
|  | @ -262,6 +267,7 @@ const UserTable = (props) => { | |||
|             console.log(`[Preferences] Error updating user.`, e); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     const handleDeleteClick = async (user) => { | ||||
|         try { | ||||
|             await userManager.delete(user.baseUrl); | ||||
|  | @ -270,6 +276,7 @@ const UserTable = (props) => { | |||
|             console.error(`[Preferences] Error deleting user for ${user.baseUrl}`, e); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <Table size="small" aria-label={t("prefs_users_table")}> | ||||
|             <TableHead> | ||||
|  | @ -289,18 +296,24 @@ const UserTable = (props) => { | |||
|                                    aria-label={t("prefs_users_table_user_header")}>{user.username}</TableCell> | ||||
|                         <TableCell aria-label={t("prefs_users_table_base_url_header")}>{user.baseUrl}</TableCell> | ||||
|                         <TableCell align="right"> | ||||
|                             {user.baseUrl !== config.baseUrl && | ||||
|                             {(!session.exists() || user.baseUrl !== config.baseUrl) && | ||||
|                                 <> | ||||
|                                     <IconButton onClick={() => handleEditClick(user)} | ||||
|                                                 aria-label={t("prefs_users_edit_button")}> | ||||
|                                     <IconButton onClick={() => handleEditClick(user)} aria-label={t("prefs_users_edit_button")}> | ||||
|                                         <EditIcon/> | ||||
|                                     </IconButton> | ||||
|                                     <IconButton onClick={() => handleDeleteClick(user)} | ||||
|                                                 aria-label={t("prefs_users_delete_button")}> | ||||
|                                     <IconButton onClick={() => handleDeleteClick(user)} aria-label={t("prefs_users_delete_button")}> | ||||
|                                         <CloseIcon/> | ||||
|                                     </IconButton> | ||||
|                                 </> | ||||
|                             } | ||||
|                             {session.exists() && user.baseUrl === config.baseUrl && | ||||
|                                 <Tooltip title={t("prefs_users_table_cannot_delete_or_edit")}> | ||||
|                                     <span> | ||||
|                                         <IconButton disabled><EditIcon/></IconButton> | ||||
|                                         <IconButton disabled><CloseIcon/></IconButton> | ||||
|                                     </span> | ||||
|                                 </Tooltip> | ||||
|                             } | ||||
|                         </TableCell> | ||||
|                     </TableRow> | ||||
|                 ))} | ||||
|  |  | |||
|  | @ -11,13 +11,16 @@ import AvatarBox from "./AvatarBox"; | |||
| import {useTranslation} from "react-i18next"; | ||||
| import WarningAmberIcon from "@mui/icons-material/WarningAmber"; | ||||
| import accountApi, {AccountCreateLimitReachedError, UsernameTakenError} from "../app/AccountApi"; | ||||
| import {InputAdornment} from "@mui/material"; | ||||
| import IconButton from "@mui/material/IconButton"; | ||||
| import {Visibility, VisibilityOff} from "@mui/icons-material"; | ||||
| 
 | ||||
| const Signup = () => { | ||||
|     const { t } = useTranslation(); | ||||
|     const [error, setError] = useState(""); | ||||
|     const [username, setUsername] = useState(""); | ||||
|     const [password, setPassword] = useState(""); | ||||
|     const [confirm, setConfirm] = useState(""); | ||||
|     const [showPassword, setShowPassword] = useState(false); | ||||
|     const handleSubmit = async (event) => { | ||||
|         event.preventDefault(); | ||||
|         const user = { username, password }; | ||||
|  | @ -70,29 +73,31 @@ const Signup = () => { | |||
|                     fullWidth | ||||
|                     name="password" | ||||
|                     label={t("signup_form_password")} | ||||
|                     type="password" | ||||
|                     type={showPassword ? "text" : "password"} | ||||
|                     id="password" | ||||
|                     autoComplete="current-password" | ||||
|                     value={password} | ||||
|                     onChange={ev => setPassword(ev.target.value.trim())} | ||||
|                 /> | ||||
|                 <TextField | ||||
|                     margin="dense" | ||||
|                     required | ||||
|                     fullWidth | ||||
|                     name="confirm-password" | ||||
|                     label={t("signup_form_confirm_password")} | ||||
|                     type="password" | ||||
|                     id="confirm-password" | ||||
|                     value={confirm} | ||||
|                     onChange={ev => setConfirm(ev.target.value.trim())} | ||||
| 
 | ||||
|                     InputProps={{ | ||||
|                         endAdornment: ( | ||||
|                             <InputAdornment position="end"> | ||||
|                                 <IconButton | ||||
|                                     aria-label={t("signup_form_toggle_password_visibility")} | ||||
|                                     onClick={() => setShowPassword(!showPassword)} | ||||
|                                     onMouseDown={(ev) => ev.preventDefault()} | ||||
|                                     edge="end" | ||||
|                                 > | ||||
|                                     {showPassword ? <VisibilityOff /> : <Visibility />} | ||||
|                                 </IconButton> | ||||
|                             </InputAdornment> | ||||
|                         ) | ||||
|                     }} | ||||
|                 /> | ||||
|                 <Button | ||||
|                     type="submit" | ||||
|                     fullWidth | ||||
|                     variant="contained" | ||||
|                     disabled={username === "" || password === "" || password !== confirm} | ||||
|                     disabled={username === "" || password === ""} | ||||
|                     sx={{mt: 2, mb: 2}} | ||||
|                 > | ||||
|                     {t("signup_form_button_submit")} | ||||
|  |  | |||
|  | @ -18,6 +18,8 @@ import {useTranslation} from "react-i18next"; | |||
| import session from "../app/Session"; | ||||
| import routes from "./routes"; | ||||
| import accountApi, {UnauthorizedError} from "../app/AccountApi"; | ||||
| import IconButton from "@mui/material/IconButton"; | ||||
| import PublicIcon from '@mui/icons-material/Public'; | ||||
| 
 | ||||
| const publicBaseUrl = "https://ntfy.sh"; | ||||
| 
 | ||||
|  | @ -123,6 +125,9 @@ const SubscribePage = (props) => { | |||
|                     {t("subscribe_dialog_subscribe_description")} | ||||
|                 </DialogContentText> | ||||
|                 <div style={{display: 'flex'}} role="row"> | ||||
|                     <IconButton color="inherit" size="large" edge="start" sx={{height: "45px", marginTop: "5px", color: "grey"}}> | ||||
|                         <PublicIcon/> | ||||
|                     </IconButton> | ||||
|                     <TextField | ||||
|                         autoFocus | ||||
|                         margin="dense" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue