mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-01 17:31:13 +00:00
Redesigned Primary note
This commit is contained in:
parent
20b4bf76da
commit
33e7efdc02
@ -7,6 +7,11 @@
|
|||||||
background-color: var(--text-tertiary);
|
background-color: var(--text-tertiary);
|
||||||
-webkit-mask: url(../../assets/icons/bookmark_empty.svg) no-repeat 0 / 16px;
|
-webkit-mask: url(../../assets/icons/bookmark_empty.svg) no-repeat 0 / 16px;
|
||||||
mask: url(../../assets/icons/bookmark_empty.svg) no-repeat 0 / 16px;
|
mask: url(../../assets/icons/bookmark_empty.svg) no-repeat 0 / 16px;
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullBookmark {
|
.fullBookmark {
|
||||||
@ -17,6 +22,11 @@
|
|||||||
background-color: var(--active-bookmarked);
|
background-color: var(--active-bookmarked);
|
||||||
-webkit-mask: url(../../assets/icons/bookmark_filled.svg) no-repeat 0 / 16px;
|
-webkit-mask: url(../../assets/icons/bookmark_filled.svg) no-repeat 0 / 16px;
|
||||||
mask: url(../../assets/icons/bookmark_filled.svg) no-repeat 0 / 16px;
|
mask: url(../../assets/icons/bookmark_filled.svg) no-repeat 0 / 16px;
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -16,7 +16,7 @@ import styles from './BookmarkNote.module.scss';
|
|||||||
import { saveBookmarks } from '../../lib/localStore';
|
import { saveBookmarks } from '../../lib/localStore';
|
||||||
import { importEvents, triggerImportEvents } from '../../lib/notes';
|
import { importEvents, triggerImportEvents } from '../../lib/notes';
|
||||||
|
|
||||||
const BookmarkNote: Component<{ note: PrimalNote }> = (props) => {
|
const BookmarkNote: Component<{ note: PrimalNote, large?: boolean }> = (props) => {
|
||||||
const account = useAccountContext();
|
const account = useAccountContext();
|
||||||
const app = useAppContext();
|
const app = useAppContext();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@ -147,10 +147,10 @@ const BookmarkNote: Component<{ note: PrimalNote }> = (props) => {
|
|||||||
<Show
|
<Show
|
||||||
when={isBookmarked()}
|
when={isBookmarked()}
|
||||||
fallback={
|
fallback={
|
||||||
<div class={styles.emptyBookmark}></div>
|
<div class={`${styles.emptyBookmark} ${props.large ? styles.large : ''}`}></div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class={styles.fullBookmark}></div>
|
<div class={`${styles.fullBookmark} ${props.large ? styles.large : ''}`}></div>
|
||||||
</Show>
|
</Show>
|
||||||
</ButtonGhost>
|
</ButtonGhost>
|
||||||
</div>
|
</div>
|
||||||
|
@ -245,8 +245,9 @@
|
|||||||
background-color: var(--background-card);
|
background-color: var(--background-card);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 12px;
|
padding-inline: 12px;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
padding-bottom: 12px;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
@ -286,6 +287,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
padding-block: 20px;
|
||||||
|
border-bottom: 1px solid var(--devider);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
|
||||||
|
.reactSummary {
|
||||||
|
&::before {
|
||||||
|
content: ' · ';
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import NoteHeader from './NoteHeader/NoteHeader';
|
|||||||
import { createStore } from 'solid-js/store';
|
import { createStore } from 'solid-js/store';
|
||||||
import { CustomZapInfo, useAppContext } from '../../contexts/AppContext';
|
import { CustomZapInfo, useAppContext } from '../../contexts/AppContext';
|
||||||
import NoteContextTrigger from './NoteContextTrigger';
|
import NoteContextTrigger from './NoteContextTrigger';
|
||||||
|
import { date, longDate, veryLongDate } from '../../lib/dates';
|
||||||
|
|
||||||
export type NoteFooterState = {
|
export type NoteFooterState = {
|
||||||
likes: number,
|
likes: number,
|
||||||
@ -146,6 +147,12 @@ const Note: Component<{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reactionSum = () => {
|
||||||
|
const { likes, zaps, reposts } = props.note.post;
|
||||||
|
|
||||||
|
return (likes || 0) + (zaps || 0) + (reposts || 0);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={noteType() === 'notification'}>
|
<Match when={noteType() === 'notification'}>
|
||||||
@ -200,12 +207,25 @@ const Note: Component<{
|
|||||||
<ParsedNote note={props.note} width={Math.min(574, window.innerWidth)} />
|
<ParsedNote note={props.note} width={Math.min(574, window.innerWidth)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={styles.time}
|
||||||
|
title={date(props.note.post?.created_at).date.toLocaleString()}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{veryLongDate(props.note.post?.created_at).replace('at', '·')}
|
||||||
|
</span>
|
||||||
|
<span class={styles.reactSummary}>
|
||||||
|
<span class={styles.number}>{reactionSum()}</span> Reactions
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<NoteFooter
|
<NoteFooter
|
||||||
note={props.note}
|
note={props.note}
|
||||||
state={footerState}
|
state={footerState}
|
||||||
updateState={updateFooterState}
|
updateState={updateFooterState}
|
||||||
customZapInfo={customZapInfo}
|
customZapInfo={customZapInfo}
|
||||||
wide={true}
|
wide={true}
|
||||||
|
large={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
import { A } from '@solidjs/router';
|
import { Component, Show } from 'solid-js';
|
||||||
import { Component, createSignal, Show } from 'solid-js';
|
import { PrimalUser } from '../../types/primal';
|
||||||
import { PrimalNote, PrimalUser } from '../../types/primal';
|
|
||||||
import ParsedNote from '../ParsedNote/ParsedNote';
|
|
||||||
import NoteFooter from './NoteFooter/NoteFooter';
|
|
||||||
import NoteHeader from './NoteHeader/NoteHeader';
|
|
||||||
|
|
||||||
import styles from './Note.module.scss';
|
import styles from './Note.module.scss';
|
||||||
import { useThreadContext } from '../../contexts/ThreadContext';
|
import { useThreadContext } from '../../contexts/ThreadContext';
|
||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import { authorName, nip05Verification, truncateNpub } from '../../stores/profile';
|
import { authorName, nip05Verification } from '../../stores/profile';
|
||||||
import { note as t } from '../../translations';
|
|
||||||
import { hookForDev } from '../../lib/devTools';
|
import { hookForDev } from '../../lib/devTools';
|
||||||
import NoteReplyHeader from './NoteHeader/NoteReplyHeader';
|
|
||||||
import Avatar from '../Avatar/Avatar';
|
|
||||||
import { date } from '../../lib/dates';
|
import { date } from '../../lib/dates';
|
||||||
import VerificationCheck from '../VerificationCheck/VerificationCheck';
|
import VerificationCheck from '../VerificationCheck/VerificationCheck';
|
||||||
|
|
||||||
|
@ -1,27 +1,17 @@
|
|||||||
import { A } from '@solidjs/router';
|
import { Component, createEffect, createSignal } from 'solid-js';
|
||||||
import { Component, createEffect, createSignal, Show } from 'solid-js';
|
import { MenuItem, NostrRelaySignedEvent } from '../../types/primal';
|
||||||
import { MenuItem, NostrRelaySignedEvent, PrimalNote, PrimalRepost, PrimalUser } from '../../types/primal';
|
|
||||||
import ParsedNote from '../ParsedNote/ParsedNote';
|
|
||||||
import NoteFooter from './NoteFooter/NoteFooter';
|
|
||||||
import NoteHeader from './NoteHeader/NoteHeader';
|
|
||||||
|
|
||||||
import styles from './Note.module.scss';
|
import styles from './Note.module.scss';
|
||||||
import { useThreadContext } from '../../contexts/ThreadContext';
|
|
||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import { authorName, nip05Verification, truncateNpub, userName } from '../../stores/profile';
|
import { authorName, userName } from '../../stores/profile';
|
||||||
import { note as t, actions as tActions, toast as tToast } from '../../translations';
|
import { note as t, actions as tActions, toast as tToast } from '../../translations';
|
||||||
import { hookForDev } from '../../lib/devTools';
|
import { hookForDev } from '../../lib/devTools';
|
||||||
import NoteReplyHeader from './NoteHeader/NoteReplyHeader';
|
|
||||||
import Avatar from '../Avatar/Avatar';
|
|
||||||
import { date } from '../../lib/dates';
|
|
||||||
import VerificationCheck from '../VerificationCheck/VerificationCheck';
|
|
||||||
import PrimalMenu from '../PrimalMenu/PrimalMenu';
|
import PrimalMenu from '../PrimalMenu/PrimalMenu';
|
||||||
import { useAccountContext } from '../../contexts/AccountContext';
|
import { useAccountContext } from '../../contexts/AccountContext';
|
||||||
import { APP_ID } from '../../App';
|
import { APP_ID } from '../../App';
|
||||||
import { reportUser } from '../../lib/profile';
|
import { reportUser } from '../../lib/profile';
|
||||||
import { useToastContext } from '../Toaster/Toaster';
|
import { useToastContext } from '../Toaster/Toaster';
|
||||||
import { broadcastEvent } from '../../lib/notes';
|
import { broadcastEvent } from '../../lib/notes';
|
||||||
import { getScreenCordinates } from '../../utils';
|
|
||||||
import { NoteContextMenuInfo } from '../../contexts/AppContext';
|
import { NoteContextMenuInfo } from '../../contexts/AppContext';
|
||||||
import ConfirmModal from '../ConfirmModal/ConfirmModal';
|
import ConfirmModal from '../ConfirmModal/ConfirmModal';
|
||||||
|
|
||||||
|
@ -2,11 +2,23 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background-color: var(--text-tertiary-2);
|
background-color: var(--text-tertiary-2);
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin typeDiv {
|
@mixin typeDiv {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contextButton {
|
.contextButton {
|
||||||
@ -59,9 +71,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stat {
|
.stat {
|
||||||
font-weight: 400;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 16px;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
@ -30,6 +30,7 @@ const NoteFooter: Component<{
|
|||||||
state: NoteFooterState,
|
state: NoteFooterState,
|
||||||
updateState: SetStoreFunction<NoteFooterState>,
|
updateState: SetStoreFunction<NoteFooterState>,
|
||||||
customZapInfo: CustomZapInfo,
|
customZapInfo: CustomZapInfo,
|
||||||
|
large?: boolean,
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
|
|
||||||
const account = useAccountContext();
|
const account = useAccountContext();
|
||||||
@ -321,6 +322,7 @@ const NoteFooter: Component<{
|
|||||||
highlighted={props.state.replied}
|
highlighted={props.state.replied}
|
||||||
label={props.state.replies === 0 ? '' : truncateNumber(props.state.replies, 2)}
|
label={props.state.replies === 0 ? '' : truncateNumber(props.state.replies, 2)}
|
||||||
title={props.state.replies.toLocaleString()}
|
title={props.state.replies.toLocaleString()}
|
||||||
|
large={props.large}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NoteFooterActionButton
|
<NoteFooterActionButton
|
||||||
@ -335,6 +337,7 @@ const NoteFooter: Component<{
|
|||||||
label={props.state.satsZapped === 0 ? '' : truncateNumber(props.state.satsZapped, 2)}
|
label={props.state.satsZapped === 0 ? '' : truncateNumber(props.state.satsZapped, 2)}
|
||||||
hidden={props.state.hideZapIcon}
|
hidden={props.state.hideZapIcon}
|
||||||
title={props.state.satsZapped.toLocaleString()}
|
title={props.state.satsZapped.toLocaleString()}
|
||||||
|
large={props.large}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NoteFooterActionButton
|
<NoteFooterActionButton
|
||||||
@ -344,6 +347,7 @@ const NoteFooter: Component<{
|
|||||||
highlighted={props.state.liked}
|
highlighted={props.state.liked}
|
||||||
label={props.state.likes === 0 ? '' : truncateNumber(props.state.likes, 2)}
|
label={props.state.likes === 0 ? '' : truncateNumber(props.state.likes, 2)}
|
||||||
title={props.state.likes.toLocaleString()}
|
title={props.state.likes.toLocaleString()}
|
||||||
|
large={props.large}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -357,7 +361,7 @@ const NoteFooter: Component<{
|
|||||||
ref={repostMenu}
|
ref={repostMenu}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class={styles.icon}
|
class={`${styles.icon} ${props.large ? styles.large : ''}`}
|
||||||
style={'visibility: visible'}
|
style={'visibility: visible'}
|
||||||
></div>
|
></div>
|
||||||
<div class={styles.statNumber}>
|
<div class={styles.statNumber}>
|
||||||
@ -373,7 +377,10 @@ const NoteFooter: Component<{
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<BookmarkNote note={props.note} />
|
<BookmarkNote
|
||||||
|
note={props.note}
|
||||||
|
large={props.large}
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -23,6 +23,7 @@ const NoteFooterActionButton: Component<{
|
|||||||
label: string | number,
|
label: string | number,
|
||||||
hidden?: boolean,
|
hidden?: boolean,
|
||||||
title?: string,
|
title?: string,
|
||||||
|
large?: boolean,
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,9 +37,9 @@ const NoteFooterActionButton: Component<{
|
|||||||
onTouchEnd={props.onTouchEnd ?? (() => {})}
|
onTouchEnd={props.onTouchEnd ?? (() => {})}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
>
|
>
|
||||||
<div class={`${buttonTypeClasses[props.type]}`}>
|
<div class={`${buttonTypeClasses[props.type]} ${props.large ? styles.large : ''}`}>
|
||||||
<div
|
<div
|
||||||
class={styles.icon}
|
class={`${styles.icon} ${props.large ? styles.large : ''}`}
|
||||||
style={props.hidden ? 'visibility: hidden': 'visibility: visible'}
|
style={props.hidden ? 'visibility: hidden': 'visibility: visible'}
|
||||||
></div>
|
></div>
|
||||||
<div class={styles.statNumber}>{props.label || ''}</div>
|
<div class={styles.statNumber}>{props.label || ''}</div>
|
||||||
|
@ -222,17 +222,7 @@ const NoteHeader: Component<{
|
|||||||
|
|
||||||
<VerificationCheck
|
<VerificationCheck
|
||||||
user={props.note.user}
|
user={props.note.user}
|
||||||
fallback={<div class={styles.ellipsisIcon}></div>}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span
|
|
||||||
class={styles.time}
|
|
||||||
title={date(props.note.post?.created_at).date.toLocaleString()}
|
|
||||||
>
|
|
||||||
{props.primary ?
|
|
||||||
longDate(props.note.post?.created_at) :
|
|
||||||
date(props.note.post?.created_at).label}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<Show
|
<Show
|
||||||
when={props.note.user?.nip05}
|
when={props.note.user?.nip05}
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import { A } from '@solidjs/router';
|
import { Component, createMemo, Show } from 'solid-js';
|
||||||
import { Component, createMemo, createSignal, Show } from 'solid-js';
|
import { PrimalNote } from '../../types/primal';
|
||||||
import { PrimalNote, PrimalRepost, PrimalUser } from '../../types/primal';
|
|
||||||
import ParsedNote from '../ParsedNote/ParsedNote';
|
|
||||||
import NoteFooter from './NoteFooter/NoteFooter';
|
|
||||||
import NoteHeader from './NoteHeader/NoteHeader';
|
|
||||||
|
|
||||||
import styles from './Note.module.scss';
|
import styles from './Note.module.scss';
|
||||||
import { useThreadContext } from '../../contexts/ThreadContext';
|
|
||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import { authorName, nip05Verification, truncateNpub, userName } from '../../stores/profile';
|
|
||||||
import { note as t } from '../../translations';
|
import { note as t } from '../../translations';
|
||||||
import { hookForDev } from '../../lib/devTools';
|
import { hookForDev } from '../../lib/devTools';
|
||||||
import MentionedUserLink from './MentionedUserLink/MentionedUserLink';
|
import MentionedUserLink from './MentionedUserLink/MentionedUserLink';
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
background-color: var(--background-card);
|
background-color: var(--background-card);
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 1px solid var(--devider);
|
border-block: 1px solid var(--devider);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -18,6 +18,16 @@ export const longDate = (timestamp: number | undefined) => {
|
|||||||
return dtf.format(date);
|
return dtf.format(date);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const veryLongDate = (timestamp: number | undefined) => {
|
||||||
|
if (!timestamp || timestamp < 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const date = new Date(timestamp * 1000);
|
||||||
|
const dtf = new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'short'});
|
||||||
|
|
||||||
|
return dtf.format(date);
|
||||||
|
};
|
||||||
|
|
||||||
export const date = (postTimestamp: number, style: Intl.RelativeTimeFormatStyle = 'short', since?: number) => {
|
export const date = (postTimestamp: number, style: Intl.RelativeTimeFormatStyle = 'short', since?: number) => {
|
||||||
const today = since ?? Math.floor((new Date()).getTime() / 1000);
|
const today = since ?? Math.floor((new Date()).getTime() / 1000);
|
||||||
const date = new Date(postTimestamp * 1000);
|
const date = new Date(postTimestamp * 1000);
|
||||||
|
@ -3,9 +3,7 @@ import Note from '../components/Note/Note';
|
|||||||
import styles from './Thread.module.scss';
|
import styles from './Thread.module.scss';
|
||||||
import { useNavigate, useParams } from '@solidjs/router';
|
import { useNavigate, useParams } from '@solidjs/router';
|
||||||
import { PrimalNote, SendNoteResult } from '../types/primal';
|
import { PrimalNote, SendNoteResult } from '../types/primal';
|
||||||
import NotePrimary from '../components/Note/NotePrimary/NotePrimary';
|
|
||||||
import PeopleList from '../components/PeopleList/PeopleList';
|
import PeopleList from '../components/PeopleList/PeopleList';
|
||||||
import PageNav from '../components/PageNav/PageNav';
|
|
||||||
import ReplyToNote from '../components/ReplyToNote/ReplyToNote';
|
import ReplyToNote from '../components/ReplyToNote/ReplyToNote';
|
||||||
|
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
@ -13,7 +11,6 @@ import { useThreadContext } from '../contexts/ThreadContext';
|
|||||||
import Wormhole from '../components/Wormhole/Wormhole';
|
import Wormhole from '../components/Wormhole/Wormhole';
|
||||||
import { useAccountContext } from '../contexts/AccountContext';
|
import { useAccountContext } from '../contexts/AccountContext';
|
||||||
import { sortByRecency } from '../stores/note';
|
import { sortByRecency } from '../stores/note';
|
||||||
import { scrollWindowTo } from '../lib/scroll';
|
|
||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import Search from '../components/Search/Search';
|
import Search from '../components/Search/Search';
|
||||||
import { placeholders as tPlaceholders, thread as t } from '../translations';
|
import { placeholders as tPlaceholders, thread as t } from '../translations';
|
||||||
|
Loading…
Reference in New Issue
Block a user