-
-
-
-
{uploader.displayName}
-
-
-
- )
-}
\ No newline at end of file
diff --git a/VoidCat/spa/src/FooterLinks.js b/VoidCat/spa/src/FooterLinks.js
index c3c332b..37c6aab 100644
--- a/VoidCat/spa/src/FooterLinks.js
+++ b/VoidCat/spa/src/FooterLinks.js
@@ -1,21 +1,12 @@
import "./FooterLinks.css"
import StrikeLogo from "./image/strike.png";
-import {Link} from "react-router-dom";
-import {useSelector} from "react-redux";
-export function FooterLinks(){
- const auth = useSelector(state => state.login.jwt);
- const profile = useSelector(state => state.login.profile);
-
+export function FooterLinks(){
return (
);
}
\ No newline at end of file
diff --git a/VoidCat/spa/src/Header.css b/VoidCat/spa/src/Header.css
new file mode 100644
index 0000000..1a0e36b
--- /dev/null
+++ b/VoidCat/spa/src/Header.css
@@ -0,0 +1,12 @@
+.header {
+ user-select: none;
+ display: flex;
+ padding: 5px 0;
+ align-items: center;
+}
+
+.header .title {
+ font-size: 30px;
+ line-height: 2;
+ flex-grow: 1;
+}
\ No newline at end of file
diff --git a/VoidCat/spa/src/Header.js b/VoidCat/spa/src/Header.js
new file mode 100644
index 0000000..fdbfe53
--- /dev/null
+++ b/VoidCat/spa/src/Header.js
@@ -0,0 +1,23 @@
+import "./Header.css";
+import {Link} from "react-router-dom";
+import {useSelector} from "react-redux";
+import {InlineProfile} from "./InlineProfile";
+
+export function Header() {
+ const profile = useSelector(state => state.login.profile);
+
+ return (
+
+
+ void.cat
+
+ {profile ?
+
:
+
+
Login
+ }
+
+ )
+}
\ No newline at end of file
diff --git a/VoidCat/spa/src/FileUploader.css b/VoidCat/spa/src/InlineProfile.css
similarity index 57%
rename from VoidCat/spa/src/FileUploader.css
rename to VoidCat/spa/src/InlineProfile.css
index 7145572..1d80d53 100644
--- a/VoidCat/spa/src/FileUploader.css
+++ b/VoidCat/spa/src/InlineProfile.css
@@ -1,14 +1,10 @@
-.uploader-info {
- margin-top: 10px;
- text-align: start;
-}
-.uploader-info .small-profile {
+.small-profile {
display: inline-flex;
align-items: center;
}
-.uploader-info .small-profile .avatar {
+.small-profile .avatar {
width: 64px;
height: 64px;
border-radius: 16px;
@@ -17,6 +13,6 @@
background-repeat: no-repeat;
}
-.uploader-info .small-profile .name {
+.small-profile .name {
padding-left: 15px;
}
\ No newline at end of file
diff --git a/VoidCat/spa/src/InlineProfile.js b/VoidCat/spa/src/InlineProfile.js
new file mode 100644
index 0000000..c6fe873
--- /dev/null
+++ b/VoidCat/spa/src/InlineProfile.js
@@ -0,0 +1,38 @@
+import "./InlineProfile.css";
+import {DefaultAvatar} from "./Const";
+import {Link} from "react-router-dom";
+
+const DefaultSize = 64;
+
+export function InlineProfile(props) {
+ const profile = props.profile;
+ const options = {
+ size: DefaultSize,
+ showName: true,
+ link: true,
+ ...props.options
+ };
+
+ let avatarUrl = profile.avatar ?? DefaultAvatar;
+ if (!avatarUrl.startsWith("http")) {
+ avatarUrl = `/d/${avatarUrl}`;
+ }
+ let avatarStyles = {
+ backgroundImage: `url(${avatarUrl})`
+ };
+ if (options.size !== DefaultSize) {
+ avatarStyles.width = `${options.size}px`;
+ avatarStyles.height = `${options.size}px`;
+ }
+
+ let elms = (
+
+
+ {options.showName ?
{profile.displayName}
: null}
+
+ );
+ if (options.link === true) {
+ return
{elms}
+ }
+ return elms;
+}
\ No newline at end of file
diff --git a/VoidCat/spa/src/LoginState.js b/VoidCat/spa/src/LoginState.js
index ffbaac1..94a08f7 100644
--- a/VoidCat/spa/src/LoginState.js
+++ b/VoidCat/spa/src/LoginState.js
@@ -22,6 +22,7 @@ export const LoginState = createSlice({
},
logout: (state) => {
state.jwt = null;
+ state.profile = null;
window.localStorage.removeItem(LocalStorageKey);
window.localStorage.removeItem(LocalStorageProfileKey);
}
diff --git a/VoidCat/spa/src/Profile.css b/VoidCat/spa/src/Profile.css
index 03f9d8c..b2674af 100644
--- a/VoidCat/spa/src/Profile.css
+++ b/VoidCat/spa/src/Profile.css
@@ -39,6 +39,10 @@
opacity: 1;
}
-.profile .roles {
- margin: 20px 0;
+.profile .roles > span {
+ margin-right: 10px;
+}
+
+.profile dt {
+ font-weight: bold;
}
\ No newline at end of file
diff --git a/VoidCat/spa/src/Profile.js b/VoidCat/spa/src/Profile.js
index 554880f..f9bca46 100644
--- a/VoidCat/spa/src/Profile.js
+++ b/VoidCat/spa/src/Profile.js
@@ -4,12 +4,15 @@ import {useApi} from "./Api";
import {ApiHost, DefaultAvatar} from "./Const";
import "./Profile.css";
import {useDispatch, useSelector} from "react-redux";
-import {setProfile as setGlobalProfile} from "./LoginState";
+import {logout, setProfile as setGlobalProfile} from "./LoginState";
import {DigestAlgo} from "./FileUpload";
import {buf2hex} from "./Util";
+import moment from "moment";
+import FeatherIcon from "feather-icons-react";
export function Profile() {
const [profile, setProfile] = useState();
+ const [saved, setSaved] = useState(false);
const auth = useSelector(state => state.login.jwt);
const localProfile = useSelector(state => state.login.profile);
const canEdit = localProfile?.id === profile?.id;
@@ -52,7 +55,7 @@ export function Profile() {
const file = res[0];
const buf = await file.arrayBuffer();
const digest = await crypto.subtle.digest(DigestAlgo, buf);
-
+
let req = await fetch(`${ApiHost}/upload`, {
mode: "cors",
method: "POST",
@@ -65,19 +68,19 @@ export function Profile() {
"Authorization": `Bearer ${auth}`
}
});
-
- if(req.ok) {
+
+ if (req.ok) {
let rsp = await req.json();
- if(rsp.ok) {
+ if (rsp.ok) {
setProfile({
...profile,
avatar: rsp.file.id
});
}
}
-
- }
-
+
+ }
+
async function saveUser() {
let r = await Api.updateUser({
id: profile.id,
@@ -88,6 +91,7 @@ export function Profile() {
if (r.ok) {
// saved
dispatch(setGlobalProfile(profile));
+ setSaved(true);
}
}
@@ -95,9 +99,15 @@ export function Profile() {
loadProfile();
}, []);
+ useEffect(() => {
+ if (saved === true) {
+ setTimeout(() => setSaved(false), 1000);
+ }
+ }, [saved]);
+
if (profile) {
let avatarUrl = profile.avatar ?? DefaultAvatar;
- if(!avatarUrl.startsWith("http")) {
+ if (!avatarUrl.startsWith("http")) {
// assume void-cat hosted avatar
avatarUrl = `/d/${avatarUrl}`;
}
@@ -113,14 +123,24 @@ export function Profile() {
onChange={(e) => editUsername(e.target.value)}/>
: profile.displayName}
-
- {canEdit ?
changeAvatar()}>
-
Edit
- : null}
-
-
-
Roles:
- {profile.roles.map(a =>
{a})}
+
+
+
+ {canEdit ?
changeAvatar()}>
+
Edit
+ : null}
+
+
+
+
+ - Created
+ - {moment(profile.created).fromNow()}
+ - Roles
+ - {profile.roles.map(a => {a})}
+ - Files
+ - 0
+
+
{canEdit ?
@@ -129,7 +149,17 @@ export function Profile() {
editPublic(e.target.checked)}/>
- Save
+
+
+
+ {saved ? : null}
+
+
+
dispatch(logout())}>Logout
+
+
: null}
diff --git a/VoidCat/spa/src/index.css b/VoidCat/spa/src/index.css
index 7423c6a..a3d36cd 100644
--- a/VoidCat/spa/src/index.css
+++ b/VoidCat/spa/src/index.css
@@ -29,4 +29,17 @@ a:hover {
padding: 10px 20px;
user-select: none;
cursor: pointer;
+ margin: 5px;
+}
+
+.flex {
+ display: flex;
+}
+
+.flx-1 {
+ flex: 1;
+}
+
+.flex-center {
+ align-items: center;
}
\ No newline at end of file