mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-08 08:54:24 +01:00
feat: build LogoutPage, improve LoginPage
This commit is contained in:
@@ -6,7 +6,6 @@ import { Wallet_Service } from "./services";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { useWrapperSetup } from "./utils/Wrapper";
|
||||
import LoadingPage from "./Components/LoadingPage/LoadingPage";
|
||||
import LoginPage from "./features/Auth/pages/LoginPage";
|
||||
|
||||
// Pages
|
||||
const FeedPage = React.lazy(() => import("./features/Posts/pages/FeedPage/FeedPage"))
|
||||
@@ -16,6 +15,8 @@ const PostDetailsPage = React.lazy(() => import("./features/Posts/pages/PostDeta
|
||||
const CategoryPage = React.lazy(() => import("src/features/Projects/pages/CategoryPage/CategoryPage"))
|
||||
const ExplorePage = React.lazy(() => import("src/features/Projects/pages/ExplorePage"))
|
||||
const DonatePage = React.lazy(() => import("./features/Donations/pages/DonatePage/DonatePage"))
|
||||
const LoginPage = React.lazy(() => import("./features/Auth/pages/LoginPage/LoginPage"))
|
||||
const LogoutPage = React.lazy(() => import("./features/Auth/pages/LogoutPage/LogoutPage"))
|
||||
|
||||
function App() {
|
||||
const { isWalletConnected } = useAppSelector(state => ({
|
||||
@@ -58,6 +59,7 @@ function App() {
|
||||
<Route path="/donate" element={<DonatePage />} />
|
||||
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/logout" element={<LogoutPage />} />
|
||||
|
||||
<Route path="/" element={<Navigate to="/products" />} />
|
||||
</Routes>
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from '@szhsin/react-menu';
|
||||
import '@szhsin/react-menu/dist/index.css';
|
||||
import { FiAward, FiChevronDown, FiFeather, FiLogIn, FiMic } from "react-icons/fi";
|
||||
import { useMeQuery } from "src/graphql";
|
||||
import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
|
||||
|
||||
|
||||
export default function NavDesktop() {
|
||||
@@ -22,6 +24,8 @@ export default function NavDesktop() {
|
||||
const communityRef = useRef(null);
|
||||
const [communitymenuProps, toggleCommunityMenu] = useMenuState({ transition: true });
|
||||
|
||||
const meQuery = useMeQuery();
|
||||
|
||||
const { isWalletConnected } = useAppSelector((state) => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
}));
|
||||
@@ -157,13 +161,34 @@ export default function NavDesktop() {
|
||||
: <Button className="ml-16 py-12 px-16 lg:px-20" onClick={onConnectWallet}><AiFillThunderbolt className='inline-block text-thunder transform scale-125' /> Connect Wallet </Button>
|
||||
} */}
|
||||
|
||||
{currentSection === 'products' && <IconButton className='ml-16 self-center' onClick={openSearch}>
|
||||
{currentSection === 'products' && <IconButton className='mr-16 self-center' onClick={openSearch}>
|
||||
<BsSearch className='scale-125 text-gray-400' />
|
||||
</IconButton>}
|
||||
</motion.div>
|
||||
<Link to='/login' className="ml-16 font-bold hover:text-primary-800 hover:underline">
|
||||
Login <FiLogIn />
|
||||
</Link>
|
||||
{meQuery.data?.me ?
|
||||
<Menu menuButton={<MenuButton ><Avatar src={meQuery.data.me.avatar} width={40} /> </MenuButton>}>
|
||||
<MenuItem
|
||||
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12 opacity-60'
|
||||
>
|
||||
Profile (soon)
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
href="/logout"
|
||||
onClick={(e) => {
|
||||
e.syntheticEvent.preventDefault();
|
||||
navigate("/logout");
|
||||
}}
|
||||
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
|
||||
>
|
||||
Logout
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
:
|
||||
<Link to='/login' className="font-bold hover:text-primary-800 hover:underline">
|
||||
Login <FiLogIn />
|
||||
</Link>
|
||||
}
|
||||
<div className="relative h-24">
|
||||
<motion.div
|
||||
initial={{
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { BsFillLightningChargeFill } from "react-icons/bs";
|
||||
import { Grid } from "react-loader-spinner";
|
||||
import { useMeQuery } from "src/graphql"
|
||||
|
||||
|
||||
export default function LoginPage() {
|
||||
const [loadingLnurl, setLoadingLnurl] = useState(true)
|
||||
const [lnurlAuth, setLnurlAuth] = useState("");
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||
const [isPollilng, setIsPollilng] = useState(false)
|
||||
|
||||
|
||||
|
||||
const meQuery = useMeQuery({
|
||||
onCompleted: (data) => {
|
||||
const stateChanged = Boolean(data.me) !== isLoggedIn;
|
||||
if (stateChanged)
|
||||
setIsPollilng(false);
|
||||
|
||||
setIsLoggedIn(Boolean(data.me));
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await fetch(process.env.REACT_APP_AUTH_END_POINT! + '/login')
|
||||
const data = await res.json()
|
||||
setLoadingLnurl(false);
|
||||
setLnurlAuth(data.encoded)
|
||||
})()
|
||||
}, [])
|
||||
|
||||
const { startPolling, stopPolling } = meQuery;
|
||||
useEffect(() => {
|
||||
if (isPollilng)
|
||||
startPolling(1500);
|
||||
else
|
||||
stopPolling();
|
||||
}, [isPollilng, startPolling, stopPolling])
|
||||
|
||||
|
||||
const onLogin = () => {
|
||||
setIsPollilng(true);
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
await fetch(process.env.REACT_APP_AUTH_END_POINT! + '/logout', {
|
||||
method: "GET",
|
||||
'credentials': "include"
|
||||
})
|
||||
setIsPollilng(true);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="min-h-[80vh] flex flex-col justify-center items-center">
|
||||
{loadingLnurl && <div className="flex flex-col gap-24 items-center">
|
||||
<Grid color="var(--primary)" width="150" />
|
||||
<p className="text-body3 font-bold">Fetching Lnurl-Auth...</p>
|
||||
</div>}
|
||||
{!loadingLnurl &&
|
||||
(isLoggedIn ?
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<h3 className="text-body4">
|
||||
Hello: <span className="font-bold">@{meQuery.data?.me?.name.slice(0, 10)}...</span>
|
||||
</h3>
|
||||
<img src={meQuery.data?.me?.avatar} className='w-80 h-80 object-cover' alt="" />
|
||||
<button
|
||||
onClick={logout}
|
||||
className="text-black font-bolder bg-gray-200 rounded-12 px-16 py-12 mt-36 active:scale-90 transition-transform">
|
||||
{isPollilng ? "Logging you out..." : "Logout"}
|
||||
</button>
|
||||
</div> :
|
||||
<a
|
||||
href={lnurlAuth}
|
||||
onClick={onLogin}
|
||||
className='text-black font-bolder bg-yellow-200 hover:bg-yellow-300 rounded-12 px-16 py-12 active:scale-90 transition-transform'>
|
||||
Login with Lightning <BsFillLightningChargeFill className="scale-125" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
86
src/features/Auth/pages/LoginPage/LoginPage.tsx
Normal file
86
src/features/Auth/pages/LoginPage/LoginPage.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { BsFillLightningChargeFill } from "react-icons/bs";
|
||||
import { Grid } from "react-loader-spinner";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useMeQuery } from "src/graphql"
|
||||
|
||||
|
||||
export default function LoginPage() {
|
||||
const [loadingLnurl, setLoadingLnurl] = useState(true)
|
||||
const [lnurlAuth, setLnurlAuth] = useState("");
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
const meQuery = useMeQuery({
|
||||
onCompleted: (data) => {
|
||||
|
||||
if (data.me) {
|
||||
setIsLoggedIn(true);
|
||||
meQuery.stopPolling();
|
||||
setTimeout(() => {
|
||||
navigate('/')
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await fetch(process.env.REACT_APP_AUTH_END_POINT! + '/login')
|
||||
const data = await res.json()
|
||||
setLoadingLnurl(false);
|
||||
setLnurlAuth(data.encoded)
|
||||
})();
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
const onLogin = () => {
|
||||
meQuery.startPolling(1500)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="min-h-[80vh] page-container flex flex-col justify-center items-center">
|
||||
{loadingLnurl && <div className="flex flex-col gap-24 items-center">
|
||||
<Grid color="var(--primary)" width="150" />
|
||||
<p className="text-body3 font-bold">Fetching Lnurl-Auth...</p>
|
||||
</div>}
|
||||
{!loadingLnurl &&
|
||||
(isLoggedIn ?
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<h3 className="text-body4">
|
||||
Hello: <span className="font-bold">@{meQuery.data?.me?.name.slice(0, 10)}...</span>
|
||||
</h3>
|
||||
<img src={meQuery.data?.me?.avatar} className='w-80 h-80 object-cover' alt="" />
|
||||
</div> :
|
||||
<div className="max-w-[326px] border-2 border-gray-200 rounded-16 p-16 flex flex-col gap-16 items-center" >
|
||||
<p className="text-body1 font-bolder text-center">
|
||||
Login
|
||||
</p>
|
||||
<p className="text-gray-600 text-body-4">
|
||||
Zero credentials authentication.
|
||||
<br />
|
||||
All you need is a connected <a href='https://getalby.com'
|
||||
target='_blank'
|
||||
className="underline text-primary-500"
|
||||
rel="noreferrer"
|
||||
>WebLN wallet</a> that supports lnurl-auth, & you are good to go !!
|
||||
</p>
|
||||
<a
|
||||
href={lnurlAuth}
|
||||
onClick={onLogin}
|
||||
className='block text-black font-bolder bg-yellow-200 hover:bg-yellow-300 rounded-12 px-16 py-12 active:scale-90 transition-transform'>
|
||||
Login with Lightning <BsFillLightningChargeFill className="scale-125" />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
33
src/features/Auth/pages/LogoutPage/LogoutPage.tsx
Normal file
33
src/features/Auth/pages/LogoutPage/LogoutPage.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { BsFillLightningChargeFill } from "react-icons/bs";
|
||||
import { Grid, LineWave } from "react-loader-spinner";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
||||
export default function LoginPage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
fetch(process.env.REACT_APP_AUTH_END_POINT! + '/logout', {
|
||||
method: "GET",
|
||||
'credentials': "include"
|
||||
})
|
||||
.then(() => {
|
||||
window.location.pathname = '/'
|
||||
})
|
||||
.catch(() => {
|
||||
window.location.pathname = '/'
|
||||
})
|
||||
}, [navigate])
|
||||
|
||||
|
||||
return (
|
||||
<div className="min-h-[80vh] flex flex-col justify-center items-center">
|
||||
<p className="text-body-2 text-gray-800">
|
||||
Logging you out...
|
||||
</p>
|
||||
<LineWave color="var(--primary)" width="150" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,7 @@ interface Props {
|
||||
|
||||
export default function Avatar({ src, alt, width = 40 }: Props) {
|
||||
return (
|
||||
<img src={src} className='shrink-0 rounded-full shadow-inner object-contain border border-gray-100' style={{
|
||||
<img src={src} className='shrink-0 rounded-full object-contain border-2 border-gray-100' style={{
|
||||
width: width,
|
||||
height: width,
|
||||
}} alt={alt ?? "avatar"} />
|
||||
|
||||
Reference in New Issue
Block a user