Skip to content

Commit

Permalink
feat: new login ui
Browse files Browse the repository at this point in the history
  • Loading branch information
OXeu committed Jul 11, 2024
1 parent 72ae3c2 commit 16cee03
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 50 deletions.
13 changes: 10 additions & 3 deletions client/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,16 @@
},
"listed": "Listed in articles",
"login": {
"required": "Login required"
"oauth_only": "Please log in to continue",
"password": {
"placeholder": "Please enter your password"
},
"required": "Please log in to continue",
"title": "Login",
"username": {
"placeholder": "Please enter your username"
}
},
"logout": "Logout",
"next": "Next Page",
"preview": "Preview",
"previous": "Previous Page",
Expand Down Expand Up @@ -214,4 +221,4 @@
},
"writing": "Writing",
"year$year": "{{year}}"
}
}
11 changes: 9 additions & 2 deletions client/public/locales/ja/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,16 @@
},
"listed": "記事にリスト",
"login": {
"required": "ログインが必要です"
"oauth_only": "続行するにはログインしてください",
"password": {
"placeholder": "パスワードを入力してください"
},
"required": "続行するにはログインしてください",
"title": "ログイン",
"username": {
"placeholder": "ユーザー名を入力してください"
}
},
"logout": "ログアウト",
"next": "次のページ",
"preview": "プレビュー",
"previous": "前のページ",
Expand Down
11 changes: 9 additions & 2 deletions client/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,16 @@
},
"listed": "列出在文章中",
"login": {
"required": "需要登录"
"oauth_only": "请登录后继续",
"password": {
"placeholder": "请输入密码"
},
"required": "请登录后继续",
"title": "登录",
"username": {
"placeholder": "请输入用户名"
}
},
"logout": "退出登录",
"next": "下一页",
"preview": "预览",
"previous": "上一页",
Expand Down
88 changes: 56 additions & 32 deletions client/src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import ReactModal from "react-modal";
import Popup from "reactjs-popup";
import { removeCookie } from "typescript-cookie";
import { Link, useLocation } from "wouter";
import { oauth_url } from "../main";
import { useLoginModal } from "../hooks/useLoginModal";
import { Profile, ProfileContext } from "../state/profile";
import { Button } from "./button";
import { Icon } from "./icon";
import { IconSmall } from "./icon";
import { Input } from "./input";
import { Padding } from "./padding";

Expand Down Expand Up @@ -92,33 +92,6 @@ function NavItem({ menu, title, selected, href, when = true, onClick }: {
)
}

function UserAvatar({ profile, className, mobile }: { className?: string, profile?: Profile, mobile?: boolean }) {
const { t } = useTranslation()
const githubLoginText = t('github_login')
return (<div className={"flex flex-row justify-end " + className}>
{profile?.avatar ? <>
<div className="relative">
<img src={profile.avatar} alt="Avatar" className="w-10 h-10 rounded-full border-2" />
<div className="z-50 absolute left-0 top-0 w-10 h-10 opacity-0 hover:opacity-100 duration-300">
<Icon label={t('logout')} name="ri-logout-circle-line ri-xl" onClick={() => {
removeCookie("token")
window.location.reload()
}} hover={false} />
</div>
</div>
</> : <>
<button title={githubLoginText} aria-label={githubLoginText}
onClick={() => window.location.href = `${oauth_url}`}
className={`flex rounded-xl border dark:border-neutral-600 ${mobile ? "bg-secondary" : "bg-w"} h-10 sm:h-auto px-2 py-2 bg-w bg-button t-primary items-center justify-center`}>
<i className="ri-github-line ri-xl"></i>
<p className="text-sm ml-1">
{githubLoginText}
</p>
</button>
</>}
</div>)
}

