mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-10-01 17:31:13 +00:00
Unify context menu and fix mute/unmute and report actions
This commit is contained in:
parent
1fe6b2851b
commit
29461e4e96
@ -19,6 +19,7 @@ import Landing from '../../pages/Landing';
|
|||||||
import ReactionsModal from '../ReactionsModal/ReactionsModal';
|
import ReactionsModal from '../ReactionsModal/ReactionsModal';
|
||||||
import { useAppContext } from '../../contexts/AppContext';
|
import { useAppContext } from '../../contexts/AppContext';
|
||||||
import CustomZap from '../CustomZap/CustomZap';
|
import CustomZap from '../CustomZap/CustomZap';
|
||||||
|
import NoteContextMenu from '../Note/NoteContextMenu';
|
||||||
|
|
||||||
export const [isHome, setIsHome] = createSignal(false);
|
export const [isHome, setIsHome] = createSignal(false);
|
||||||
|
|
||||||
@ -183,6 +184,11 @@ const Layout: Component = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<NoteContextMenu
|
||||||
|
open={app?.showNoteContextMenu}
|
||||||
|
onClose={app?.actions.closeContextMenu}
|
||||||
|
data={app?.noteContextMenuInfo}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
|
@ -185,46 +185,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contextButton {
|
||||||
|
width: 16px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
position: relative;
|
position: absolute;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
.contextButton {
|
|
||||||
width: 16px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,11 +22,13 @@ 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 { getScreenCordinates } from '../../utils';
|
||||||
|
import { NoteContextMenuInfo } from '../../contexts/AppContext';
|
||||||
|
import ConfirmModal from '../ConfirmModal/ConfirmModal';
|
||||||
|
|
||||||
const NoteContextMenu: Component<{
|
const NoteContextMenu: Component<{
|
||||||
note: PrimalNote,
|
data: NoteContextMenuInfo,
|
||||||
openCustomZap?: () => void;
|
open: boolean,
|
||||||
openReactions?: () => void,
|
onClose: () => void,
|
||||||
id?: string,
|
id?: string,
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const account = useAccountContext();
|
const account = useAccountContext();
|
||||||
@ -37,62 +39,95 @@ const NoteContextMenu: Component<{
|
|||||||
const [confirmReportUser, setConfirmReportUser] = createSignal(false);
|
const [confirmReportUser, setConfirmReportUser] = createSignal(false);
|
||||||
const [confirmMuteUser, setConfirmMuteUser] = createSignal(false);
|
const [confirmMuteUser, setConfirmMuteUser] = createSignal(false);
|
||||||
|
|
||||||
const openContextMenu = (e: MouseEvent) => {
|
const [orientation, setOrientation] = createSignal<'down' | 'up'>('down')
|
||||||
e.preventDefault();
|
|
||||||
setContext(true);
|
const note = () => props.data?.note;
|
||||||
|
const position = () => {
|
||||||
|
return props.data?.position;
|
||||||
};
|
};
|
||||||
|
const openCustomZap = props.data?.openCustomZap || (() => {});
|
||||||
|
const openReactions = props.data?.openReactions || (() => {});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if(!context) return;
|
||||||
|
|
||||||
|
if (!props.open) {
|
||||||
|
context.setAttribute('style',`top: -1024px; left: -1034px;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const docRect = document.documentElement.getBoundingClientRect();
|
||||||
|
const pos = {
|
||||||
|
top: (Math.floor(position()?.top || 0) - docRect.top),
|
||||||
|
left: (Math.floor(position()?.left || 0)),
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setAttribute('style',`top: ${pos.top + 12}px; left: ${pos.left + 12}px;`);
|
||||||
|
|
||||||
|
const height = 440;
|
||||||
|
const orient = Math.floor(position()?.bottom || 0) + height < window.innerHeight ? 'down' : 'up';
|
||||||
|
|
||||||
|
setOrientation(() => orient);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const doMuteUser = () => {
|
const doMuteUser = () => {
|
||||||
account?.actions.addToMuteList(props.note.post.pubkey);
|
account?.actions.addToMuteList(note()?.post.pubkey);
|
||||||
|
props.onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const doUnmuteUser = () => {
|
const doUnmuteUser = () => {
|
||||||
account?.actions.removeFromMuteList(props.note.post.pubkey);
|
account?.actions.removeFromMuteList(note()?.post.pubkey);
|
||||||
|
props.onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const doReportUser = () => {
|
const doReportUser = () => {
|
||||||
reportUser(props.note.user.pubkey, `report_user_${APP_ID}`, props.note.user);
|
reportUser(note()?.user.pubkey, `report_user_${APP_ID}`, note()?.user);
|
||||||
setContext(false);
|
props.onClose();
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteAuthorReported, { name: userName(props.note.user)}));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteAuthorReported, { name: userName(note()?.user)}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyNoteLink = () => {
|
const copyNoteLink = () => {
|
||||||
navigator.clipboard.writeText(`${window.location.origin}/e/${props.note.post.noteId}`);
|
if (!props.data) return;
|
||||||
setContext(false);
|
navigator.clipboard.writeText(`${window.location.origin}/e/${note().post.noteId}`);
|
||||||
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalLinkCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalLinkCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyNoteText = () => {
|
const copyNoteText = () => {
|
||||||
navigator.clipboard.writeText(`${props.note.post.content}`);
|
if (!props.data) return;
|
||||||
setContext(false);
|
navigator.clipboard.writeText(`${note().post.content}`);
|
||||||
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalTextCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.notePrimalTextCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyNoteId = () => {
|
const copyNoteId = () => {
|
||||||
navigator.clipboard.writeText(`${props.note.post.noteId}`);
|
if (!props.data) return;
|
||||||
setContext(false);
|
navigator.clipboard.writeText(`${note().post.noteId}`);
|
||||||
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteIdCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteIdCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyRawData = () => {
|
const copyRawData = () => {
|
||||||
navigator.clipboard.writeText(`${JSON.stringify(props.note.msg)}`);
|
if (!props.data) return;
|
||||||
setContext(false);
|
navigator.clipboard.writeText(`${JSON.stringify(note().msg)}`);
|
||||||
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteRawDataCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteRawDataCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyUserNpub = () => {
|
const copyUserNpub = () => {
|
||||||
navigator.clipboard.writeText(`${props.note.user.npub}`);
|
if (!props.data) return;
|
||||||
setContext(false);
|
navigator.clipboard.writeText(`${note().user.npub}`);
|
||||||
|
props.onClose()
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteAuthorNpubCoppied));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteAuthorNpubCoppied));
|
||||||
};
|
};
|
||||||
|
|
||||||
const broadcastNote = async () => {
|
const broadcastNote = async () => {
|
||||||
if (!account) {
|
if (!account || !props.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { success } = await broadcastEvent(props.note.msg as NostrRelaySignedEvent, account?.relays, account?.relaySettings);
|
const { success } = await broadcastEvent(note().msg as NostrRelaySignedEvent, account?.relays, account?.relaySettings);
|
||||||
setContext(false);
|
props.onClose()
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteBroadcastSuccess));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteBroadcastSuccess));
|
||||||
@ -103,14 +138,15 @@ const NoteContextMenu: Component<{
|
|||||||
|
|
||||||
const onClickOutside = (e: MouseEvent) => {
|
const onClickOutside = (e: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
!document?.getElementById(`note_context_${props.note.post.id}`)?.contains(e.target as Node)
|
!props.data ||
|
||||||
|
!document?.getElementById(`note_context_${note().post.id}`)?.contains(e.target as Node)
|
||||||
) {
|
) {
|
||||||
setContext(false);
|
props.onClose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (showContext()) {
|
if (props.open) {
|
||||||
document.addEventListener('click', onClickOutside);
|
document.addEventListener('click', onClickOutside);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -119,24 +155,24 @@ const NoteContextMenu: Component<{
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isVerifiedByPrimal = () => {
|
const isVerifiedByPrimal = () => {
|
||||||
return !!props.note.user.nip05 &&
|
return props.data && !!note().user.nip05 &&
|
||||||
props.note.user.nip05.endsWith('primal.net');
|
note().user.nip05.endsWith('primal.net');
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteContextForEveryone: MenuItem[] = [
|
const noteContextForEveryone: MenuItem[] = [
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(tActions.noteContext.reactions),
|
label: intl.formatMessage(tActions.noteContext.reactions),
|
||||||
action: () => {
|
action: () => {
|
||||||
props.openReactions && props.openReactions();
|
openReactions();
|
||||||
setContext(false);
|
props.onClose()
|
||||||
},
|
},
|
||||||
icon: 'heart',
|
icon: 'heart',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(tActions.noteContext.zap),
|
label: intl.formatMessage(tActions.noteContext.zap),
|
||||||
action: () => {
|
action: () => {
|
||||||
props.openCustomZap && props.openCustomZap();
|
openCustomZap();
|
||||||
setContext(false);
|
props.onClose()
|
||||||
},
|
},
|
||||||
icon: 'feed_zap',
|
icon: 'feed_zap',
|
||||||
},
|
},
|
||||||
@ -172,53 +208,65 @@ const NoteContextMenu: Component<{
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const noteContextForOtherPeople: MenuItem[] = [
|
const noteContextForOtherPeople: () => MenuItem[] = () => {
|
||||||
{
|
const isMuted = account?.muted.includes(note()?.user.pubkey);
|
||||||
label: intl.formatMessage(tActions.noteContext.muteAuthor),
|
|
||||||
action: () => {
|
|
||||||
setConfirmMuteUser(true);
|
|
||||||
setContext(false);
|
|
||||||
},
|
|
||||||
icon: 'mute_user',
|
|
||||||
warning: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: intl.formatMessage(tActions.noteContext.reportAuthor),
|
|
||||||
action: () => {
|
|
||||||
setConfirmReportUser(true);
|
|
||||||
setContext(false);
|
|
||||||
},
|
|
||||||
icon: 'report',
|
|
||||||
warning: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const noteContext = account?.publicKey !== props.note.post.pubkey ?
|
return [
|
||||||
[ ...noteContextForEveryone, ...noteContextForOtherPeople] :
|
{
|
||||||
|
label: isMuted ? intl.formatMessage(tActions.noteContext.unmuteAuthor) : intl.formatMessage(tActions.noteContext.muteAuthor),
|
||||||
|
action: () => {
|
||||||
|
isMuted ? doUnmuteUser() : setConfirmMuteUser(true);
|
||||||
|
props.onClose()
|
||||||
|
},
|
||||||
|
icon: 'mute_user',
|
||||||
|
warning: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: intl.formatMessage(tActions.noteContext.reportAuthor),
|
||||||
|
action: () => {
|
||||||
|
setConfirmReportUser(true);
|
||||||
|
props.onClose()
|
||||||
|
},
|
||||||
|
icon: 'report',
|
||||||
|
warning: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const noteContext = () => account?.publicKey !== note()?.post.pubkey ?
|
||||||
|
[ ...noteContextForEveryone, ...noteContextForOtherPeople()] :
|
||||||
noteContextForEveryone;
|
noteContextForEveryone;
|
||||||
|
|
||||||
let context: HTMLDivElement | undefined;
|
let context: HTMLDivElement | undefined;
|
||||||
|
|
||||||
const determineOrient = () => {
|
|
||||||
const coor = getScreenCordinates(context);
|
|
||||||
const height = 440;
|
|
||||||
return (coor.y || 0) + height < window.innerHeight + window.scrollY ? 'down' : 'up';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={styles.contextMenu} ref={context}>
|
<div class={styles.contextMenu} ref={context}>
|
||||||
<button
|
<ConfirmModal
|
||||||
class={styles.contextButton}
|
open={confirmReportUser()}
|
||||||
onClick={openContextMenu}
|
description={intl.formatMessage(tActions.reportUserConfirm, { name: authorName(note()?.user) })}
|
||||||
>
|
onConfirm={() => {
|
||||||
<div class={styles.contextIcon} ></div>
|
doReportUser();
|
||||||
</button>
|
setConfirmReportUser(false);
|
||||||
|
}}
|
||||||
|
onAbort={() => setConfirmReportUser(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
open={confirmMuteUser()}
|
||||||
|
description={intl.formatMessage(tActions.muteUserConfirm, { name: authorName(note()?.user) })}
|
||||||
|
onConfirm={() => {
|
||||||
|
doMuteUser();
|
||||||
|
setConfirmMuteUser(false);
|
||||||
|
}}
|
||||||
|
onAbort={() => setConfirmMuteUser(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
<PrimalMenu
|
<PrimalMenu
|
||||||
id={`note_context_${props.note.post.id}`}
|
id={`note_context_${note()?.post.id}`}
|
||||||
items={noteContext}
|
items={noteContext()}
|
||||||
hidden={!showContext()}
|
hidden={!props.open}
|
||||||
position="note_footer"
|
position="note_footer"
|
||||||
orientation={determineOrient()}
|
orientation={orientation()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,38 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contextButton {
|
||||||
|
width: 48px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 128px 128px 128px 128px 16px;
|
grid-template-columns: 128px 128px 128px 128px 16px;
|
||||||
|
@ -46,6 +46,7 @@ const NoteFooter: Component<{ note: PrimalNote, wide?: boolean, id?: string }> =
|
|||||||
const [isRepostMenuVisible, setIsRepostMenuVisible] = createSignal(false);
|
const [isRepostMenuVisible, setIsRepostMenuVisible] = createSignal(false);
|
||||||
|
|
||||||
let footerDiv: HTMLDivElement | undefined;
|
let footerDiv: HTMLDivElement | undefined;
|
||||||
|
let noteContextMenu: HTMLDivElement | undefined;
|
||||||
|
|
||||||
const repostMenuItems: MenuItem[] = [
|
const repostMenuItems: MenuItem[] = [
|
||||||
{
|
{
|
||||||
@ -374,7 +375,7 @@ const NoteFooter: Component<{ note: PrimalNote, wide?: boolean, id?: string }> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={props.id} class={`${styles.footer} ${props.wide ? styles.wide : ''}`} ref={footerDiv}>
|
<div id={props.id} class={`${styles.footer} ${props.wide ? styles.wide : ''}`} ref={footerDiv} onClick={(e) => {e.preventDefault();}}>
|
||||||
|
|
||||||
<Show when={showZapAnim()}>
|
<Show when={showZapAnim()}>
|
||||||
<ZapAnimation
|
<ZapAnimation
|
||||||
@ -442,21 +443,32 @@ const NoteFooter: Component<{ note: PrimalNote, wide?: boolean, id?: string }> =
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class={styles.context}>
|
<div ref={noteContextMenu} class={styles.context}>
|
||||||
<NoteContextMenu
|
<button
|
||||||
note={props.note}
|
class={styles.contextButton}
|
||||||
openCustomZap={() => {
|
onClick={(e) => {
|
||||||
app?.actions.openCustomZapModal(customZapInfo);
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
app?.actions.openContextMenu(
|
||||||
|
props.note,
|
||||||
|
noteContextMenu?.getBoundingClientRect(),
|
||||||
|
() => {
|
||||||
|
app?.actions.openCustomZapModal(customZapInfo);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
app?.actions.openReactionModal(props.note.post.id, {
|
||||||
|
likes: likes(),
|
||||||
|
zaps: zapCount(),
|
||||||
|
reposts: reposts(),
|
||||||
|
quotes: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
openReactions={() => {
|
>
|
||||||
app?.actions.openReactionModal(props.note.post.id, {
|
<div class={styles.contextIcon} ></div>
|
||||||
likes: likes(),
|
</button>
|
||||||
zaps: zapCount(),
|
|
||||||
reposts: reposts(),
|
|
||||||
quotes: 0,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -32,6 +32,8 @@ const ProfileTabs: Component<{
|
|||||||
const profile = useProfileContext();
|
const profile = useProfileContext();
|
||||||
const account = useAccountContext();
|
const account = useAccountContext();
|
||||||
|
|
||||||
|
const [currentTab, setCurrentTab] = createSignal<string>('notes');
|
||||||
|
|
||||||
const addToAllowlist = async () => {
|
const addToAllowlist = async () => {
|
||||||
const pk = profile?.profileKey;
|
const pk = profile?.profileKey;
|
||||||
if (pk) {
|
if (pk) {
|
||||||
@ -58,7 +60,10 @@ const ProfileTabs: Component<{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
account.actions.removeFromMuteList(pk);
|
account.actions.removeFromMuteList(pk, () => {
|
||||||
|
props.setProfile && props.setProfile(pk);
|
||||||
|
onChangeValue(currentTab());
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onContactAction = (remove: boolean, pubkey: string) => {
|
const onContactAction = (remove: boolean, pubkey: string) => {
|
||||||
@ -125,6 +130,8 @@ const ProfileTabs: Component<{
|
|||||||
const onChangeValue = (value: string) => {
|
const onChangeValue = (value: string) => {
|
||||||
if (!profile) return;
|
if (!profile) return;
|
||||||
|
|
||||||
|
setCurrentTab(() => value);
|
||||||
|
|
||||||
switch(value) {
|
switch(value) {
|
||||||
case 'notes':
|
case 'notes':
|
||||||
profile.notes.length === 0 && profile.actions.fetchNotes(profile.profileKey);
|
profile.notes.length === 0 && profile.actions.fetchNotes(profile.profileKey);
|
||||||
|
@ -25,6 +25,13 @@ export type CustomZapInfo = {
|
|||||||
onCancel: (zapOption: ZapOption) => void,
|
onCancel: (zapOption: ZapOption) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NoteContextMenuInfo = {
|
||||||
|
note: PrimalNote,
|
||||||
|
position: DOMRect | undefined,
|
||||||
|
openCustomZap?: () => void,
|
||||||
|
openReactions?: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
export type AppContextStore = {
|
export type AppContextStore = {
|
||||||
isInactive: boolean,
|
isInactive: boolean,
|
||||||
appState: 'sleep' | 'waking' | 'woke',
|
appState: 'sleep' | 'waking' | 'woke',
|
||||||
@ -32,11 +39,15 @@ export type AppContextStore = {
|
|||||||
reactionStats: ReactionStats,
|
reactionStats: ReactionStats,
|
||||||
showCustomZapModal: boolean,
|
showCustomZapModal: boolean,
|
||||||
customZap: CustomZapInfo | undefined,
|
customZap: CustomZapInfo | undefined,
|
||||||
|
showNoteContextMenu: boolean,
|
||||||
|
noteContextMenuInfo: NoteContextMenuInfo | undefined,
|
||||||
actions: {
|
actions: {
|
||||||
openReactionModal: (noteId: string, stats: ReactionStats) => void,
|
openReactionModal: (noteId: string, stats: ReactionStats) => void,
|
||||||
closeReactionModal: () => void,
|
closeReactionModal: () => void,
|
||||||
openCustomZapModal: (custonZapInfo: CustomZapInfo) => void,
|
openCustomZapModal: (custonZapInfo: CustomZapInfo) => void,
|
||||||
closeCustomZapModal: () => void,
|
closeCustomZapModal: () => void,
|
||||||
|
openContextMenu: (note: PrimalNote, position: DOMRect | undefined, openCustomZapModal: () => void, openReactionModal: () => void) => void,
|
||||||
|
closeContextMenu: () => void,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +63,8 @@ const initialData: Omit<AppContextStore, 'actions'> = {
|
|||||||
},
|
},
|
||||||
showCustomZapModal: false,
|
showCustomZapModal: false,
|
||||||
customZap: undefined,
|
customZap: undefined,
|
||||||
|
showNoteContextMenu: false,
|
||||||
|
noteContextMenuInfo: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AppContext = createContext<AppContextStore>();
|
export const AppContext = createContext<AppContextStore>();
|
||||||
@ -96,6 +109,20 @@ export const AppProvider = (props: { children: JSXElement }) => {
|
|||||||
updateStore('showCustomZapModal', () => false);
|
updateStore('showCustomZapModal', () => false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openContextMenu = (note: PrimalNote, position: DOMRect | undefined, openCustomZapModal: () => void, openReactionModal: () => void) => {
|
||||||
|
updateStore('noteContextMenuInfo', reconcile({
|
||||||
|
note,
|
||||||
|
position,
|
||||||
|
openCustomZapModal,
|
||||||
|
openReactionModal,
|
||||||
|
}))
|
||||||
|
updateStore('showNoteContextMenu', () => true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeContextMenu = () => {
|
||||||
|
updateStore('showNoteContextMenu', () => false);
|
||||||
|
};
|
||||||
|
|
||||||
// EFFECTS --------------------------------------
|
// EFFECTS --------------------------------------
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@ -138,6 +165,8 @@ export const AppProvider = (props: { children: JSXElement }) => {
|
|||||||
closeReactionModal,
|
closeReactionModal,
|
||||||
openCustomZapModal,
|
openCustomZapModal,
|
||||||
closeCustomZapModal,
|
closeCustomZapModal,
|
||||||
|
openContextMenu,
|
||||||
|
closeContextMenu,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -128,6 +128,7 @@ export type ProfileContextStore = {
|
|||||||
fetchFollowerList: (pubkey: string | undefined) => void,
|
fetchFollowerList: (pubkey: string | undefined) => void,
|
||||||
fetchRelayList: (pubkey: string | undefined) => void,
|
fetchRelayList: (pubkey: string | undefined) => void,
|
||||||
clearContacts: () => void,
|
clearContacts: () => void,
|
||||||
|
clearFilterReason: () => void,
|
||||||
removeContact: (pubkey: string) => void,
|
removeContact: (pubkey: string) => void,
|
||||||
addContact: (pubkey: string, source: PrimalUser[]) => void,
|
addContact: (pubkey: string, source: PrimalUser[]) => void,
|
||||||
fetchZapList: (pubkey: string | undefined) => void,
|
fetchZapList: (pubkey: string | undefined) => void,
|
||||||
@ -514,6 +515,10 @@ export const ProfileProvider = (props: { children: ContextChildren }) => {
|
|||||||
updateStore('profileStats', () => ({}));
|
updateStore('profileStats', () => ({}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearFilterReason = () => {
|
||||||
|
updateStore('filterReason', () => null);
|
||||||
|
};
|
||||||
|
|
||||||
const fetchNextPage = () => {
|
const fetchNextPage = () => {
|
||||||
const lastNote = store.notes[store.notes.length - 1];
|
const lastNote = store.notes[store.notes.length - 1];
|
||||||
|
|
||||||
@ -1018,6 +1023,8 @@ export const ProfileProvider = (props: { children: ContextChildren }) => {
|
|||||||
|
|
||||||
if (reason?.action === 'block') {
|
if (reason?.action === 'block') {
|
||||||
updateStore('filterReason', () => ({ ...reason }));
|
updateStore('filterReason', () => ({ ...reason }));
|
||||||
|
} else {
|
||||||
|
updateStore('filterReason', () => null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1229,6 +1236,7 @@ export const ProfileProvider = (props: { children: ContextChildren }) => {
|
|||||||
fetchNextZapsPage,
|
fetchNextZapsPage,
|
||||||
clearZaps,
|
clearZaps,
|
||||||
resetProfile,
|
resetProfile,
|
||||||
|
clearFilterReason,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -121,6 +121,7 @@ const Profile: Component = () => {
|
|||||||
profile?.actions.clearReplies();
|
profile?.actions.clearReplies();
|
||||||
profile?.actions.clearContacts();
|
profile?.actions.clearContacts();
|
||||||
profile?.actions.clearZaps();
|
profile?.actions.clearZaps();
|
||||||
|
profile?.actions.clearFilterReason();
|
||||||
}
|
}
|
||||||
|
|
||||||
let keyIsDone = false
|
let keyIsDone = false
|
||||||
@ -416,7 +417,6 @@ const Profile: Component = () => {
|
|||||||
toaster?.sendSuccess(intl.formatMessage(tToast.noteAuthorReported, { name: userName(profile?.userProfile)}));
|
toaster?.sendSuccess(intl.formatMessage(tToast.noteAuthorReported, { name: userName(profile?.userProfile)}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const addToAllowlist = async () => {
|
const addToAllowlist = async () => {
|
||||||
const pk = getHex();
|
const pk = getHex();
|
||||||
if (pk) {
|
if (pk) {
|
||||||
@ -679,7 +679,7 @@ const Profile: Component = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ProfileTabs />
|
<ProfileTabs setProfile={setProfile} />
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
open={confirmReportUser()}
|
open={confirmReportUser()}
|
||||||
|
@ -405,6 +405,11 @@ export const actions = {
|
|||||||
defaultMessage: 'Mute user',
|
defaultMessage: 'Mute user',
|
||||||
description: 'Label for muting user from context menu',
|
description: 'Label for muting user from context menu',
|
||||||
},
|
},
|
||||||
|
unmuteAuthor: {
|
||||||
|
id: 'actions.noteContext.unmuteAuthor',
|
||||||
|
defaultMessage: 'Unmute user',
|
||||||
|
description: 'Label for unmuting user from context menu',
|
||||||
|
},
|
||||||
reportAuthor: {
|
reportAuthor: {
|
||||||
id: 'actions.noteContext.reportAuthor',
|
id: 'actions.noteContext.reportAuthor',
|
||||||
defaultMessage: 'Report user',
|
defaultMessage: 'Report user',
|
||||||
|
Loading…
Reference in New Issue
Block a user