feat: note creator button on deck

This commit is contained in:
Kieran 2023-10-16 14:41:55 +01:00
parent 65552e126b
commit f0110e9009
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
9 changed files with 88 additions and 82 deletions

View File

@ -80,7 +80,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
return (
<>
<button className="btn-rnd" onClick={() => attachFile()}>
<button className="circle flex flex-center" onClick={() => attachFile()}>
{uploading ? <Spinner width={20} /> : <Icon name="attachment" size={20} />}
</button>
<div className="w-max">
@ -97,7 +97,7 @@ export default function WriteMessage({ chat }: { chat: Chat }) {
/>
{error && <b className="error">{error}</b>}
</div>
<button className="btn-rnd" onClick={() => sendMessage()}>
<button className="circle flex flex-center" onClick={() => sendMessage()}>
{sending ? <Spinner width={20} /> : <Icon name="arrow-right" size={20} />}
</button>
</>

View File

@ -5,13 +5,13 @@ import "./Nav.css";
import Icon from "Icons/Icon";
import { Link } from "react-router-dom";
import { profileLink } from "SnortUtils";
import { NoteCreatorButton } from "Element/Event/NoteCreatorButton";
export function DeckNav() {
const { publicKey } = useLogin();
const profile = useUserProfile(publicKey);
const unreadDms = 0;
const hasNotifications = false;
return (
<nav className="deck flex-column f-space">
@ -20,15 +20,12 @@ export function DeckNav() {
<Icon name="mail" size={24} />
{unreadDms > 0 && <span className="has-unread"></span>}
</Link>
<Link className="btn" to="/notifications">
<Icon name="bell-02" size={24} />
{hasNotifications && <span className="has-unread"></span>}
</Link>
<NoteCreatorButton />
</div>
<div className="flex-column f-center g16">
<Link className="btn" to="/">
{/*<Link className="btn" to="/">
<Icon name="grid-01" size={24} />
</Link>
</Link>*/}
<Link className="btn" to="/settings">
<Icon name="settings-02" size={24} />
</Link>

View File

@ -84,27 +84,6 @@
height: 32px;
}
.note-create-button {
width: 48px;
height: 48px;
color: white;
background: linear-gradient(90deg, rgba(239, 150, 68, 1) 0%, rgba(123, 65, 246, 1) 100%);
border: none;
border-radius: 100%;
position: fixed;
bottom: 40px;
right: calc(((100vw - 640px) / 2) - 75px);
display: flex;
align-items: center;
justify-content: center;
}
@media (max-width: 768px) {
.note-create-button {
right: 16px;
}
}
.light .note-creator textarea {
background-color: #fff;
}

View File

@ -0,0 +1,20 @@
.note-create-button {
width: 48px;
height: 48px;
color: white;
background: linear-gradient(90deg, rgba(239, 150, 68, 1) 0%, rgba(123, 65, 246, 1) 100%);
border: none;
border-radius: 100%;
position: fixed;
bottom: 40px;
right: calc(((100vw - 640px) / 2) - 75px);
display: flex;
align-items: center;
justify-content: center;
}
@media (max-width: 768px) {
.note-create-button {
right: 16px;
}
}

View File

@ -0,0 +1,51 @@
import "./NoteCreatorButton.css";
import { useRef, useMemo } from "react";
import { useLocation } from "react-router-dom";
import { isFormElement } from "SnortUtils";
import useKeyboardShortcut from "Hooks/useKeyboardShortcut";
import useLogin from "Hooks/useLogin";
import Icon from "Icons/Icon";
import { useNoteCreator } from "State/NoteCreator";
import { NoteCreator } from "./NoteCreator";
export const NoteCreatorButton = ({ className }: { className?: string }) => {
const buttonRef = useRef<HTMLButtonElement>(null);
const location = useLocation();
const { readonly } = useLogin(s => ({ readonly: s.readonly }));
const { show, replyTo, update } = useNoteCreator(v => ({ show: v.show, replyTo: v.replyTo, update: v.update }));
useKeyboardShortcut("n", event => {
// if event happened in a form element, do nothing, otherwise focus on search input
if (event.target && !isFormElement(event.target as HTMLElement)) {
event.preventDefault();
if (buttonRef.current) {
buttonRef.current.click();
}
}
});
const shouldHideNoteCreator = useMemo(() => {
const isReplyNoteCreatorShowing = replyTo && show;
const hideOn = ["/settings", "/messages", "/new", "/login", "/donate", "/e", "/subscribe"];
return readonly || isReplyNoteCreatorShowing || hideOn.some(a => location.pathname.startsWith(a));
}, [location, readonly]);
if (shouldHideNoteCreator) return null;
return (
<>
<button
ref={buttonRef}
className={`primary circle${className ? ` ${className}` : ""}`}
onClick={() =>
update(v => {
v.replyTo = undefined;
v.show = true;
})
}>
<Icon name="plus" size={16} />
</button>
<NoteCreator key="global-note-creator" />
</>
);
};

View File

@ -1,5 +1,5 @@
import "./Layout.css";
import { useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
import { FormattedMessage, useIntl } from "react-intl";
import { useUserProfile } from "@snort/system-react";
@ -9,7 +9,6 @@ import messages from "./messages";
import Icon from "Icons/Icon";
import useLoginFeed from "Feed/LoginFeed";
import { NoteCreator } from "Element/Event/NoteCreator";
import { mapPlanName } from "./subscribe";
import useLogin from "Hooks/useLogin";
import Avatar from "Element/User/Avatar";
@ -20,10 +19,10 @@ import Spinner from "Icons/Spinner";
import { fetchNip05Pubkey } from "Nip05/Verifier";
import { useTheme } from "Hooks/useTheme";
import { useLoginRelays } from "Hooks/useLoginRelays";
import { useNoteCreator } from "State/NoteCreator";
import { LoginUnlock } from "Element/PinPrompt";
import useKeyboardShortcut from "Hooks/useKeyboardShortcut";
import { LoginStore } from "Login";
import { NoteCreatorButton } from "Element/Event/NoteCreatorButton";
export default function Layout() {
const location = useLocation();
@ -69,7 +68,7 @@ export default function Layout() {
</header>
)}
<Outlet />
<NoteCreatorButton />
<NoteCreatorButton className="note-create-button" />
<Toaster />
</div>
<LoginUnlock />
@ -79,7 +78,7 @@ export default function Layout() {
onClick={() => {
LoginStore.removeSession(id);
}}>
<button type="button" className="btn btn-rnd">
<button type="button" className="circle flex flex-center">
<Icon name="close" />
</button>
</div>
@ -88,47 +87,6 @@ export default function Layout() {
);
}
const NoteCreatorButton = () => {
const buttonRef = useRef<HTMLButtonElement>(null);
const location = useLocation();
const { readonly } = useLogin(s => ({ readonly: s.readonly }));
const { show, replyTo, update } = useNoteCreator(v => ({ show: v.show, replyTo: v.replyTo, update: v.update }));
useKeyboardShortcut("n", event => {
// if event happened in a form element, do nothing, otherwise focus on search input
if (event.target && !isFormElement(event.target as HTMLElement)) {
event.preventDefault();
if (buttonRef.current) {
buttonRef.current.click();
}
}
});
const shouldHideNoteCreator = useMemo(() => {
const isReplyNoteCreatorShowing = replyTo && show;
const hideOn = ["/settings", "/messages", "/new", "/login", "/donate", "/e", "/subscribe"];
return readonly || isReplyNoteCreatorShowing || hideOn.some(a => location.pathname.startsWith(a));
}, [location, readonly]);
if (shouldHideNoteCreator) return;
return (
<>
<button
ref={buttonRef}
className="primary note-create-button"
onClick={() =>
update(v => {
v.replyTo = undefined;
v.show = true;
})
}>
<Icon name="plus" size={16} />
</button>
<NoteCreator key="global-note-creator" />
</>
);
};
const AccountHeader = () => {
const navigate = useNavigate();
const { formatMessage } = useIntl();

View File

@ -33,7 +33,7 @@
bottom: -32px;
}
.settings .image-settings .avatar-stack .btn-rnd {
.settings .image-settings .avatar-stack button {
position: absolute;
padding: 0;
width: 32px;

View File

@ -205,7 +205,11 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
{(props.avatar ?? true) && (
<div className="avatar-stack">
<Avatar pubkey={id} user={user} image={picture} />
<AsyncButton type="button" className="btn-rnd" onClick={() => setNewAvatar()} disabled={readonly}>
<AsyncButton
type="button"
className="circle flex flex-center"
onClick={() => setNewAvatar()}
disabled={readonly}>
<Icon name="upload-01" />
</AsyncButton>
</div>

View File

@ -364,12 +364,9 @@ button.icon:hover {
padding: 5px;
}
.btn-rnd {
.circle {
border-radius: 100%;
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
}
textarea {