highlight hashtags #29
@ -62,3 +62,8 @@ export const InvoiceRegex = /(lnbc\w+)/i;
|
|||||||
* YouTube URL regex
|
* YouTube URL regex
|
||||||
*/
|
*/
|
||||||
export const YoutubeUrlRegex = /^(?:https?:\/\/)?(?:www|m\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/
|
export const YoutubeUrlRegex = /^(?:https?:\/\/)?(?:www|m\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashtag regex
|
||||||
|
*/
|
||||||
|
export const HashtagRegex = /(#[a-z\d-]+)/ig
|
||||||
|
18
src/Text.js
18
src/Text.js
@ -1,9 +1,10 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import Invoice from "./element/Invoice";
|
import Invoice from "./element/Invoice";
|
||||||
import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex } from "./Const";
|
import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex, HashtagRegex } from "./Const";
|
||||||
import { eventLink, hexToBech32, profileLink } from "./Util";
|
import { eventLink, hexToBech32, profileLink } from "./Util";
|
||||||
import LazyImage from "./element/LazyImage";
|
import LazyImage from "./element/LazyImage";
|
||||||
|
import Hashtag from "./element/Hashtag";
|
||||||
|
|
||||||
function transformHttpLink(a) {
|
function transformHttpLink(a) {
|
||||||
try {
|
try {
|
||||||
@ -111,3 +112,18 @@ export function extractInvoices(fragments) {
|
|||||||
return f;
|
return f;
|
||||||
}).flat();
|
}).flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function extractHashtags(fragments) {
|
||||||
|
return fragments.map(f => {
|
||||||
|
if (typeof f === "string") {
|
||||||
|
return f.split(HashtagRegex).map(i => {
|
||||||
|
if (i.toLowerCase().startsWith("#")) {
|
||||||
|
return <Hashtag>{i}</Hashtag>
|
||||||
|
} else {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}).flat();
|
||||||
|
}
|
||||||
|
3
src/element/Hashtag.css
Normal file
3
src/element/Hashtag.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.hashtag {
|
||||||
|
color: var(--highlight);
|
||||||
|
}
|
11
src/element/Hashtag.js
Normal file
11
src/element/Hashtag.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import './Hashtag.css'
|
||||||
|
|
||||||
|
const Hashtag = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<span className="hashtag">
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Hashtag
|
@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
|
|
||||||
import Event from "../nostr/Event";
|
import Event from "../nostr/Event";
|
||||||
import ProfileImage from "./ProfileImage";
|
import ProfileImage from "./ProfileImage";
|
||||||
import { extractLinks, extractMentions, extractInvoices } from "../Text";
|
import { extractLinks, extractMentions, extractInvoices, extractHashtags } from "../Text";
|
||||||
import { eventLink, hexToBech32 } from "../Util";
|
import { eventLink, hexToBech32 } from "../Util";
|
||||||
import NoteFooter from "./NoteFooter";
|
import NoteFooter from "./NoteFooter";
|
||||||
import NoteTime from "./NoteTime";
|
import NoteTime from "./NoteTime";
|
||||||
@ -32,6 +32,7 @@ export default function Note(props) {
|
|||||||
let fragments = extractLinks([body]);
|
let fragments = extractLinks([body]);
|
||||||
fragments = extractMentions(fragments, ev.Tags, users);
|
fragments = extractMentions(fragments, ev.Tags, users);
|
||||||
fragments = extractInvoices(fragments);
|
fragments = extractInvoices(fragments);
|
||||||
|
fragments = extractHashtags(fragments);
|
||||||
if (deletion?.length > 0) {
|
if (deletion?.length > 0) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -272,7 +272,7 @@ body.scroll-lock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root-tabs {
|
.root-tabs {
|
||||||
padding: 0 2px;
|
padding: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import useProfile from "../feed/ProfileFeed";
|
|||||||
import FollowButton from "../element/FollowButton";
|
import FollowButton from "../element/FollowButton";
|
||||||
import { extractLnAddress, parseId } from "../Util";
|
import { extractLnAddress, parseId } from "../Util";
|
||||||
import Timeline from "../element/Timeline";
|
import Timeline from "../element/Timeline";
|
||||||
import { extractLinks } from '../Text'
|
import { extractLinks, extractHashtags } from '../Text'
|
||||||
import LNURLTip from "../element/LNURLTip";
|
import LNURLTip from "../element/LNURLTip";
|
||||||
import Nip05 from "../element/Nip05";
|
import Nip05 from "../element/Nip05";
|
||||||
import Copy from "../element/Copy";
|
import Copy from "../element/Copy";
|
||||||
@ -36,6 +36,7 @@ export default function ProfilePage() {
|
|||||||
const isMe = loginPubKey === id;
|
const isMe = loginPubKey === id;
|
||||||
const [showLnQr, setShowLnQr] = useState(false);
|
const [showLnQr, setShowLnQr] = useState(false);
|
||||||
const [tab, setTab] = useState(ProfileTab.Notes);
|
const [tab, setTab] = useState(ProfileTab.Notes);
|
||||||
|
const about = extractHashtags(extractLinks([user?.about]))
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTab(ProfileTab.Notes);
|
setTab(ProfileTab.Notes);
|
||||||
@ -60,7 +61,7 @@ export default function ProfilePage() {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{extractLinks([user?.about])}</p>
|
<p>{about}</p>
|
||||||
|
|
||||||
{user?.website && (
|
{user?.website && (
|
||||||
<div className="website f-ellipsis">
|
<div className="website f-ellipsis">
|
||||||
|
Loading…
Reference in New Issue
Block a user