Note creator V2 styles

This commit is contained in:
Kieran 2023-08-23 13:19:48 +01:00
parent d4bbd4e87d
commit fb29cfa952
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
16 changed files with 285 additions and 276 deletions

View File

@ -169,15 +169,6 @@
<symbol id="wifi" viewBox="0 0 24 18" fill="none">
<path d="M12 16.5H12.01M22.8064 5.70076C19.9595 3.09199 16.1656 1.5 11.9999 1.5C7.83414 1.5 4.04023 3.09199 1.19336 5.70076M4.73193 9.24297C6.67006 7.53566 9.21407 6.5 12 6.5C14.7859 6.5 17.3299 7.53566 19.268 9.24297M15.6983 12.7751C14.6792 11.9763 13.3952 11.5 11.9999 11.5C10.5835 11.5 9.28172 11.9908 8.25537 12.8116" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="snort-by" viewBox="0 0 1715.5309 201.961" fill="none">
<g transform="translate(-0.001,-634.57194)">
<path fill="currentColor" d="m 24.77,750.25 c 2.722,-36.745 14.697,-72.401 32.934,-108.058 47.089,-5.444 92.544,-7.35 136.91,-7.35 45.183,0 89.821,0.816 134.188,4.628 l -14.97,49.537 c -30.757,-1.36 -59.064,-3.266 -100.165,-3.266 -38.65,0 -66.686,1.36 -97.17,3.538 -2.722,4.899 -4.627,9.526 -5.172,16.876 64.508,3.266 131.466,0.816 194.069,14.153 -3.267,35.929 -14.698,72.401 -33.207,108.603 -47.36,5.443 -93.632,7.349 -138.815,7.349 -44.639,0 -89.005,-1.089 -133.371,-4.627 l 14.971,-49.538 c 31.029,1.633 60.97,2.994 103.703,2.994 38.378,0 66.141,-1.361 93.903,-3.267 2.45,-4.627 4.899,-11.159 5.444,-17.964 C 152.152,761.954 87.916,762.227 24.77,750.25 Z" />
<path fill="currentColor" d="m 640.713,833.539 h -78.934 l -87.1,-138.543 c -4.083,-0.544 -9.799,-0.816 -15.243,-0.816 L 416.975,833.539 H 337.77 l 49.811,-163.312 -31.846,-26.946 1.905,-5.717 h 49.266 c 42.461,0 81.384,1.089 132.282,3.812 l 53.893,89.549 28.58,-93.36 h 78.934 z" />
<path fill="currentColor" d="m 835.313,836.533 c -49.538,0 -97.715,-2.722 -145.62,-8.982 13.882,-61.786 32.663,-122.212 56.343,-183.998 51.716,-6.26 101.525,-8.981 151.063,-8.981 49.538,0 97.715,2.722 145.619,8.981 -14.153,62.059 -32.662,122.212 -56.342,183.998 -51.716,6.261 -101.525,8.982 -151.063,8.982 z m -47.906,-59.881 c 20.959,2.178 43.55,3.266 65.325,3.266 21.774,0 44.91,-1.088 66.958,-3.266 11.704,-27.491 19.869,-54.438 25.313,-82.2 -20.958,-2.178 -43.55,-3.267 -65.324,-3.267 -21.775,0 -44.911,1.089 -66.958,3.267 -5.988,14.153 -10.888,26.946 -14.97,41.1 -4.355,14.154 -7.622,26.946 -10.344,41.1 z" />
<path fill="currentColor" d="m 1124.904,833.539 h -75.939 l 50.082,-164.4 -30.757,-25.857 1.905,-5.717 h 181.275 c 41.645,0 98.26,0.817 139.088,7.35 -6.261,43.822 -16.604,88.188 -41.101,134.46 -13.882,0.544 -29.668,2.178 -45.183,3.267 l 43.277,50.898 h -95.81 l -79.751,-91.727 1.634,-5.988 h 33.479 c 31.029,0 55.799,0 82.2,-1.361 7.077,-16.331 11.977,-31.301 14.698,-47.36 -22.047,-1.089 -41.372,-1.089 -68.047,-1.089 h -66.141 z" />
<path fill="currentColor" d="m 1698.113,694.724 c -53.893,-1.633 -85.466,-2.449 -106.425,-2.722 l -43.005,141.537 h -79.479 l 43.006,-141.537 c -21.231,0.272 -53.077,1.089 -108.059,2.722 l 17.42,-56.342 c 50.082,-2.723 99.076,-3.539 148.069,-3.539 49.266,0 97.442,0.816 145.892,3.539 z" />
</g>
</symbol>
<!-- V2 -->
<symbol id="mail" viewBox="0 0 24 24" fill="none">
<g>
@ -298,6 +289,13 @@
<path d="M2.00016 9.33398C2.36835 9.33398 2.66683 9.63246 2.66683 10.0007V10.8007C2.66683 11.3717 2.66735 11.7599 2.69186 12.06C2.71574 12.3522 2.75903 12.5017 2.81215 12.606C2.93999 12.8569 3.14396 13.0608 3.39484 13.1887C3.49911 13.2418 3.64858 13.2851 3.94086 13.3089C4.24091 13.3335 4.62911 13.334 5.20016 13.334H10.8002C11.3712 13.334 11.7594 13.3335 12.0595 13.3089C12.3517 13.2851 12.5012 13.2418 12.6055 13.1887C12.8564 13.0608 13.0603 12.8569 13.1882 12.606C13.2413 12.5017 13.2846 12.3522 13.3085 12.06C13.333 11.7599 13.3335 11.3717 13.3335 10.8007V10.0007C13.3335 9.63246 13.632 9.33398 14.0002 9.33398C14.3684 9.33398 14.6668 9.63246 14.6668 10.0007V10.8282C14.6668 11.3648 14.6668 11.8077 14.6374 12.1685C14.6067 12.5433 14.541 12.8877 14.3762 13.2113C14.1205 13.7131 13.7126 14.121 13.2108 14.3767C12.8872 14.5415 12.5428 14.6072 12.168 14.6379C11.8073 14.6673 11.3643 14.6673 10.8277 14.6673H5.17263C4.63598 14.6673 4.19308 14.6673 3.83228 14.6379C3.45755 14.6072 3.11308 14.5415 2.78952 14.3767C2.28776 14.121 1.87981 13.7131 1.62415 13.2113C1.45929 12.8877 1.39358 12.5433 1.36296 12.1685C1.33348 11.8077 1.33349 11.3648 1.3335 10.8282V10.0007C1.3335 9.63246 1.63197 9.33398 2.00016 9.33398Z" fill="currentColor"/>
</g>
</symbol>
<symbol id="image-plus" viewBox="0 0 24 24" fill="none">
<g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 8.5C5.5 6.84315 6.84315 5.5 8.5 5.5C10.1569 5.5 11.5 6.84315 11.5 8.5C11.5 10.1569 10.1569 11.5 8.5 11.5C6.84315 11.5 5.5 10.1569 5.5 8.5Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 2H7.7587C6.95374 1.99999 6.28937 1.99998 5.74818 2.04419C5.18608 2.09012 4.66937 2.18868 4.18404 2.43598C3.43139 2.81947 2.81947 3.43139 2.43598 4.18404C2.18868 4.66937 2.09012 5.18608 2.04419 5.74818C1.99998 6.28937 1.99999 6.95372 2 7.75869V16.2413C1.99999 17.0463 1.99998 17.7106 2.04419 18.2518C2.09012 18.8139 2.18868 19.3306 2.43598 19.816C2.81947 20.5686 3.43139 21.1805 4.18404 21.564C4.66937 21.8113 5.18608 21.9099 5.74818 21.9558C5.92356 21.9701 6.11188 21.9798 6.31374 21.9864C6.52305 22.0003 6.7734 22.0002 7.03144 22.0002C10.3543 22.0002 13.6771 22 17 22C17.0465 22 17.0924 22 17.1376 22C17.933 22.0005 18.5236 22.0008 19.0353 21.8637C20.4156 21.4938 21.4938 20.4156 21.8637 19.0353C22.039 18.381 22.0002 17.6805 22 17.0095C22.0018 16.8202 22.0001 16.6308 22.0001 16.4415C22.0006 15.9726 22.0011 15.5594 21.8923 15.1647C21.7969 14.8182 21.6399 14.4917 21.429 14.2007C21.1887 13.8692 20.8658 13.6114 20.4993 13.3189L17.6683 11.0541C17.4984 10.9182 17.3304 10.7838 17.1779 10.6797C17.0083 10.5639 16.7995 10.4436 16.5382 10.3766C16.1709 10.2824 15.7843 10.2946 15.4237 10.4118C15.1671 10.4951 14.9663 10.6283 14.8043 10.7545C14.6586 10.8681 14.4995 11.0128 14.3385 11.1592L5.83046 18.8938C5.61698 19.0878 5.41061 19.2754 5.2589 19.4395C5.19807 19.5054 5.10567 19.6077 5.01929 19.743C4.67627 19.5501 4.39723 19.2598 4.21799 18.908C4.1383 18.7516 4.07337 18.5274 4.03755 18.089C4.00078 17.6389 4 17.0566 4 16.2V7.8C4 6.94342 4.00078 6.36113 4.03755 5.91104C4.07337 5.47262 4.1383 5.24842 4.21799 5.09202C4.40973 4.7157 4.7157 4.40973 5.09202 4.21799C5.24842 4.1383 5.47262 4.07337 5.91104 4.03755C6.36113 4.00078 6.94342 4 7.8 4H12.5C13.0523 4 13.5 3.55229 13.5 3C13.5 2.44772 13.0523 2 12.5 2Z" fill="currentColor"/>
<path d="M20 2C20 1.44772 19.5523 1 19 1C18.4477 1 18 1.44772 18 2V4H16C15.4477 4 15 4.44772 15 5C15 5.55228 15.4477 6 16 6H18V8C18 8.55228 18.4477 9 19 9C19.5523 9 20 8.55228 20 8V6H22C22.5523 6 23 5.55228 23 5C23 4.44772 22.5523 4 22 4H20V2Z" fill="currentColor"/>
</g>
</symbol>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1,37 @@
import Icon from "Icons/Icon";
import Spinner from "Icons/Spinner";
import { HTMLProps, useState } from "react";
export interface AsyncIconProps extends HTMLProps<HTMLDivElement> {
iconName: string;
iconSize?: number;
loading?: boolean;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => Promise<void>;
}
export function AsyncIcon(props: AsyncIconProps) {
const [loading, setLoading] = useState(props.loading ?? false);
async function handleClick(e: React.MouseEvent<HTMLDivElement>) {
setLoading(true);
try {
if (props.onClick) {
await props.onClick(e);
}
} catch (ex) {
console.error(ex);
}
setLoading(false);
}
const mergedProps = { ...props } as Record<string, unknown>;
delete mergedProps["iconName"];
delete mergedProps["iconSize"];
delete mergedProps["loading"];
return (
<div {...mergedProps} onClick={e => handleClick(e)}>
{loading ? <Spinner /> : <Icon name={props.iconName} size={props.iconSize} />}
{props.children}
</div>
);
}

