blowater/UI/setting.tsx
2023-09-07 13:11:59 +00:00

217 lines
8.4 KiB
TypeScript

/** @jsx h */
import { Fragment, h } from "https://esm.sh/preact@10.17.1";
import { tw } from "https://esm.sh/twind@0.16.16";
import { computed, signal } from "https://esm.sh/@preact/signals@1.2.1";
import {
CenterClass,
inputBorderClass,
InputClass,
LinearGradientsClass,
NoOutlineClass,
} from "./components/tw.ts";
import KeyView from "./key-view.tsx";
import { InMemoryAccountContext, NostrAccountContext } from "../lib/nostr-ts/nostr.ts";
import { PrivateKey } from "../lib/nostr-ts/key.ts";
import {
DividerBackgroundColor,
ErrorColor,
HoverButtonBackgroudColor,
PrimaryTextColor,
SecondaryBackgroundColor,
SuccessColor,
TitleIconColor,
WarnColor,
} from "./style/colors.ts";
import { RelayIcon } from "./icons2/relay-icon.tsx";
import { DeleteIcon } from "./icons2/delete-icon.tsx";
import { RelayConfig } from "./setting.ts";
import { ConnectionPool } from "../lib/nostr-ts/relay.ts";
import { emitFunc, EventEmitter } from "../event-bus.ts";
export interface SettingProps {
logout: () => void;
relayConfig: RelayConfig;
relayPool: ConnectionPool;
myAccountContext: NostrAccountContext;
emit: emitFunc<RelayConfigChange>;
}
const colors = {
"Connecting": WarnColor,
"Open": SuccessColor,
"Closing": WarnColor,
"Closed": ErrorColor,
"Not In Pool": ErrorColor,
};
export const Setting = (props: SettingProps) => {
let priKey: PrivateKey | undefined;
const ctx = props.myAccountContext;
if (ctx instanceof InMemoryAccountContext) {
priKey = ctx.privateKey;
}
return (
<div class={tw`min-w-full min-h-full px-2 bg-[${SecondaryBackgroundColor}]`}>
<div class={tw`max-w-[41rem] m-auto py-[1.5rem]`}>
<div class={tw`px-[1rem] py-[1.5rem] ${inputBorderClass} rounded-lg mt-[1.5rem]`}>
{RelaySetting(props)}
</div>
<div
class={tw`px-[1rem] py-[0.5rem] ${inputBorderClass} rounded-lg mt-[1.5rem] text-[${PrimaryTextColor}]`}
>
<KeyView
privateKey={priKey}
publicKey={props.myAccountContext.publicKey}
/>
</div>
<button
class={tw`w-full p-[0.75rem] mt-[1.5rem] rounded-lg ${NoOutlineClass} ${CenterClass} ${LinearGradientsClass} hover:bg-gradient-to-l text-[${PrimaryTextColor}]`}
onClick={props.logout}
>
Logout
</button>
</div>
</div>
);
};
const error = signal("");
const addRelayInput = signal("");
const relayStatus = signal<{ url: string; status: keyof typeof colors }[]>([]);
export type RelayConfigChange = {
type: "RelayConfigChange";
};
export function RelaySetting(props: {
relayConfig: RelayConfig;
relayPool: ConnectionPool;
emit: emitFunc<RelayConfigChange>;
}) {
function computeRelayStatus() {
const _relayStatus: { url: string; status: keyof typeof colors }[] = [];
for (const url of props.relayConfig.getRelayURLs()) {
const relay = props.relayPool.getRelay(url);
let status: keyof typeof colors = "Not In Pool";
if (relay) {
status = relay.ws.status();
}
_relayStatus.push({
url,
status,
});
}
return _relayStatus;
}
(async () => {
console.log("relayConfig.syncWithPool", props.relayConfig, props.relayPool);
const err = await props.relayConfig.syncWithPool(props.relayPool);
if (err != undefined) {
error.value = err.map((e) => e.message).join("\n");
}
relayStatus.value = computeRelayStatus();
})();
relayStatus.value = computeRelayStatus();
const addRelay = async () => {
// props.eventBus.emit({ type: "AddRelay" });
console.log("add", addRelayInput.value);
if (addRelayInput.value.length > 0) {
props.relayConfig.add(addRelayInput.value);
addRelayInput.value = "";
relayStatus.value = computeRelayStatus();
const err = await props.relayConfig.syncWithPool(props.relayPool);
if (err != undefined) {
error.value = err.map((e) => e.message).join("\n");
}
relayStatus.value = computeRelayStatus();
props.emit({ type: "RelayConfigChange" });
}
};
return (
<Fragment>
<p class={tw`text-[1.3125rem] flex text-[${PrimaryTextColor}]`}>
<RelayIcon
class={tw`w-[2rem] h-[2rem] mr-[1rem]`}
style={{
stroke: TitleIconColor,
}}
/>
Relays
</p>
<p class={tw`mt-[1.75rem] text-[${PrimaryTextColor}]`}>
Add Relay
</p>
<div class={tw`mt-[0.5rem] flex text-[${PrimaryTextColor}]`}>
<input
autofocus={true}
onInput={(e) => {
addRelayInput.value = e.currentTarget.value;
console.log("|", addRelayInput.value);
}}
value={addRelayInput}
placeholder="wss://"
type="text"
class={tw`${InputClass}`}
/>
<button
class={tw`ml-[0.75rem] w-[5.9375rem] h-[3rem] p-[0.75rem] rounded-lg ${NoOutlineClass} bg-[${DividerBackgroundColor}] hover:bg-[${HoverButtonBackgroudColor}] ${CenterClass} text-[${PrimaryTextColor}]`}
onClick={addRelay}
>
Add
</button>
</div>
{error.value
? <p class={tw`mt-2 text-[${ErrorColor}] text-[0.875rem]`}>{error.value}</p>
: undefined}
<ul class={tw`mt-[1.5rem] text-[${PrimaryTextColor}]`}>
{computed(() => {
return relayStatus.value.map((r) => {
return (
<li
class={tw`w-full px-[1rem] py-[0.75rem] rounded-lg bg-[${DividerBackgroundColor}80] mb-[0.5rem] flex items-center justify-between`}
>
<div class={tw`flex items-center flex-1 overflow-hidden`}>
<span
class={tw`bg-[${
colors[r.status]
}] text-center block py-1 px-2 rounded text-[0.8rem] mr-2 font-bold`}
>
{r.status}
</span>
<span class={tw`truncate`}>{r.url}</span>
</div>
<button
class={tw`w-[2rem] h-[2rem] rounded-lg bg-transparent hover:bg-[${DividerBackgroundColor}] ${CenterClass} ${NoOutlineClass}`}
onClick={async function remove() {
props.relayConfig.remove(r.url);
relayStatus.value = computeRelayStatus();
const err = await props.relayConfig.syncWithPool(props.relayPool);
if (err != undefined) {
error.value = err.map((e) => e.message).join("\n");
}
relayStatus.value = computeRelayStatus();
props.emit({ type: "RelayConfigChange" });
}}
>
<DeleteIcon
class={tw`w-[1rem] h-[1rem]`}
style={{
stroke: ErrorColor,
}}
/>
</button>
</li>
);
});
})}
</ul>
</Fragment>
);
}