summaryrefslogtreecommitdiff
path: root/front/src/pages/Settings.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'front/src/pages/Settings.tsx')
-rw-r--r--front/src/pages/Settings.tsx257
1 files changed, 187 insertions, 70 deletions
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>