diff --git a/server/server.go b/server/server.go index 120a8f99..9801edf6 100644 --- a/server/server.go +++ b/server/server.go @@ -36,6 +36,7 @@ import ( /* TODO + return rate limit information in account stats expire tokens auto-refresh tokens from UI reserve topics @@ -48,7 +49,8 @@ import ( - Pricing - change email - - + Polishing: + aria-label for everything */ diff --git a/web/src/components/Account.js b/web/src/components/Account.js index 1c27e1e5..b5ca6a80 100644 --- a/web/src/components/Account.js +++ b/web/src/components/Account.js @@ -1,5 +1,7 @@ import * as React from 'react'; -import {Stack, useMediaQuery} from "@mui/material"; +import {useState} from 'react'; +import {LinearProgress, Stack, useMediaQuery} from "@mui/material"; +import Tooltip from '@mui/material/Tooltip'; import Typography from "@mui/material/Typography"; import EditIcon from '@mui/icons-material/Edit'; import Container from "@mui/material/Container"; @@ -7,24 +9,26 @@ import Card from "@mui/material/Card"; import Button from "@mui/material/Button"; import {useTranslation} from "react-i18next"; import session from "../app/Session"; -import {useEffect, useState} from "react"; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import theme from "./theme"; -import {validUrl} from "../app/utils"; import Dialog from "@mui/material/Dialog"; import DialogTitle from "@mui/material/DialogTitle"; import DialogContent from "@mui/material/DialogContent"; import TextField from "@mui/material/TextField"; import DialogActions from "@mui/material/DialogActions"; -import userManager from "../app/UserManager"; import api from "../app/Api"; import routes from "./routes"; +import IconButton from "@mui/material/IconButton"; +import {NavLink, useOutletContext} from "react-router-dom"; +import Box from "@mui/material/Box"; const Account = () => { return ( - + + ); @@ -38,14 +42,84 @@ const Basics = () => { Account - {session.username()} + + + + ); +}; + +const Stats = () => { + const { t } = useTranslation(); + const { account } = useOutletContext(); + return ( + + + {t("Usage")} + + + +
+ {account?.role === "admin" + ? <>Unlimited 👑 + : "Free"} +
+
+ +
+ 123 + of 1000 +
+ +
+ +
+ 15 MB used + of 150 MB +
+ +
+ +
+ 2 + of 15 +
+ +
+
+
+ ); +}; + +const Delete = () => { + const { t } = useTranslation(); + return ( + + + {t("Delete account")} + + ); }; +const Username = () => { + const { t } = useTranslation(); + const { account } = useOutletContext(); + return ( + +
+ {session.username()} + {account?.role === "admin" + ? <>{" "}👑 + : ""} +
+
+ ) +}; + const ChangePassword = () => { const { t } = useTranslation(); const [dialogKey, setDialogKey] = useState(0); @@ -69,10 +143,13 @@ const ChangePassword = () => { } }; return ( - - + +
+ ⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤ + + + +
{ } }; return ( - - + +
+ +
{ {t("Delete account")} - {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.")} + {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()})} { const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); const [notificationsGranted, setNotificationsGranted] = useState(notifier.granted()); const [sendDialogOpenMode, setSendDialogOpenMode] = useState(""); + const [account, setAccount] = useState(null); const users = useLiveQuery(() => userManager.all()); const subscriptions = useLiveQuery(() => subscriptionManager.all()); const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0; @@ -95,24 +96,25 @@ const Layout = () => { useEffect(() => { (async () => { - const account = await api.getAccountSettings("http://localhost:2586", session.token()); - if (account) { - if (account.language) { - await i18n.changeLanguage(account.language); + const acc = await api.getAccountSettings("http://localhost:2586", session.token()); + if (acc) { + setAccount(acc); + if (acc.language) { + await i18n.changeLanguage(acc.language); } - if (account.notification) { - if (account.notification.sound) { - await prefs.setSound(account.notification.sound); + if (acc.notification) { + if (acc.notification.sound) { + await prefs.setSound(acc.notification.sound); } - if (account.notification.delete_after) { - await prefs.setDeleteAfter(account.notification.delete_after); + if (acc.notification.delete_after) { + await prefs.setDeleteAfter(acc.notification.delete_after); } - if (account.notification.min_priority) { - await prefs.setMinPriority(account.notification.min_priority); + if (acc.notification.min_priority) { + await prefs.setMinPriority(acc.notification.min_priority); } } - if (account.subscriptions) { - await subscriptionManager.syncFromRemote(account.subscriptions); + if (acc.subscriptions) { + await subscriptionManager.syncFromRemote(acc.subscriptions); } } })(); @@ -135,7 +137,7 @@ const Layout = () => { />
- +