View File

@ -12,11 +12,16 @@
}
.modal-body {
background-color: var(--note-bg);
padding: 10px;
border-radius: 10px;
background-color: var(--gray-superdark);
padding: 16px 24px;
border-radius: 12px;
display: flex;
flex-direction: column;
width: 500px;
border: 1px solid var(--font-tertiary-color);
margin-top: auto;
margin-bottom: auto;
}
.modal-body button:hover {
background-color: var(--gray);
}

View File

@ -48,11 +48,13 @@ export interface NoteProps {
depth?: number;
options?: {
showHeader?: boolean;
showContextMenu?: boolean;
showTime?: boolean;
showPinned?: boolean;
showBookmarked?: boolean;
showFooter?: boolean;
showReactionsLink?: boolean;
showMedia?: boolean;
canUnpin?: boolean;
canUnbookmark?: boolean;
canClick?: boolean;
@ -151,6 +153,7 @@ export default function Note(props: NoteProps) {
showFooter: true,
canUnpin: false,
canUnbookmark: false,
showContextMenu: true,
...opt,
};
@ -209,7 +212,15 @@ export default function Note(props: NoteProps) {
</Reveal>
);
}
return <Text content={body} tags={ev.tags} creator={ev.pubkey} depth={props.depth} />;
return (
<Text
content={body}
tags={ev.tags}
creator={ev.pubkey}
depth={props.depth}
disableMedia={!(options.showMedia ?? true)}
/>
);
};
function goToEvent(
@ -355,12 +366,14 @@ export default function Note(props: NoteProps) {
<Icon name="pin" /> <FormattedMessage {...messages.Pinned} />
</div>
)}
<NoteContextMenu
ev={ev}
react={async () => {}}
onTranslated={t => setTranslated(t)}
setShowReactions={setShowReactions}
/>
{options.showContextMenu && (
<NoteContextMenu
ev={ev}
react={async () => {}}
onTranslated={t => setTranslated(t)}
setShowReactions={setShowReactions}
/>
)}
</div>
</div>
)}

