feat: add public profile pages at /p/:npub

- Make AuthorCard clickable to navigate to user profiles
- Add /p/:npub and /p/:npub/writings routes
- Reuse Me component for public profiles with pubkey prop
- Show highlights and writings tabs for any user
- Hide private tabs (reading-list, archive) on public profiles
- Public profiles show only public data (highlights, writings)
- Private data (bookmarks, read articles) only visible on own profile
- Add clickable author card hover styles with indigo border
- Decode npub to pubkey for profile viewing
- DRY: Single Me component serves both /me and /p/:npub routes
This commit is contained in:
Gigi
2025-10-14 01:01:10 +02:00
parent 6b240b01ec
commit 4c720aa049
6 changed files with 148 additions and 64 deletions

View File

@@ -31,6 +31,7 @@ interface ThreePaneLayoutProps {
showSettings: boolean
showExplore?: boolean
showMe?: boolean
showProfile?: boolean
// Bookmarks pane
bookmarks: Bookmark[]
@@ -89,6 +90,9 @@ interface ThreePaneLayoutProps {
// Optional Me content
me?: React.ReactNode
// Optional Profile content
profile?: React.ReactNode
}
const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
@@ -221,8 +225,8 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
return (
<>
{/* Mobile bookmark button - only show when viewing article (not on settings/explore/me) */}
{isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed && !props.showSettings && !props.showExplore && !props.showMe && (
{/* Mobile bookmark button - only show when viewing article (not on settings/explore/me/profile) */}
{isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed && !props.showSettings && !props.showExplore && !props.showMe && !props.showProfile && (
<button
className={`fixed z-[900] bg-zinc-800/70 border border-zinc-600/40 rounded-lg text-zinc-200 flex items-center justify-center transition-all duration-300 active:scale-95 backdrop-blur-sm md:hidden ${
showMobileButtons ? 'opacity-90 visible' : 'opacity-0 invisible pointer-events-none'
@@ -241,8 +245,8 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
</button>
)}
{/* Mobile highlights button - only show when viewing article (not on settings/explore/me) */}
{isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed && !props.showSettings && !props.showExplore && !props.showMe && (
{/* Mobile highlights button - only show when viewing article (not on settings/explore/me/profile) */}
{isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed && !props.showSettings && !props.showExplore && !props.showMe && !props.showProfile && (
<button
className={`fixed z-[900] border border-zinc-600/40 rounded-lg flex items-center justify-center transition-all duration-300 active:scale-95 backdrop-blur-sm md:hidden ${
showMobileButtons ? 'opacity-90 visible' : 'opacity-0 invisible pointer-events-none'
@@ -320,6 +324,11 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
<>
{props.me}
</>
) : props.showProfile && props.profile ? (
// Render Profile inside the main pane to keep side panels
<>
{props.profile}
</>
) : (
<ContentPanel
loading={props.readerLoading}