Refactor top zaps into a single list

This commit is contained in:
Bojan Mojsilovic 2024-04-29 12:45:38 +02:00
parent a55c5122f7
commit 9455df93ab
3 changed files with 116 additions and 184 deletions

View File

@ -336,8 +336,8 @@
.zapHighlights {
display: flex;
flex-direction: column;
gap: 8px;
flex-wrap: wrap;
gap: 6px;
align-items: flex-start;
&.onlyFew {
@ -346,6 +346,11 @@
gap: 6px;
}
.break {
flex-basis: 100%;
height: 0;
}
.firstZap {
display: flex;
align-items: center;
@ -383,93 +388,6 @@
background: var(--subtile-devider);
}
}
.topZaps {
display: flex;
align-self: center;
gap: 6px;
width: 100%;
.zapList {
display: flex;
align-self: center;
gap: 6px;
max-width: calc(100% - 30px);
overflow-x: scroll;
overflow-y: hidden;
/* Hide scrollbar for Chrome, Safari and Opera */
&::-webkit-scrollbar {
display: none !important;
}
/* Hide scrollbar for IE, Edge and Firefox */
-ms-overflow-style: none !important; /* IE and Edge */
scrollbar-width: none !important; /* Firefox */
.topZap {
display: flex;
align-items: center;
gap: 6px;
padding-left: 2px;
padding-right: 10px;
padding-block: 2px;
margin: 0;
border-radius: 12px;
background: var(--devider);
width: fit-content;
text-decoration: none;
border: none;
outline: none;
.amount {
color: var(--text-primary);
font-size: 14px;
font-weight: 600;
line-height: 14px;
}
.description {
color: var(--text-secondary-2);
font-size: 14px;
font-weight: 400;
line-height: 14px;
}
&:hover {
background: var(--subtile-devider);
}
}
}
.moreZaps {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--devider);
width: 26px;
height: 26px;
padding: 0;
margin: 0;
border: none;
outline: none;
.contextIcon {
width: 16px;
height: 14px;
background-color: var(--text-secondary-2);
-webkit-mask: url(../../assets/icons/context.svg) no-repeat 0 / 100%;
mask: url(../../assets/icons/context.svg) no-repeat 0 / 100%;
}
&:hover {
.contextIcon {
background-color: var(--text-primary);
}
}
}
}
}

View File

