Account menu

This commit is contained in:
Kieran 2023-07-20 15:40:47 +01:00
parent 1857d90ccb
commit da190423bc
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
9 changed files with 195 additions and 60 deletions

View File

@ -15,6 +15,7 @@
"@snort/shared": "^1.0.4",
"@snort/system": "^1.0.16",
"@snort/system-react": "^1.0.11",
"@szhsin/react-menu": "^4.0.2",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
@ -78,6 +79,8 @@
"@testing-library/dom": "^9.3.1",
"@types/lodash": "^4.14.195",
"@types/lodash.uniqby": "^4.7.7",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@webbtc/webln-types": "^1.0.12",

View File

@ -9,8 +9,11 @@
<symbol id="search" viewBox="0 0 20 21" fill="none">
<path d="M19 19.5L14.65 15.15M17 9.5C17 13.9183 13.4183 17.5 9 17.5C4.58172 17.5 1 13.9183 1 9.5C1 5.08172 4.58172 1.5 9 1.5C13.4183 1.5 17 5.08172 17 9.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</symbol>
<symbol id="logout" viewBox="0 0 22 20" fill="none">
<path d="M17 6L21 10M21 10L17 14M21 10H8M14 2.20404C12.7252 1.43827 11.2452 1 9.66667 1C4.8802 1 1 5.02944 1 10C1 14.9706 4.8802 19 9.66667 19C11.2452 19 12.7252 18.5617 14 17.796" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<symbol id="logout" viewBox="0 0 24 25" fill="none">
<g>
<path d="M7.7587 2.5H9C9.55229 2.5 10 2.94772 10 3.5C10 4.05229 9.55229 4.5 9 4.5H7.8C6.94342 4.5 6.36113 4.50078 5.91104 4.53755C5.47262 4.57337 5.24842 4.6383 5.09202 4.71799C4.7157 4.90973 4.40974 5.2157 4.21799 5.59202C4.1383 5.74842 4.07337 5.97262 4.03755 6.41104C4.00078 6.86113 4 7.44342 4 8.3L4 16.7C4 17.5566 4.00078 18.1389 4.03755 18.589C4.07337 19.0274 4.1383 19.2516 4.21799 19.408C4.40973 19.7843 4.7157 20.0903 5.09202 20.282C5.24842 20.3617 5.47262 20.4266 5.91104 20.4624C6.36113 20.4992 6.94342 20.5 7.8 20.5H9C9.55229 20.5 10 20.9477 10 21.5C10 22.0523 9.55229 22.5 9 22.5H7.75868C6.95372 22.5 6.28936 22.5 5.74818 22.4558C5.18608 22.4099 4.66937 22.3113 4.18404 22.064C3.43139 21.6805 2.81947 21.0686 2.43598 20.316C2.18868 19.8306 2.09012 19.3139 2.04419 18.7518C1.99998 18.2106 1.99999 17.5463 2 16.7413V8.25871C1.99999 7.45375 1.99998 6.78936 2.0442 6.24818C2.09012 5.68608 2.18869 5.16937 2.43598 4.68404C2.81947 3.93139 3.43139 3.31947 4.18404 2.93598C4.66938 2.68868 5.18608 2.59012 5.74818 2.54419C6.28937 2.49998 6.95374 2.49999 7.7587 2.5Z" fill="currentColor"/>
<path d="M15.2929 6.79289C15.6834 6.40237 16.3166 6.40237 16.7071 6.79289L21.7071 11.7929C22.0976 12.1834 22.0976 12.8166 21.7071 13.2071L16.7071 18.2071C16.3166 18.5976 15.6834 18.5976 15.2929 18.2071C14.9024 17.8166 14.9024 17.1834 15.2929 16.7929L18.5858 13.5H9C8.44772 13.5 8 13.0523 8 12.5C8 11.9477 8.44772 11.5 9 11.5H18.5858L15.2929 8.20711C14.9024 7.81658 14.9024 7.18342 15.2929 6.79289Z" fill="currentColor"/>
</g>
</symbol>
<symbol id="message" viewBox="0 0 18 16" fill="none">
<path d="M7.75036 8.00004H3.16702M3.09648 8.24296L1.15071 14.0552C0.997847 14.5118 0.921417 14.7401 0.976267 14.8807C1.0239 15.0028 1.1262 15.0954 1.25244 15.1306C1.3978 15.1712 1.61736 15.0724 2.05647 14.8748L15.9827 8.60799C16.4113 8.41512 16.6256 8.31868 16.6918 8.18471C16.7494 8.06832 16.7494 7.93176 16.6918 7.81537C16.6256 7.6814 16.4113 7.58497 15.9827 7.39209L2.05161 1.12314C1.61383 0.926139 1.39493 0.827637 1.24971 0.868044C1.1236 0.903136 1.0213 0.995457 0.973507 1.11733C0.91847 1.25766 0.994084 1.48547 1.14531 1.9411L3.09702 7.82131C3.12299 7.89957 3.13598 7.9387 3.14111 7.97871C3.14565 8.01422 3.14561 8.05017 3.14097 8.08567C3.13574 8.12567 3.12265 8.16477 3.09648 8.24296Z" stroke="currentColor" stroke-opacity="0.5" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
@ -41,5 +44,11 @@
<symbol id="copy" viewBox="0 0 20 20" fill="none">
<path d="M13.3333 13.3327V15.666C13.3333 16.5994 13.3333 17.0661 13.1516 17.4227C12.9918 17.7363 12.7369 17.9912 12.4233 18.151C12.0668 18.3327 11.6 18.3327 10.6666 18.3327H4.33329C3.39987 18.3327 2.93316 18.3327 2.57664 18.151C2.26304 17.9912 2.00807 17.7363 1.84828 17.4227C1.66663 17.0661 1.66663 16.5994 1.66663 15.666V9.33268C1.66663 8.39926 1.66663 7.93255 1.84828 7.57603C2.00807 7.26243 2.26304 7.00746 2.57664 6.84767C2.93316 6.66602 3.39987 6.66602 4.33329 6.66602H6.66663M9.33329 13.3327H15.6666C16.6 13.3327 17.0668 13.3327 17.4233 13.151C17.7369 12.9912 17.9918 12.7363 18.1516 12.4227C18.3333 12.0661 18.3333 11.5994 18.3333 10.666V4.33268C18.3333 3.39926 18.3333 2.93255 18.1516 2.57603C17.9918 2.26243 17.7369 2.00746 17.4233 1.84767C17.0668 1.66602 16.6 1.66602 15.6666 1.66602H9.33329C8.39987 1.66602 7.93316 1.66602 7.57664 1.84767C7.26304 2.00746 7.00807 2.26243 6.84828 2.57603C6.66663 2.93255 6.66663 3.39926 6.66663 4.33268V10.666C6.66663 11.5994 6.66663 12.0661 6.84828 12.4227C7.00807 12.7363 7.26304 12.9912 7.57664 13.151C7.93316 13.3327 8.39987 13.3327 9.33329 13.3327Z" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
</symbol>
<symbol id="user" viewBox="0 0 24 25" fill="none">
<g>
<path d="M3 20.5C5.33579 18.0226 8.50702 16.5 12 16.5C15.493 16.5 18.6642 18.0226 21 20.5M16.5 8C16.5 10.4853 14.4853 12.5 12 12.5C9.51472 12.5 7.5 10.4853 7.5 8C7.5 5.51472 9.51472 3.5 12 3.5C14.4853 3.5 16.5 5.51472 16.5 8Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</symbol>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -30,6 +30,7 @@ export function LoginSignup({ close }: { close: () => void }) {
const pub = await EventPublisher.nip7();
if (pub) {
Login.loginWithPubkey(pub.pubKey, LoginType.Nip7);
close();
}
} catch (e) {
console.error(e);

View File

@ -33,12 +33,14 @@ export function Profile({
avatarClassname,
options,
profile,
linkToProfile,
}: {
pubkey: string;
icon?: ReactNode;
avatarClassname?: string;
options?: ProfileOptions;
profile?: UserMetadata;
linkToProfile?: boolean;
}) {
const { inView, ref } = useInView();
const pLoaded =
@ -69,7 +71,7 @@ export function Profile({
</>
);
return pubkey === "anon" ? (
return pubkey === "anon" || linkToProfile === false ? (
<div className="profile" ref={ref}>
{content}
</div>

View File

@ -1,14 +1,14 @@
body {
margin: 0;
font-family: 'Outfit', sans-serif;
font-family: "Outfit", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #0A0A0A;
background-color: #0a0a0a;
color: white;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
@ -50,7 +50,7 @@ a {
}
.pill.live {
background: #F838D9;
background: #f838d9;
color: white;
}
@ -77,7 +77,7 @@ a {
border: 1px solid transparent;
color: inherit;
background: linear-gradient(black, black) padding-box,
linear-gradient(94.73deg, #2BD9FF 0%, #F838D9 100%) border-box;
linear-gradient(94.73deg, #2bd9ff 0%, #f838d9 100%) border-box;
transition: 0.3s;
}
@ -87,7 +87,7 @@ a {
}
.btn-primary {
background: #FFF;
background: #fff;
color: #0a0a0a;
}
@ -96,11 +96,11 @@ a {
}
.btn-warning {
background: #FF563F;
background: #ff563f;
color: white;
}
.btn>span {
.btn > span {
display: flex;
align-items: center;
justify-content: center;
@ -133,7 +133,7 @@ input[type="checkbox"] {
}
input[type="checkbox"]:after {
content: ' ';
content: " ";
position: relative;
left: 40%;
top: 20%;
@ -164,11 +164,11 @@ div.paper {
}
.warning {
color: #FF563F;
color: #ff563f;
}
.border-warning {
border: 1px solid #FF563F;
border: 1px solid #ff563f;
}
.dialog-content {
@ -191,4 +191,30 @@ div.paper {
display: block;
color: #868686;
margin: 6px;
}
}
.ctx-menu {
font-size: 16px;
font-weight: 700;
line-height: 20px;
border-radius: 12px;
background: #434343;
padding: 12px 0px;
color: white;
display: flex;
flex-direction: column;
}
.ctx-menu li {
padding: 12px 24px;
display: flex;
gap: 16px;
}
.ctx-menu li:hover {
background: #505050;
}
.szh-menu__item--hover {
background-color: unset;
}

View File

@ -1,3 +1,4 @@
import "@szhsin/react-menu/dist/index.css";
import "./index.css";
import React from "react";
@ -49,7 +50,7 @@ const router = createBrowserRouter([
},
{
path: "/nsfw",
element: <RootPage nsfw={true} />
element: <RootPage nsfw={true} />,
},
{
path: "/:id",

View File

@ -5,7 +5,7 @@ import { EventPublisher, Nip7Signer, PrivateKeySigner } from "@snort/system";
export enum LoginType {
Nip7 = "nip7",
PrivateKey = "private-key"
PrivateKey = "private-key",
}
export interface LoginSession {
@ -25,7 +25,6 @@ export class LoginStore extends ExternalStore<LoginSession | undefined> {
this.#session = JSON.parse(json);
if (this.#session) {
this.#session.type ??= LoginType.Nip7;
}
}
}
@ -49,12 +48,21 @@ export class LoginStore extends ExternalStore<LoginSession | undefined> {
this.#save();
}
logout() {
this.#session = undefined;
this.#save();
}
takeSnapshot() {
return this.#session ? { ...this.#session } : undefined;
}
#save() {
window.localStorage.setItem("session", JSON.stringify(this.#session));
if (this.#session) {
window.localStorage.setItem("session", JSON.stringify(this.#session));
} else {
window.localStorage.removeItem("session");
}
this.notifyChange();
}
}
@ -65,7 +73,10 @@ export function getPublisher(session: LoginSession) {
return new EventPublisher(new Nip7Signer(), session.pubkey);
}
case LoginType.PrivateKey: {
return new EventPublisher(new PrivateKeySigner(session.privateKey!), session.pubkey);
return new EventPublisher(
new PrivateKeySigner(session.privateKey!),
session.pubkey
);
}
}
}
}

View File

@ -8,6 +8,9 @@ import { useLogin } from "hooks/login";
import { Profile } from "element/profile";
import { NewStreamDialog } from "element/new-stream";
import { LoginSignup } from "element/login-signup";
import { Menu, MenuItem } from "@szhsin/react-menu";
import { hexToBech32 } from "@snort/shared";
import { Login } from "index";
export function LayoutPage() {
const navigate = useNavigate();
@ -21,13 +24,34 @@ export function LayoutPage() {
return (
<>
<NewStreamDialog btnClassName="btn btn-primary" />
<Profile
avatarClassname="mb-squared"
pubkey={login.pubkey}
options={{
showName: false,
}}
/>
<Menu
menuClassName="ctx-menu"
menuButton={
<div>
<Profile
avatarClassname="mb-squared"
pubkey={login.pubkey}
options={{
showName: false,
}}
linkToProfile={false}
/>
</div>
}
align="end"
gap={5}
>
<MenuItem
onClick={() => navigate(`/p/${hexToBech32("npub", login.pubkey)}`)}
>
<Icon name="user" size={24} />
Profile
</MenuItem>
<MenuItem onClick={() => Login.logout()}>
<Icon name="logout" size={24} />
Logout
</MenuItem>
</Menu>
</>
);
}
@ -35,31 +59,40 @@ export function LayoutPage() {
function loggedOut() {
if (login) return;
return <Dialog.Root open={showLogin} onOpenChange={setShowLogin}>
<Dialog.Trigger asChild>
<button type="button" className="btn btn-border" onClick={() => setShowLogin(true)}>
Login
<Icon name="login" />
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="dialog-overlay" />
<Dialog.Content className="dialog-content">
<LoginSignup close={() => setShowLogin(false)} />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
return (
<Dialog.Root open={showLogin} onOpenChange={setShowLogin}>
<Dialog.Trigger asChild>
<button
type="button"
className="btn btn-border"
onClick={() => setShowLogin(true)}
>
Login
<Icon name="login" />
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="dialog-overlay" />
<Dialog.Content className="dialog-content">
<LoginSignup close={() => setShowLogin(false)} />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
const isNsfw = window.location.pathname === "/nsfw";
return (
<div
className={
location.pathname === "/" || location.pathname.startsWith("/p/") || location.pathname.startsWith("/providers") || location.pathname === "/nsfw"
location.pathname === "/" ||
location.pathname.startsWith("/p/") ||
location.pathname.startsWith("/providers") ||
location.pathname === "/nsfw"
? "page only-content"
: location.pathname.startsWith("/chat/")
? "page chat"
: "page"
? "page chat"
: "page"
}
>
<header>
@ -81,14 +114,15 @@ export function LayoutPage() {
</header>
<Outlet />
{isNsfw && <ContentWarningOverlay />}
</div>
);
}
function ContentWarningOverlay() {
const navigate = useNavigate();
const [is18Plus, setIs18Plus] = useState(Boolean(window.localStorage.getItem("accepted-content-warning")));
const [is18Plus, setIs18Plus] = useState(
Boolean(window.localStorage.getItem("accepted-content-warning"))
);
if (is18Plus) return null;
function grownUp() {
@ -96,16 +130,18 @@ function ContentWarningOverlay() {
setIs18Plus(true);
}
return <div className="fullscreen-exclusive age-check">
<h1>Sexually explicit material ahead!</h1>
<h2>Confirm your age</h2>
<div className="flex g24">
<button className="btn btn-warning" onClick={grownUp}>
Yes, I am over 18
</button>
<button className="btn" onClick={() => navigate("/")}>
No, I am under 18
</button>
return (
<div className="fullscreen-exclusive age-check">
<h1>Sexually explicit material ahead!</h1>
<h2>Confirm your age</h2>
<div className="flex g24">
<button className="btn btn-warning" onClick={grownUp}>
Yes, I am over 18
</button>
<button className="btn" onClick={() => navigate("/")}>
No, I am under 18
</button>
</div>
</div>
</div>
}
);
}

View File

@ -2579,6 +2579,19 @@ __metadata:
languageName: node
linkType: hard
"@szhsin/react-menu@npm:^4.0.2":
version: 4.0.2
resolution: "@szhsin/react-menu@npm:4.0.2"
dependencies:
prop-types: ^15.7.2
react-transition-state: ^2.1.0
peerDependencies:
react: ">=16.14.0"
react-dom: ">=16.14.0"
checksum: ac84a6f84b2671044d96667011ae747e8962a1b90507080155e5aec7807a705cbd7020f6a6396df34d68cd6b9de87b979af785916eeb29eb683d0da3fd0aa8dd
languageName: node
linkType: hard
"@testing-library/dom@npm:^8.5.0":
version: 8.20.1
resolution: "@testing-library/dom@npm:8.20.1"
@ -2966,6 +2979,15 @@ __metadata:
languageName: node
linkType: hard
"@types/react-dom@npm:^18.2.7":
version: 18.2.7
resolution: "@types/react-dom@npm:18.2.7"
dependencies:
"@types/react": "*"
checksum: e02ea908289a7ad26053308248d2b87f6aeafd73d0e2de2a3d435947bcea0422599016ffd1c3e38ff36c42f5e1c87c7417f05b0a157e48649e4a02f21727d54f
languageName: node
linkType: hard
"@types/react@npm:*":
version: 18.2.13
resolution: "@types/react@npm:18.2.13"
@ -2977,6 +2999,17 @@ __metadata:
languageName: node
linkType: hard
"@types/react@npm:^18.2.15":
version: 18.2.15
resolution: "@types/react@npm:18.2.15"
dependencies:
"@types/prop-types": "*"
"@types/scheduler": "*"
csstype: ^3.0.2
checksum: 36989f638201bfe2f4110b06c119180f6df9c0e13d7060481e82e7a745f81745a01ae543c478a25b61e0767cb52e82da2ad5b0dedacabf99339e523d06176705
languageName: node
linkType: hard
"@types/resolve@npm:1.17.1":
version: 1.17.1
resolution: "@types/resolve@npm:1.17.1"
@ -8279,7 +8312,7 @@ __metadata:
languageName: node
linkType: hard
"prop-types@npm:^15.8.1":
"prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@ -8506,6 +8539,16 @@ __metadata:
languageName: node
linkType: hard
"react-transition-state@npm:^2.1.0":
version: 2.1.1
resolution: "react-transition-state@npm:2.1.1"
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
checksum: 6091d0c59a255b9ec47b6bfc8f766737bdb1a21bb15f0c738e0feef9efd20df5a3209699029cab2fcb18c99fa8ad97cdbce95ceabee9fe6c6dd8b98a11af00b5
languageName: node
linkType: hard
"react@npm:^18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
@ -9313,12 +9356,15 @@ __metadata:
"@snort/shared": ^1.0.4
"@snort/system": ^1.0.16
"@snort/system-react": ^1.0.11
"@szhsin/react-menu": ^4.0.2
"@testing-library/dom": ^9.3.1
"@testing-library/jest-dom": ^5.14.1
"@testing-library/react": ^13.0.0
"@testing-library/user-event": ^13.2.1
"@types/lodash": ^4.14.195
"@types/lodash.uniqby": ^4.7.7
"@types/react": ^18.2.15
"@types/react-dom": ^18.2.7
"@types/webscopeio__react-textarea-autocomplete": ^4.7.2
"@typescript-eslint/eslint-plugin": ^6.1.0
"@typescript-eslint/parser": ^6.1.0