From 985fa2f7c99832cdf3c3351d2273c8fd05402b78 Mon Sep 17 00:00:00 2001 From: polwex Date: Wed, 17 Sep 2025 21:45:18 +0700 Subject: basic comms working --- front/src/components/ProfileEditor.tsx | 280 +++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 front/src/components/ProfileEditor.tsx (limited to 'front/src/components/ProfileEditor.tsx') diff --git a/front/src/components/ProfileEditor.tsx b/front/src/components/ProfileEditor.tsx new file mode 100644 index 0000000..9a7493f --- /dev/null +++ b/front/src/components/ProfileEditor.tsx @@ -0,0 +1,280 @@ +import { useState, useEffect } from "react"; +import type { UserProfile } from "@/types/nostrill"; +import useLocalState from "@/state/state"; +import Icon from "@/components/Icon"; +import toast from "react-hot-toast"; +import Avatar from "./Avatar"; + +interface ProfileEditorProps { + ship: string; + onSave?: () => void; +} + +const ProfileEditor: React.FC = ({ ship, onSave }) => { + const { api, profiles } = useLocalState((s) => ({ + api: s.api, + profiles: s.profiles, + })); + const isOwnProfile = ship === api?.airlock.our; + + // Initialize state with existing profile or defaults + const existingProfile = profiles.get(ship); + const [name, setName] = useState(existingProfile?.name || ""); + const [picture, setPicture] = useState(existingProfile?.picture || ""); + const [about, setAbout] = useState(existingProfile?.about || ""); + const [customFields, setCustomFields] = useState< + Array<{ key: string; value: string }> + >( + Object.entries(existingProfile?.other || {}).map(([key, value]) => ({ + key, + value, + })), + ); + const [isEditing, setIsEditing] = useState(false); + const [isSaving, setIsSaving] = useState(false); + + useEffect(() => { + const profile = profiles.get(ship); + if (profile) { + setName(profile.name || ""); + setPicture(profile.picture || ""); + setAbout(profile.about || ""); + setCustomFields( + Object.entries(profile.other || {}).map(([key, value]) => ({ + key, + value, + })), + ); + } + }, [ship, profiles]); + + const handleAddCustomField = () => { + setCustomFields([...customFields, { key: "", value: "" }]); + }; + + const handleUpdateCustomField = ( + index: number, + field: "key" | "value", + newValue: string, + ) => { + const updated = [...customFields]; + updated[index][field] = newValue; + setCustomFields(updated); + }; + + const handleRemoveCustomField = (index: number) => { + setCustomFields(customFields.filter((_, i) => i !== index)); + }; + + const handleSave = async () => { + setIsSaving(true); + try { + // Convert custom fields array to object + const other: Record = {}; + customFields.forEach(({ key, value }) => { + if (key.trim()) { + other[key.trim()] = value; + } + }); + + const profile: UserProfile = { + name, + picture, + about, + other, + }; + + // Call API to save profile + if (api && typeof api.createProfile === "function") { + await api.createProfile(profile); + } else { + throw new Error("Profile update API not available"); + } + + toast.success("Profile updated successfully"); + setIsEditing(false); + onSave?.(); + } catch (error) { + toast.error("Failed to update profile"); + console.error("Failed to save profile:", error); + } finally { + setIsSaving(false); + } + }; + + const handleCancel = () => { + // Reset to original values + const profile = profiles.get(ship); + if (profile) { + setName(profile.name || ""); + setPicture(profile.picture || ""); + setAbout(profile.about || ""); + setCustomFields( + Object.entries(profile.other || {}).map(([key, value]) => ({ + key, + value, + })), + ); + } + setIsEditing(false); + }; + + if (!isOwnProfile) { + // View-only mode for other users' profiles - no editing allowed + return ( +
+
+ +
+
+

{name || ship}

+ {about &&

{about}

} + + {customFields.length > 0 && ( +
+

Additional Info

+ {customFields.map(({ key, value }, index) => ( +
+ {key}: + {value} +
+ ))} +
+ )} +
+
+ ); + } + + return ( +
+
+

Edit Profile

+ {isOwnProfile && !isEditing && ( + + )} +
+ + {isEditing ? ( +
+
+ + setName(e.target.value)} + placeholder="Your display name" + /> +
+ +
+ + setPicture(e.target.value)} + placeholder="https://example.com/avatar.jpg" + /> +
+ +
+
+ +
+ +