mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-06 23:03:07 +00:00
chore: persist profile state on edit
This commit is contained in:
parent
4892614df8
commit
a746af365a
@ -29,7 +29,9 @@ import { Cell } from '@/types/forum';
|
|||||||
import { usePending } from '@/hooks/usePending';
|
import { usePending } from '@/hooks/usePending';
|
||||||
|
|
||||||
// Empty State Component
|
// Empty State Component
|
||||||
const EmptyState: React.FC<{ canCreateCell: boolean }> = ({ canCreateCell }) => {
|
const EmptyState: React.FC<{ canCreateCell: boolean }> = ({
|
||||||
|
canCreateCell,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="col-span-2 flex flex-col items-center justify-center py-16 px-4">
|
<div className="col-span-2 flex flex-col items-center justify-center py-16 px-4">
|
||||||
{/* Visual Element */}
|
{/* Visual Element */}
|
||||||
@ -76,10 +78,7 @@ const CellItem: React.FC<{ cell: Cell }> = ({ cell }) => {
|
|||||||
const pending = usePending(cell.id);
|
const pending = usePending(cell.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link to={`/cell/${cell.id}`} className="group block board-card">
|
||||||
to={`/cell/${cell.id}`}
|
|
||||||
className="group block board-card"
|
|
||||||
>
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<CypherImage
|
<CypherImage
|
||||||
src={cell.icon}
|
src={cell.icon}
|
||||||
@ -162,64 +161,72 @@ const CellList = () => {
|
|||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="page-title">
|
<h1 className="page-title">Decentralized Cells</h1>
|
||||||
Decentralized Cells
|
|
||||||
</h1>
|
|
||||||
<p className="page-subtitle">
|
<p className="page-subtitle">
|
||||||
Discover communities built on Bitcoin Ordinals
|
Discover communities built on Bitcoin Ordinals
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Only show controls when cells exist */}
|
{/* Only show controls when cells exist */}
|
||||||
{hasCells && (
|
{hasCells && (
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<ModerationToggle />
|
<ModerationToggle />
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={sortOption}
|
value={sortOption}
|
||||||
onValueChange={(value: SortOption) => setSortOption(value)}
|
onValueChange={(value: SortOption) => setSortOption(value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-40 bg-cyber-muted/50 border-cyber-muted text-cyber-light">
|
<SelectTrigger className="w-40 bg-cyber-muted/50 border-cyber-muted text-cyber-light">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-cyber-dark border-cyber-muted/30">
|
<SelectContent className="bg-cyber-dark border-cyber-muted/30">
|
||||||
<SelectItem value="relevance" className="text-cyber-light hover:bg-cyber-muted/30">
|
<SelectItem
|
||||||
<TrendingUp className="w-4 h-4 mr-2 inline" />
|
value="relevance"
|
||||||
Relevance
|
className="text-cyber-light hover:bg-cyber-muted/30"
|
||||||
</SelectItem>
|
>
|
||||||
<SelectItem value="activity" className="text-cyber-light hover:bg-cyber-muted/30">
|
<TrendingUp className="w-4 h-4 mr-2 inline" />
|
||||||
<MessageSquare className="w-4 h-4 mr-2 inline" />
|
Relevance
|
||||||
Activity
|
</SelectItem>
|
||||||
</SelectItem>
|
<SelectItem
|
||||||
<SelectItem value="newest" className="text-cyber-light hover:bg-cyber-muted/30">
|
value="activity"
|
||||||
<Clock className="w-4 h-4 mr-2 inline" />
|
className="text-cyber-light hover:bg-cyber-muted/30"
|
||||||
Newest
|
>
|
||||||
</SelectItem>
|
<MessageSquare className="w-4 h-4 mr-2 inline" />
|
||||||
<SelectItem value="alphabetical" className="text-cyber-light hover:bg-cyber-muted/30">
|
Activity
|
||||||
<Layout className="w-4 h-4 mr-2 inline" />
|
</SelectItem>
|
||||||
A-Z
|
<SelectItem
|
||||||
</SelectItem>
|
value="newest"
|
||||||
</SelectContent>
|
className="text-cyber-light hover:bg-cyber-muted/30"
|
||||||
</Select>
|
>
|
||||||
|
<Clock className="w-4 h-4 mr-2 inline" />
|
||||||
|
Newest
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem
|
||||||
|
value="alphabetical"
|
||||||
|
className="text-cyber-light hover:bg-cyber-muted/30"
|
||||||
|
>
|
||||||
|
<Layout className="w-4 h-4 mr-2 inline" />
|
||||||
|
A-Z
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={refreshData}
|
onClick={refreshData}
|
||||||
disabled={isInitialLoading}
|
disabled={isInitialLoading}
|
||||||
title="Refresh data"
|
title="Refresh data"
|
||||||
className="px-3 border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
className="px-3 border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
||||||
>
|
>
|
||||||
<RefreshCw
|
<RefreshCw
|
||||||
className={`w-4 h-4 ${isInitialLoading ? 'animate-spin' : ''}`}
|
className={`w-4 h-4 ${isInitialLoading ? 'animate-spin' : ''}`}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{canCreateCell && (
|
{canCreateCell && <CreateCellDialog />}
|
||||||
<CreateCellDialog />
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -122,7 +122,6 @@ const Header = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getStatusIcon = () => {
|
const getStatusIcon = () => {
|
||||||
if (!isConnected) return <CircleSlash className="w-4 h-4" />;
|
if (!isConnected) return <CircleSlash className="w-4 h-4" />;
|
||||||
|
|
||||||
@ -194,21 +193,26 @@ const Header = () => {
|
|||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={`font-mono text-xs border-0 ${
|
className={`font-mono text-xs border-0 ${
|
||||||
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED && delegationInfo?.isValid
|
verificationStatus ===
|
||||||
|
EVerificationStatus.ENS_ORDINAL_VERIFIED &&
|
||||||
|
delegationInfo?.isValid
|
||||||
? 'bg-green-500/20 text-green-400 border-green-500/30'
|
? 'bg-green-500/20 text-green-400 border-green-500/30'
|
||||||
: verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED
|
: verificationStatus ===
|
||||||
? 'bg-orange-500/20 text-orange-400 border-orange-500/30'
|
EVerificationStatus.ENS_ORDINAL_VERIFIED
|
||||||
: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'
|
? 'bg-orange-500/20 text-orange-400 border-orange-500/30'
|
||||||
|
: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{getStatusIcon()}
|
{getStatusIcon()}
|
||||||
<span className="ml-1">
|
<span className="ml-1">
|
||||||
{verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED && delegationInfo?.isValid
|
{verificationStatus ===
|
||||||
|
EVerificationStatus.ENS_ORDINAL_VERIFIED &&
|
||||||
|
delegationInfo?.isValid
|
||||||
? 'READY'
|
? 'READY'
|
||||||
: verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED
|
: verificationStatus ===
|
||||||
? 'EXPIRED'
|
EVerificationStatus.ENS_ORDINAL_VERIFIED
|
||||||
: 'VERIFY'
|
? 'EXPIRED'
|
||||||
}
|
: 'VERIFY'}
|
||||||
</span>
|
</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
@ -224,21 +228,34 @@ const Header = () => {
|
|||||||
<Settings className="w-4 h-4" />
|
<Settings className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="w-56 bg-black/95 border-cyber-muted/30">
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
className="w-56 bg-black/95 border-cyber-muted/30"
|
||||||
|
>
|
||||||
<div className="px-3 py-2 border-b border-cyber-muted/30">
|
<div className="px-3 py-2 border-b border-cyber-muted/30">
|
||||||
<div className="text-sm font-medium text-white">{displayName}</div>
|
<div className="text-sm font-medium text-white">
|
||||||
<div className="text-xs text-cyber-neutral">{address?.slice(0, 8)}...{address?.slice(-4)}</div>
|
{displayName}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-cyber-neutral">
|
||||||
|
{address?.slice(0, 8)}...{address?.slice(-4)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link to="/profile" className="flex items-center space-x-2">
|
<Link
|
||||||
|
to="/profile"
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
<User className="w-4 h-4" />
|
<User className="w-4 h-4" />
|
||||||
<span>Profile</span>
|
<span>Profile</span>
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link to="/bookmarks" className="flex items-center space-x-2">
|
<Link
|
||||||
|
to="/bookmarks"
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
<Bookmark className="w-4 h-4" />
|
<Bookmark className="w-4 h-4" />
|
||||||
<span>Bookmarks</span>
|
<span>Bookmarks</span>
|
||||||
</Link>
|
</Link>
|
||||||
@ -246,7 +263,10 @@ const Header = () => {
|
|||||||
|
|
||||||
<DropdownMenuSeparator className="bg-cyber-muted/30" />
|
<DropdownMenuSeparator className="bg-cyber-muted/30" />
|
||||||
|
|
||||||
<DropdownMenuItem onClick={handleOpenWizard} className="flex items-center space-x-2">
|
<DropdownMenuItem
|
||||||
|
onClick={handleOpenWizard}
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
<Settings className="w-4 h-4" />
|
<Settings className="w-4 h-4" />
|
||||||
<span>Setup Wizard</span>
|
<span>Setup Wizard</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@ -279,7 +299,11 @@ const Header = () => {
|
|||||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
className="md:hidden text-white hover:bg-cyber-muted/30"
|
className="md:hidden text-white hover:bg-cyber-muted/30"
|
||||||
>
|
>
|
||||||
{mobileMenuOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
|
{mobileMenuOpen ? (
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<Menu className="w-5 h-5" />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -267,10 +267,7 @@ const PostList = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
visiblePosts.map(post => (
|
visiblePosts.map(post => (
|
||||||
<div
|
<div key={post.id} className="thread-card">
|
||||||
key={post.id}
|
|
||||||
className="thread-card"
|
|
||||||
>
|
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -66,7 +66,6 @@ export function WalletWizard({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const renderStepIcon = (step: WizardStep) => {
|
const renderStepIcon = (step: WizardStep) => {
|
||||||
const status = getStepStatus(step);
|
const status = getStepStatus(step);
|
||||||
|
|
||||||
@ -126,10 +125,12 @@ export function WalletWizard({
|
|||||||
className={`text-sm ${
|
className={`text-sm ${
|
||||||
getStepStatus(step as WizardStep) === 'current'
|
getStepStatus(step as WizardStep) === 'current'
|
||||||
? 'text-blue-500 font-medium'
|
? 'text-blue-500 font-medium'
|
||||||
: (getStepStatus(step as WizardStep) === 'complete' ||
|
: getStepStatus(step as WizardStep) === 'complete' ||
|
||||||
(step === 1 && isAuthenticated) ||
|
(step === 1 && isAuthenticated) ||
|
||||||
(step === 2 && verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED) ||
|
(step === 2 &&
|
||||||
(step === 3 && delegationStatus.isValid))
|
verificationStatus !==
|
||||||
|
EVerificationStatus.WALLET_UNCONNECTED) ||
|
||||||
|
(step === 3 && delegationStatus.isValid)
|
||||||
? 'text-green-500'
|
? 'text-green-500'
|
||||||
: 'text-gray-400'
|
: 'text-gray-400'
|
||||||
}`}
|
}`}
|
||||||
@ -142,7 +143,9 @@ export function WalletWizard({
|
|||||||
className={`w-8 h-px mx-2 ${
|
className={`w-8 h-px mx-2 ${
|
||||||
getStepStatus(step as WizardStep) === 'complete' ||
|
getStepStatus(step as WizardStep) === 'complete' ||
|
||||||
(step === 1 && isAuthenticated) ||
|
(step === 1 && isAuthenticated) ||
|
||||||
(step === 2 && verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED)
|
(step === 2 &&
|
||||||
|
verificationStatus !==
|
||||||
|
EVerificationStatus.WALLET_UNCONNECTED)
|
||||||
? 'bg-green-500'
|
? 'bg-green-500'
|
||||||
: 'bg-gray-600'
|
: 'bg-gray-600'
|
||||||
}`}
|
}`}
|
||||||
|
|||||||
@ -129,44 +129,42 @@ const BookmarksPage = () => {
|
|||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<BookmarkIcon className="text-cyber-accent" size={32} />
|
<BookmarkIcon className="text-cyber-accent" size={32} />
|
||||||
<h1 className="page-title">
|
<h1 className="page-title">My Bookmarks</h1>
|
||||||
My Bookmarks
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{bookmarks.length > 0 && (
|
{bookmarks.length > 0 && (
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="text-red-400 border-red-400/30 hover:bg-red-400/10"
|
className="text-red-400 border-red-400/30 hover:bg-red-400/10"
|
||||||
>
|
|
||||||
<Trash2 size={16} className="mr-2" />
|
|
||||||
Clear All
|
|
||||||
</Button>
|
|
||||||
</AlertDialogTrigger>
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle>Clear All Bookmarks</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
Are you sure you want to remove all your bookmarks? This
|
|
||||||
action cannot be undone.
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
||||||
<AlertDialogAction
|
|
||||||
onClick={handleClearAll}
|
|
||||||
className="bg-red-600 hover:bg-red-700"
|
|
||||||
>
|
>
|
||||||
|
<Trash2 size={16} className="mr-2" />
|
||||||
Clear All
|
Clear All
|
||||||
</AlertDialogAction>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</AlertDialogTrigger>
|
||||||
</AlertDialogContent>
|
<AlertDialogContent>
|
||||||
</AlertDialog>
|
<AlertDialogHeader>
|
||||||
)}
|
<AlertDialogTitle>Clear All Bookmarks</AlertDialogTitle>
|
||||||
</div>
|
<AlertDialogDescription>
|
||||||
|
Are you sure you want to remove all your bookmarks? This
|
||||||
|
action cannot be undone.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={handleClearAll}
|
||||||
|
className="bg-red-600 hover:bg-red-700"
|
||||||
|
>
|
||||||
|
Clear All
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<p className="page-subtitle">
|
<p className="page-subtitle">
|
||||||
Your saved posts and comments. Bookmarks are stored locally and
|
Your saved posts and comments. Bookmarks are stored locally and
|
||||||
@ -174,83 +172,83 @@ const BookmarksPage = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
{bookmarks.length > 0 && (
|
{bookmarks.length > 0 && (
|
||||||
<div className="flex gap-4 mb-6">
|
<div className="flex gap-4 mb-6">
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border-cyber-accent/30 text-cyber-accent"
|
className="border-cyber-accent/30 text-cyber-accent"
|
||||||
>
|
>
|
||||||
<FileText size={14} className="mr-1" />
|
<FileText size={14} className="mr-1" />
|
||||||
{postBookmarks.length} Posts
|
{postBookmarks.length} Posts
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border-cyber-accent/30 text-cyber-accent"
|
className="border-cyber-accent/30 text-cyber-accent"
|
||||||
>
|
>
|
||||||
<MessageSquare size={14} className="mr-1" />
|
<MessageSquare size={14} className="mr-1" />
|
||||||
{commentBookmarks.length} Comments
|
{commentBookmarks.length} Comments
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border-cyber-accent/30 text-cyber-accent"
|
className="border-cyber-accent/30 text-cyber-accent"
|
||||||
>
|
>
|
||||||
<BookmarkIcon size={14} className="mr-1" />
|
<BookmarkIcon size={14} className="mr-1" />
|
||||||
{bookmarks.length} Total
|
{bookmarks.length} Total
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<Tabs
|
<Tabs
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onValueChange={value =>
|
onValueChange={value =>
|
||||||
setActiveTab(value as 'all' | 'posts' | 'comments')
|
setActiveTab(value as 'all' | 'posts' | 'comments')
|
||||||
}
|
}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<TabsList className="grid w-full grid-cols-3 mb-6">
|
<TabsList className="grid w-full grid-cols-3 mb-6">
|
||||||
<TabsTrigger value="all" className="flex items-center gap-2">
|
<TabsTrigger value="all" className="flex items-center gap-2">
|
||||||
<BookmarkIcon size={16} />
|
<BookmarkIcon size={16} />
|
||||||
All ({bookmarks.length})
|
All ({bookmarks.length})
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="posts" className="flex items-center gap-2">
|
<TabsTrigger value="posts" className="flex items-center gap-2">
|
||||||
<FileText size={16} />
|
<FileText size={16} />
|
||||||
Posts ({postBookmarks.length})
|
Posts ({postBookmarks.length})
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="comments" className="flex items-center gap-2">
|
<TabsTrigger value="comments" className="flex items-center gap-2">
|
||||||
<MessageSquare size={16} />
|
<MessageSquare size={16} />
|
||||||
Comments ({commentBookmarks.length})
|
Comments ({commentBookmarks.length})
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="all">
|
<TabsContent value="all">
|
||||||
<BookmarkList
|
<BookmarkList
|
||||||
bookmarks={getFilteredBookmarks()}
|
bookmarks={getFilteredBookmarks()}
|
||||||
onRemove={removeBookmark}
|
onRemove={removeBookmark}
|
||||||
onNavigate={handleNavigate}
|
onNavigate={handleNavigate}
|
||||||
emptyMessage="No bookmarks yet"
|
emptyMessage="No bookmarks yet"
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="posts">
|
<TabsContent value="posts">
|
||||||
<BookmarkList
|
<BookmarkList
|
||||||
bookmarks={getFilteredBookmarks()}
|
bookmarks={getFilteredBookmarks()}
|
||||||
onRemove={removeBookmark}
|
onRemove={removeBookmark}
|
||||||
onNavigate={handleNavigate}
|
onNavigate={handleNavigate}
|
||||||
emptyMessage="No bookmarked posts yet"
|
emptyMessage="No bookmarked posts yet"
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="comments">
|
<TabsContent value="comments">
|
||||||
<BookmarkList
|
<BookmarkList
|
||||||
bookmarks={getFilteredBookmarks()}
|
bookmarks={getFilteredBookmarks()}
|
||||||
onRemove={removeBookmark}
|
onRemove={removeBookmark}
|
||||||
onNavigate={handleNavigate}
|
onNavigate={handleNavigate}
|
||||||
emptyMessage="No bookmarked comments yet"
|
emptyMessage="No bookmarked comments yet"
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@ -105,49 +105,47 @@ const FeedPage: React.FC = () => {
|
|||||||
<h1 className="page-title text-glow text-cyber-accent">
|
<h1 className="page-title text-glow text-cyber-accent">
|
||||||
Popular Posts
|
Popular Posts
|
||||||
</h1>
|
</h1>
|
||||||
<p className="page-subtitle">
|
<p className="page-subtitle">Latest posts from all cells</p>
|
||||||
Latest posts from all cells
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<ModerationToggle />
|
<ModerationToggle />
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={sortOption}
|
value={sortOption}
|
||||||
onValueChange={(value: SortOption) => setSortOption(value)}
|
onValueChange={(value: SortOption) => setSortOption(value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-40">
|
<SelectTrigger className="w-40">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="relevance">
|
<SelectItem value="relevance">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<TrendingUp className="w-4 h-4" />
|
<TrendingUp className="w-4 h-4" />
|
||||||
<span>Relevance</span>
|
<span>Relevance</span>
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="time">
|
<SelectItem value="time">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Clock className="w-4 h-4" />
|
<Clock className="w-4 h-4" />
|
||||||
<span>Newest</span>
|
<span>Newest</span>
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={refreshData}
|
onClick={refreshData}
|
||||||
disabled={isRefreshing}
|
disabled={isRefreshing}
|
||||||
className="flex items-center space-x-2"
|
className="flex items-center space-x-2"
|
||||||
>
|
>
|
||||||
<RefreshCw
|
<RefreshCw
|
||||||
className={`w-4 h-4 ${isRefreshing ? 'animate-spin' : ''}`}
|
className={`w-4 h-4 ${isRefreshing ? 'animate-spin' : ''}`}
|
||||||
/>
|
/>
|
||||||
<span>Refresh</span>
|
<span>Refresh</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -58,21 +58,21 @@ export default function ProfilePage() {
|
|||||||
|
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [callSign, setCallSign] = useState(currentUser?.callSign || '');
|
const [callSign, setCallSign] = useState('');
|
||||||
const [displayPreference, setDisplayPreference] = useState(
|
const [displayPreference, setDisplayPreference] = useState(EDisplayPreference.WALLET_ADDRESS);
|
||||||
currentUser?.displayPreference || EDisplayPreference.WALLET_ADDRESS
|
|
||||||
);
|
|
||||||
const [walletWizardOpen, setWalletWizardOpen] = useState(false);
|
const [walletWizardOpen, setWalletWizardOpen] = useState(false);
|
||||||
|
|
||||||
// Update local state when user data changes
|
// Initialize and update local state when user data changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
setCallSign(currentUser.callSign || '');
|
// Use the same data source as the display (userInfo) for consistency
|
||||||
setDisplayPreference(
|
const currentCallSign = userInfo.callSign || currentUser.callSign || '';
|
||||||
currentUser.displayPreference || EDisplayPreference.WALLET_ADDRESS
|
const currentDisplayPreference = userInfo.displayPreference || currentUser.displayPreference || EDisplayPreference.WALLET_ADDRESS;
|
||||||
);
|
|
||||||
|
setCallSign(currentCallSign);
|
||||||
|
setDisplayPreference(currentDisplayPreference);
|
||||||
}
|
}
|
||||||
}, [currentUser]);
|
}, [currentUser, userInfo.callSign, userInfo.displayPreference]);
|
||||||
|
|
||||||
// Copy to clipboard function
|
// Copy to clipboard function
|
||||||
const copyToClipboard = async (text: string, label: string) => {
|
const copyToClipboard = async (text: string, label: string) => {
|
||||||
@ -100,8 +100,12 @@ export default function ProfilePage() {
|
|||||||
<CardContent className="pt-6">
|
<CardContent className="pt-6">
|
||||||
<div className="text-center text-cyber-neutral">
|
<div className="text-center text-cyber-neutral">
|
||||||
<User className="w-12 h-12 mx-auto mb-4 text-cyber-accent" />
|
<User className="w-12 h-12 mx-auto mb-4 text-cyber-accent" />
|
||||||
<h2 className="text-xl font-mono font-bold mb-2">Connect Required</h2>
|
<h2 className="text-xl font-mono font-bold mb-2">
|
||||||
<p className="text-sm">Please connect your wallet to view your profile.</p>
|
Connect Required
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm">
|
||||||
|
Please connect your wallet to view your profile.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@ -168,8 +172,12 @@ export default function ProfilePage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
setCallSign(currentUser.callSign || '');
|
// Reset to the same data source as display for consistency
|
||||||
setDisplayPreference(currentUser.displayPreference);
|
const currentCallSign = userInfo.callSign || currentUser.callSign || '';
|
||||||
|
const currentDisplayPreference = userInfo.displayPreference || currentUser.displayPreference || EDisplayPreference.WALLET_ADDRESS;
|
||||||
|
|
||||||
|
setCallSign(currentCallSign);
|
||||||
|
setDisplayPreference(currentDisplayPreference);
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -220,7 +228,9 @@ export default function ProfilePage() {
|
|||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
<h1 className="page-title">Profile</h1>
|
<h1 className="page-title">Profile</h1>
|
||||||
<p className="page-subtitle">Manage your account settings and preferences</p>
|
<p className="page-subtitle">
|
||||||
|
Manage your account settings and preferences
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Two-Card Layout: User Profile + Security Status */}
|
{/* Two-Card Layout: User Profile + Security Status */}
|
||||||
@ -283,12 +293,15 @@ export default function ProfilePage() {
|
|||||||
</Label>
|
</Label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex-1 font-mono text-sm bg-cyber-dark/50 border border-cyber-muted/30 px-3 py-2 rounded-md text-cyber-light">
|
<div className="flex-1 font-mono text-sm bg-cyber-dark/50 border border-cyber-muted/30 px-3 py-2 rounded-md text-cyber-light">
|
||||||
{currentUser.address.slice(0, 8)}...{currentUser.address.slice(-6)}
|
{currentUser.address.slice(0, 8)}...
|
||||||
|
{currentUser.address.slice(-6)}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => copyToClipboard(currentUser.address, 'Address')}
|
onClick={() =>
|
||||||
|
copyToClipboard(currentUser.address, 'Address')
|
||||||
|
}
|
||||||
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
||||||
>
|
>
|
||||||
<Copy className="w-4 h-4" />
|
<Copy className="w-4 h-4" />
|
||||||
@ -301,7 +314,10 @@ export default function ProfilePage() {
|
|||||||
</Label>
|
</Label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Globe className="w-4 h-4 text-cyber-neutral" />
|
<Globe className="w-4 h-4 text-cyber-neutral" />
|
||||||
<Badge variant="outline" className="capitalize bg-cyber-accent/20 text-cyber-accent border-cyber-accent/30">
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="capitalize bg-cyber-accent/20 text-cyber-accent border-cyber-accent/30"
|
||||||
|
>
|
||||||
{currentUser.walletType}
|
{currentUser.walletType}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
@ -316,7 +332,10 @@ export default function ProfilePage() {
|
|||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="callSign" className="text-sm font-medium text-cyber-neutral">
|
<Label
|
||||||
|
htmlFor="callSign"
|
||||||
|
className="text-sm font-medium text-cyber-neutral"
|
||||||
|
>
|
||||||
Call Sign
|
Call Sign
|
||||||
</Label>
|
</Label>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
@ -330,16 +349,22 @@ export default function ProfilePage() {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm bg-cyber-dark/50 border border-cyber-muted/30 px-3 py-2 rounded-md text-cyber-light">
|
<div className="text-sm bg-cyber-dark/50 border border-cyber-muted/30 px-3 py-2 rounded-md text-cyber-light">
|
||||||
{userInfo.callSign || currentUser.callSign || 'Not set'}
|
{userInfo.callSign ||
|
||||||
|
currentUser.callSign ||
|
||||||
|
'Not set'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-cyber-neutral">
|
<p className="text-xs text-cyber-neutral">
|
||||||
3-20 characters, letters, numbers, underscores, and hyphens only
|
3-20 characters, letters, numbers, underscores, and
|
||||||
|
hyphens only
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="displayPreference" className="text-sm font-medium text-cyber-neutral">
|
<Label
|
||||||
|
htmlFor="displayPreference"
|
||||||
|
className="text-sm font-medium text-cyber-neutral"
|
||||||
|
>
|
||||||
Display Preference
|
Display Preference
|
||||||
</Label>
|
</Label>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
@ -354,17 +379,24 @@ export default function ProfilePage() {
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-cyber-dark border-cyber-muted/30">
|
<SelectContent className="bg-cyber-dark border-cyber-muted/30">
|
||||||
<SelectItem value={EDisplayPreference.CALL_SIGN} className="text-cyber-light hover:bg-cyber-muted/30">
|
<SelectItem
|
||||||
|
value={EDisplayPreference.CALL_SIGN}
|
||||||
|
className="text-cyber-light hover:bg-cyber-muted/30"
|
||||||
|
>
|
||||||
Call Sign (when available)
|
Call Sign (when available)
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value={EDisplayPreference.WALLET_ADDRESS} className="text-cyber-light hover:bg-cyber-muted/30">
|
<SelectItem
|
||||||
|
value={EDisplayPreference.WALLET_ADDRESS}
|
||||||
|
className="text-cyber-light hover:bg-cyber-muted/30"
|
||||||
|
>
|
||||||
Wallet Address
|
Wallet Address
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm bg-cyber-dark/50 border border-cyber-muted/30 px-3 py-2 rounded-md text-cyber-light">
|
<div className="text-sm bg-cyber-dark/50 border border-cyber-muted/30 px-3 py-2 rounded-md text-cyber-light">
|
||||||
{(userInfo.displayPreference || displayPreference) ===
|
{(userInfo.displayPreference ||
|
||||||
|
displayPreference) ===
|
||||||
EDisplayPreference.CALL_SIGN
|
EDisplayPreference.CALL_SIGN
|
||||||
? 'Call Sign (when available)'
|
? 'Call Sign (when available)'
|
||||||
: 'Wallet Address'}
|
: 'Wallet Address'}
|
||||||
@ -413,7 +445,8 @@ export default function ProfilePage() {
|
|||||||
<Shield className="h-5 w-5 text-cyber-accent" />
|
<Shield className="h-5 w-5 text-cyber-accent" />
|
||||||
Security
|
Security
|
||||||
</div>
|
</div>
|
||||||
{(delegationStatus.hasDelegation || delegationInfo?.hasDelegation) && (
|
{(delegationStatus.hasDelegation ||
|
||||||
|
delegationInfo?.hasDelegation) && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -421,7 +454,9 @@ export default function ProfilePage() {
|
|||||||
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
||||||
>
|
>
|
||||||
<Settings className="w-4 h-4 mr-2" />
|
<Settings className="w-4 h-4 mr-2" />
|
||||||
{(delegationStatus.isValid || delegationInfo?.isValid) ? 'Renew' : 'Setup'}
|
{delegationStatus.isValid || delegationInfo?.isValid
|
||||||
|
? 'Renew'
|
||||||
|
: 'Setup'}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
@ -430,41 +465,61 @@ export default function ProfilePage() {
|
|||||||
{/* Delegation Status */}
|
{/* Delegation Status */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium text-cyber-neutral">Delegation</span>
|
<span className="text-sm font-medium text-cyber-neutral">
|
||||||
|
Delegation
|
||||||
|
</span>
|
||||||
<Badge
|
<Badge
|
||||||
variant={(delegationStatus.isValid || delegationInfo?.isValid) ? 'default' : 'secondary'}
|
variant={
|
||||||
|
delegationStatus.isValid || delegationInfo?.isValid
|
||||||
|
? 'default'
|
||||||
|
: 'secondary'
|
||||||
|
}
|
||||||
className={
|
className={
|
||||||
(delegationStatus.isValid || delegationInfo?.isValid)
|
delegationStatus.isValid || delegationInfo?.isValid
|
||||||
? 'bg-green-500/20 text-green-400 border-green-500/30'
|
? 'bg-green-500/20 text-green-400 border-green-500/30'
|
||||||
: 'bg-red-500/20 text-red-400 border-red-500/30'
|
: 'bg-red-500/20 text-red-400 border-red-500/30'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(delegationStatus.isValid || delegationInfo?.isValid) ? 'Active' : 'Inactive'}
|
{delegationStatus.isValid || delegationInfo?.isValid
|
||||||
|
? 'Active'
|
||||||
|
: 'Inactive'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Expiry Date */}
|
{/* Expiry Date */}
|
||||||
{(delegationStatus.expiresAt || currentUser.delegationExpiry) && (
|
{(delegationStatus.expiresAt ||
|
||||||
|
currentUser.delegationExpiry) && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<span className="text-xs text-cyber-neutral">Valid until</span>
|
<span className="text-xs text-cyber-neutral">
|
||||||
|
Valid until
|
||||||
|
</span>
|
||||||
<div className="text-sm font-mono text-cyber-light">
|
<div className="text-sm font-mono text-cyber-light">
|
||||||
{(delegationStatus.expiresAt || new Date(currentUser.delegationExpiry!)).toLocaleDateString()}
|
{(
|
||||||
|
delegationStatus.expiresAt ||
|
||||||
|
new Date(currentUser.delegationExpiry!)
|
||||||
|
).toLocaleDateString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Signature Status */}
|
{/* Signature Status */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium text-cyber-neutral">Signature</span>
|
<span className="text-sm font-medium text-cyber-neutral">
|
||||||
|
Signature
|
||||||
|
</span>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={
|
className={
|
||||||
(delegationStatus.isValid || currentUser.delegationSignature === 'valid')
|
delegationStatus.isValid ||
|
||||||
|
currentUser.delegationSignature === 'valid'
|
||||||
? 'text-green-400 border-green-500/30 bg-green-500/10'
|
? 'text-green-400 border-green-500/30 bg-green-500/10'
|
||||||
: 'text-red-400 border-red-500/30 bg-red-500/10'
|
: 'text-red-400 border-red-500/30 bg-red-500/10'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(delegationStatus.isValid || currentUser.delegationSignature === 'valid') ? 'Valid' : 'Not signed'}
|
{delegationStatus.isValid ||
|
||||||
|
currentUser.delegationSignature === 'valid'
|
||||||
|
? 'Valid'
|
||||||
|
: 'Not signed'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -476,15 +531,22 @@ export default function ProfilePage() {
|
|||||||
</Label>
|
</Label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex-1 font-mono text-xs bg-cyber-dark/50 border border-cyber-muted/30 px-2 py-1 rounded text-cyber-light">
|
<div className="flex-1 font-mono text-xs bg-cyber-dark/50 border border-cyber-muted/30 px-2 py-1 rounded text-cyber-light">
|
||||||
{(delegationStatus.publicKey || currentUser.browserPubKey)
|
{delegationStatus.publicKey || currentUser.browserPubKey
|
||||||
? `${(delegationStatus.publicKey || currentUser.browserPubKey!).slice(0, 12)}...${(delegationStatus.publicKey || currentUser.browserPubKey!).slice(-8)}`
|
? `${(delegationStatus.publicKey || currentUser.browserPubKey!).slice(0, 12)}...${(delegationStatus.publicKey || currentUser.browserPubKey!).slice(-8)}`
|
||||||
: 'Not delegated'}
|
: 'Not delegated'}
|
||||||
</div>
|
</div>
|
||||||
{(delegationStatus.publicKey || currentUser.browserPubKey) && (
|
{(delegationStatus.publicKey ||
|
||||||
|
currentUser.browserPubKey) && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => copyToClipboard(delegationStatus.publicKey || currentUser.browserPubKey!, 'Public Key')}
|
onClick={() =>
|
||||||
|
copyToClipboard(
|
||||||
|
delegationStatus.publicKey ||
|
||||||
|
currentUser.browserPubKey!,
|
||||||
|
'Public Key'
|
||||||
|
)
|
||||||
|
}
|
||||||
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
|
||||||
>
|
>
|
||||||
<Copy className="w-3 h-3" />
|
<Copy className="w-3 h-3" />
|
||||||
@ -494,21 +556,24 @@ export default function ProfilePage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Warning for expired delegation */}
|
{/* Warning for expired delegation */}
|
||||||
{(!delegationStatus.isValid && delegationStatus.hasDelegation) || (!delegationInfo?.isValid && delegationInfo?.hasDelegation) && (
|
{(!delegationStatus.isValid &&
|
||||||
<div className="p-3 bg-orange-500/10 border border-orange-500/30 rounded-md">
|
delegationStatus.hasDelegation) ||
|
||||||
<div className="flex items-center gap-2 text-orange-400">
|
(!delegationInfo?.isValid &&
|
||||||
<AlertTriangle className="w-4 h-4" />
|
delegationInfo?.hasDelegation && (
|
||||||
<span className="text-xs font-medium">
|
<div className="p-3 bg-orange-500/10 border border-orange-500/30 rounded-md">
|
||||||
Delegation expired. Renew to continue using your browser key.
|
<div className="flex items-center gap-2 text-orange-400">
|
||||||
</span>
|
<AlertTriangle className="w-4 h-4" />
|
||||||
</div>
|
<span className="text-xs font-medium">
|
||||||
</div>
|
Delegation expired. Renew to continue using your
|
||||||
)}
|
browser key.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user