Consolidate profile and settings tabs, fix some styles

This commit is contained in:
austinkelsay 2025-03-28 10:59:52 -05:00
parent ffa57f9455
commit 3a59114683
No known key found for this signature in database
GPG Key ID: 5A763922E5BA08EE
11 changed files with 41 additions and 74 deletions

@ -3,7 +3,7 @@ import { Dialog } from 'primereact/dialog';
import { Tooltip } from 'primereact/tooltip'; import { Tooltip } from 'primereact/tooltip';
import useWindowWidth from '@/hooks/useWindowWidth'; import useWindowWidth from '@/hooks/useWindowWidth';
const MoreInfo = ({ tooltip, modalTitle, modalBody, className = '' }) => { const MoreInfo = ({ tooltip, modalTitle, modalBody, className = '', tooltipPosition = 'right' }) => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const windowWidth = useWindowWidth(); const windowWidth = useWindowWidth();
const isMobile = windowWidth < 768; const isMobile = windowWidth < 768;
@ -14,11 +14,9 @@ const MoreInfo = ({ tooltip, modalTitle, modalBody, className = '' }) => {
className={`pi pi-question-circle cursor-pointer ${className}`} className={`pi pi-question-circle cursor-pointer ${className}`}
onClick={() => setVisible(true)} onClick={() => setVisible(true)}
data-pr-tooltip={tooltip} data-pr-tooltip={tooltip}
data-pr-position="right" data-pr-position={tooltipPosition}
data-pr-at="right+5 top"
data-pr-my="left center-2"
/> />
{!isMobile && <Tooltip target=".pi-question-circle" />} {!isMobile && <Tooltip target=".pi-question-circle" position={tooltipPosition} />}
<Dialog <Dialog
header={modalTitle} header={modalTitle}

@ -1,20 +1,19 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import UserAvatar from './user/UserAvatar'; import UserAvatar from './user/UserAvatar';
import { Menubar } from 'primereact/menubar';
import { Menu } from 'primereact/menu'; import { Menu } from 'primereact/menu';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import SearchBar from '../search/SearchBar'; import SearchBar from '../search/SearchBar';
import { useSession } from 'next-auth/react';
import 'primereact/resources/primereact.min.css'; import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css'; import 'primeicons/primeicons.css';
import { useNDKContext } from '@/context/NDKContext';
import useWindowWidth from '@/hooks/useWindowWidth'; import useWindowWidth from '@/hooks/useWindowWidth';
const Navbar = () => { const Navbar = () => {
const router = useRouter(); const router = useRouter();
const windowWidth = useWindowWidth(); const windowWidth = useWindowWidth();
const navbarHeight = '60px'; const navbarHeight = '60px';
const { ndk } = useNDKContext(); const { data: session } = useSession();
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const [showMobileSearch, setShowMobileSearch] = useState(false); const [showMobileSearch, setShowMobileSearch] = useState(false);
const menu = useRef(null); const menu = useRef(null);
@ -47,7 +46,7 @@ const Navbar = () => {
{ {
label: 'Subscribe', label: 'Subscribe',
icon: 'pi pi-star', icon: 'pi pi-star',
command: () => router.push('/about') command: () => session?.user ? router.push('/profile?tab=subscribe') : router.push('/about')
}, },
{ {
label: 'About', label: 'About',
@ -62,7 +61,7 @@ const Navbar = () => {
<div className='px-10 py-8 bg-gray-800 border-t-0 border-l-0 border-r-0 rounded-none fixed z-10 w-[100vw] max-tab:px-[5%] max-mob:px-[5%] flex justify-between' style={{ height: navbarHeight }}> <div className='px-10 py-8 bg-gray-800 border-t-0 border-l-0 border-r-0 rounded-none fixed z-10 w-[100vw] max-tab:px-[5%] max-mob:px-[5%] flex justify-between' style={{ height: navbarHeight }}>
{/* Left section */} {/* Left section */}
<div className='flex items-center flex-1'> <div className='flex items-center flex-1'>
<div onClick={() => router.push('/')} className="flex flex-row items-center justify-center cursor-pointer"> <div onClick={() => router.push('/')} className="flex flex-row items-center justify-center cursor-pointer hover:opacity-80">
<Image <Image
alt="logo" alt="logo"
src="/images/plebdevs-icon.png" src="/images/plebdevs-icon.png"

@ -58,10 +58,10 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
<div className="text-[#f8f8ff]"> <div className="text-[#f8f8ff]">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h2 className="text-xl font-bold">Relays</h2>
<p className="text-gray-400">Manage your connected relays</p> <p className="text-gray-400">Manage your connected relays</p>
</div> </div>
<GenericButton <GenericButton
outlined
icon="pi pi-plus" icon="pi pi-plus"
label="Add Relay" label="Add Relay"
severity="success" severity="success"
@ -78,7 +78,7 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
className="flex-1" className="flex-1"
/> />
<GenericButton <GenericButton
label="Add" label="+"
severity="success" severity="success"
outlined outlined
onClick={addRelay} onClick={addRelay}
@ -122,7 +122,7 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
}; };
return ( return (
<div className="bg-gray-800 rounded-lg border border-gray-700 w-full"> <div className="w-full">
<DataTable <DataTable
value={userRelays} value={userRelays}
className="border-none" className="border-none"

@ -10,6 +10,7 @@ import UserProgress from "@/components/profile/progress/UserProgress";
import UserProgressTable from '@/components/profile/DataTables/UserProgressTable'; import UserProgressTable from '@/components/profile/DataTables/UserProgressTable';
import UserPurchaseTable from '@/components/profile/DataTables/UserPurchaseTable'; import UserPurchaseTable from '@/components/profile/DataTables/UserPurchaseTable';
import BitcoinLightningCard from '@/components/profile/BitcoinLightningCard'; import BitcoinLightningCard from '@/components/profile/BitcoinLightningCard';
import UserAccountLinking from "@/components/profile/UserAccountLinking";
const UserProfile = () => { const UserProfile = () => {
const windowWidth = useWindowWidth(); const windowWidth = useWindowWidth();
@ -42,6 +43,7 @@ const UserProfile = () => {
<div className="w-[22%] h-full max-lap:w-full"> <div className="w-[22%] h-full max-lap:w-full">
{user && <UserProfileCard user={user} />} {user && <UserProfileCard user={user} />}
<BitcoinLightningCard /> <BitcoinLightningCard />
{user && <UserAccountLinking session={session} />}
</div> </div>
<div className="w-[78%] flex flex-col justify-center mx-auto max-lap:w-full"> <div className="w-[78%] flex flex-col justify-center mx-auto max-lap:w-full">

@ -2,19 +2,24 @@ import React, { useRef, useState } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import { Menu } from 'primereact/menu'; import { Menu } from 'primereact/menu';
import { Tooltip } from 'primereact/tooltip'; import { Tooltip } from 'primereact/tooltip';
import { Dialog } from 'primereact/dialog';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { useImageProxy } from '@/hooks/useImageProxy'; import { useImageProxy } from '@/hooks/useImageProxy';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import UserBadges from '@/components/profile/UserBadges'; import UserBadges from '@/components/profile/UserBadges';
import useWindowWidth from '@/hooks/useWindowWidth'; import useWindowWidth from '@/hooks/useWindowWidth';
import MoreInfo from '@/components/MoreInfo'; import MoreInfo from '@/components/MoreInfo';
import UserRelaysTable from '@/components/profile/DataTables/UserRelaysTable';
import { useNDKContext } from "@/context/NDKContext";
const UserProfileCard = ({ user }) => { const UserProfileCard = ({ user }) => {
const [showBadges, setShowBadges] = useState(false); const [showBadges, setShowBadges] = useState(false);
const [showRelaysModal, setShowRelaysModal] = useState(false);
const menu = useRef(null); const menu = useRef(null);
const { showToast } = useToast(); const { showToast } = useToast();
const { returnImageProxy } = useImageProxy(); const { returnImageProxy } = useImageProxy();
const windowWidth = useWindowWidth(); const windowWidth = useWindowWidth();
const { ndk, userRelays, setUserRelays, reInitializeNDK } = useNDKContext();
const copyToClipboard = (text) => { const copyToClipboard = (text) => {
navigator.clipboard.writeText(text); navigator.clipboard.writeText(text);
@ -43,6 +48,11 @@ const UserProfileCard = ({ user }) => {
label: 'Open Nostr Profile', label: 'Open Nostr Profile',
icon: 'pi pi-external-link', icon: 'pi pi-external-link',
command: () => window.open(`https://nostr.com/${nip19.npubEncode(user?.pubkey)}`, '_blank') command: () => window.open(`https://nostr.com/${nip19.npubEncode(user?.pubkey)}`, '_blank')
},
{
label: 'Manage Relays',
icon: 'pi pi-server',
command: () => setShowRelaysModal(true)
} }
]; ];
@ -282,6 +292,20 @@ const UserProfileCard = ({ user }) => {
visible={showBadges} visible={showBadges}
onHide={() => setShowBadges(false)} onHide={() => setShowBadges(false)}
/> />
<Dialog
visible={showRelaysModal}
onHide={() => setShowRelaysModal(false)}
header="Manage Relays"
className="w-[90vw] max-w-[800px]"
modal
>
<UserRelaysTable
ndk={ndk}
userRelays={userRelays}
setUserRelays={setUserRelays}
reInitializeNDK={reInitializeNDK}
/>
</Dialog>
</> </>
); );
}; };

@ -1,47 +0,0 @@
import React, { useState, useEffect } from "react";
import UserProfileCard from "@/components/profile/UserProfileCard";
import { useSession } from 'next-auth/react';
import { useNDKContext } from "@/context/NDKContext";
import useWindowWidth from "@/hooks/useWindowWidth";
import UserRelaysTable from "@/components/profile/DataTables/UserRelaysTable";
import UserAccountLinking from "@/components/profile/UserAccountLinking";
const UserSettings = () => {
const [user, setUser] = useState(null);
const { ndk, userRelays, setUserRelays, reInitializeNDK } = useNDKContext();
const { data: session } = useSession();
const windowWidth = useWindowWidth();
useEffect(() => {
if (session?.user) {
setUser(session.user);
}
}, [session]);
return (
user && (
<div className="py-4 px-1">
{windowWidth < 768 && (
<h1 className="text-3xl font-bold mb-6">Settings</h1>
)}
<div className="w-full flex flex-row max-lap:flex-col">
<div className="w-[22%] h-full max-lap:w-full">
<UserProfileCard user={user} />
{user && <UserAccountLinking session={session} />}
</div>
<div className="w-[78%] flex flex-col justify-center mx-2 max-lap:mx-0 max-lap:w-full">
<UserRelaysTable
ndk={ndk}
userRelays={userRelays}
setUserRelays={setUserRelays}
reInitializeNDK={reInitializeNDK}
/>
</div>
</div>
</div>
)
);
};
export default UserSettings;

@ -117,7 +117,7 @@ const UserSubscription = () => {
{!subscribed && ( {!subscribed && (
<Card <Card
title="Subscribe to PlebDevs" title="Subscribe to PlebDevs"
className="mb-2 max-lap:h-auto border border-gray-700" className="max-lap:h-auto border border-gray-700"
pt={{ pt={{
body: { className: 'py-2' }, body: { className: 'py-2' },
content: { className: 'pt-0' } content: { className: 'pt-0' }
@ -190,7 +190,7 @@ const UserSubscription = () => {
</> </>
)} )}
<Card title="Frequently Asked Questions" className="border border-gray-700 rounded-lg"> <Card title="Frequently Asked Questions" className="mt-2 border border-gray-700 rounded-lg">
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div> <div>
<h3 className="text-lg font-semibold">How does the subscription work?</h3> <h3 className="text-lg font-semibold">How does the subscription work?</h3>

@ -9,7 +9,7 @@ const appConfig = {
"wss://purplerelay.com/", "wss://purplerelay.com/",
"wss://relay.devs.tools/" "wss://relay.devs.tools/"
], ],
authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345"], authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345", "6260f29fa75c91aaa292f082e5e87b438d2ab4fdf96af398567b01802ee2fcd4"],
customLightningAddresses: [ customLightningAddresses: [
{ {
// todo remove need for lowercase // todo remove need for lowercase

@ -211,6 +211,7 @@ const AboutPage = () => {
<h2 className="text-xl font-bold m-0">Subscribe to PlebDevs</h2> <h2 className="text-xl font-bold m-0">Subscribe to PlebDevs</h2>
<MoreInfo <MoreInfo
tooltip="Subscription FAQ" tooltip="Subscription FAQ"
tooltipPosition='top'
modalTitle="Frequently Asked Questions" modalTitle="Frequently Asked Questions"
modalBody={faqContent} modalBody={faqContent}
className="text-gray-400 hover:text-white" className="text-gray-400 hover:text-white"

@ -116,8 +116,6 @@ export default function Home() {
const [selectedTopic, setSelectedTopic] = useState('Top'); const [selectedTopic, setSelectedTopic] = useState('Top');
const [filteredContent, setFilteredContent] = useState([]); const [filteredContent, setFilteredContent] = useState([]);
const memoizedFilteredContent = useMemo(() => filteredContent, [filteredContent]);
useEffect(() => { useEffect(() => {
if (documents && !documentsLoading) { if (documents && !documentsLoading) {
const processedDocuments = documents.map(document => ({...parseEvent(document), type: 'document'})); const processedDocuments = documents.map(document => ({...parseEvent(document), type: 'document'}));

@ -1,7 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { TabView, TabPanel } from "primereact/tabview"; import { TabView, TabPanel } from "primereact/tabview";
import UserProfile from "@/components/profile/UserProfile"; import UserProfile from "@/components/profile/UserProfile";
import UserSettings from "@/components/profile/UserSettings";
import UserContent from "@/components/profile/UserContent"; import UserContent from "@/components/profile/UserContent";
import UserSubscription from "@/components/profile/subscription/UserSubscription"; import UserSubscription from "@/components/profile/subscription/UserSubscription";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -16,7 +15,7 @@ const Profile = () => {
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const {isAdmin, isLoading} = useIsAdmin(); const {isAdmin, isLoading} = useIsAdmin();
const tabs = ["profile", "settings", "content", "subscribe"]; const tabs = ["profile", "content", "subscribe"];
useEffect(() => { useEffect(() => {
const { tab } = router.query; const { tab } = router.query;
@ -74,13 +73,6 @@ const Profile = () => {
}}> }}>
<UserProfile /> <UserProfile />
</TabPanel> </TabPanel>
<TabPanel header="Settings" pt={{
headerAction: {
className: "bg-transparent"
},
}}>
<UserSettings />
</TabPanel>
{isAdmin && ( {isAdmin && (
<TabPanel header="Content" pt={{ <TabPanel header="Content" pt={{
headerAction: { headerAction: {