send sats and show latest improvements
This commit is contained in:
parent
a63701f7d0
commit
2c09d6af79
@ -64,17 +64,11 @@
|
|||||||
.amounts {
|
.amounts {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: scroll;
|
|
||||||
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
|
||||||
scrollbar-width: none; /* for Firefox */
|
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.amounts::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sat-amount {
|
.sat-amount {
|
||||||
|
flex: 1 1 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: #2a2a2a;
|
background-color: #2a2a2a;
|
||||||
|
@ -14,7 +14,6 @@ import Modal from "Element/Modal";
|
|||||||
import QrCode from "Element/QrCode";
|
import QrCode from "Element/QrCode";
|
||||||
import Copy from "Element/Copy";
|
import Copy from "Element/Copy";
|
||||||
import useWebln from "Hooks/useWebln";
|
import useWebln from "Hooks/useWebln";
|
||||||
import useHorizontalScroll from "Hooks/useHorizontalScroll";
|
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
|
||||||
@ -49,6 +48,18 @@ export interface LNURLTipProps {
|
|||||||
author?: HexKey;
|
author?: HexKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chunks(arr: any[], length: number) {
|
||||||
|
const result = [];
|
||||||
|
let idx = 0;
|
||||||
|
let n = arr.length / length;
|
||||||
|
while (n > 0) {
|
||||||
|
result.push(arr.slice(idx, idx + length));
|
||||||
|
idx += length;
|
||||||
|
n -= 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export default function LNURLTip(props: LNURLTipProps) {
|
export default function LNURLTip(props: LNURLTipProps) {
|
||||||
const onClose = props.onClose || (() => undefined);
|
const onClose = props.onClose || (() => undefined);
|
||||||
const service = props.svc;
|
const service = props.svc;
|
||||||
@ -74,7 +85,6 @@ export default function LNURLTip(props: LNURLTipProps) {
|
|||||||
const webln = useWebln(show);
|
const webln = useWebln(show);
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const publisher = useEventPublisher();
|
const publisher = useEventPublisher();
|
||||||
const horizontalScroll = useHorizontalScroll();
|
|
||||||
const canComment = (payService?.commentAllowed ?? 0) > 0 || payService?.nostrPubkey;
|
const canComment = (payService?.commentAllowed ?? 0) > 0 || payService?.nostrPubkey;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -100,6 +110,7 @@ export default function LNURLTip(props: LNURLTipProps) {
|
|||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}, [payService]);
|
}, [payService]);
|
||||||
|
const amountRows = useMemo(() => chunks(serviceAmounts, 3), [serviceAmounts]);
|
||||||
|
|
||||||
const selectAmount = (a: number) => {
|
const selectAmount = (a: number) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
@ -204,6 +215,19 @@ export default function LNURLTip(props: LNURLTipProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAmounts(amount: number, amounts: number[]) {
|
||||||
|
return (
|
||||||
|
<div className="amounts">
|
||||||
|
{amounts.map(a => (
|
||||||
|
<span className={`sat-amount ${amount === a ? "active" : ""}`} key={a} onClick={() => selectAmount(a)}>
|
||||||
|
{emojis[a] && <>{emojis[a]} </>}
|
||||||
|
{a === 1000 ? "1K" : formatShort(a)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function invoiceForm() {
|
function invoiceForm() {
|
||||||
if (invoice) return null;
|
if (invoice) return null;
|
||||||
return (
|
return (
|
||||||
@ -211,14 +235,7 @@ export default function LNURLTip(props: LNURLTipProps) {
|
|||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage {...messages.ZapAmount} />
|
<FormattedMessage {...messages.ZapAmount} />
|
||||||
</h3>
|
</h3>
|
||||||
<div className="amounts" ref={horizontalScroll}>
|
{amountRows.map(amounts => renderAmounts(amount, amounts))}
|
||||||
{serviceAmounts.map(a => (
|
|
||||||
<span className={`sat-amount ${amount === a ? "active" : ""}`} key={a} onClick={() => selectAmount(a)}>
|
|
||||||
{emojis[a] && <>{emojis[a]} </>}
|
|
||||||
{formatShort(a)}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{payService && custom()}
|
{payService && custom()}
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{canComment && (
|
{canComment && (
|
||||||
|
@ -31,7 +31,7 @@ function clamp(n: number, min: number, max: number) {
|
|||||||
|
|
||||||
export const TabElement = ({ t, tab, setTab }: TabElementProps) => {
|
export const TabElement = ({ t, tab, setTab }: TabElementProps) => {
|
||||||
const style = useMemo(() => {
|
const style = useMemo(() => {
|
||||||
return { minWidth: `${clamp(t.text.length, 2, 7)}em` } as CSSProperties;
|
return { minWidth: `${clamp(t.text.length, 3, 8) * 14}px` } as CSSProperties;
|
||||||
}, [t.text]);
|
}, [t.text]);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -15,7 +15,13 @@
|
|||||||
box-shadow: 0px 0px 15px rgba(78, 0, 255, 0.6);
|
box-shadow: 0px 0px 15px rgba(78, 0, 255, 0.6);
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
z-index: 42;
|
z-index: 42;
|
||||||
opacity: 0.8;
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-notes-fixed {
|
||||||
|
position: fixed;
|
||||||
|
left: calc(50% - 261px / 2 + 0.5px);
|
||||||
|
top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 520px) {
|
@media (max-width: 520px) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import "./Timeline.css";
|
import "./Timeline.css";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
|
import { useInView } from "react-intersection-observer";
|
||||||
|
|
||||||
import ArrowUp from "Icons/ArrowUp";
|
import ArrowUp from "Icons/ArrowUp";
|
||||||
import { dedupeByPubkey } from "Util";
|
import { dedupeByPubkey } from "Util";
|
||||||
@ -33,15 +34,16 @@ export default function Timeline({
|
|||||||
postsOnly = false,
|
postsOnly = false,
|
||||||
method,
|
method,
|
||||||
ignoreModeration = false,
|
ignoreModeration = false,
|
||||||
window,
|
window: timeWindow,
|
||||||
relay,
|
relay,
|
||||||
}: TimelineProps) {
|
}: TimelineProps) {
|
||||||
const { muted, isMuted } = useModeration();
|
const { muted, isMuted } = useModeration();
|
||||||
const { main, related, latest, parent, loadMore, showLatest } = useTimelineFeed(subject, {
|
const { main, related, latest, parent, loadMore, showLatest } = useTimelineFeed(subject, {
|
||||||
method,
|
method,
|
||||||
window: window,
|
window: timeWindow,
|
||||||
relay,
|
relay,
|
||||||
});
|
});
|
||||||
|
const { ref, inView } = useInView();
|
||||||
|
|
||||||
const filterPosts = useCallback(
|
const filterPosts = useCallback(
|
||||||
(nts: TaggedRawEvent[]) => {
|
(nts: TaggedRawEvent[]) => {
|
||||||
@ -84,19 +86,40 @@ export default function Timeline({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onShowLatest(scrollToTop = false) {
|
||||||
|
showLatest();
|
||||||
|
if (scrollToTop) {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main-content">
|
<div className="main-content">
|
||||||
{latestFeed.length > 0 && (
|
{latestFeed.length > 0 && (
|
||||||
<div className="card latest-notes pointer" onClick={() => showLatest()}>
|
<>
|
||||||
{latestAuthors.slice(0, 3).map(p => {
|
<div className="card latest-notes pointer" onClick={() => onShowLatest()} ref={ref}>
|
||||||
return <ProfileImage pubkey={p} showUsername={false} linkToProfile={false} />;
|
{latestAuthors.slice(0, 3).map(p => {
|
||||||
})}
|
return <ProfileImage pubkey={p} showUsername={false} linkToProfile={false} />;
|
||||||
<FormattedMessage
|
})}
|
||||||
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
|
<FormattedMessage
|
||||||
values={{ n: latestFeed.length }}
|
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
|
||||||
/>
|
values={{ n: latestFeed.length }}
|
||||||
<ArrowUp />
|
/>
|
||||||
</div>
|
<ArrowUp />
|
||||||
|
</div>
|
||||||
|
{!inView && (
|
||||||
|
<div className="card latest-notes latest-notes-fixed pointer" onClick={() => onShowLatest(true)}>
|
||||||
|
{latestAuthors.slice(0, 3).map(p => {
|
||||||
|
return <ProfileImage pubkey={p} showUsername={false} linkToProfile={false} />;
|
||||||
|
})}
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
|
||||||
|
values={{ n: latestFeed.length }}
|
||||||
|
/>
|
||||||
|
<ArrowUp />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{mainFeed.map(eventElement)}
|
{mainFeed.map(eventElement)}
|
||||||
<LoadMore onLoadMore={loadMore} shouldLoadMore={main.end}>
|
<LoadMore onLoadMore={loadMore} shouldLoadMore={main.end}>
|
||||||
|
@ -39,6 +39,10 @@
|
|||||||
--strike-army-gradient: linear-gradient(to bottom right, #ccff00, #a1c900);
|
--strike-army-gradient: linear-gradient(to bottom right, #ccff00, #a1c900);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
html.light {
|
html.light {
|
||||||
--bg-color: #f8f8f8;
|
--bg-color: #f8f8f8;
|
||||||
--font-color: #27272a;
|
--font-color: #27272a;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user