From 23522fa8ad44ac7cdec0e93e62f5d1997ff00c06 Mon Sep 17 00:00:00 2001 From: Mohammed Rayan A Date: Mon, 13 Jul 2026 11:13:20 +0530 Subject: [PATCH] Add UI features for discovering and accessing public user profiles via username --- app/components/Profile.tsx | 48 ++++++++++++++++++++++- app/components/SharedDump.tsx | 72 +++++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/app/components/Profile.tsx b/app/components/Profile.tsx index 63d85be..7937649 100644 --- a/app/components/Profile.tsx +++ b/app/components/Profile.tsx @@ -1,6 +1,6 @@ 'use client' -import { CheckCircle2, Globe, Loader2, Save, ShieldCheck, User } from 'lucide-react' +import { CheckCircle2, Globe, Loader2, Save, ShieldCheck, User, Copy, ExternalLink, Share2 } from 'lucide-react' import { ReactNode, useEffect, useState } from 'react' import { useAuth } from '../contexts/AuthContext' import { authFetch, jsonAuthFetch } from '../lib/authFetch' @@ -22,6 +22,19 @@ export function Profile() { const [username, setUsername] = useState('') const [shareByDefault, setShareByDefault] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) + const [copied, setCopied] = useState(false) + + const handleCopyLink = async () => { + if (!profile?.username) return + const link = `https://dumpit-three.vercel.app/u/${profile.username.toLowerCase()}` + try { + await navigator.clipboard.writeText(link) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } catch (err) { + console.error('Failed to copy link:', err) + } + } useEffect(() => { if (user) { @@ -182,6 +195,39 @@ export function Profile() { + {profile?.username && ( +
+
+ + Public Library Link +
+

+ Anyone can view your publicly shared resources by visiting your personal link. +

+
+ https://dumpit-three.vercel.app/u/{profile.username.toLowerCase()} +
+
+ + + + View Library + +
+
+ )} + {profile && (

Vault stats

diff --git a/app/components/SharedDump.tsx b/app/components/SharedDump.tsx index 22af0ee..d0fc9ba 100644 --- a/app/components/SharedDump.tsx +++ b/app/components/SharedDump.tsx @@ -2,6 +2,7 @@ import { CheckCircle2, ExternalLink, Filter, Globe2, Loader2, Plus, Search, Users } from 'lucide-react' import { useEffect, useMemo, useState } from 'react' +import { useRouter } from 'next/navigation' import { useAuth } from '../contexts/AuthContext' import { authFetch, jsonAuthFetch } from '../lib/authFetch' @@ -39,10 +40,28 @@ interface Resource { export function SharedDump() { const { user } = useAuth() + const router = useRouter() const [resources, setResources] = useState([]) const [loading, setLoading] = useState(true) const [searchQuery, setSearchQuery] = useState('') const [selectedTag, setSelectedTag] = useState('all') + const [userSearchInput, setUserSearchInput] = useState('') + const [searchUserError, setSearchUserError] = useState('') + + const handleUserSearchSubmit = (e: React.FormEvent) => { + e.preventDefault() + const cleaned = userSearchInput.trim().replace(/^@/, '').toLowerCase() + if (!cleaned) return + + const usernameRegex = /^[a-z0-9_-]{3,20}$/ + if (!usernameRegex.test(cleaned)) { + setSearchUserError('Username must be 3-20 characters, lowercase letters, numbers, underscores, or hyphens.') + return + } + + setSearchUserError('') + router.push(`/u/${cleaned}`) + } const [savedResources, setSavedResources] = useState>(new Set()) const [savingId, setSavingId] = useState(null) const [error, setError] = useState('') @@ -158,6 +177,38 @@ export function SharedDump() { +
+

+ + Browse a Contributor's Library +

+

+ Enter a user's unique username to explore all of their publicly shared links and bookmarks. +

+
+
+ @ + setUserSearchInput(e.target.value)} + className="app-input pl-7 text-sm" + required + /> +
+ +
+ {searchUserError && ( +

{searchUserError}

+ )} +
+ {error && (
{error} @@ -254,10 +305,23 @@ export function SharedDump() { {resource.link}
- - - {resource.username || 'Anonymous'} - + {resource.username ? ( + + + @{resource.username} + + ) : ( + + + Anonymous + + )} {formatDate(resource.created_at)}