function Menu() {
const profile = useContext(ProfileContext);
const [isOpen, setOpen] = useState(false)
Expand Down Expand Up @@ -152,7 +125,7 @@ function Menu() {
<div className="flex flex-row justify-end space-x-2">
<SearchButton onClose={onClose} />
<LanguageSwitch />
<UserAvatar profile={profile} mobile />
<UserAvatar profile={profile} />
</div>
<NavBar menu={true} onClick={onClose} />
</div>
Expand Down Expand Up @@ -227,7 +200,8 @@ function SearchButton({ className, onClose }: { className?: string, onClose?: ()
const key = `${encodeURIComponent(value)}`
setTimeout(() => {
setIsOpened(false)
onClose?.()
if (value.length !== 0)
onClose?.()
}, 100)
if (value.length !== 0)
setLocation(`/search/${key}`)
Expand Down Expand Up @@ -272,4 +246,54 @@ function SearchButton({ className, onClose }: { className?: string, onClose?: ()
</ReactModal>
</div>
)
}
}


function UserAvatar({ className, profile, onClose }: { className?: string, profile?: Profile, onClose?: () => void }) {
const { t } = useTranslation()
const { LoginModal, setIsOpened } = useLoginModal(onClose)
const label = t('github_login')

return (<div className={className + " flex flex-row items-center"}>
{profile?.avatar ? <>
<div className="w-8 relative">
<img src={profile.avatar} alt="Avatar" className="w-8 h-8 rounded-full border" />
<div className="z-50 absolute left-0 top-0 w-10 h-8 opacity-0 hover:opacity-100 duration-300">
<IconSmall label={t('logout')} name="ri-logout-circle-line" onClick={() => {
removeCookie("token")
window.location.reload()
}} hover={false} />
</div>
</div>
</> : <>
<button onClick={() => setIsOpened(true)} title={label} aria-label={label}
className="flex rounded-full border dark:border-neutral-600 px-2 bg-w aspect-[1] items-center justify-center t-primary bg-button">
<i className="ri-user-received-line"></i>
</button>
</>}
{LoginModal}
</div>
)
}

// function UserAvatar({ profile, className, mobile }: { className?: string, profile?: Profile, mobile?: boolean }) {
// const { t } = useTranslation()
// const githubLoginText = t('github_login')
// return (<div className={"flex flex-row justify-end " + className}>
// {profile?.avatar ? <>
// <div className="relative">
// <img src={profile.avatar} alt="Avatar" className="w-10 h-10 rounded-full border-2" />
// <div className="z-50 absolute left-0 top-0 w-10 h-10 opacity-0 hover:opacity-100 duration-300">
// <Icon label={t('logout')} name="ri-logout-circle-line ri-xl" onClick={() => {
// removeCookie("token")
// window.location.reload()
// }} hover={false} />
// </div>
// </div>
// </> : <>
// <Icon label={t('github_login')} name="ri-user-received-line ri-xl" onClick={() => {
// window.location.href = `${oauth_url}`
// }} hover={false} />
// </>}
// </div>)
// }
4 changes: 2 additions & 2 deletions client/src/components/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export function Icon({ name, label, className, onClick, hover = true }: { name: string, label: string, className?: string, onClick: () => any, hover?: boolean }) {
return (
<button title={label} aria-label={label} onClick={onClick} className={`max-w-12 flex rounded-full border px-2 bg-w aspect-[1] items-center justify-center t-primary ${hover ? "bg-button" : ""} ` + className}>
<button title={label} aria-label={label} onClick={onClick} className={`max-w-12 flex rounded-full border dark:border-neutral-600 px-2 bg-w aspect-[1] items-center justify-center t-primary ${hover ? "bg-button" : ""} ` + className}>
<i className={name}></i>
</button>
)
}

export function IconSmall({ name, label, className, onClick, hover = true }: { name: string, label: string, className?: string, onClick: () => any, hover?: boolean }) {
return (
<button title={label} aria-label={label} onClick={onClick} className={`max-w-8 flex rounded-full border px-2 bg-w aspect-[1] items-center justify-center t-primary ${hover ? "bg-button" : ""} ` + className}>
<button title={label} aria-label={label} onClick={onClick} className={`max-w-8 flex rounded-full border dark:border-neutral-600 px-2 bg-w aspect-[1] items-center justify-center t-primary ${hover ? "bg-button" : ""} ` + className}>
<i className={name}></i>
</button>
)
Expand Down
74 changes: 74 additions & 0 deletions client/src/hooks/useLoginModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { t } from "i18next";
import { Button } from "primereact/button";
import { useCallback, useMemo, useState } from "react";
import ReactModal from "react-modal";
import { Icon } from "../components/icon";
import { Input } from "../components/input";
import { oauth_url } from "../main";

export function useLoginModal(onClose?: () => void) {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [isOpened, setIsOpened] = useState(false);
const onLogin = useCallback(() => {
setTimeout(() => {
setIsOpened(false)
onClose?.()
}, 100)
}, [username, password])
const LoginModal = useMemo(() => {
return (
<ReactModal
isOpen={isOpened}
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
padding: "0",
border: "none",
borderRadius: "16px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
background: "none",
},
overlay: {
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: 1000,
},
}}
onRequestClose={() => setIsOpened(false)}
>
<div className="bg-w w-full flex flex-col items-center justify-between p-4 space-y-2 t-primary min-w-64">
<p className="text-xl">{t('login.title')}</p>
{false && <>
<Input value={username} setValue={setUsername} placeholder={t('login.username.placeholder')}
autofocus
/>
<Input value={password} setValue={setPassword} placeholder={t('login.password.placeholder')}
autofocus
onSubmit={onLogin} />
<div className="flex flex-row items-center space-x-4">
<Button title={t("login.title")} onClick={onLogin} />
</div>
</>
}
<div className="flex flex-col justify-center items-center space-y-2">
<p className="text-xs t-secondary">{t('login.oauth_only')}</p>
<div className="flex flex-row items-center space-x-4">
<Icon label={t('github_login')} name="ri-github-line" onClick={() => {
window.location.href = `${oauth_url}`
}} hover={true} />
</div>
</div>
</div>
</ReactModal>
)
}, [username, password, isOpened, onLogin])
return { LoginModal, setIsOpened }
}
37 changes: 28 additions & 9 deletions client/src/page/feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { siteName } from "../utils/constants";
import { timeago } from "../utils/timeago";
import { Button } from "../components/button";
import { Tips } from "../components/tips";
import { useLoginModal } from "../hooks/useLoginModal";

type Feed = {
id: number;
Expand Down Expand Up @@ -171,7 +172,7 @@ export function FeedPage({ id, TOC, clean }: { id: string, TOC: () => JSX.Elemen
<Button
title={t("index.back")}
onClick={() => (window.location.href = "/")}
/>
/>
</div>
</>
)}
Expand Down Expand Up @@ -349,12 +350,18 @@ function CommentInput({
const [content, setContent] = useState("");
const [error, setError] = useState("");
const { showAlert, AlertUI } = useAlert();
const profile = useContext(ProfileContext);
const { LoginModal, setIsOpened } = useLoginModal()
function errorHumanize(error: string) {
if (error === "Unauthorized") return t("login.required");
else if (error === "Content is required") return t("comment.empty");
return error;
}
function submit() {
if (!profile) {
setIsOpened(true)
return;
}
client.feed
.comment({ feed: id })
.post(
Expand All @@ -377,24 +384,36 @@ function CommentInput({
}
return (
<div className="w-full rounded-2xl bg-w t-primary m-2 p-6 items-end flex flex-col">
<div className="flex flex-col w-full items-start space-y-4">
<div className="flex flex-col w-full items-start mb-4">
<label htmlFor="comment">{t("comment.title")}</label>
</div>
{profile ? (<>
<textarea
id="comment"
placeholder={t("comment.placeholder.title")}
className="bg-w w-full h-24 rounded-lg"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
<button
className="mt-2 bg-theme text-white px-4 py-2 rounded-full"
onClick={submit}
>
{t("comment.submit")}
</button>
<button
className="mt-4 bg-theme text-white px-4 py-2 rounded-full"
onClick={submit}
>
{t("comment.submit")}
</button>
</>) : (
<div className="flex flex-row w-full items-center justify-center space-x-2 py-12">
<button
className="mt-2 bg-theme text-white px-4 py-2 rounded-full"
onClick={() => setIsOpened(true)}
>
{t("login.required")}
</button>
</div>
)}
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
<AlertUI />
{LoginModal}
</div>
);
}
Expand Down

0 comments on commit 16cee03

Please sign in to comment.