From e443ec3cd7c83e5d4511b92d3e401f6881552567 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Fri, 15 Oct 2021 21:53:02 +0100 Subject: [PATCH] Cleanup navbar, display user picture with dropdown for user-related things --- chartered-db/src/organisations.rs | 2 +- chartered-frontend/src/index.sass | 1 + chartered-frontend/src/useAuth.tsx | 27 +++++++++++++++++++++++++++ chartered-frontend/src/util.tsx | 14 ++++++++------ chartered-frontend/src/pages/Dashboard.tsx | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- chartered-frontend/src/sections/Nav.tsx | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- chartered-frontend/src/pages/organisations/CreateOrganisation.tsx | 19 ++++++++++--------- chartered-web/src/endpoints/web_api/auth/mod.rs | 2 ++ 8 files changed, 143 insertions(+), 74 deletions(-) diff --git a/chartered-db/src/organisations.rs b/chartered-db/src/organisations.rs index 00953b0..b4495b8 100644 --- a/chartered-db/src/organisations.rs +++ a/chartered-db/src/organisations.rs @@ -106,7 +106,7 @@ let conn = conn.get()?; conn.transaction::<_, crate::Error, _>(|| { - use organisations::dsl::{description, id, name, uuid, public}; + use organisations::dsl::{description, id, name, public, uuid}; use user_organisation_permissions::dsl::{organisation_id, permissions, user_id}; let generated_uuid = SqlUuid::random(); diff --git a/chartered-frontend/src/index.sass b/chartered-frontend/src/index.sass index 1697bf7..1d272bf 100644 --- a/chartered-frontend/src/index.sass +++ a/chartered-frontend/src/index.sass @@ -1,5 +1,6 @@ $primary: #0d6efd $font-family-monospace: "Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace +$font-size-base: 0.875rem $enable-cssgrid: true @import "../node_modules/react-placeholder/lib/reactPlaceholder.css" diff --git a/chartered-frontend/src/useAuth.tsx b/chartered-frontend/src/useAuth.tsx index 8fd1679..99f28f3 100644 --- a/chartered-frontend/src/useAuth.tsx +++ a/chartered-frontend/src/useAuth.tsx @@ -13,6 +13,7 @@ key: string; expires: number; error?: string; + picture_url?: string; } export interface AuthContext { @@ -21,6 +22,7 @@ logout: () => Promise; getAuthKey: () => Promise; getUserUuid: () => string; + getPictureUrl: () => string; handleLoginResponse: (json: LoginResponse) => any; } @@ -66,7 +68,12 @@ function useProvideAuth(): AuthContext { const [auth, setAuth] = useState(() => { let authStorage = getAuthStorage(); - return [authStorage.userUuid, authStorage.authKey, authStorage.expires]; + return [ + authStorage.userUuid, + authStorage.authKey, + authStorage.expires, + authStorage.pictureUrl, + ]; }); useEffect(() => { @@ -76,6 +83,7 @@ userUuid: auth?.[0], authKey: auth?.[1], expires: auth?.[2], + pictureUrl: auth?.[3], }) ); }, [auth]); @@ -85,7 +93,12 @@ throw new Error(response.error); } - setAuth([response.user_uuid, response.key, new Date(response.expires)]); + setAuth([ + response.user_uuid, + response.key, + new Date(response.expires), + response.picture_url, + ]); }; const login = async (username: string, password: string) => { @@ -143,11 +156,20 @@ } }; + const getPictureUrl = () => { + if (auth?.[2] > new Date()) { + return auth[3]; + } else if (auth) { + return null; + } + }; + return { login, logout, getAuthKey, getUserUuid, + getPictureUrl, oauthLogin, handleLoginResponse, }; @@ -160,5 +182,6 @@ userUuid: initial?.userUuid || null, authKey: initial?.authKey || null, expires: initial?.expires ? new Date(initial.expires) : null, + pictureUrl: initial?.pictureUrl, }; } diff --git a/chartered-frontend/src/util.tsx b/chartered-frontend/src/util.tsx index d3addf4..5c57d39 100644 --- a/chartered-frontend/src/util.tsx +++ a/chartered-frontend/src/util.tsx @@ -1,7 +1,7 @@ import React = require("react"); import ReactPlaceholder from "react-placeholder"; import { AuthContext } from "./useAuth"; -import {PersonFill} from "react-bootstrap-icons"; +import { PersonFill } from "react-bootstrap-icons"; export const BASE_URL = process.env.BASE_URL || "http://localhost:8888"; @@ -99,11 +99,13 @@ className={`position-relative rounded-circle d-inline-flex justify-content-center align-items-center ${className}`} style={{ width, height, background: "rgb(235, 235, 235)" }} > - + ); } diff --git a/chartered-frontend/src/pages/Dashboard.tsx b/chartered-frontend/src/pages/Dashboard.tsx index cf72159..b9318da 100644 --- a/chartered-frontend/src/pages/Dashboard.tsx +++ a/chartered-frontend/src/pages/Dashboard.tsx @@ -1,12 +1,12 @@ import React = require("react"); import { Link } from "react-router-dom"; import { useAuth } from "../useAuth"; import Nav from "../sections/Nav"; -import {Calendar3, ChevronRight, Download} from "react-bootstrap-icons"; +import { Calendar3, ChevronRight, Download } from "react-bootstrap-icons"; import { useAuthenticatedRequest } from "../util"; import HumanTime from "react-human-time"; -import {OverlayTrigger, Tooltip} from "react-bootstrap"; +import { OverlayTrigger, Tooltip } from "react-bootstrap"; interface RecentlyCreatedResponse { crates: RecentlyCreatedResponseVersion[]; @@ -42,10 +42,10 @@ const auth = useAuth(); const { response: recentlyCreated, error: recentlyCreatedError } = - useAuthenticatedRequest({ - auth, - endpoint: "crates/recently-created", - }); + useAuthenticatedRequest({ + auth, + endpoint: "crates/recently-created", + }); const { response: recentlyUpdated, error: recentlyUpdatedError } = useAuthenticatedRequest({ @@ -54,10 +54,10 @@ }); const { response: mostDownloaded, error: mostDownloadedError } = - useAuthenticatedRequest({ - auth, - endpoint: "crates/most-downloaded", - }); + useAuthenticatedRequest({ + auth, + endpoint: "crates/most-downloaded", + }); return (
@@ -84,40 +84,50 @@

Newly Created

{(recentlyCreated?.crates || []).map((v) => ( - - - {new Date(v.created_at).toLocaleString()} - - } - > - - {" "} - - - - + + + {new Date(v.created_at).toLocaleString()} + + } + > + + {" "} + + + + ))}

