1
0
Fork 0
mirror of https://github.com/binwiederhier/ntfy.git synced 2025-05-28 17:35:36 +02:00

Login page of "subscribe dialog", still WIP, but looking nice

This commit is contained in:
Philipp Heckel 2022-02-25 16:07:25 -05:00
parent 1599793de2
commit 6d343c0f1a
8 changed files with 366 additions and 11 deletions

View file

@ -23,7 +23,10 @@ class Api {
async auth(baseUrl, topic, user) {
const url = topicUrlAuth(baseUrl, topic);
console.log(`[Api] Checking auth for ${url}`);
const response = await fetch(url);
const headers = this.maybeAddAuthorization({}, user);
const response = await fetch(url, {
headers: headers
});
if (response.status >= 200 && response.status <= 299) {
return true;
} else if (!user && response.status === 404) {
@ -33,6 +36,14 @@ class Api {
}
throw new Error(`Unexpected server response ${response.status}`);
}
maybeAddAuthorization(headers, user) {
if (user) {
const encoded = new Buffer(`${user.username}:${user.password}`).toString('base64');
headers['Authorization'] = `Basic ${encoded}`;
}
return headers;
}
}
const api = new Api();

View file

@ -4,11 +4,11 @@ import Subscriptions from "./Subscriptions";
export class Repository {
loadSubscriptions() {
console.log(`[Repository] Loading subscriptions from localStorage`);
const subscriptions = new Subscriptions();
const serialized = localStorage.getItem('subscriptions');
if (serialized === null) return subscriptions;
if (serialized === null) {
return subscriptions;
}
try {
const serializedSubscriptions = JSON.parse(serialized);
serializedSubscriptions.forEach(s => {
@ -26,7 +26,6 @@ export class Repository {
saveSubscriptions(subscriptions) {
console.log(`[Repository] Saving ${subscriptions.size()} subscription(s) to localStorage`);
const serialized = JSON.stringify(subscriptions.map( (id, subscription) => {
return {
baseUrl: subscription.baseUrl,
@ -37,6 +36,30 @@ export class Repository {
}));
localStorage.setItem('subscriptions', serialized);
}
loadUsers() {
console.log(`[Repository] Loading users from localStorage`);
const serialized = localStorage.getItem('users');
if (serialized === null) {
return {};
}
try {
return JSON.parse(serialized);
} catch (e) {
console.log(`[Repository] Unable to deserialize users: ${e.message}`);
return {};
}
}
saveUser(baseUrl, username, password) {
console.log(`[Repository] Saving users to localStorage`);
const users = this.loadUsers();
users[baseUrl] = {
username: username,
password: password
};
localStorage.setItem('users', users);
}
}
const repository = new Repository();

View file

@ -12,6 +12,7 @@ import {useMediaQuery} from "@mui/material";
import theme from "./theme";
import api from "../app/Api";
import {topicUrl} from "../app/utils";
import useStyles from "./styles";
const defaultBaseUrl = "http://127.0.0.1"
//const defaultBaseUrl = "https://ntfy.sh"
@ -19,6 +20,7 @@ const defaultBaseUrl = "http://127.0.0.1"
const SubscribeDialog = (props) => {
const [baseUrl, setBaseUrl] = useState(defaultBaseUrl); // FIXME
const [topic, setTopic] = useState("");
const [user, setUser] = useState(null);
const [showLoginPage, setShowLoginPage] = useState(false);
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const handleCancel = () => {
@ -45,8 +47,10 @@ const SubscribeDialog = (props) => {
onSubmit={handleSubmit}
/>}
{showLoginPage && <LoginPage
baseUrl={baseUrl}
topic={topic}
onBack={() => setShowLoginPage(false)}
onSubmit={handleSubmit}
/>}
</Dialog>
);
@ -82,6 +86,22 @@ const SubscribePage = (props) => {
};
const LoginPage = (props) => {
const styles = useStyles();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [errorText, setErrorText] = useState("");
const baseUrl = props.baseUrl;
const topic = props.topic;
const handleLogin = async () => {
const user = {username: username, password: password};
const success = await api.auth(baseUrl, topic, user);
if (!success) {
console.log(`[SubscribeDialog] Login to ${topicUrl(baseUrl, topic)} failed for user ${username}`);
setErrorText(`User ${username} not authorized`);
return;
}
console.log(`[SubscribeDialog] Login to ${topicUrl(baseUrl, topic)} successful for user ${username}`);
};
return (
<>
<DialogTitle>Login required</DialogTitle>
@ -95,6 +115,8 @@ const LoginPage = (props) => {
margin="dense"
id="username"
label="Username, e.g. phil"
value={username}
onChange={ev => setUsername(ev.target.value)}
type="text"
fullWidth
variant="standard"
@ -104,14 +126,21 @@ const LoginPage = (props) => {
id="password"
label="Password"
type="password"
value={password}
onChange={ev => setPassword(ev.target.value)}
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={props.onBack}>Back</Button>
<Button>Login</Button>
</DialogActions>
<div className={styles.bottomBar}>
<DialogContentText className={styles.statusText}>
{errorText}
</DialogContentText>
<DialogActions>
<Button onClick={props.onBack}>Back</Button>
<Button onClick={handleLogin}>Login</Button>
</DialogActions>
</div>
</>
);
};

View file

@ -0,0 +1,18 @@
import {makeStyles} from "@mui/styles";
const useStyles = makeStyles(theme => ({
bottomBar: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: '24px',
paddingTop: '8px 24px',
paddingBottom: '8px 24px',
},
statusText: {
margin: '0px',
paddingTop: '8px',
}
}));
export default useStyles;

View file

@ -1,7 +1,6 @@
import { red } from '@mui/material/colors';
import { createTheme } from '@mui/material/styles';
// A custom theme for this app
const theme = createTheme({
palette: {
primary: {