@ -21,6 +21,7 @@ import { hexToNpub } from '../../lib/keys';
import { thread, zapCustomOption } from '../../translations';
import { useAccountContext } from '../../contexts/AccountContext';
import { uuidv4 } from '../../utils';
import NoteTopZaps from './NoteTopZaps';
export type NoteReactionsState = {
likes: number,
@ -206,41 +207,6 @@ const Note: Component<{
return (likes || 0) + (zapCount || 0) + (reposts || 0) + (quoteCount || 0);
};
const firstZap = createMemo(() => reactionsState.topZaps[0]);
const restZaps = createMemo(() => {
const zaps = reactionsState.topZaps.slice(1);
let limit = 0;
let digits = 0;
for (let i=0; i< zaps.length; i++) {
const amount = zaps[i].amount || 0;
const length = Math.log(amount) * Math.LOG10E + 1 | 0;
digits += length;
if (digits > 25 || limit > 6) break;
limit++;
}
const rest = zaps.slice(0, limit);
if (rest.length < reactionsState.zapCount - 1) {
updateReactionsState('moreZapsAvailable', () => true);
}
else {
updateReactionsState('moreZapsAvailable', () => false);
}
return rest;
});
const zapSender = (zap: TopZap) => {
return threadContext?.users.find(u => u.pubkey === zap.pubkey);
};
createEffect(() => {
updateReactionsState('topZaps', () => [ ...(threadContext?.topZaps[props.note.post.id] || []) ]);
});
@ -299,66 +265,11 @@ const Note: Component<{
<ParsedNote note={props.note} width={Math.min(574, window.innerWidth)} />
</div>
<Show
when={!threadContext?.isFetchingTopZaps}
fallback={
<div class={styles.topZapsLoading}>
<div class={styles.firstZap}></div>
<div class={styles.topZaps}>
<div class={styles.zapList}>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
</div>
</div>
</div>
}
>
<div class={`${styles.zapHighlights} ${reactionsState.topZaps.length < 4 ? styles.onlyFew : ''}`}>
<Show when={firstZap()}>
<button
class={styles.firstZap}
onClick={() => openReactionModal('zaps')}
>
<Avatar user={zapSender(firstZap())} size="micro" />
<div class={styles.amount}>
{firstZap().amount.toLocaleString()}
</div>
<div class={styles.description}>
{firstZap().message}
</div>
</button>
</Show>
<div class={styles.topZaps}>
<div class={styles.zapList}>
<For each={restZaps()}>
{zap => (
<button
class={styles.topZap}
onClick={() => openReactionModal('zaps')}
>
<Avatar user={zapSender(zap)} size="micro" />
<div class={styles.amount}>
{zap.amount.toLocaleString()}
</div>
</button>
)}
</For>
</div>
<Show when={reactionsState.moreZapsAvailable}>
<button
class={styles.moreZaps}
onClick={() => openReactionModal('zaps')}
>
<div class={styles.contextIcon}></div>
</button>
</Show>
</div>
</div>
</Show>
<NoteTopZaps
topZaps={reactionsState.topZaps}
zapCount={reactionsState.zapCount}
action={() => openReactionModal('zaps')}
/>
<div
class={styles.time}

View File

@ -0,0 +1,103 @@
import { Component, createMemo, createSignal, For, Show } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import { TopZap, useThreadContext } from "../../contexts/ThreadContext";
import Avatar from "../Avatar/Avatar";
import styles from "./Note.module.scss";
const NoteTopZaps: Component<{
topZaps: TopZap[],
zapCount: number,
action: () => void,
id?: string,
}> = (props) => {
const threadContext = useThreadContext();
const [hasMoreZaps, setHasMoreZaps] = createSignal(false);
const topZaps = () => {
const zaps = [...props.topZaps];
let limit = 0;
let digits = 0;
for (let i=0; i< zaps.length; i++) {
const amount = zaps[i].amount || 0;
const length = Math.log(amount) * Math.LOG10E + 1 | 0;
digits += length;
if (digits > 25 || limit > 7) break;
limit++;
}
const highlights = zaps.slice(0, limit);
setHasMoreZaps(() => highlights.length < props.zapCount - 1);
return highlights;
}
const zapSender = (zap: TopZap) => {
return threadContext?.users.find(u => u.pubkey === zap.pubkey);
};
return (
<Show
when={!threadContext?.isFetchingTopZaps}
fallback={
<div class={styles.topZapsLoading}>
<div class={styles.firstZap}></div>
<div class={styles.topZaps}>
<div class={styles.zapList}>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
<div class={styles.topZap}></div>
</div>
</div>
</div>
}
>
<div class={`${styles.zapHighlights}`}>
<For each={topZaps()}>
{(zap, index) => (
<>
<button
class={styles.firstZap}
onClick={() => props.action()}
>
<Avatar user={zapSender(zap)} size="micro" />
<div class={styles.amount}>
{zap.amount.toLocaleString()}
</div>
<Show when={index() === 0}>
<div class={styles.description}>
{zap.message}
</div>
</Show>
</button>
<Show when={index() === 0 && props.topZaps.length > 3}>
<div class={styles.break}></div>
</Show>
</>
)}
</For>
<Show when={hasMoreZaps()}>
<button
class={styles.moreZaps}
onClick={() => props.action()}
>
<div class={styles.contextIcon}></div>
</button>
</Show>
</div>
</Show>
);
}
export default hookForDev(NoteTopZaps);