Optout of display of images and video (show them as links) (#1358)
* optout of display of images/video * fix disableFreebies warning in settings * preview trusted images Co-authored-by: ekzyis <ek@stacker.news> --------- Co-authored-by: ekzyis <ek@stacker.news>
This commit is contained in:
parent
6f68a700ce
commit
07b98c3253
@ -84,6 +84,7 @@ export default gql`
|
|||||||
hideIsContributor: Boolean!
|
hideIsContributor: Boolean!
|
||||||
hideWalletBalance: Boolean!
|
hideWalletBalance: Boolean!
|
||||||
imgproxyOnly: Boolean!
|
imgproxyOnly: Boolean!
|
||||||
|
showImagesAndVideos: Boolean!
|
||||||
nostrCrossposting: Boolean!
|
nostrCrossposting: Boolean!
|
||||||
nostrPubkey: String
|
nostrPubkey: String
|
||||||
nostrRelays: [String!]
|
nostrRelays: [String!]
|
||||||
@ -155,6 +156,7 @@ export default gql`
|
|||||||
hideIsContributor: Boolean!
|
hideIsContributor: Boolean!
|
||||||
hideWalletBalance: Boolean!
|
hideWalletBalance: Boolean!
|
||||||
imgproxyOnly: Boolean!
|
imgproxyOnly: Boolean!
|
||||||
|
showImagesAndVideos: Boolean!
|
||||||
nostrCrossposting: Boolean!
|
nostrCrossposting: Boolean!
|
||||||
nostrPubkey: String
|
nostrPubkey: String
|
||||||
nostrRelays: [String!]
|
nostrRelays: [String!]
|
||||||
|
@ -19,13 +19,25 @@ export function decodeOriginalUrl (imgproxyUrl) {
|
|||||||
return originalUrl
|
return originalUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImageOriginal ({ src, topLevel, rel, tab, children, onClick, ...props }) {
|
function LinkRaw ({ href, children, src, rel, onClick, ...props }) {
|
||||||
|
const isRawURL = /^https?:\/\//.test(children?.[0])
|
||||||
|
return (
|
||||||
|
// eslint-disable-next-line
|
||||||
|
<a
|
||||||
|
target='_blank'
|
||||||
|
rel={rel ?? UNKNOWN_LINK_REL}
|
||||||
|
href={src}
|
||||||
|
>{isRawURL || !children ? src : children}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImageOriginal ({ src, topLevel, tab, onClick, ...props }) {
|
||||||
const me = useMe()
|
const me = useMe()
|
||||||
const [showImage, setShowImage] = useState(false)
|
const [showImage, setShowImage] = useState(false)
|
||||||
const [showVideo, setShowVideo] = useState(false)
|
const [showVideo, setShowVideo] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (me?.privates?.imgproxyOnly && tab !== 'preview') return
|
|
||||||
// make sure it's not a false negative by trying to load URL as <img>
|
// make sure it's not a false negative by trying to load URL as <img>
|
||||||
const img = new window.Image()
|
const img = new window.Image()
|
||||||
img.onload = () => setShowImage(true)
|
img.onload = () => setShowImage(true)
|
||||||
@ -42,7 +54,9 @@ function ImageOriginal ({ src, topLevel, rel, tab, children, onClick, ...props }
|
|||||||
}
|
}
|
||||||
}, [src, showImage])
|
}, [src, showImage])
|
||||||
|
|
||||||
if (showImage) {
|
const showMedia = (tab === 'preview' || (me?.privates?.showImagesAndVideos !== false && !me?.privates?.imgproxyOnly))
|
||||||
|
|
||||||
|
if (showImage && showMedia) {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
className={topLevel ? styles.topLevel : undefined}
|
className={topLevel ? styles.topLevel : undefined}
|
||||||
@ -51,23 +65,14 @@ function ImageOriginal ({ src, topLevel, rel, tab, children, onClick, ...props }
|
|||||||
onError={() => setShowImage(false)}
|
onError={() => setShowImage(false)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (showVideo) {
|
} else if (showVideo && showMedia) {
|
||||||
return <video src={src} controls />
|
return <video src={src} controls />
|
||||||
} else {
|
} else {
|
||||||
// user is not okay with loading original url automatically or there was an error loading the image
|
// user is not okay with loading original url automatically or there was an error loading the image
|
||||||
|
|
||||||
// If element parsed by markdown is a raw URL, we use src as the text to not mislead users.
|
// If element parsed by markdown is a raw URL, we use src as the text to not mislead users.
|
||||||
// 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])
|
return <LinkRaw src={src} {...props} />
|
||||||
return (
|
|
||||||
// eslint-disable-next-line
|
|
||||||
<a
|
|
||||||
target='_blank'
|
|
||||||
rel={rel ?? UNKNOWN_LINK_REL}
|
|
||||||
href={src}
|
|
||||||
>{isRawURL || !children ? src : children}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +143,7 @@ const Image = memo(({ className, src, srcSet, sizes, width, height, onClick, onE
|
|||||||
|
|
||||||
export default function ZoomableImage ({ src, srcSet, ...props }) {
|
export default function ZoomableImage ({ src, srcSet, ...props }) {
|
||||||
const showModal = useShowModal()
|
const showModal = useShowModal()
|
||||||
|
const me = useMe()
|
||||||
|
|
||||||
// if `srcSet` is falsy, it means the image was not processed by worker yet
|
// if `srcSet` is falsy, it means the image was not processed by worker yet
|
||||||
const [trustedDomain, setTrustedDomain] = useState(!!srcSet || IMGPROXY_URL_REGEXP.test(src) || MEDIA_DOMAIN_REGEXP.test(src))
|
const [trustedDomain, setTrustedDomain] = useState(!!srcSet || IMGPROXY_URL_REGEXP.test(src) || MEDIA_DOMAIN_REGEXP.test(src))
|
||||||
@ -171,12 +177,16 @@ export default function ZoomableImage ({ src, srcSet, ...props }) {
|
|||||||
if (!src) return null
|
if (!src) return null
|
||||||
|
|
||||||
if (trustedDomain) {
|
if (trustedDomain) {
|
||||||
|
if (props.tab === 'preview' || !me || me.privates.showImagesAndVideos) {
|
||||||
return (
|
return (
|
||||||
<TrustedImage
|
<TrustedImage
|
||||||
src={src} srcSet={srcSet}
|
src={src} srcSet={srcSet}
|
||||||
onClick={handleClick} onError={handleError} {...props}
|
onClick={handleClick} onError={handleError} {...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
return <LinkRaw src={src} onClick={handleClick} {...props} />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ImageOriginal src={originalUrl} onClick={handleClick} {...props} />
|
return <ImageOriginal src={originalUrl} onClick={handleClick} {...props} />
|
||||||
|
@ -26,6 +26,7 @@ export const ME = gql`
|
|||||||
hideWalletBalance
|
hideWalletBalance
|
||||||
hideWelcomeBanner
|
hideWelcomeBanner
|
||||||
imgproxyOnly
|
imgproxyOnly
|
||||||
|
showImagesAndVideos
|
||||||
lastCheckedJobs
|
lastCheckedJobs
|
||||||
nostrCrossposting
|
nostrCrossposting
|
||||||
noteAllDescendants
|
noteAllDescendants
|
||||||
@ -98,6 +99,7 @@ export const SETTINGS_FIELDS = gql`
|
|||||||
hideTwitter
|
hideTwitter
|
||||||
hideIsContributor
|
hideIsContributor
|
||||||
imgproxyOnly
|
imgproxyOnly
|
||||||
|
showImagesAndVideos
|
||||||
hideWalletBalance
|
hideWalletBalance
|
||||||
diagnostics
|
diagnostics
|
||||||
noReferralLinks
|
noReferralLinks
|
||||||
|
@ -116,7 +116,7 @@ export default function Settings ({ ssrData }) {
|
|||||||
tipRandomMin: settings?.tipRandomMin || 1,
|
tipRandomMin: settings?.tipRandomMin || 1,
|
||||||
tipRandomMax: settings?.tipRandomMax || 10,
|
tipRandomMax: settings?.tipRandomMax || 10,
|
||||||
turboTipping: settings?.turboTipping,
|
turboTipping: settings?.turboTipping,
|
||||||
disableFreebies: settings?.disableFreebies,
|
disableFreebies: settings?.disableFreebies || undefined,
|
||||||
zapUndos: settings?.zapUndos || (settings?.tipDefault ? 100 * settings.tipDefault : 2100),
|
zapUndos: settings?.zapUndos || (settings?.tipDefault ? 100 * settings.tipDefault : 2100),
|
||||||
zapUndosEnabled: settings?.zapUndos !== null,
|
zapUndosEnabled: settings?.zapUndos !== null,
|
||||||
fiatCurrency: settings?.fiatCurrency || 'USD',
|
fiatCurrency: settings?.fiatCurrency || 'USD',
|
||||||
@ -140,6 +140,7 @@ export default function Settings ({ ssrData }) {
|
|||||||
hideNostr: settings?.hideNostr,
|
hideNostr: settings?.hideNostr,
|
||||||
hideTwitter: settings?.hideTwitter,
|
hideTwitter: settings?.hideTwitter,
|
||||||
imgproxyOnly: settings?.imgproxyOnly,
|
imgproxyOnly: settings?.imgproxyOnly,
|
||||||
|
showImagesAndVideos: settings?.showImagesAndVideos,
|
||||||
wildWestMode: settings?.wildWestMode,
|
wildWestMode: settings?.wildWestMode,
|
||||||
satsFilter: settings?.satsFilter,
|
satsFilter: settings?.satsFilter,
|
||||||
nsfwMode: settings?.nsfwMode,
|
nsfwMode: settings?.nsfwMode,
|
||||||
@ -508,6 +509,17 @@ export default function Settings ({ ssrData }) {
|
|||||||
required
|
required
|
||||||
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={
|
||||||
|
<div className='d-flex align-items-center'>show images and video
|
||||||
|
<Info>
|
||||||
|
<p>disable to show images and videos as links instead of embedding them</p>
|
||||||
|
</Info>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
name='showImagesAndVideos'
|
||||||
|
groupClassName='mb-0'
|
||||||
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={
|
label={
|
||||||
<div className='d-flex align-items-center'>wild west mode
|
<div className='d-flex align-items-center'>wild west mode
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" ADD COLUMN "showImagesAndVideos" BOOLEAN NOT NULL DEFAULT true;
|
@ -63,6 +63,7 @@ model User {
|
|||||||
turboTipping Boolean @default(false)
|
turboTipping Boolean @default(false)
|
||||||
zapUndos Int?
|
zapUndos Int?
|
||||||
imgproxyOnly Boolean @default(false)
|
imgproxyOnly Boolean @default(false)
|
||||||
|
showImagesAndVideos Boolean @default(true)
|
||||||
hideWalletBalance Boolean @default(false)
|
hideWalletBalance Boolean @default(false)
|
||||||
disableFreebies Boolean?
|
disableFreebies Boolean?
|
||||||
referrerId Int?
|
referrerId Int?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user