From 6f0dc2e430d8345eca0ac5ec611603767d188f39 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 21 Jun 2023 15:28:17 +0100 Subject: [PATCH] Login --- package.json | 3 ++- src/element/live-chat.tsx | 47 ++++++++++++++++++++++--------------- src/element/profile.tsx | 16 +++++++++++-- src/hooks/login.ts | 6 +++++ src/index.css | 6 +++++ src/index.tsx | 2 ++ src/login.ts | 29 +++++++++++++++++++++++ src/pages/layout.css | 5 ++++ src/pages/layout.tsx | 49 ++++++++++++++++++++++++++++++++------- src/pages/stream-page.css | 6 ----- yarn.lock | 32 ++++++++++++++++--------- 11 files changed, 154 insertions(+), 47 deletions(-) create mode 100644 src/hooks/login.ts create mode 100644 src/login.ts diff --git a/package.json b/package.json index 3b6aa22..d65c76d 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@snort/system-react": "^1.0.2", + "@snort/system-react": "^1.0.3", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.2.1", @@ -39,6 +39,7 @@ ] }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "typescript": "^5.1.3" } } diff --git a/src/element/live-chat.tsx b/src/element/live-chat.tsx index 1e37d03..ee48d2d 100644 --- a/src/element/live-chat.tsx +++ b/src/element/live-chat.tsx @@ -8,6 +8,7 @@ import AsyncButton from "./async-button"; import { Profile } from "./profile"; import { Icon } from "./icon"; import Spinner from "./spinner"; +import { useLogin } from "hooks/login"; export interface LiveChatOptions { canWrite?: boolean, @@ -17,6 +18,7 @@ export interface LiveChatOptions { export function LiveChat({ link, options }: { link: NostrLink, options?: LiveChatOptions }) { const [chat, setChat] = useState(""); const messages = useLiveChatFeed(link); + const login = useLogin(); async function sendChatMessage() { const pub = await EventPublisher.nip7(); @@ -35,6 +37,31 @@ export function LiveChat({ link, options }: { link: NostrLink, options?: LiveCha setChat(""); } } + + function writeMessage() { + return <> +
+ setChat(v.target.value)} + value={chat} + placeholder="Message" + onKeyDown={async e => { + if (e.code === "Enter") { + e.preventDefault(); + await sendChatMessage(); + } + }} + /> + +
+ + Send + + + } + return (
{(options?.showHeader ?? true) &&
@@ -49,25 +76,7 @@ export function LiveChat({ link, options }: { link: NostrLink, options?: LiveCha {messages.data === undefined && }
{(options?.canWrite ?? true) &&
-
- setChat(v.target.value)} - value={chat} - placeholder="Message" - onKeyDown={async e => { - if (e.code === "Enter") { - e.preventDefault(); - await sendChatMessage(); - } - }} - /> - -
- - Send - + {login ? writeMessage() :

Please login to write messages!

}
}
); diff --git a/src/element/profile.tsx b/src/element/profile.tsx index e58bbef..f97d9ff 100644 --- a/src/element/profile.tsx +++ b/src/element/profile.tsx @@ -1,12 +1,24 @@ import "./profile.css"; import { useUserProfile } from "@snort/system-react"; +import { UserMetadata } from "@snort/system"; +import { hexToBech32 } from "@snort/shared"; import { System } from "index"; -export function Profile({ pubkey }: { pubkey: string }) { +export interface ProfileOptions { + showName?: boolean, + suffix?: string +} + +export function getName(pk: string, user?: UserMetadata) { + const shortPubkey = hexToBech32("npub", pk).slice(0, 12); + return user?.display_name ?? user?.name ?? shortPubkey +} + +export function Profile({ pubkey, options }: { pubkey: string, options?: ProfileOptions }) { const profile = useUserProfile(System, pubkey); return
- {profile?.display_name ?? profile?.name} + {(options?.showName ?? true) && getName(pubkey, profile)}
} \ No newline at end of file diff --git a/src/hooks/login.ts b/src/hooks/login.ts new file mode 100644 index 0000000..1829200 --- /dev/null +++ b/src/hooks/login.ts @@ -0,0 +1,6 @@ +import { Login } from "index"; +import { useSyncExternalStore } from "react"; + +export function useLogin() { + return useSyncExternalStore(c => Login.hook(c), () => Login.snapshot()); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index fa42c31..964974a 100644 --- a/src/index.css +++ b/src/index.css @@ -65,4 +65,10 @@ a { .btn-primary { background: linear-gradient(94.73deg, #2BD9FF 0%, #8C8DED 47.4%, #F838D9 100%); color: white; +} + +.btn>span { + display: flex; + align-items: center; + gap: 8px; } \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 5cdbe1d..e3e9dca 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,10 +7,12 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import { LayoutPage } from 'pages/layout'; import { StreamPage } from 'pages/stream-page'; import { ChatPopout } from 'pages/chat-popout'; +import { LoginStore } from 'login'; export const System = new NostrSystem({ }); +export const Login = new LoginStore(); [ "wss://relay.snort.social", diff --git a/src/login.ts b/src/login.ts new file mode 100644 index 0000000..c856704 --- /dev/null +++ b/src/login.ts @@ -0,0 +1,29 @@ +import { ExternalStore } from "@snort/shared"; + +export interface LoginSession { + pubkey: string +} + +export class LoginStore extends ExternalStore { + #session?: LoginSession; + + constructor() { + super(); + const json = window.localStorage.getItem("session"); + if (json) { + this.#session = JSON.parse(json); + } + } + + loginWithPubkey(pk: string) { + this.#session = { + pubkey: pk + }; + window.localStorage.setItem("session", JSON.stringify(this.#session)); + this.notifyChange(); + } + + takeSnapshot() { + return this.#session ? { ...this.#session } : undefined; + } +} \ No newline at end of file diff --git a/src/pages/layout.css b/src/pages/layout.css index 1de7348..0419167 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -54,4 +54,9 @@ header button { display: flex; align-items: center; gap: 8px; +} + +header .profile img { + width: 48px; + height: 48px; } \ No newline at end of file diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 659445d..603ce17 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -1,9 +1,48 @@ import { Icon } from "element/icon"; import "./layout.css"; +import { EventPublisher } from "@snort/system"; import { Outlet, useNavigate } from "react-router-dom"; +import AsyncButton from "element/async-button"; +import { Login } from "index"; +import { useLogin } from "hooks/login"; +import { Profile } from "element/profile"; export function LayoutPage() { const navigate = useNavigate(); + const login = useLogin(); + + async function doLogin() { + const pub = await EventPublisher.nip7(); + if (pub) { + Login.loginWithPubkey(pub.pubKey); + } + } + + function loggedIn() { + if (!login) return; + + return <> + + + + } + + function loggedOut() { + if (login) return; + + return <> + + Login + + + + } + return <>
navigate("/")}> @@ -14,14 +53,8 @@ export function LayoutPage() {
- - + {loggedIn()} + {loggedOut()}
diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index 8c8c99c..b4e1c3f 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -50,10 +50,4 @@ .live-page .tags { display: flex; gap: 8px; -} - -.live-page .btn-primary>span { - display: flex; - align-items: center; - gap: 8px; } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index f933419..be013d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,7 +81,7 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.22.5": +"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== @@ -106,7 +106,7 @@ lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.5": +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== @@ -366,6 +366,16 @@ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== +"@babel/plugin-proposal-private-property-in-object@^7.21.11": + version "7.21.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" + integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" @@ -1771,19 +1781,19 @@ debug "^4.3.4" dexie "^3.2.4" -"@snort/system-react@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@snort/system-react/-/system-react-1.0.2.tgz#980069bfeba1b6a0ea80cee7a9f4d5522b487191" - integrity sha512-K0tCf31SHOeIvmxBhNs1gl4IMZLb+jcKwbm39qv+MLHfYkKonMVjMXV5fTNJ0UFkVdnui9YXZjE/ORAaHHu/Qw== +"@snort/system-react@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@snort/system-react/-/system-react-1.0.3.tgz#a64ac4b96f084faab6b19264c4699d523d543927" + integrity sha512-7ZoYtmzThjOwJpM1I2UWBKfWRSdXfdZK+r59LSZFuqp5gTzAYKeqF97toQKG+PKbcNlNnqpifTIeNaZHnXkOhw== dependencies: "@snort/shared" "^1.0.1" - "@snort/system" "^1.0.7" + "@snort/system" "^1.0.8" react "^18.2.0" -"@snort/system@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@snort/system/-/system-1.0.7.tgz#1542e17c3e880e41734e2937a819d460b832c320" - integrity sha512-PQqeR794pNlYAaDETO7Ab2mtFQz16bMeObb2eb/F09BtMalfwa3SymjZ0VqkEiT1TgGn+NkTu7bNfOEQOA+/Rg== +"@snort/system@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@snort/system/-/system-1.0.8.tgz#c4a7000320ebf65374298cc1ed5a0d42103a6557" + integrity sha512-IxiD4q3cnW8YEA43a42yxq/OmIeCbXR8Wd1kO59JqZA0xwhhASJQHXxWSuKPXNt6LgkEENPkmlTKY2BIkLvTfg== dependencies: "@noble/curves" "^1.0.0" "@scure/base" "^1.1.1"