Add note context menu
This commit is contained in:
parent
34762d7039
commit
e4b317f634
@ -10,6 +10,7 @@
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@reduxjs/toolkit": "^1.9.1",
|
||||
"@szhsin/react-menu": "^3.3.1",
|
||||
"@types/jest": "^29.2.5",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/react": "^18.0.26",
|
||||
|
@ -1,43 +1,55 @@
|
||||
.note.thread {
|
||||
border-bottom: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.note > .header .reply {
|
||||
font-size: var(--font-size-tiny);
|
||||
color: var(--font-secondary-color);
|
||||
.note>.header .reply {
|
||||
font-size: var(--font-size-tiny);
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
|
||||
.note > .header > .info {
|
||||
font-size: var(--font-size);
|
||||
white-space: nowrap;
|
||||
color: var(--font-secondary-color);
|
||||
.note>.header>.info {
|
||||
font-size: var(--font-size);
|
||||
white-space: nowrap;
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
|
||||
.note > .body {
|
||||
margin-top: 12px;
|
||||
padding-left: 56px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
.note>.body {
|
||||
margin-top: 12px;
|
||||
padding-left: 56px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.note > .footer {
|
||||
padding-left: 46px;
|
||||
.note>.footer {
|
||||
padding-left: 46px;
|
||||
}
|
||||
|
||||
.note > .header img:hover, .note > .header .name > .reply:hover, .note .body:hover {
|
||||
cursor: pointer;
|
||||
.note>.footer .ctx-menu {
|
||||
background-color: var(--note-bg);
|
||||
color: var(--font-secondary-color);
|
||||
border: 1px solid var(--font-secondary-color);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.note > .note-creator {
|
||||
.note>.header img:hover, .note>.header .name>.reply:hover, .note .body:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.note>.note-creator {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
@media (min-width: 720px) {
|
||||
.note > .footer { margin-top: 24px; }
|
||||
.note > .note-creator { margin-top: 24px; }
|
||||
.note>.footer {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.note>.note-creator {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -55,8 +67,8 @@
|
||||
}
|
||||
|
||||
.indented {
|
||||
border-left: 3px solid var(--gray-tertiary);
|
||||
padding-left: 2px;
|
||||
border-left: 3px solid var(--gray-tertiary);
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.note:last-child {
|
||||
@ -69,16 +81,16 @@
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.indented > .indented .note:last-child {
|
||||
.indented>.indented .note:last-child {
|
||||
border-bottom-right-radius: 0px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.indented .active {
|
||||
background-color: var(--gray-tertiary);
|
||||
margin-left: -5px;
|
||||
border-left: 3px solid var(--highlight);
|
||||
border-radius: 0;
|
||||
background-color: var(--gray-tertiary);
|
||||
margin-left: -5px;
|
||||
border-left: 3px solid var(--highlight);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.reaction-pill {
|
||||
@ -95,7 +107,7 @@
|
||||
}
|
||||
|
||||
.reaction-pill.reacted {
|
||||
color: var(--highlight);
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
.reaction-pill:hover {
|
||||
@ -107,33 +119,36 @@
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.note.active > .header .reply {
|
||||
.note.active>.header .reply {
|
||||
color: var(--font-tertiary-color);
|
||||
}
|
||||
|
||||
.note.active > .header > .info {
|
||||
.note.active>.header>.info {
|
||||
color: var(--font-tertiary-color);
|
||||
}
|
||||
|
||||
.note.active > .footer > .reaction-pill {
|
||||
.note.active>.footer>.reaction-pill {
|
||||
color: var(--font-tertiary-color);
|
||||
}
|
||||
|
||||
.note.active > .footer > .reaction-pill.reacted {
|
||||
.note.active>.footer>.reaction-pill.reacted {
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.indented .active {
|
||||
background-color: var(--gray-secondary);
|
||||
background-color: var(--gray-secondary);
|
||||
}
|
||||
.note.active > .header .reply {
|
||||
|
||||
.note.active>.header .reply {
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
.note.active > .header > .info {
|
||||
|
||||
.note.active>.header>.info {
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
.note.active > .footer > .reaction-pill {
|
||||
|
||||
.note.active>.footer>.reaction-pill {
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { faHeart, faReply, faThumbsDown, faTrash, faBolt, faRepeat } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faHeart, faReply, faThumbsDown, faTrash, faBolt, faRepeat, faEllipsisVertical, faShareNodes } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Menu, MenuItem } from '@szhsin/react-menu';
|
||||
|
||||
import { formatShort } from "Number";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import { getReactions, normalizeReaction, Reaction } from "Util";
|
||||
import { getReactions, hexToBech32, normalizeReaction, Reaction } from "Util";
|
||||
import { NoteCreator } from "Element/NoteCreator";
|
||||
import LNURLTip from "Element/LNURLTip";
|
||||
import useProfile from "Feed/ProfileFeed";
|
||||
@ -79,7 +80,7 @@ export default function NoteFooter(props: NoteFooterProps) {
|
||||
if (service) {
|
||||
return (
|
||||
<>
|
||||
<div className="reaction-pill" onClick={(e) => setTip(true)}>
|
||||
<div className="reaction-pill" onClick={() => setTip(true)}>
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faBolt} />
|
||||
</div>
|
||||
@ -111,7 +112,7 @@ export default function NoteFooter(props: NoteFooterProps) {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className={`reaction-pill ${hasReacted('+') ? 'reacted' : ''} `} onClick={(e) => react("+")}>
|
||||
<div className={`reaction-pill ${hasReacted('+') ? 'reacted' : ''} `} onClick={() => react("+")}>
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faHeart} />
|
||||
</div>
|
||||
@ -119,19 +120,57 @@ export default function NoteFooter(props: NoteFooterProps) {
|
||||
{formatShort(groupReactions[Reaction.Positive])}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`reaction-pill ${hasReacted('-') ? 'reacted' : ''}`} onClick={(e) => react("-")}>
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faThumbsDown} />
|
||||
</div>
|
||||
<div className="reaction-pill-number">
|
||||
{formatShort(groupReactions[Reaction.Negative])}
|
||||
</div>
|
||||
</div>
|
||||
{repostIcon()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
async function share() {
|
||||
const url = `${window.location.protocol}//${window.location.host}/e/${hexToBech32("npub", ev.Id)}`;
|
||||
if ("share" in window.navigator) {
|
||||
await window.navigator.share({
|
||||
title: "Snort",
|
||||
url: url
|
||||
});
|
||||
} else {
|
||||
await navigator.clipboard.writeText(url);
|
||||
}
|
||||
}
|
||||
|
||||
function menuItems() {
|
||||
return (
|
||||
<>
|
||||
<MenuItem onClick={() => react("-")}>
|
||||
<div className={`reaction-pill ${hasReacted('-') ? 'reacted' : ''}`}>
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faThumbsDown} />
|
||||
</div>
|
||||
<div className="reaction-pill-number">
|
||||
{formatShort(groupReactions[Reaction.Negative])}
|
||||
</div>
|
||||
</div>
|
||||
Dislike
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => share()}>
|
||||
<div className="reaction-pill mr-auto">
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faShareNodes} />
|
||||
</div>
|
||||
</div>
|
||||
Share
|
||||
</MenuItem>
|
||||
|
||||
{isMine && (
|
||||
<MenuItem onClick={() => deleteEvent()}>
|
||||
<div className="reaction-pill trash-icon">
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</div>
|
||||
Delete
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="footer">
|
||||
@ -140,15 +179,16 @@ export default function NoteFooter(props: NoteFooterProps) {
|
||||
<FontAwesomeIcon icon={faReply} />
|
||||
</div>
|
||||
</div>
|
||||
<Menu menuButton={<div className="reaction-pill">
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faEllipsisVertical} />
|
||||
</div>
|
||||
</div>} menuClassName="ctx-menu">
|
||||
{menuItems()}
|
||||
</Menu>
|
||||
|
||||
{reactionIcons()}
|
||||
{tipButton()}
|
||||
{isMine && (
|
||||
<div className="reaction-pill trash-icon">
|
||||
<div className="reaction-pill-icon">
|
||||
<FontAwesomeIcon icon={faTrash} onClick={(e) => deleteEvent()} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<NoteCreator
|
||||
autoFocus={true}
|
||||
|
@ -320,6 +320,10 @@ body.scroll-lock {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mr-auto {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import './index.css';
|
||||
import '@szhsin/react-menu/dist/index.css';
|
||||
|
||||
import { StrictMode } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
27
yarn.lock
27
yarn.lock
@ -1804,6 +1804,14 @@
|
||||
"@svgr/plugin-svgo" "^5.5.0"
|
||||
loader-utils "^2.0.0"
|
||||
|
||||
"@szhsin/react-menu@^3.3.1":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@szhsin/react-menu/-/react-menu-3.3.1.tgz#c974c887bfcf94aca4c4cc07688a3c20d4e952bd"
|
||||
integrity sha512-e8vK+N1YWwTdYXElvRRf5GIImtcDecqTCzpAa0DkGAknKwfQwtQtUnBn+DECodwsWi5H5ONKTU+kn0qJ70hEYQ==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
react-transition-state "^1.1.5"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||
@ -6035,6 +6043,11 @@ js-sha3@0.8.0:
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
js-stylesheet@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/js-stylesheet/-/js-stylesheet-0.0.1.tgz#12cc1451220e454184b46de3b098c0d154762c38"
|
||||
integrity sha512-jSPbDIaHlK8IFXEbE6MZkeAQshRHHxxOcQ+LCZejm3KpW+POpi2TE3GXWoFsOOG2+AhCTWHjD7AXRFt3FDm8Zw==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -7790,7 +7803,7 @@ prompts@^2.0.1, prompts@^2.4.2:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.0.0, prop-types@^15.8.1:
|
||||
prop-types@^15.0.0, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
@ -7986,6 +7999,13 @@ react-markdown@^8.0.4:
|
||||
unist-util-visit "^4.0.0"
|
||||
vfile "^5.0.0"
|
||||
|
||||
react-menu@^0.0.10:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/react-menu/-/react-menu-0.0.10.tgz#c2b338cdd88e4c436325969239089e0752a142bc"
|
||||
integrity sha512-SGl/OZljPUB1ITWBG2wt1p7hyE3Y449O9FUezxkKzu+JM5HWkPjrU/JRmG4ZguyegLAnZx3qyjhdFYqB44sqJw==
|
||||
dependencies:
|
||||
js-stylesheet "0.0.1"
|
||||
|
||||
react-query@^3.39.2:
|
||||
version "3.39.2"
|
||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.2.tgz#9224140f0296f01e9664b78ed6e4f69a0cc9216f"
|
||||
@ -8091,6 +8111,11 @@ react-textarea-autosize@^8.4.0:
|
||||
use-composed-ref "^1.3.0"
|
||||
use-latest "^1.2.1"
|
||||
|
||||
react-transition-state@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-state/-/react-transition-state-1.1.5.tgz#22accee21d0011b1d0245be24b6262ae67f494c3"
|
||||
integrity sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==
|
||||
|
||||
react-twitter-embed@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-twitter-embed/-/react-twitter-embed-4.0.4.tgz#4a6b8354acc266876ff1110b9f648518ea20db6d"
|
||||
|
Loading…
x
Reference in New Issue
Block a user