mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-30 00:41:09 +00:00
Fix footer and context menu in article preview
This commit is contained in:
parent
e28299e9f0
commit
b606e90532
@ -1,4 +1,5 @@
|
|||||||
.article {
|
.article {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -115,3 +116,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upRightFloater {
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ import { PrimalArticle, ZapOption } from '../../types/primal';
|
|||||||
import { uuidv4 } from '../../utils';
|
import { uuidv4 } from '../../utils';
|
||||||
import Avatar from '../Avatar/Avatar';
|
import Avatar from '../Avatar/Avatar';
|
||||||
import { NoteReactionsState } from '../Note/Note';
|
import { NoteReactionsState } from '../Note/Note';
|
||||||
|
import NoteContextTrigger from '../Note/NoteContextTrigger';
|
||||||
import ArticleFooter from '../Note/NoteFooter/ArticleFooter';
|
import ArticleFooter from '../Note/NoteFooter/ArticleFooter';
|
||||||
import NoteFooter from '../Note/NoteFooter/NoteFooter';
|
import NoteFooter from '../Note/NoteFooter/NoteFooter';
|
||||||
import NoteTopZaps from '../Note/NoteTopZaps';
|
import NoteTopZaps from '../Note/NoteTopZaps';
|
||||||
@ -53,6 +54,7 @@ const ArticlePreview: Component<{
|
|||||||
|
|
||||||
let latestTopZap: string = '';
|
let latestTopZap: string = '';
|
||||||
let latestTopZapFeed: string = '';
|
let latestTopZapFeed: string = '';
|
||||||
|
let articleContextMenu: HTMLDivElement | undefined;
|
||||||
|
|
||||||
const onConfirmZap = (zapOption: ZapOption) => {
|
const onConfirmZap = (zapOption: ZapOption) => {
|
||||||
app?.actions.closeCustomZapModal();
|
app?.actions.closeCustomZapModal();
|
||||||
@ -181,19 +183,48 @@ const ArticlePreview: Component<{
|
|||||||
onCancel: onCancelZap,
|
onCancel: onCancelZap,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const openReactionModal = (openOn = 'likes') => {
|
||||||
|
app?.actions.openReactionModal(props.article.id, {
|
||||||
|
likes: reactionsState.likes,
|
||||||
|
zaps: reactionsState.zapCount,
|
||||||
|
reposts: reactionsState.reposts,
|
||||||
|
quotes: reactionsState.quoteCount,
|
||||||
|
openOn,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onContextMenuTrigger = () => {
|
||||||
|
app?.actions.openContextMenu(
|
||||||
|
props.article,
|
||||||
|
articleContextMenu?.getBoundingClientRect(),
|
||||||
|
() => {
|
||||||
|
app?.actions.openCustomZapModal(customZapInfo());
|
||||||
|
},
|
||||||
|
openReactionModal,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<A class={styles.article} href={`/e/${props.article.naddr}`}>
|
<A class={styles.article} href={`/e/${props.article.naddr}`}>
|
||||||
|
<div class={styles.upRightFloater}>
|
||||||
|
<NoteContextTrigger
|
||||||
|
ref={articleContextMenu}
|
||||||
|
onClick={onContextMenuTrigger}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class={styles.header}>
|
<div class={styles.header}>
|
||||||
<div class={styles.userInfo}>
|
<div class={styles.userInfo}>
|
||||||
<Avatar user={props.article.author} size="micro"/>
|
<Avatar user={props.article.user} size="micro"/>
|
||||||
<div class={styles.userName}>{userName(props.article.author)}</div>
|
<div class={styles.userName}>{userName(props.article.user)}</div>
|
||||||
<VerificationCheck user={props.article.author} />
|
<VerificationCheck user={props.article.user} />
|
||||||
<div class={styles.nip05}>{props.article.author.nip05 || ''}</div>
|
<div class={styles.nip05}>{props.article.user.nip05 || ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class={styles.time}>
|
<div class={styles.time}>
|
||||||
{shortDate(props.article.published)}
|
{shortDate(props.article.published)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={styles.body}>
|
<div class={styles.body}>
|
||||||
<div class={styles.text}>
|
<div class={styles.text}>
|
||||||
<div class={styles.content}>
|
<div class={styles.content}>
|
||||||
|
@ -60,12 +60,12 @@ const NoteContextMenu: Component<{
|
|||||||
|
|
||||||
|
|
||||||
const doMuteUser = () => {
|
const doMuteUser = () => {
|
||||||
account?.actions.addToMuteList(note()?.post.pubkey);
|
account?.actions.addToMuteList(note()?.pubkey);
|
||||||
props.onClose();
|
props.onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const doUnmuteUser = () => {
|
const doUnmuteUser = () => {
|
||||||
account?.actions.removeFromMuteList(note()?.post.pubkey);
|
account?.actions.removeFromMuteList(note()?.pubkey);
|
||||||
props.onClose();
|
props.onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,21 +77,21 @@ const NoteContextMenu: Component<{
|
|||||||
|
|
||||||
const copyNoteLink = () => {
|
const copyNoteLink = () => {
|
||||||
if (!props.data) return;
|
if (!props.data) return;
|
||||||
navigator.clipboard.writeText(`${window.location.origin}/e/${note().post.noteId}`);
|
navigator.clipboard.writeText(`${window.location.origin}/e/${note().noteId}`);
|
||||||
props.onClose()
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalLinkCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalLinkCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyNoteText = () => {
|
const copyNoteText = () => {
|
||||||
if (!props.data) return;
|
if (!props.data) return;
|
||||||
navigator.clipboard.writeText(`${note().post.content}`);
|
navigator.clipboard.writeText(`${note().content}`);
|
||||||
props.onClose()
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalTextCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalTextCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyNoteId = () => {
|
const copyNoteId = () => {
|
||||||
if (!props.data) return;
|
if (!props.data) return;
|
||||||
navigator.clipboard.writeText(`${note().post.noteId}`);
|
navigator.clipboard.writeText(`${note().noteId}`);
|
||||||
props.onClose()
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteIdCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteIdCoppied));
|
||||||
};
|
};
|
||||||
@ -128,7 +128,7 @@ const NoteContextMenu: Component<{
|
|||||||
const onClickOutside = (e: MouseEvent) => {
|
const onClickOutside = (e: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
!props.data ||
|
!props.data ||
|
||||||
!document?.getElementById(`note_context_${note().post.id}`)?.contains(e.target as Node)
|
!document?.getElementById(`note_context_${note().id}`)?.contains(e.target as Node)
|
||||||
) {
|
) {
|
||||||
props.onClose()
|
props.onClose()
|
||||||
}
|
}
|
||||||
@ -222,7 +222,7 @@ const NoteContextMenu: Component<{
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const noteContext = () => account?.publicKey !== note()?.post.pubkey ?
|
const noteContext = () => account?.publicKey !== note()?.pubkey ?
|
||||||
[ ...noteContextForEveryone, ...noteContextForOtherPeople()] :
|
[ ...noteContextForEveryone, ...noteContextForOtherPeople()] :
|
||||||
noteContextForEveryone;
|
noteContextForEveryone;
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ const NoteContextMenu: Component<{
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<PrimalMenu
|
<PrimalMenu
|
||||||
id={`note_context_${note()?.post.id}`}
|
id={`note_context_${note()?.id}`}
|
||||||
items={noteContext()}
|
items={noteContext()}
|
||||||
hidden={!props.open}
|
hidden={!props.open}
|
||||||
position="note_footer"
|
position="note_footer"
|
||||||
|
@ -181,7 +181,7 @@ const ArticleFooter: Component<{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canUserReceiveZaps(props.note.author)) {
|
if (!canUserReceiveZaps(props.note.user)) {
|
||||||
toast?.sendWarning(
|
toast?.sendWarning(
|
||||||
intl.formatMessage(t.zapUnavailable),
|
intl.formatMessage(t.zapUnavailable),
|
||||||
);
|
);
|
||||||
@ -206,7 +206,7 @@ const ArticleFooter: Component<{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.relays.length === 0 || !canUserReceiveZaps(props.note.author)) {
|
if (account.relays.length === 0 || !canUserReceiveZaps(props.note.user)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export type CustomZapInfo = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type NoteContextMenuInfo = {
|
export type NoteContextMenuInfo = {
|
||||||
note: PrimalNote,
|
note: PrimalNote | PrimalArticle,
|
||||||
position: DOMRect | undefined,
|
position: DOMRect | undefined,
|
||||||
openCustomZap?: () => void,
|
openCustomZap?: () => void,
|
||||||
openReactions?: () => void,
|
openReactions?: () => void,
|
||||||
@ -72,7 +72,7 @@ export type AppContextStore = {
|
|||||||
openCustomZapModal: (custonZapInfo: CustomZapInfo) => void,
|
openCustomZapModal: (custonZapInfo: CustomZapInfo) => void,
|
||||||
closeCustomZapModal: () => void,
|
closeCustomZapModal: () => void,
|
||||||
resetCustomZap: () => void,
|
resetCustomZap: () => void,
|
||||||
openContextMenu: (note: PrimalNote, position: DOMRect | undefined, openCustomZapModal: () => void, openReactionModal: () => void) => void,
|
openContextMenu: (note: PrimalNote | PrimalArticle, position: DOMRect | undefined, openCustomZapModal: () => void, openReactionModal: () => void) => void,
|
||||||
closeContextMenu: () => void,
|
closeContextMenu: () => void,
|
||||||
openLnbcModal: (lnbc: string, onPay: () => void) => void,
|
openLnbcModal: (lnbc: string, onPay: () => void) => void,
|
||||||
closeLnbcModal: () => void,
|
closeLnbcModal: () => void,
|
||||||
@ -155,7 +155,7 @@ export const AppProvider = (props: { children: JSXElement }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openContextMenu = (
|
const openContextMenu = (
|
||||||
note: PrimalNote,
|
note: PrimalNote | PrimalArticle,
|
||||||
position: DOMRect | undefined,
|
position: DOMRect | undefined,
|
||||||
openCustomZap: () => void,
|
openCustomZap: () => void,
|
||||||
openReactions: () => void,
|
openReactions: () => void,
|
||||||
|
@ -2,7 +2,7 @@ import { nip19 } from "nostr-tools";
|
|||||||
import { createContext, createEffect, onCleanup, useContext } from "solid-js";
|
import { createContext, createEffect, onCleanup, useContext } from "solid-js";
|
||||||
import { createStore, reconcile, unwrap } from "solid-js/store";
|
import { createStore, reconcile, unwrap } from "solid-js/store";
|
||||||
import { APP_ID } from "../App";
|
import { APP_ID } from "../App";
|
||||||
import { Kind } from "../constants";
|
import { Kind, minKnownProfiles } from "../constants";
|
||||||
import { getArticlesFeed, getEvents, getExploreFeed, getFeed, getFutureArticlesFeed, getFutureExploreFeed, getFutureFeed } from "../lib/feed";
|
import { getArticlesFeed, getEvents, getExploreFeed, getFeed, getFutureArticlesFeed, getFutureExploreFeed, getFutureFeed } from "../lib/feed";
|
||||||
import { fetchStoredFeed, saveStoredFeed } from "../lib/localStore";
|
import { fetchStoredFeed, saveStoredFeed } from "../lib/localStore";
|
||||||
import { setLinkPreviews } from "../lib/notes";
|
import { setLinkPreviews } from "../lib/notes";
|
||||||
@ -307,7 +307,8 @@ export const ReadsProvider = (props: { children: ContextChildren }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchNotes = (topic: string, subId: string, until = 0, includeReplies?: boolean) => {
|
const fetchNotes = (topic: string, subId: string, until = 0, includeReplies?: boolean) => {
|
||||||
const [scope, timeframe] = topic.split(';');
|
const t = account?.publicKey || '532d830dffe09c13e75e8b145c825718fc12b0003f61d61e9077721c7fff93cb';
|
||||||
|
const [scope, timeframe] = t.split(';');
|
||||||
|
|
||||||
updateStore('isFetching', true);
|
updateStore('isFetching', true);
|
||||||
updateStore('page', () => ({ messages: [], users: {}, postStats: {} }));
|
updateStore('page', () => ({ messages: [], users: {}, postStats: {} }));
|
||||||
@ -329,7 +330,7 @@ export const ReadsProvider = (props: { children: ContextChildren }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getArticlesFeed(account?.publicKey, topic, `reads_feed_${subId}`, until, 20);
|
getArticlesFeed(account?.publicKey, t, `reads_feed_${subId}`, until, 20);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearNotes = () => {
|
const clearNotes = () => {
|
||||||
|
@ -32,6 +32,7 @@ export const getFeed = (user_pubkey: string | undefined, pubkey: string | undef
|
|||||||
let payload = { limit, until: time, pubkey };
|
let payload = { limit, until: time, pubkey };
|
||||||
|
|
||||||
if (user_pubkey) {
|
if (user_pubkey) {
|
||||||
|
// @ts-ignore dynamic property
|
||||||
payload.user_pubkey = user_pubkey;
|
payload.user_pubkey = user_pubkey;
|
||||||
}
|
}
|
||||||
if (include_replies) {
|
if (include_replies) {
|
||||||
|
@ -19,8 +19,8 @@ export const zapNote = async (note: PrimalNote, sender: string | undefined, amou
|
|||||||
const sats = Math.round(amount * 1000);
|
const sats = Math.round(amount * 1000);
|
||||||
|
|
||||||
let payload = {
|
let payload = {
|
||||||
profile: note.post.pubkey,
|
profile: note.pubkey,
|
||||||
event: note.msg.id,
|
event: note.id,
|
||||||
amount: sats,
|
amount: sats,
|
||||||
relays: relays.map(r => r.url)
|
relays: relays.map(r => r.url)
|
||||||
};
|
};
|
||||||
@ -55,7 +55,7 @@ export const zapArticle = async (note: PrimalArticle, sender: string | undefined
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const callback = await getZapEndpoint(note.author);
|
const callback = await getZapEndpoint(note.user);
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -83,7 +83,14 @@ const Notifications: Component = () => {
|
|||||||
const [relatedNotes, setRelatedNotes] = createStore<NotificationStore>({
|
const [relatedNotes, setRelatedNotes] = createStore<NotificationStore>({
|
||||||
notes: [],
|
notes: [],
|
||||||
users: [],
|
users: [],
|
||||||
page: { messages: [], users: {}, postStats: {}, mentions: {}, noteActions: {} },
|
page: {
|
||||||
|
messages: [],
|
||||||
|
users: {},
|
||||||
|
postStats: {},
|
||||||
|
mentions: {},
|
||||||
|
noteActions: {},
|
||||||
|
topZaps: {},
|
||||||
|
},
|
||||||
reposts: {},
|
reposts: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -91,7 +98,15 @@ const Notifications: Component = () => {
|
|||||||
notes: [],
|
notes: [],
|
||||||
users: {},
|
users: {},
|
||||||
userStats: {},
|
userStats: {},
|
||||||
page: { messages: [], users: {}, postStats: {}, notifications: [], mentions: {}, noteActions: {} },
|
page: {
|
||||||
|
messages: [],
|
||||||
|
users: {},
|
||||||
|
postStats: {},
|
||||||
|
notifications: [],
|
||||||
|
mentions: {},
|
||||||
|
noteActions: {},
|
||||||
|
topZaps: {},
|
||||||
|
},
|
||||||
reposts: {},
|
reposts: {},
|
||||||
notifications: [],
|
notifications: [],
|
||||||
})
|
})
|
||||||
|
@ -94,7 +94,7 @@ const Home: Component = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newPostAuthors.length < 3) {
|
if (newPostAuthors.length < 3) {
|
||||||
const users = context?.future.notes.map(note => note.author) || [];
|
const users = context?.future.notes.map(note => note.user) || [];
|
||||||
|
|
||||||
const uniqueUsers = users.reduce<PrimalUser[]>((acc, user) => {
|
const uniqueUsers = users.reduce<PrimalUser[]>((acc, user) => {
|
||||||
const isDuplicate = acc.find(u => u && u.pubkey === user.pubkey);
|
const isDuplicate = acc.find(u => u && u.pubkey === user.pubkey);
|
||||||
|
@ -366,8 +366,10 @@ export const convertToNotes: ConvertToNotes = (page, topZaps) => {
|
|||||||
replyTo: replyTo && replyTo[1],
|
replyTo: replyTo && replyTo[1],
|
||||||
tags: msg.tags,
|
tags: msg.tags,
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
|
noteId: nip19.noteEncode(msg.id),
|
||||||
pubkey: msg.pubkey,
|
pubkey: msg.pubkey,
|
||||||
topZaps: [ ...tz ],
|
topZaps: [ ...tz ],
|
||||||
|
content: sanitize(msg.content),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -466,10 +468,11 @@ export const convertToArticles: ConvertToArticles = (page, topZaps) => {
|
|||||||
image: '',
|
image: '',
|
||||||
tags: [],
|
tags: [],
|
||||||
published: msg.created_at || 0,
|
published: msg.created_at || 0,
|
||||||
content: msg.content,
|
content: sanitize(msg.content),
|
||||||
author: convertToUser(user),
|
user: convertToUser(user),
|
||||||
topZaps: [...tz],
|
topZaps: [...tz],
|
||||||
naddr: nip19.naddrEncode({ identifier, pubkey, kind }),
|
naddr: nip19.naddrEncode({ identifier, pubkey, kind }),
|
||||||
|
noteId: nip19.naddrEncode({ identifier, pubkey, kind }),
|
||||||
msg,
|
msg,
|
||||||
mentionedNotes,
|
mentionedNotes,
|
||||||
mentionedUsers,
|
mentionedUsers,
|
||||||
|
5
src/types/primal.d.ts
vendored
5
src/types/primal.d.ts
vendored
@ -498,8 +498,10 @@ export type PrimalNote = {
|
|||||||
replyTo?: string,
|
replyTo?: string,
|
||||||
id: string,
|
id: string,
|
||||||
pubkey: string,
|
pubkey: string,
|
||||||
|
noteId: string,
|
||||||
tags: string[][],
|
tags: string[][],
|
||||||
topZaps: TopZap[],
|
topZaps: TopZap[],
|
||||||
|
content: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -510,7 +512,7 @@ export type PrimalArticle = {
|
|||||||
tags: string[],
|
tags: string[],
|
||||||
published: number,
|
published: number,
|
||||||
content: string,
|
content: string,
|
||||||
author: PrimalUser,
|
user: PrimalUser,
|
||||||
topZaps: TopZap[],
|
topZaps: TopZap[],
|
||||||
repost?: PrimalRepost,
|
repost?: PrimalRepost,
|
||||||
mentionedNotes?: Record<string, PrimalNote>,
|
mentionedNotes?: Record<string, PrimalNote>,
|
||||||
@ -519,6 +521,7 @@ export type PrimalArticle = {
|
|||||||
id: string,
|
id: string,
|
||||||
pubkey: string,
|
pubkey: string,
|
||||||
naddr: string,
|
naddr: string,
|
||||||
|
noteId: string,
|
||||||
msg: NostrNoteContent,
|
msg: NostrNoteContent,
|
||||||
wordCount: number,
|
wordCount: number,
|
||||||
noteActions: NoteActions,
|
noteActions: NoteActions,
|
||||||
|
Loading…
Reference in New Issue
Block a user