Recently Updated

{(recentlyUpdated?.versions || []).map((v) => ( - v{v.version} + + v{v.version} + ))}

Most Downloaded

{(mostDownloaded?.crates || []).map((v) => ( - - {v.downloads.toLocaleString()} - + + {v.downloads.toLocaleString()} + ))}
@@ -126,7 +136,11 @@ ); } -function CrateCard({ name, organisation, children }: React.PropsWithChildren<{ name: string, organisation: string }>) { +function CrateCard({ + name, + organisation, + children, +}: React.PropsWithChildren<{ name: string; organisation: string }>) { return ( +
✈️ chartered - - - - -
+
    +
  • + + + + + + + + Your profile + + + + + + Logout + + + +
  • +
+ - +
); } diff --git a/chartered-frontend/src/pages/organisations/CreateOrganisation.tsx b/chartered-frontend/src/pages/organisations/CreateOrganisation.tsx index 75de76e..b4b7006 100644 --- a/chartered-frontend/src/pages/organisations/CreateOrganisation.tsx +++ a/chartered-frontend/src/pages/organisations/CreateOrganisation.tsx @@ -33,7 +33,7 @@ headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ name, description, 'public': publicOrg }), + body: JSON.stringify({ name, description, public: publicOrg }), }); let json = await res.json(); @@ -70,7 +70,7 @@ className="btn-close" aria-label="Close" onClick={() => setError("")} - /> + />
@@ -111,15 +111,16 @@
setPublicOrg(e.target.checked)} - disabled={loading} + type="checkbox" + checked={publicOrg} + id="org-public" + className="form-check-input" + onChange={(e) => setPublicOrg(e.target.checked)} + disabled={loading} />
diff --git a/chartered-web/src/endpoints/web_api/auth/mod.rs b/chartered-web/src/endpoints/web_api/auth/mod.rs index 3711f44..1b3303e 100644 --- a/chartered-web/src/endpoints/web_api/auth/mod.rs +++ a/chartered-web/src/endpoints/web_api/auth/mod.rs @@ -38,6 +38,7 @@ user_uuid: Uuid, key: String, expires: chrono::DateTime, + picture_url: Option, } pub async fn login( @@ -67,5 +68,6 @@ user_uuid: user.uuid.0, key: key.session_key, expires, + picture_url: user.picture_url, }) } -- rgit 0.1.3