This commit is contained in:
Kilian Hofmann 2024-07-27 21:49:23 +02:00
parent eb3516bfb2
commit a3ec9ae318
7 changed files with 41 additions and 19 deletions

1
exam/dist/assets/index-Bw9FCd3M.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/phpCourse/exam/dist/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/phpCourse/exam/dist/assets/index-CMO4zI4G.js"></script>
<script type="module" crossorigin src="/phpCourse/exam/dist/assets/index-Bw9FCd3M.js"></script>
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/react-DXd9vB-a.js">
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/mui-CxHUbSMi.js">
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/tanstack-xmxrKlZO.js">

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@ class ApiImpl {
private token?: string;
private refreshToken?: string;
private self?: User;
private userListeners: ((user?: User) => void)[] = [];
constructor() {
if (instance) {
@ -26,10 +27,19 @@ class ApiImpl {
public getAuthenticatedUser = () => this.self;
public getCurrentSession = () => [this.token, this.refreshToken];
public subscribeToAuthenticatedUser = (callback: (user?: User) => void) => {
this.userListeners.push(callback);
return () => {
this.userListeners = this.userListeners.filter((item) => item !== callback);
};
};
public logIn = async (email: string, password: string): Promise<void> => {
const { user, token } = await (await this.post('login', { email, password })).json();
this.self = user;
this.token = token;
this.userListeners.forEach((listener) => listener(user));
};
public logOut = async (): Promise<boolean> => {
@ -40,6 +50,7 @@ class ApiImpl {
} finally {
this.self = undefined;
this.token = undefined;
this.userListeners.forEach((listener) => listener());
}
};
@ -62,6 +73,7 @@ class ApiImpl {
public updateUser = async (data: UserUpdate, id?: number): Promise<User> => {
const user = await (await this.patch(`users/${id ?? 'self'}`, data as Record<string, unknown>)).json();
this.self = user;
this.userListeners.forEach((listener) => listener(user));
return user;
};
@ -72,6 +84,7 @@ class ApiImpl {
const user = await (await this.postAuthRaw(`users/${id ?? 'self'}/image`, formData)).json();
this.self = user;
this.userListeners.forEach((listener) => listener(user));
return user;
};

View File

@ -168,14 +168,18 @@ const UserImageDialog: FC<Props> = ({ user, open, onClose }) => {
<InputLabel size="small">{t('Predefined')}</InputLabel>
<Select
name={field.name}
value={field.state.value}
value={field.state.value ?? ''}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
size="small"
label={t('Predefined')}
autoComplete="off"
fullWidth
//renderValue={(selected) => selected ?? 'Keine Auswahl'}
>
<MenuItem value="" selected>
Keine Auswahl
</MenuItem>
{[...Array(10).keys()].map((i) => (
<MenuItem key={`avatar-${i + 1}`} value={`avatar-${i + 1}`}>
{t('Avatar', { name: i + 1 })}

View File

@ -10,9 +10,10 @@ import {
useScrollTrigger,
} from '@mui/material';
import { Link, useRouterState } from '@tanstack/react-router';
import { cloneElement, FC, ReactElement, useState } from 'react';
import { cloneElement, FC, ReactElement, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Api from '../../api/Api';
import { User } from '../../types/User';
import LanguageMenu from '../Menus/Language/LanguageMenu';
import UserMenu from '../Menus/User/UserMenu';
@ -30,11 +31,12 @@ const ElevationScroll = ({ children }: { children: ReactElement }) => {
const Header: FC = () => {
const [anchorUserMenu, setAnchorUserMenu] = useState<null | HTMLElement>(null);
const [anchorLanguageMenu, setAnchorLanguageMenu] = useState<null | HTMLElement>(null);
const [user, setUser] = useState<User>();
const { t } = useTranslation();
const isLoading = useRouterState({ select: (s) => s.status === 'pending' });
const user = Api.getAuthenticatedUser();
useEffect(() => Api.subscribeToAuthenticatedUser((user) => setUser(user)), []);
const handleClose = () => {
setAnchorLanguageMenu(null);
@ -52,20 +54,23 @@ const Header: FC = () => {
</MUILink>
{isLoading && <CircularProgress size={16} thickness={10} sx={{ color: 'white' }} />}
</Box>
<IconButton size="large" onClick={(event) => setAnchorLanguageMenu(event.currentTarget)}>
<Translate sx={{ color: 'white' }} />
</IconButton>
{user ? (
<IconButton onClick={(event) => setAnchorUserMenu(event.currentTarget)} sx={{ p: 0 }}>
<Avatar alt={user.username} src={`storage/${user.image}`}>
<Person />
</Avatar>
<Box sx={{ display: 'flex', gap: 1 }}>
<IconButton size="large" onClick={(event) => setAnchorLanguageMenu(event.currentTarget)}>
<Translate sx={{ color: 'white' }} />
</IconButton>
) : (
<IconButton size="large" onClick={(event) => setAnchorUserMenu(event.currentTarget)} color="inherit">
<AccountCircle />
</IconButton>
)}
{user ? (
<IconButton onClick={(event) => setAnchorUserMenu(event.currentTarget)} sx={{ p: 0 }}>
<Avatar alt={user.username} src={`storage/${user.image}`}>
<Person />
</Avatar>
</IconButton>
) : (
<IconButton size="large" onClick={(event) => setAnchorUserMenu(event.currentTarget)} color="inherit">
<AccountCircle />
</IconButton>
)}
</Box>
<LanguageMenu anchorEl={anchorLanguageMenu} handleClose={handleClose} />
<UserMenu anchorEl={anchorUserMenu} handleClose={handleClose} />
</Toolbar>