bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
111 lines (110 loc) • 9.58 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { MagnifyingGlassIcon, Pencil1Icon, PersonIcon, PlusIcon, RocketIcon, } from "@radix-ui/react-icons";
import { useState } from "react";
import { useBitcoinAuth } from "../../hooks/useBitcoinAuth.js";
import { useBapProfileSync } from "../bap-identity/hooks/useBapProfileSync.js";
import { Button } from "../ui/button.js";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs.js";
import { ProfileEditor } from "./ProfileEditor.js";
import { ProfilePublisher } from "./ProfilePublisher.js";
import { ProfileSwitcher } from "./ProfileSwitcher.js";
import { ProfileViewer } from "./ProfileViewer.js";
/**
* ProfileManager - Complete profile management interface
*
* Features:
* - View current profile details
* - Edit profile information
* - Publish profiles to blockchain
* - Switch between multiple profiles
* - Create new profiles
* - Tabbed interface for different actions
*/
export function ProfileManager({ user: userProp, onProfileUpdate, onProfilePublish, onProfileCreate, onProfileSwitch, showPublisher = true, maxProfiles = 10, className = "", enableProfileSync = true, maxDiscoveryAttempts = 50, }) {
const { user: authUser, createProfile, switchProfile } = useBitcoinAuth();
const user = userProp || authUser;
const [activeTab, setActiveTab] = useState("view");
const [isCreating, setIsCreating] = useState(false);
// Profile synchronization
const profileSync = useBapProfileSync({
autoSync: false, // Manual sync only in ProfileManager
maxDiscoveryAttempts,
});
if (!user) {
return (_jsx("div", { className: className, children: _jsx("p", { className: "text-base text-muted-foreground", children: "Please sign in to manage your profiles." }) }));
}
const activeProfile = user.profiles.find((p) => p.id === user.activeProfileId) ||
user.profiles[0];
if (!activeProfile) {
return (_jsx("div", { className: className, children: _jsxs("div", { className: "flex flex-col gap-4 items-center py-8", children: [_jsx(PersonIcon, { width: "48", height: "48", className: "text-muted-foreground" }), _jsx("h3", { className: "text-base font-medium", children: "No Profiles Yet" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Create your first profile to get started" }), _jsxs(Button, { size: "lg", onClick: async () => {
setIsCreating(true);
try {
const newProfile = onProfileCreate
? await onProfileCreate()
: await createProfile();
if (onProfileSwitch) {
onProfileSwitch(newProfile.id);
}
else if (switchProfile) {
switchProfile(newProfile.id);
}
}
catch (error) {
console.error("Failed to create profile:", error);
}
finally {
setIsCreating(false);
}
}, disabled: isCreating, children: [_jsx(PlusIcon, { className: "h-4 w-4 mr-2" }), isCreating ? "Creating..." : "Create First Profile"] })] }) }));
}
const handleProfileUpdate = async (updates) => {
if (onProfileUpdate) {
await onProfileUpdate({ ...activeProfile, ...updates });
}
setActiveTab("view");
};
const handleProfilePublish = async (profileId) => {
if (onProfilePublish) {
return await onProfilePublish(profileId);
}
throw new Error("No publish handler available");
};
const handleProfileSwitch = (profileId) => {
if (onProfileSwitch) {
onProfileSwitch(profileId);
}
else if (switchProfile) {
switchProfile(profileId);
}
};
const handleProfileCreate = async () => {
setIsCreating(true);
try {
const newProfile = onProfileCreate
? await onProfileCreate()
: await createProfile();
handleProfileSwitch(newProfile.id);
}
catch (error) {
console.error("Failed to create profile:", error);
}
finally {
setIsCreating(false);
}
};
return (_jsx("div", { className: className, children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "text-2xl font-bold", children: "Profile Management" }), user.profiles.length > 1 && (_jsx(ProfileSwitcher, { profiles: user.profiles, activeProfileId: user.activeProfileId, onSwitch: handleProfileSwitch, onCreate: user.profiles.length < maxProfiles
? handleProfileCreate
: undefined, maxProfiles: maxProfiles }))] }), _jsxs(Tabs, { value: activeTab, onValueChange: (value) => setActiveTab(value), children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "view", className: "flex items-center gap-2", children: [_jsx(PersonIcon, { className: "h-4 w-4" }), "View Profile"] }), _jsxs(TabsTrigger, { value: "edit", className: "flex items-center gap-2", children: [_jsx(Pencil1Icon, { className: "h-4 w-4" }), "Edit Profile"] }), showPublisher && !activeProfile.isPublished && (_jsxs(TabsTrigger, { value: "publish", className: "flex items-center gap-2", children: [_jsx(RocketIcon, { className: "h-4 w-4" }), "Publish"] })), enableProfileSync && (_jsxs(TabsTrigger, { value: "sync", className: "flex items-center gap-2", children: [_jsx(MagnifyingGlassIcon, { className: "h-4 w-4" }), "Discover Profiles"] }))] }), _jsxs("div", { className: "pt-4", children: [_jsx(TabsContent, { value: "view", children: _jsx(ProfileViewer, { profile: activeProfile, onEdit: () => setActiveTab("edit"), onPublish: !activeProfile.isPublished
? () => setActiveTab("publish")
: undefined }) }), _jsx(TabsContent, { value: "edit", children: _jsx(ProfileEditor, { profile: activeProfile, onSave: handleProfileUpdate, onCancel: () => setActiveTab("view") }) }), showPublisher && (_jsx(TabsContent, { value: "publish", children: _jsx(ProfilePublisher, { profile: activeProfile, onPublish: handleProfilePublish, onSuccess: (txid) => {
console.log("Profile published:", txid);
setActiveTab("view");
}, onError: (error) => {
console.error("Publishing failed:", error);
} }) })), enableProfileSync && (_jsx(TabsContent, { value: "sync", children: _jsxs("div", { children: [_jsx("h3", { className: "text-lg font-bold mb-3", children: "Discover Additional Profiles" }), _jsx("p", { className: "text-sm text-muted-foreground mb-4", children: "Scan the blockchain for additional BAP identities associated with your master backup." }), profileSync.isScanning && (_jsxs("div", { className: "mb-4", children: [_jsxs("p", { className: "text-sm mb-2", children: ["Scanning for profiles... ", profileSync.progress, "%"] }), _jsx("div", { className: "flex justify-between items-center", children: _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Current ID: ", profileSync.currentId, " | Found:", " ", profileSync.foundCount] }) })] })), profileSync.isSuccess && profileSync.result && (_jsxs("div", { className: "mb-4 p-3 bg-green-50 border border-green-200 rounded-lg", children: [_jsx("p", { className: "text-sm font-medium text-green-800", children: "Discovery Complete!" }), _jsxs("p", { className: "text-sm text-green-700", children: ["Found ", profileSync.result.totalFound, " profiles total.", profileSync.result.updatedBackup
? " Your backup has been updated with new identities."
: " No new identities discovered."] })] })), profileSync.error && (_jsx("div", { className: "mb-4 p-3 bg-red-50 border border-red-200 rounded-lg", children: _jsxs("p", { className: "text-sm text-red-700", children: ["Error: ", profileSync.error.message] }) })), _jsxs("div", { className: "flex gap-3", children: [_jsxs(Button, { variant: "default", onClick: profileSync.syncProfiles, disabled: profileSync.isScanning, children: [_jsx(MagnifyingGlassIcon, { className: "h-4 w-4 mr-2" }), profileSync.isScanning
? "Scanning..."
: "Start Discovery"] }), (profileSync.error || profileSync.isSuccess) && (_jsx(Button, { variant: "outline", onClick: profileSync.resetSync, children: "Reset" }))] })] }) }))] })] }), user.profiles.length < maxProfiles && user.profiles.length === 1 && (_jsx("div", { className: "flex justify-center pt-4", children: _jsxs(Button, { variant: "outline", size: "sm", onClick: handleProfileCreate, disabled: isCreating, children: [_jsx(PlusIcon, { className: "h-4 w-4 mr-2" }), isCreating ? "Creating..." : "Create Additional Profile"] }) }))] }) }));
}