better link rel attr handling
This commit is contained in:
parent
0a0bfbbb37
commit
b16234630b
@ -9,7 +9,7 @@ import {
|
|||||||
ITEM_SPAM_INTERVAL, ITEM_FILTER_THRESHOLD,
|
ITEM_SPAM_INTERVAL, ITEM_FILTER_THRESHOLD,
|
||||||
COMMENT_DEPTH_LIMIT, COMMENT_TYPE_QUERY,
|
COMMENT_DEPTH_LIMIT, COMMENT_TYPE_QUERY,
|
||||||
ANON_USER_ID, ANON_ITEM_SPAM_INTERVAL, POLL_COST,
|
ANON_USER_ID, ANON_ITEM_SPAM_INTERVAL, POLL_COST,
|
||||||
ITEM_ALLOW_EDITS, GLOBAL_SEED, ANON_FEE_MULTIPLIER
|
ITEM_ALLOW_EDITS, GLOBAL_SEED, ANON_FEE_MULTIPLIER, NOFOLLOW_LIMIT, UNKNOWN_LINK_REL
|
||||||
} from '../../lib/constants'
|
} from '../../lib/constants'
|
||||||
import { msatsToSats } from '../../lib/format'
|
import { msatsToSats } from '../../lib/format'
|
||||||
import { parse } from 'tldts'
|
import { parse } from 'tldts'
|
||||||
@ -1146,6 +1146,11 @@ export default {
|
|||||||
}
|
}
|
||||||
return item.outlawed || item.weightedVotes - item.weightedDownVotes <= -ITEM_FILTER_THRESHOLD
|
return item.outlawed || item.weightedVotes - item.weightedDownVotes <= -ITEM_FILTER_THRESHOLD
|
||||||
},
|
},
|
||||||
|
rel: async (item, args, { me, models }) => {
|
||||||
|
const sats = item.msats ? msatsToSats(item.msats) : 0
|
||||||
|
const boost = item.boost ?? 0
|
||||||
|
return (sats + boost < NOFOLLOW_LIMIT) ? UNKNOWN_LINK_REL : 'noopener noreferrer'
|
||||||
|
},
|
||||||
mine: async (item, args, { me, models }) => {
|
mine: async (item, args, { me, models }) => {
|
||||||
return me?.id === item.userId
|
return me?.id === item.userId
|
||||||
},
|
},
|
||||||
|
@ -123,6 +123,7 @@ export default gql`
|
|||||||
parentOtsHash: String
|
parentOtsHash: String
|
||||||
forwards: [ItemForward]
|
forwards: [ItemForward]
|
||||||
imgproxyUrls: JSONObject
|
imgproxyUrls: JSONObject
|
||||||
|
rel: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ItemForwardInput {
|
input ItemForwardInput {
|
||||||
|
@ -9,7 +9,7 @@ import Eye from '../svgs/eye-fill.svg'
|
|||||||
import EyeClose from '../svgs/eye-close-line.svg'
|
import EyeClose from '../svgs/eye-close-line.svg'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import CommentEdit from './comment-edit'
|
import CommentEdit from './comment-edit'
|
||||||
import { ANON_USER_ID, COMMENT_DEPTH_LIMIT, NOFOLLOW_LIMIT } from '../lib/constants'
|
import { ANON_USER_ID, COMMENT_DEPTH_LIMIT, UNKNOWN_LINK_REL } from '../lib/constants'
|
||||||
import { ignoreClick } from '../lib/clicks'
|
import { ignoreClick } from '../lib/clicks'
|
||||||
import PayBounty from './pay-bounty'
|
import PayBounty from './pay-bounty'
|
||||||
import BountyIcon from '../svgs/bounty-bag.svg'
|
import BountyIcon from '../svgs/bounty-bag.svg'
|
||||||
@ -212,7 +212,7 @@ export default function Comment ({
|
|||||||
{item.searchText
|
{item.searchText
|
||||||
? <SearchText text={item.searchText} />
|
? <SearchText text={item.searchText} />
|
||||||
: (
|
: (
|
||||||
<Text itemId={item.id} topLevel={topLevel} nofollow={item.sats + item.boost < NOFOLLOW_LIMIT} imgproxyUrls={item.imgproxyUrls}>
|
<Text itemId={item.id} topLevel={topLevel} rel={item.rel ?? UNKNOWN_LINK_REL} imgproxyUrls={item.imgproxyUrls}>
|
||||||
{item.outlawed && !me?.privates?.wildWestMode
|
{item.outlawed && !me?.privates?.wildWestMode
|
||||||
? '*stackers have outlawed this. turn on wild west mode in your [settings](/settings) to see outlawed content.*'
|
? '*stackers have outlawed this. turn on wild west mode in your [settings](/settings) to see outlawed content.*'
|
||||||
: truncate ? truncateString(item.text) : item.text}
|
: truncate ? truncateString(item.text) : item.text}
|
||||||
|
@ -4,7 +4,7 @@ import { IMGPROXY_URL_REGEXP } from '../lib/url'
|
|||||||
import { useShowModal } from './modal'
|
import { useShowModal } from './modal'
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
import { Dropdown } from 'react-bootstrap'
|
import { Dropdown } from 'react-bootstrap'
|
||||||
import { UPLOAD_TYPES_ALLOW } from '../lib/constants'
|
import { UNKNOWN_LINK_REL, UPLOAD_TYPES_ALLOW } from '../lib/constants'
|
||||||
import { useToast } from './toast'
|
import { useToast } from './toast'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { useMutation } from '@apollo/client'
|
import { useMutation } from '@apollo/client'
|
||||||
@ -19,7 +19,7 @@ export function decodeOriginalUrl (imgproxyUrl) {
|
|||||||
return originalUrl
|
return originalUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImageOriginal ({ src, topLevel, nofollow, tab, children, onClick, ...props }) {
|
function ImageOriginal ({ src, topLevel, rel, tab, children, onClick, ...props }) {
|
||||||
const me = useMe()
|
const me = useMe()
|
||||||
const [showImage, setShowImage] = useState(false)
|
const [showImage, setShowImage] = useState(false)
|
||||||
|
|
||||||
@ -52,9 +52,10 @@ function ImageOriginal ({ src, topLevel, nofollow, tab, children, onClick, ...pr
|
|||||||
// This will not be the case if [text](url) format is used. Then we will show what was chosen as text.
|
// This will not be the case if [text](url) format is used. Then we will show what was chosen as text.
|
||||||
const isRawURL = /^https?:\/\//.test(children?.[0])
|
const isRawURL = /^https?:\/\//.test(children?.[0])
|
||||||
return (
|
return (
|
||||||
|
// eslint-disable-next-line
|
||||||
<a
|
<a
|
||||||
target='_blank'
|
target='_blank'
|
||||||
rel={`noreferrer ${nofollow ? 'nofollow' : ''} noopener`}
|
rel={rel ?? UNKNOWN_LINK_REL}
|
||||||
href={src}
|
href={src}
|
||||||
>{isRawURL ? src : children}
|
>{isRawURL ? src : children}
|
||||||
</a>
|
</a>
|
||||||
@ -118,7 +119,7 @@ export default function ZoomableImage ({ src, srcSet, ...props }) {
|
|||||||
overflow: (
|
overflow: (
|
||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
href={originalUrl} target='_blank'
|
href={originalUrl} target='_blank'
|
||||||
rel={`noreferrer ${props.nofollow ? 'nofollow' : ''} noopener`}
|
rel={props.rel ?? UNKNOWN_LINK_REL}
|
||||||
>
|
>
|
||||||
open original
|
open original
|
||||||
</Dropdown.Item>)
|
</Dropdown.Item>)
|
||||||
|
@ -7,7 +7,6 @@ import ZoomableImage from './image'
|
|||||||
import Comments from './comments'
|
import Comments from './comments'
|
||||||
import styles from '../styles/item.module.css'
|
import styles from '../styles/item.module.css'
|
||||||
import itemStyles from './item.module.css'
|
import itemStyles from './item.module.css'
|
||||||
import { NOFOLLOW_LIMIT } from '../lib/constants'
|
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
import Button from 'react-bootstrap/Button'
|
import Button from 'react-bootstrap/Button'
|
||||||
import { TwitterTweetEmbed } from 'react-twitter-embed'
|
import { TwitterTweetEmbed } from 'react-twitter-embed'
|
||||||
@ -26,6 +25,7 @@ import { RootProvider } from './root'
|
|||||||
import { IMGPROXY_URL_REGEXP } from '../lib/url'
|
import { IMGPROXY_URL_REGEXP } from '../lib/url'
|
||||||
import { numWithUnits } from '../lib/format'
|
import { numWithUnits } from '../lib/format'
|
||||||
import { useQuoteReply } from './use-quote-reply'
|
import { useQuoteReply } from './use-quote-reply'
|
||||||
|
import { UNKNOWN_LINK_REL } from '../lib/constants'
|
||||||
|
|
||||||
function BioItem ({ item, handleClick }) {
|
function BioItem ({ item, handleClick }) {
|
||||||
const me = useMe()
|
const me = useMe()
|
||||||
@ -99,7 +99,7 @@ function ItemEmbed ({ item }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (item.url?.match(IMGPROXY_URL_REGEXP)) {
|
if (item.url?.match(IMGPROXY_URL_REGEXP)) {
|
||||||
return <ZoomableImage src={item.url} />
|
return <ZoomableImage src={item.url} rel={item.rel ?? UNKNOWN_LINK_REL} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@ -171,7 +171,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
|
|||||||
function ItemText ({ item }) {
|
function ItemText ({ item }) {
|
||||||
return item.searchText
|
return item.searchText
|
||||||
? <SearchText text={item.searchText} />
|
? <SearchText text={item.searchText} />
|
||||||
: <Text itemId={item.id} topLevel nofollow={item.sats + item.boost < NOFOLLOW_LIMIT} imgproxyUrls={item.imgproxyUrls}>{item.text}</Text>
|
: <Text itemId={item.id} topLevel rel={item.rel ?? UNKNOWN_LINK_REL} imgproxyUrls={item.imgproxyUrls}>{item.text}</Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ItemFull ({ item, bio, rank, ...props }) {
|
export default function ItemFull ({ item, bio, rank, ...props }) {
|
||||||
|
@ -25,7 +25,7 @@ import { MuteSubDropdownItem, PinSubDropdownItem } from './territory-header'
|
|||||||
export default function ItemInfo ({
|
export default function ItemInfo ({
|
||||||
item, full, commentsText = 'comments',
|
item, full, commentsText = 'comments',
|
||||||
commentTextSingular = 'comment', className, embellishUser, extraInfo, onEdit, editText,
|
commentTextSingular = 'comment', className, embellishUser, extraInfo, onEdit, editText,
|
||||||
onQuoteReply, nofollow, extraBadges, nested, pinnable
|
onQuoteReply, extraBadges, nested, pinnable
|
||||||
}) {
|
}) {
|
||||||
const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000
|
const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000
|
||||||
const me = useMe()
|
const me = useMe()
|
||||||
@ -79,7 +79,6 @@ export default function ItemInfo ({
|
|||||||
<span> \ </span>
|
<span> \ </span>
|
||||||
</>}
|
</>}
|
||||||
<Link
|
<Link
|
||||||
rel={nofollow}
|
|
||||||
href={`/items/${item.id}`} onClick={(e) => {
|
href={`/items/${item.id}`} onClick={(e) => {
|
||||||
const viewedAt = commentsViewedAt(item)
|
const viewedAt = commentsViewedAt(item)
|
||||||
if (viewedAt) {
|
if (viewedAt) {
|
||||||
@ -107,7 +106,7 @@ export default function ItemInfo ({
|
|||||||
{embellishUser}
|
{embellishUser}
|
||||||
</Link>
|
</Link>
|
||||||
<span> </span>
|
<span> </span>
|
||||||
<Link rel={nofollow} href={`/items/${item.id}`} title={item.createdAt} className='text-reset' suppressHydrationWarning>
|
<Link href={`/items/${item.id}`} title={item.createdAt} className='text-reset' suppressHydrationWarning>
|
||||||
{timeSince(new Date(item.createdAt))}
|
{timeSince(new Date(item.createdAt))}
|
||||||
</Link>
|
</Link>
|
||||||
{item.prior &&
|
{item.prior &&
|
||||||
@ -161,7 +160,7 @@ export default function ItemInfo ({
|
|||||||
opentimestamp
|
opentimestamp
|
||||||
</Link>}
|
</Link>}
|
||||||
{item?.noteId && (
|
{item?.noteId && (
|
||||||
<Dropdown.Item onClick={() => window.open(`https://nostr.com/${item.noteId}`, '_blank', 'noopener')}>
|
<Dropdown.Item onClick={() => window.open(`https://nostr.com/${item.noteId}`, '_blank', 'noopener,noreferrer,nofollow')}>
|
||||||
nostr note
|
nostr note
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
)}
|
)}
|
||||||
|
@ -2,7 +2,7 @@ import Link from 'next/link'
|
|||||||
import styles from './item.module.css'
|
import styles from './item.module.css'
|
||||||
import UpVote from './upvote'
|
import UpVote from './upvote'
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { AD_USER_ID, NOFOLLOW_LIMIT } from '../lib/constants'
|
import { AD_USER_ID, UNKNOWN_LINK_REL } from '../lib/constants'
|
||||||
import Pin from '../svgs/pushpin-fill.svg'
|
import Pin from '../svgs/pushpin-fill.svg'
|
||||||
import reactStringReplace from 'react-string-replace'
|
import reactStringReplace from 'react-string-replace'
|
||||||
import PollIcon from '../svgs/bar-chart-horizontal-fill.svg'
|
import PollIcon from '../svgs/bar-chart-horizontal-fill.svg'
|
||||||
@ -29,7 +29,6 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const image = item.url && item.url.startsWith(process.env.NEXT_PUBLIC_IMGPROXY_URL)
|
const image = item.url && item.url.startsWith(process.env.NEXT_PUBLIC_IMGPROXY_URL)
|
||||||
const nofollow = item.sats + item.boost < NOFOLLOW_LIMIT && !item.position ? 'nofollow' : ''
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -50,7 +49,6 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
|
|||||||
<div className={styles.hunk}>
|
<div className={styles.hunk}>
|
||||||
<div className={`${styles.main} flex-wrap`}>
|
<div className={`${styles.main} flex-wrap`}>
|
||||||
<Link
|
<Link
|
||||||
rel={nofollow}
|
|
||||||
href={`/items/${item.id}`}
|
href={`/items/${item.id}`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
const viewedAt = commentsViewedAt(item)
|
const viewedAt = commentsViewedAt(item)
|
||||||
@ -82,19 +80,17 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
|
|||||||
{image && <span className={styles.icon}><ImageIcon className='fill-grey ms-2' height={16} width={16} /></span>}
|
{image && <span className={styles.icon}><ImageIcon className='fill-grey ms-2' height={16} width={16} /></span>}
|
||||||
</Link>
|
</Link>
|
||||||
{item.url && !image &&
|
{item.url && !image &&
|
||||||
<>
|
// eslint-disable-next-line
|
||||||
<a
|
<a
|
||||||
className={styles.link} target='_blank' href={item.url}
|
className={styles.link} target='_blank' href={item.url}
|
||||||
rel={`noreferrer ${nofollow} noopener`}
|
rel={item.rel ?? UNKNOWN_LINK_REL}
|
||||||
>
|
>
|
||||||
{item.url.replace(/(^https?:|^)\/\//, '')}
|
{item.url.replace(/(^https?:|^)\/\//, '')}
|
||||||
</a>
|
</a>}
|
||||||
</>}
|
|
||||||
</div>
|
</div>
|
||||||
<ItemInfo
|
<ItemInfo
|
||||||
full={full} item={item}
|
full={full} item={item}
|
||||||
onQuoteReply={onQuoteReply}
|
onQuoteReply={onQuoteReply}
|
||||||
nofollow={nofollow}
|
|
||||||
pinnable={pinnable}
|
pinnable={pinnable}
|
||||||
extraBadges={Number(item?.user?.id) === AD_USER_ID && <Badge className={styles.newComment} bg={null}>AD</Badge>}
|
extraBadges={Number(item?.user?.id) === AD_USER_ID && <Badge className={styles.newComment} bg={null}>AD</Badge>}
|
||||||
/>
|
/>
|
||||||
|
@ -11,7 +11,7 @@ import { dayMonthYear, timeSince } from '../lib/time'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Check from '../svgs/check-double-line.svg'
|
import Check from '../svgs/check-double-line.svg'
|
||||||
import HandCoin from '../svgs/hand-coin-fill.svg'
|
import HandCoin from '../svgs/hand-coin-fill.svg'
|
||||||
import { LOST_BLURBS, FOUND_BLURBS } from '../lib/constants'
|
import { LOST_BLURBS, FOUND_BLURBS, UNKNOWN_LINK_REL } from '../lib/constants'
|
||||||
import CowboyHatIcon from '../svgs/cowboy.svg'
|
import CowboyHatIcon from '../svgs/cowboy.svg'
|
||||||
import BaldIcon from '../svgs/bald.svg'
|
import BaldIcon from '../svgs/bald.svg'
|
||||||
import { RootProvider } from './root'
|
import { RootProvider } from './root'
|
||||||
@ -230,12 +230,15 @@ function NostrZap ({ n }) {
|
|||||||
<>
|
<>
|
||||||
<div className='fw-bold text-nostr ms-2 py-1'>
|
<div className='fw-bold text-nostr ms-2 py-1'>
|
||||||
<NostrIcon width={24} height={24} className='fill-nostr me-1' />{numWithUnits(n.earnedSats)} zap from
|
<NostrIcon width={24} height={24} className='fill-nostr me-1' />{numWithUnits(n.earnedSats)} zap from
|
||||||
<Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/p/${npub}`} rel='noreferrer'>
|
{// eslint-disable-next-line
|
||||||
|
<Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/p/${npub}`} rel={UNKNOWN_LINK_REL}>
|
||||||
{npub.slice(0, 10)}...
|
{npub.slice(0, 10)}...
|
||||||
</Link>
|
</Link>
|
||||||
|
}
|
||||||
on {note
|
on {note
|
||||||
? (
|
? (
|
||||||
<Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/e/${note}`} rel='noreferrer'>
|
// eslint-disable-next-line
|
||||||
|
<Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/e/${note}`} rel={UNKNOWN_LINK_REL}>
|
||||||
{note.slice(0, 12)}...
|
{note.slice(0, 12)}...
|
||||||
</Link>)
|
</Link>)
|
||||||
: 'nostr'}
|
: 'nostr'}
|
||||||
|
@ -19,6 +19,7 @@ import { rehypeInlineCodeProperty } from '../lib/md'
|
|||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { UNKNOWN_LINK_REL } from '../lib/constants'
|
||||||
|
|
||||||
export function SearchText ({ text }) {
|
export function SearchText ({ text }) {
|
||||||
return (
|
return (
|
||||||
@ -33,7 +34,7 @@ export function SearchText ({ text }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this is one of the slowest components to render
|
// this is one of the slowest components to render
|
||||||
export default memo(function Text ({ nofollow, imgproxyUrls, children, tab, itemId, ...outerProps }) {
|
export default memo(function Text ({ rel, imgproxyUrls, children, tab, itemId, ...outerProps }) {
|
||||||
const [overflowing, setOverflowing] = useState(false)
|
const [overflowing, setOverflowing] = useState(false)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [show, setShow] = useState(false)
|
const [show, setShow] = useState(false)
|
||||||
@ -139,7 +140,7 @@ export default memo(function Text ({ nofollow, imgproxyUrls, children, tab, item
|
|||||||
const Img = useCallback(({ node, src, ...props }) => {
|
const Img = useCallback(({ node, src, ...props }) => {
|
||||||
const url = IMGPROXY_URL_REGEXP.test(src) ? decodeOriginalUrl(src) : src
|
const url = IMGPROXY_URL_REGEXP.test(src) ? decodeOriginalUrl(src) : src
|
||||||
const srcSet = imgproxyUrls?.[url]
|
const srcSet = imgproxyUrls?.[url]
|
||||||
return <ZoomableImage srcSet={srcSet} tab={tab} src={src} {...props} {...outerProps} />
|
return <ZoomableImage srcSet={srcSet} tab={tab} src={src} rel={rel ?? UNKNOWN_LINK_REL} {...props} {...outerProps} />
|
||||||
}, [imgproxyUrls, outerProps, tab])
|
}, [imgproxyUrls, outerProps, tab])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -174,14 +175,14 @@ export default memo(function Text ({ nofollow, imgproxyUrls, children, tab, item
|
|||||||
<Link
|
<Link
|
||||||
{...props}
|
{...props}
|
||||||
id={props.id && itemId ? `${props.id}-${itemId}` : props.id}
|
id={props.id && itemId ? `${props.id}-${itemId}` : props.id}
|
||||||
rel={`noreferrer ${nofollow ? 'nofollow' : ''} noopener`}
|
|
||||||
href={itemId ? `${href}-${itemId}` : href}
|
href={itemId ? `${href}-${itemId}` : href}
|
||||||
>{text}
|
>{text}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<a id={props.id} target='_blank' rel={`noreferrer ${nofollow ? 'nofollow' : ''} noopener`} href={href}>{text}</a>
|
// eslint-disable-next-line
|
||||||
|
<a id={props.id} target='_blank' rel={rel ?? UNKNOWN_LINK_REL} href={href}>{text}</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +212,7 @@ export default memo(function Text ({ nofollow, imgproxyUrls, children, tab, item
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assume the link is an image which will fallback to link if it's not
|
// assume the link is an image which will fallback to link if it's not
|
||||||
return <Img src={href} nofollow={nofollow} {...props}>{children}</Img>
|
return <Img src={href} rel={rel ?? UNKNOWN_LINK_REL} {...props}>{children}</Img>
|
||||||
},
|
},
|
||||||
img: Img
|
img: Img
|
||||||
}}
|
}}
|
||||||
|
@ -28,6 +28,7 @@ import { hexToBech32 } from '../lib/nostr'
|
|||||||
import NostrIcon from '../svgs/nostr.svg'
|
import NostrIcon from '../svgs/nostr.svg'
|
||||||
import GithubIcon from '../svgs/github-fill.svg'
|
import GithubIcon from '../svgs/github-fill.svg'
|
||||||
import TwitterIcon from '../svgs/twitter-fill.svg'
|
import TwitterIcon from '../svgs/twitter-fill.svg'
|
||||||
|
import { UNKNOWN_LINK_REL } from '../lib/constants'
|
||||||
|
|
||||||
export default function UserHeader ({ user }) {
|
export default function UserHeader ({ user }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -206,21 +207,24 @@ function SocialLink ({ name, id }) {
|
|||||||
if (name === 'Nostr') {
|
if (name === 'Nostr') {
|
||||||
const npub = hexToBech32(id)
|
const npub = hexToBech32(id)
|
||||||
return (
|
return (
|
||||||
<Link className={className} target='_blank' href={`https://nostr.com/${npub}`} rel='noreferrer'>
|
// eslint-disable-next-line
|
||||||
|
<Link className={className} target='_blank' href={`https://nostr.com/${npub}`} rel={UNKNOWN_LINK_REL}>
|
||||||
<NostrIcon width={20} height={20} className='me-1' />
|
<NostrIcon width={20} height={20} className='me-1' />
|
||||||
{npub.slice(0, 10)}...{npub.slice(-10)}
|
{npub.slice(0, 10)}...{npub.slice(-10)}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
} else if (name === 'Github') {
|
} else if (name === 'Github') {
|
||||||
return (
|
return (
|
||||||
<Link className={className} target='_blank' href={`https://github.com/${id}`} rel='noreferrer'>
|
// eslint-disable-next-line
|
||||||
|
<Link className={className} target='_blank' href={`https://github.com/${id}`} rel={UNKNOWN_LINK_REL}>
|
||||||
<GithubIcon width={20} height={20} className='me-1' />
|
<GithubIcon width={20} height={20} className='me-1' />
|
||||||
{id}
|
{id}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
} else if (name === 'Twitter') {
|
} else if (name === 'Twitter') {
|
||||||
return (
|
return (
|
||||||
<Link className={className} target='_blank' href={`https://twitter.com/${id}`} rel='noreferrer'>
|
// eslint-disable-next-line
|
||||||
|
<Link className={className} target='_blank' href={`https://twitter.com/${id}`} rel={UNKNOWN_LINK_REL}>
|
||||||
<TwitterIcon width={20} height={20} className='me-1' />
|
<TwitterIcon width={20} height={20} className='me-1' />
|
||||||
@{id}
|
@{id}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -33,6 +33,7 @@ export const COMMENT_FIELDS = gql`
|
|||||||
otsHash
|
otsHash
|
||||||
ncomments
|
ncomments
|
||||||
imgproxyUrls
|
imgproxyUrls
|
||||||
|
rel
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ export const ITEM_FIELDS = gql`
|
|||||||
uploadId
|
uploadId
|
||||||
mine
|
mine
|
||||||
imgproxyUrls
|
imgproxyUrls
|
||||||
|
rel
|
||||||
}`
|
}`
|
||||||
|
|
||||||
export const ITEM_FULL_FIELDS = gql`
|
export const ITEM_FULL_FIELDS = gql`
|
||||||
|
@ -4,6 +4,7 @@ export const DEFAULT_SUBS = ['bitcoin', 'nostr', 'tech', 'meta', 'jobs']
|
|||||||
export const DEFAULT_SUBS_NO_JOBS = DEFAULT_SUBS.filter(s => s !== 'jobs')
|
export const DEFAULT_SUBS_NO_JOBS = DEFAULT_SUBS.filter(s => s !== 'jobs')
|
||||||
|
|
||||||
export const NOFOLLOW_LIMIT = 1000
|
export const NOFOLLOW_LIMIT = 1000
|
||||||
|
export const UNKNOWN_LINK_REL = 'noreferrer nofollow noopener'
|
||||||
export const BOOST_MULT = 5000
|
export const BOOST_MULT = 5000
|
||||||
export const BOOST_MIN = BOOST_MULT * 5
|
export const BOOST_MIN = BOOST_MULT * 5
|
||||||
export const UPLOAD_SIZE_MAX = 25 * 1024 * 1024
|
export const UPLOAD_SIZE_MAX = 25 * 1024 * 1024
|
||||||
|
Loading…
x
Reference in New Issue
Block a user