From 985fa2f7c99832cdf3c3351d2273c8fd05402b78 Mon Sep 17 00:00:00 2001
From: polwex <polwex@sortug.com>
Date: Wed, 17 Sep 2025 21:45:18 +0700
Subject: basic comms working

---
 front/src/pages/Settings.tsx | 257 +++++++++++++++++++++++++++++++------------
 1 file changed, 187 insertions(+), 70 deletions(-)

(limited to 'front/src/pages/Settings.tsx')

diff --git a/front/src/pages/Settings.tsx b/front/src/pages/Settings.tsx
index e0f1da9..6b6f7bd 100644
--- a/front/src/pages/Settings.tsx
+++ b/front/src/pages/Settings.tsx
@@ -1,89 +1,206 @@
 import useLocalState from "@/state/state";
-import type { UserProfile } from "@/types/nostril";
 import { useState } from "react";
+import toast from "react-hot-toast";
+import { ThemeSwitcher } from "@/styles/ThemeSwitcher";
+import Icon from "@/components/Icon";
+import "@/styles/Settings.css";
 
 function Settings() {
-  const { UISettings, keys, profiles, relays, api } = useLocalState();
+  const { key, relays, api } = useLocalState((s) => ({
+    key: s.key,
+    relays: s.relays,
+    api: s.api,
+  }));
   const [newRelay, setNewRelay] = useState("");
-  async function saveSetting(
-    bucket: string,
-    key: string,
-    value: string | boolean | number | string[],
-  ) {
-    const json = {
-      "put-entry": {
-        desk: "trill",
-        "bucket-key": bucket,
-        "entry-key": key,
-        value,
-      },
-    };
-    // const res = await poke("settings", "settings-event", json);
-    // if (res) refetchSettings();
-  }
+  const [isAddingRelay, setIsAddingRelay] = useState(false);
+  const [isCyclingKey, setIsCyclingKey] = useState(false);
+
   async function removeRelay(url: string) {
-    console.log({ url });
+    try {
+      await api?.deleteRelay(url);
+      toast.success("Relay removed");
+    } catch (error) {
+      toast.error("Failed to remove relay");
+      console.error("Remove relay error:", error);
+    }
   }
+
   async function addNewRelay() {
-    //
-    // await addnr(newRelay);
-  }
-  async function removeProfile(pubkey: string) {
-    api!.removeKey(pubkey);
+    if (!newRelay.trim()) {
+      toast.error("Please enter a relay URL");
+      return;
+    }
+
+    setIsAddingRelay(true);
+    try {
+      const valid = ["wss:", "ws:"];
+      const url = new URL(newRelay);
+      if (!valid.includes(url.protocol)) {
+        toast.error("Invalid Relay URL - must use wss:// or ws://");
+        return;
+      }
+
+      await api?.addRelay(newRelay);
+      toast.success("Relay added");
+      setNewRelay("");
+    } catch (error) {
+      toast.error("Invalid relay URL or failed to add relay");
+      console.error("Add relay error:", error);
+    } finally {
+      setIsAddingRelay(false);
+    }
   }
-  async function createProfile() {
-    //
-    api!.createKey();
+
+  async function cycleKey() {
+    setIsCyclingKey(true);
+    try {
+      await api?.cycleKeys();
+      toast.success("Key cycled successfully");
+    } catch (error) {
+      toast.error("Failed to cycle key");
+      console.error("Cycle key error:", error);
+    } finally {
+      setIsCyclingKey(false);
+    }
   }
 
+  const handleKeyPress = (e: React.KeyboardEvent) => {
+    if (e.key === "Enter") {
+      addNewRelay();
+    }
+  };
+
   return (
-    <div id="settings">
-      <h1>Settings</h1>
-      <div className="setting">
-        <label>Pubkeys</label>
-        {keys.map((k) => {
-          const profile = profiles.get(k);
-          const profileDiv = !profile ? (
-            <div className="profile">
-              <div>Pubkey: {k}</div>
-              <p>No profile set</p>)
-            </div>
-          ) : (
-            <div className="profile">
-              {profile.picture && <img src={profile.picture} />}
-              <div>Name: {profile.name}</div>
-              <div>Pubkey: {k}</div>
-              <div>About: {profile.about}</div>
-              <button onClick={() => removeProfile(k)}>x</button>
+    <div className="settings-page">
+      <div className="settings-header">
+        <h1>Settings</h1>
+        <p>Manage your Nostrill configuration and preferences</p>
+      </div>
+
+      <div className="settings-content">
+        {/* Appearance Section */}
+        <div className="settings-section">
+          <div className="section-header">
+            <Icon name="settings" size={20} />
+            <h2>Appearance</h2>
+          </div>
+          <div className="section-content">
+            <div className="setting-item">
+              <div className="setting-info">
+                <label>Theme</label>
+                <p>Choose your preferred color theme</p>
+              </div>
+              <div className="setting-control">
+                <ThemeSwitcher />
+              </div>
             </div>
-          );
-          return (
-            <div className="options flex" key={k}>
-              {profileDiv}
+          </div>
+        </div>
+
+        {/* Identity Section */}
+        <div className="settings-section">
+          <div className="section-header">
+            <Icon name="key" size={20} />
+            <h2>Identity</h2>
+          </div>
+          <div className="section-content">
+            <div className="setting-item">
+              <div className="setting-info">
+                <label>Nostr Public Key</label>
+                <p>Your unique identifier on the Nostr network</p>
+              </div>
+              <div className="setting-control">
+                <div className="key-display">
+                  <code className="pubkey">{key || "No key generated"}</code>
+                  <button
+                    onClick={cycleKey}
+                    disabled={isCyclingKey}
+                    className="cycle-btn"
+                    title="Generate new key pair"
+                  >
+                    {isCyclingKey ? (
+                      <Icon name="settings" size={16} />
+                    ) : (
+                      <Icon name="settings" size={16} />
+                    )}
+                    {isCyclingKey ? "Cycling..." : "Cycle Key"}
+                  </button>
+                </div>
+              </div>
             </div>
-          );
-        })}
-        <div className="options flex">
-          <button onClick={createProfile}>Create New</button>
+          </div>
         </div>
-      </div>
-      <div className="setting">
-        <label>Nostr Relays</label>
-        {Object.keys(relays).map((r) => (
-          // TODO: add connect button to connect and disc to relay one by one
-          <div className="options flex" key={r}>
-            <div>{r}</div>
-            <button onClick={() => removeRelay(r)}>x</button>
+
+        {/* Nostr Relays Section */}
+        <div className="settings-section">
+          <div className="section-header">
+            <Icon name="nostr" size={20} />
+            <h2>Nostr Relays</h2>
+          </div>
+          <div className="section-content">
+            <div className="setting-item">
+              <div className="setting-info">
+                <label>Connected Relays</label>
+                <p>Manage your Nostr relay connections</p>
+              </div>
+              <div className="setting-control">
+                <div className="relay-list">
+                  {Object.keys(relays).length === 0 ? (
+                    <div className="no-relays">
+                      <Icon name="nostr" size={24} color="textMuted" />
+                      <p>No relays configured</p>
+                    </div>
+                  ) : (
+                    Object.keys(relays).map((url) => (
+                      <div key={url} className="relay-item">
+                        <div className="relay-info">
+                          <span className="relay-url">{url}</span>
+                          <span className="relay-status">Connected</span>
+                        </div>
+                        <button
+                          onClick={() => removeRelay(url)}
+                          className="remove-relay-btn"
+                          title="Remove relay"
+                        >
+                          ×
+                        </button>
+                      </div>
+                    ))
+                  )}
+
+                  <div className="add-relay-form">
+                    <div className="relay-input-group">
+                      <input
+                        type="text"
+                        value={newRelay}
+                        onChange={(e) => setNewRelay(e.target.value)}
+                        onKeyPress={handleKeyPress}
+                        placeholder="wss://relay.example.com"
+                        className="relay-input"
+                      />
+                      <button
+                        onClick={addNewRelay}
+                        disabled={isAddingRelay || !newRelay.trim()}
+                        className="add-relay-btn"
+                      >
+                        {isAddingRelay ? (
+                          <>
+                            <Icon name="settings" size={16} />
+                            Adding...
+                          </>
+                        ) : (
+                          <>
+                            <Icon name="settings" size={16} />
+                            Add Relay
+                          </>
+                        )}
+                      </button>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
           </div>
-        ))}
-        <div className="options flex">
-          <label>Add new</label>
-          <input
-            type="text"
-            value={newRelay}
-            onChange={(e) => setNewRelay(e.target.value)}
-          />
-          <button onClick={addNewRelay}>Add</button>
         </div>
       </div>
     </div>
-- 
cgit v1.2.3