View File

@ -1,22 +1,43 @@
.note-creator {
margin-bottom: 10px;
background-color: var(--note-bg);
border: none;
border-radius: 10px;
padding: 6px;
position: relative;
border: 1px solid transparent;
border-radius: 12px;
box-shadow: 0px 0px 6px 1px rgba(182, 108, 156, 0.3);
background: linear-gradient(var(--gray-superdark), var(--gray-superdark)) padding-box,
linear-gradient(90deg, #ef9644, #fd7c49, #ff5e58, #ff3b70, #ff088e, #eb00b1, #c31ed5, #7b41f6) border-box;
}
.note-reply {
margin: 10px;
.note-creator-modal .modal-body {
gap: 16px;
}
.note-creator-modal .note.card {
padding: 8px 12px;
border-radius: 12px;
background-color: var(--gray-dark);
}
.note-creator-modal h4 {
font-size: 11px;
font-weight: 600;
letter-spacing: 1.21px;
text-transform: uppercase;
color: var(--gray-light);
margin: 0;
}
.note-creator-relay {
background-color: var(--gray-dark);
border-radius: 12px;
}
.note-creator textarea {
border: none;
outline: none;
resize: none;
background-color: var(--note-bg);
border-radius: 10px 10px 0 0;
padding: 0;
border-radius: 0;
margin: 8px 12px;
background-color: var(--gray-superdark);
min-height: 100px;
width: stretch;
width: -webkit-fill-available;
@ -30,67 +51,9 @@
line-height: 24px;
}
@media (min-width: 520px) {
.note-creator textarea {
min-height: 210px;
}
}
@media (min-width: 720px) {
.note-creator textarea {
min-height: 321px;
}
}
.note-creator.poll textarea {
min-height: 120px;
}
.note-creator-actions {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
margin-bottom: 5px;
}
.note-creator .insert {
display: flex;
justify-content: flex-end;
width: stretch;
width: -webkit-fill-available;
width: -moz-available;
}
.note-creator .insert > button {
width: 48px;
height: 36px;
background: var(--gray-dark);
color: white;
border-radius: 17px;
margin-right: 5px;
display: flex;
justify-content: center;
align-items: center;
}
.note-creator .attachment:hover {
background: var(--font-color);
color: var(--gray-dark);
}
.light .note-creator .attachment {
background: var(--gray-light);
}
.light .note-creator .attachment:hover {
background: var(--gray-dark);
color: white;
}
.note-creator-actions button:not(:last-child) {
margin-right: 4px;
}
.note-creator .error {
position: absolute;
@ -101,6 +64,19 @@
font-size: 16px;
}
.note-creator-icon {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
}
.note-creator-icon.pfp .avatar {
width: 32px;
height: 32px;
}
.note-create-button {
width: 48px;
height: 48px;
@ -120,17 +96,3 @@
right: 16px;
}
}
.note-creator-modal .modal-body {
background: var(--modal-bg-color);
}
.note-preview {
word-break: break-all;
}
.note-preview-body {
text-overflow: ellipsis;
padding: 4px 4px 0 56px;
font-size: 14px;
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import "./NoteCreator.css";
import { FormattedMessage, useIntl } from "react-intl";
import { useDispatch, useSelector } from "react-redux";
@ -37,22 +38,7 @@ import { getCurrentSubscription } from "Subscription";
import useLogin from "Hooks/useLogin";
import { System } from "index";
import AsyncButton from "Element/AsyncButton";
interface NotePreviewProps {
note: TaggedNostrEvent;
}
function NotePreview({ note }: NotePreviewProps) {
return (
<div className="note-preview">
<ProfileImage pubkey={note.pubkey} />
<div className="note-preview-body">
{note.content.slice(0, 136)}
{note.content.length > 140 && "..."}
</div>
</div>
);
}
import { AsyncIcon } from "Element/AsyncIcon";
export function NoteCreator() {
const { formatMessage } = useIntl();
@ -72,7 +58,6 @@ export function NoteCreator() {
selectedCustomRelays,
error,
} = useSelector((s: RootState) => s.noteCreator);
const [uploadInProgress, setUploadInProgress] = useState(false);
const dispatch = useDispatch();
const sub = getCurrentSubscription(LoginStore.allSubscriptions());
const login = useLogin();
@ -138,7 +123,6 @@ export function NoteCreator() {
}
async function uploadFile(file: File | Blob) {
setUploadInProgress(true);
try {
if (file) {
const rx = await uploader.upload(file, file.name);
@ -156,8 +140,6 @@ export function NoteCreator() {
if (error instanceof Error) {
dispatch(setError(error?.message));
}
} finally {
setUploadInProgress(false);
}
}
@ -198,8 +180,10 @@ export function NoteCreator() {
data={preview as TaggedNostrEvent}
related={[]}
options={{
showContextMenu: false,
showFooter: false,
canClick: false,
showTime: false,
}}
/>
);
@ -254,14 +238,12 @@ export function NoteCreator() {
function renderRelayCustomisation() {
return (
<div>
<div className="flex-column g8">
{Object.keys(relays.item || {})
.filter(el => relays.item[el].write)
.map((r, i, a) => (
<div className="card flex">
<div className="flex f-col f-grow">
<div>{r}</div>
</div>
<div className="p flex f-space note-creator-relay">
<div>{r}</div>
<div>
<input
type="checkbox"
@ -322,103 +304,97 @@ export function NoteCreator() {
<>
{show && (
<Modal className="note-creator-modal" onClose={() => dispatch(setShow(false))}>
{replyTo && <NotePreview note={replyTo} />}
{replyTo && (
<Note
data={replyTo}
related={[]}
options={{
showFooter: false,
showContextMenu: false,
showTime: false,
canClick: false,
showMedia: false,
}}
/>
)}
{preview && getPreviewNote()}
{!preview && (
<div
onPaste={handlePaste}
className={`flex note-creator${replyTo ? " note-reply" : ""}${pollOptions ? " poll" : ""}`}>
<div className="flex f-col f-grow">
<Textarea
autoFocus
className={`textarea ${active ? "textarea--focused" : ""}`}
onChange={onChange}
value={note}
onFocus={() => dispatch(setActive(true))}
onKeyDown={e => {
if (e.key === "Enter" && e.metaKey) {
sendNote().catch(console.warn);
}
}}
/>
{renderPollOptions()}
<div className="insert">
{sub && (
<Menu
menuButton={
<button>
<Icon name="code-circle" />
</button>
}
menuClassName="ctx-menu">
{listAccounts()}
</Menu>
)}
{pollOptions === undefined && !replyTo && (
<button onClick={() => dispatch(setPollOptions(["A", "B"]))}>
<Icon name="pie-chart" />
</button>
)}
<button onClick={attachFile}>
<Icon name="attachment" />
</button>
</div>
</div>
{error && <span className="error">{error}</span>}
<div onPaste={handlePaste} className={`note-creator${pollOptions ? " poll" : ""}`}>
<Textarea
autoFocus
className={`textarea ${active ? "textarea--focused" : ""}`}
onChange={onChange}
value={note}
onFocus={() => dispatch(setActive(true))}
onKeyDown={e => {
if (e.key === "Enter" && e.metaKey) {
sendNote().catch(console.warn);
}
}}
/>
{renderPollOptions()}
</div>
)}
<div className="note-creator-actions">
{uploadInProgress && <Spinner />}
<button className="secondary" onClick={() => dispatch(setShowAdvanced(!showAdvanced))}>
<FormattedMessage defaultMessage="Advanced" />
</button>
<button className="secondary" onClick={cancel}>
<FormattedMessage {...messages.Cancel} />
</button>
<AsyncButton onClick={onSubmit}>
{replyTo ? <FormattedMessage {...messages.Reply} /> : <FormattedMessage {...messages.Send} />}
</AsyncButton>
<div className="flex f-space">
<div className="flex g8">
<ProfileImage pubkey={login.publicKey ?? ""} className="note-creator-icon" link="" showUsername={false} />
{pollOptions === undefined && !replyTo && (
<div className="note-creator-icon">
<Icon name="pie-chart" onClick={() => dispatch(setPollOptions(["A", "B"]))} size={24} />
</div>
)}
<AsyncIcon iconName="image-plus" iconSize={24} onClick={attachFile} className="note-creator-icon" />
<button className="secondary" onClick={() => dispatch(setShowAdvanced(!showAdvanced))}>
<FormattedMessage defaultMessage="Advanced" />
</button>
</div>
<div className="flex g8">
<button className="secondary" onClick={cancel}>
<FormattedMessage defaultMessage="Cancel" />
</button>
<AsyncButton onClick={onSubmit}>
{replyTo ? <FormattedMessage defaultMessage="Reply" /> : <FormattedMessage defaultMessage="Send" />}
</AsyncButton>
</div>
</div>
{error && <span className="error">{error}</span>}
{showAdvanced && (
<div>
<>
<button className="secondary" onClick={loadPreview}>
<FormattedMessage defaultMessage="Toggle Preview" />
</button>
<h4>
<FormattedMessage defaultMessage="Custom Relays" />
</h4>
<p>
<FormattedMessage defaultMessage="Send note to a subset of your write relays" />
</p>
{renderRelayCustomisation()}
<h4>
<FormattedMessage defaultMessage="Forward Zaps" />
</h4>
<p>
<div>
<h4>
<FormattedMessage defaultMessage="Custom Relays" />
</h4>
<p>
<FormattedMessage defaultMessage="Send note to a subset of your write relays" />
</p>
{renderRelayCustomisation()}
</div>
<div className="flex-column g8">
<h4>
<FormattedMessage defaultMessage="Forward Zaps" />
</h4>
<FormattedMessage defaultMessage="All zaps sent to this note will be received by the following LNURL" />
</p>
<b className="warning">
<FormattedMessage defaultMessage="Not all clients support this yet" />
</b>
<input
type="text"
className="w-max"
placeholder={formatMessage({
defaultMessage: "LNURL to forward zaps to",
})}
value={zapForward}
onChange={e => dispatch(setZapForward(e.target.value))}
/>
<h4>
<FormattedMessage defaultMessage="Sensitive Content" />
</h4>
<p>
<input
type="text"
className="w-max"
placeholder={formatMessage({
defaultMessage: "LNURL to forward zaps to",
})}
value={zapForward}
onChange={e => dispatch(setZapForward(e.target.value))}
/>
<span className="warning">
<FormattedMessage defaultMessage="Not all clients support this yet" />
</span>
</div>
<div className="flex-column g8">
<h4>
<FormattedMessage defaultMessage="Sensitive Content" />
</h4>
<FormattedMessage defaultMessage="Users must accept the content warning to show the content of your note." />
</p>
<b className="warning">
<FormattedMessage defaultMessage="Not all clients support this yet" />
</b>
<div className="flex">
<input
className="w-max"
type="text"
@ -430,8 +406,11 @@ export function NoteCreator() {
defaultMessage: "Reason",
})}
/>
<span className="warning">
<FormattedMessage defaultMessage="Not all clients support this yet" />
</span>
</div>
</div>
</>
)}
</Modal>
)}

View File

@ -6,9 +6,6 @@ import { TaggedNostrEvent, HexKey, u256, ParsedZap, countLeadingZeros } from "@s
import { LNURL } from "@snort/shared";
import { useUserProfile } from "@snort/system-react";
import Icon from "Icons/Icon";
import Spinner from "Icons/Spinner";
import { formatShort } from "Number";
import useEventPublisher from "Feed/EventPublisher";
import { delay, findTag, normalizeReaction, unwrap } from "SnortUtils";
@ -17,6 +14,7 @@ import SendSats from "Element/SendSats";
import { ZapsSummary } from "Element/Zap";
import { RootState } from "State/Store";
import { setReplyTo, setShow, reset } from "State/NoteCreator";
import { AsyncIcon } from "Element/AsyncIcon";
import { useWallet } from "Wallet";
import useLogin from "Hooks/useLogin";
@ -282,27 +280,14 @@ interface AsyncFooterIconProps extends HTMLProps<HTMLDivElement> {
}
function AsyncFooterIcon(props: AsyncFooterIconProps) {
const [loading, setLoading] = useState(props.loading ?? false);
async function handleClick(e: React.MouseEvent<HTMLDivElement>) {
setLoading(true);
try {
if (props.onClick) {
await props.onClick(e);
}
} catch (ex) {
console.error(ex);
}
setLoading(false);
}
const mergedProps = {
...props,
iconSize: 18,
className: `reaction-pill${props.className ? ` ${props.className}` : ""}`,
};
return (
<div
{...props}
className={`reaction-pill${props.className ? ` ${props.className}` : ""}`}
onClick={e => handleClick(e)}>
{loading ? <Spinner /> : <Icon name={props.iconName} size={18} />}
<AsyncIcon {...mergedProps}>
{props.value > 0 && <div className="reaction-pill-number">{formatShort(props.value)}</div>}
</div>
</AsyncIcon>
);
}

View File

@ -48,25 +48,37 @@ export default function ProfileImage({
}
}
return (
<Link
className={`pfp${className ? ` ${className}` : ""}`}
to={link === undefined ? profileLink(pubkey) : link}
onClick={handleClick}>
<div className="avatar-wrapper">
<Avatar user={user} size={size} />
</div>
{showUsername && (
<div className="f-ellipsis">
<div className="username">
<div>{name.trim()}</div>
{nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
</div>
<div className="subheader">{subHeader}</div>
function inner() {
return (
<>
<div className="avatar-wrapper">
<Avatar user={user} size={size} />
</div>
)}
</Link>
);
{showUsername && (
<div className="f-ellipsis">
<div className="username">
<div>{name.trim()}</div>
{nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
</div>
<div className="subheader">{subHeader}</div>
</div>
)}
</>
);
}
if (link === "") {
return <div className={`pfp${className ? ` ${className}` : ""}`}>{inner()}</div>;
} else {
return (
<Link
className={`pfp${className ? ` ${className}` : ""}`}
to={link === undefined ? profileLink(pubkey) : link}
onClick={handleClick}>
{inner()}
</Link>
);
}
}
export function getDisplayName(user: UserMetadata | undefined, pubkey: HexKey) {

View File

@ -7,7 +7,7 @@
}
.light .reactions-modal .modal-body {
background-color: var(--note-bg);
background-color: var(--gray-superdark);
}
@media (max-width: 720px) {

View File

@ -22,7 +22,7 @@
}
.light .lnurl-tip {
background-color: var(--note-bg);
background-color: var(--gray-superdark);
}
.lnurl-tip h3 {
@ -95,7 +95,7 @@
.sat-amount.active {
font-weight: bold;
color: var(--note-bg);
color: var(--gray-superdark);
background-color: var(--font-color);
}

View File

@ -13,7 +13,7 @@
.user-item,
.emoji-item {
color: var(--font-color);
background: var(--note-bg);
background: var(--gray-superdark);
display: flex;
flex-direction: row;
align-items: center;

View File

@ -37,7 +37,7 @@
}
.thread-container .show-more {
background: var(--note-bg);
background: var(--gray-superdark);
padding-left: 76px;
width: 100%;
text-align: left;
@ -95,12 +95,12 @@
.thread-container .collapsed,
.thread-container .show-more-container {
background: var(--note-bg);
background: var(--gray-superdark);
min-height: 48px;
}
.thread-container .collapsed {
background-color: var(--note-bg);
background-color: var(--gray-superdark);
}
.thread-container .hidden-note {

View File

@ -56,6 +56,7 @@ const HowDoKeysWork = () => {
};
const Extensions = () => {
const { preferences } = useLogin();
return (
<CollapsedSection
title={
@ -71,6 +72,11 @@ const Extensions = () => {
<a href="https://getalby.com/" target="_blank" rel="noreferrer">
Alby
</a>
{(preferences.language === "ru" || preferences.language === "ru-RU") && (
<a href="https://nostr.21ideas.org/docs/guides/Alby.html" target="_blank" rel="noreferrer">
(Tony's Guide)
</a>
)}
</li>
<li>
<a href="https://github.com/fiatjaf/nos2x" target="_blank" rel="noreferrer">

View File

@ -26,9 +26,10 @@ export function getPublicKey(privKey: HexKey) {
export async function openFile(): Promise<File | undefined> {
return new Promise(resolve => {
const elm = document.createElement("input");
let lock = false;
elm.type = "file";
const handleInput = (e: Event) => {
console.debug(e);
lock = true;
const elm = e.target as HTMLInputElement;
if ((elm.files?.length ?? 0) > 0) {
resolve(elm.files![0]);
@ -38,8 +39,19 @@ export async function openFile(): Promise<File | undefined> {
};
elm.onchange = e => handleInput(e);
elm.onblur = e => handleInput(e);
elm.click();
window.addEventListener(
"focus",
() => {
setTimeout(() => {
if (!lock) {
console.debug("FOCUS WINDOW UPLOAD");
resolve(undefined);
}
}, 300);
},
{ once: true }
);
});
}

View File

@ -8,7 +8,6 @@
--font-size-small: 13px;
--font-size-tiny: 11px;
--modal-bg-color: rgba(0, 0, 0, 0.8);
--note-bg: #0c0c0c;
--highlight: #ac88ff;
--error: #ff6053;
--success: #2ad544;
@ -23,25 +22,25 @@
--gray: #333;
--gray-secondary: #222;
--gray-tertiary: #444;
--gray-dark: #2b2b2b;
--gray-superdark: #1a1a1a;
--gray-dark: #2c2c2c;
--gray-superdark: #1b1b1b;
--gray-ultradark: #111;
--gray-gradient: linear-gradient(to bottom right, var(--gray-superlight), var(--gray), var(--gray-light));
--snort-gradient: linear-gradient(90deg, #a178ff 0%, #ff6baf 100%);
--dm-gradient: linear-gradient(90deg, #5722d2 0%, #db1771 100%);
--invoice-gradient: linear-gradient(
45deg,
var(--note-bg) 50%,
var(--gray-superdark) 50%,
rgba(161, 120, 255, 0.2),
rgba(255, 107, 175, 0.2) 108.33%
);
--paid-invoice-gradient: linear-gradient(
45deg,
var(--note-bg) 50%,
var(--gray-superdark) 50%,
rgba(161, 120, 255, 0.6),
rgba(255, 107, 175, 0.6) 108.33%
);
--expired-invoice-gradient: linear-gradient(45deg, var(--note-bg) 50%, var(--gray), var(--gray-superdark));
--expired-invoice-gradient: linear-gradient(45deg, var(--gray-superdark) 50%, var(--gray), var(--gray-superdark));
--strike-army-gradient: linear-gradient(to bottom right, #ccff00, #a1c900);
}
@ -78,8 +77,6 @@ html.light {
--highlight: #7139f1;
--modal-bg-color: rgba(240, 240, 240, 0.8);
--note-bg: white;
--gray: #aaa;
--gray-secondary: #bbb;
--gray-tertiary: #ccc;
@ -89,8 +86,8 @@ html.light {
--gray-superdark: #eee;
--dm-gradient: var(--gray);
--invoice-gradient: linear-gradient(45deg, var(--note-bg) 50%, #f7b73333, #fc4a1a33);
--paid-invoice-gradient: linear-gradient(45deg, var(--note-bg) 50%, #f7b73399, #fc4a1a99);
--invoice-gradient: linear-gradient(45deg, var(--gray-superdark) 50%, #f7b73333, #fc4a1a33);
--paid-invoice-gradient: linear-gradient(45deg, var(--gray-superdark) 50%, #f7b73399, #fc4a1a99);
}
body {
@ -705,7 +702,7 @@ button.tall {
}
.light .ctx-menu li {
background: var(--note-bg) !important;
background: var(--gray-superdark) !important;
}
.ctx-menu li:first-of-type {

View File

@ -152,6 +152,9 @@ export class FlatNoteStore extends HookedNoteStore<Readonly<Array<TaggedNostrEve
} else {
const existing = this.#events.find(b => b.id === a.id);
if (existing) {
if(!Boolean(Object.getOwnPropertyDescriptor(existing, "relays")?.writable)) {
debugger;
}
existing.relays = appendDedupe(existing.relays, a.relays);
}
}
@ -239,7 +242,7 @@ export class ReplaceableNoteStore extends HookedNoteStore<Readonly<TaggedNostrEv
takeSnapshot() {
if (this.#event) {
return Object.freeze({ ...this.#event });
return { ...this.#event };
}
}
}