Watermark posts in screenshots (#9637)
* watermark posts in screenshots * disable when app is backgrounded * fix alignment * show watermark even when there isn't a button
This commit is contained in:
@@ -156,6 +156,7 @@
|
||||
"expo-location": "~19.0.8",
|
||||
"expo-media-library": "~18.2.1",
|
||||
"expo-notifications": "~0.32.14",
|
||||
"expo-privacy-sensitive": "^0.1.0",
|
||||
"expo-screen-orientation": "~9.0.8",
|
||||
"expo-sharing": "~14.0.8",
|
||||
"expo-sms": "^14.0.7",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {PrivacySensitive} from 'expo-privacy-sensitive'
|
||||
|
||||
import {useAppState} from '#/lib/hooks/useAppState'
|
||||
import {isIOS} from '#/platform/detection'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {sizes as iconSizes} from '#/components/icons/common'
|
||||
import {Mark as Logo} from '#/components/icons/Logo'
|
||||
|
||||
const ICON_SIZE = 'xl' as const
|
||||
|
||||
export function GrowthHack({
|
||||
children,
|
||||
align = 'right',
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
align?: 'left' | 'right'
|
||||
}) {
|
||||
const t = useTheme()
|
||||
|
||||
// the button has a variable width and is absolutely positioned, so we need to manually
|
||||
// set the minimum width of the underlying button
|
||||
const [width, setWidth] = useState<number | undefined>(undefined)
|
||||
|
||||
const appState = useAppState()
|
||||
|
||||
if (!isIOS || appState !== 'active') return children
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.relative,
|
||||
a.justify_center,
|
||||
align === 'right' ? a.align_end : a.align_start,
|
||||
width === undefined ? {opacity: 0} : {minWidth: width},
|
||||
]}>
|
||||
<PrivacySensitive
|
||||
style={[
|
||||
a.absolute,
|
||||
a.z_10,
|
||||
a.flex_col,
|
||||
align === 'right'
|
||||
? [a.right_0, a.align_end]
|
||||
: [a.left_0, a.align_start],
|
||||
// when finding the size of the button, we need the containing
|
||||
// element to have a concrete size otherwise the text will
|
||||
// collapse to 0 width. so set it to a really big number
|
||||
// and hide the entire thing (see above)
|
||||
width === undefined && {width: 10000},
|
||||
]}>
|
||||
<View
|
||||
onLayout={evt => setWidth(evt.nativeEvent.layout.width)}
|
||||
style={[
|
||||
t.atoms.bg,
|
||||
// make sure it covers the icon! the won't always be a button
|
||||
{minWidth: iconSizes[ICON_SIZE], minHeight: iconSizes[ICON_SIZE]},
|
||||
]}>
|
||||
{children}
|
||||
</View>
|
||||
</PrivacySensitive>
|
||||
<Logo size={ICON_SIZE} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -381,7 +381,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
</View>
|
||||
</Link>
|
||||
{showFollowButton && (
|
||||
<View collapsable={false}>
|
||||
<View collapsable={false} style={[a.self_center]}>
|
||||
<ThreadItemAnchorFollowButton did={post.author.did} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {useLingui} from '@lingui/react'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {isIOS} from '#/platform/detection'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {
|
||||
useProfileFollowMutationQueue,
|
||||
@@ -14,10 +15,23 @@ import {useRequireAuth} from '#/state/session'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useBreakpoints} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
|
||||
import {GrowthHack} from './GrowthHack'
|
||||
|
||||
export function ThreadItemAnchorFollowButton({did}: {did: string}) {
|
||||
if (isIOS) {
|
||||
return (
|
||||
<GrowthHack>
|
||||
<ThreadItemAnchorFollowButtonInner did={did} />
|
||||
</GrowthHack>
|
||||
)
|
||||
}
|
||||
|
||||
return <ThreadItemAnchorFollowButtonInner did={did} />
|
||||
}
|
||||
|
||||
export function ThreadItemAnchorFollowButtonInner({did}: {did: string}) {
|
||||
const {data: profile, isLoading} = useProfileQuery({did})
|
||||
|
||||
// We will never hit this - the profile will always be cached or loaded above
|
||||
@@ -113,15 +127,10 @@ function PostThreadFollowBtnLoaded({
|
||||
label={_(msg`Follow ${profile.handle}`)}
|
||||
onPress={onPress}
|
||||
size="small"
|
||||
variant="solid"
|
||||
color={isFollowing ? 'secondary' : 'secondary_inverted'}
|
||||
style={[a.rounded_full]}>
|
||||
{gtMobile && (
|
||||
<ButtonIcon
|
||||
icon={isFollowing ? Check : Plus}
|
||||
position="left"
|
||||
size="sm"
|
||||
/>
|
||||
<ButtonIcon icon={isFollowing ? CheckIcon : PlusIcon} size="sm" />
|
||||
)}
|
||||
<ButtonText>
|
||||
{!isFollowing ? (
|
||||
|
||||
@@ -11372,6 +11372,11 @@ expo-notifications@~0.32.14:
|
||||
expo-application "~7.0.8"
|
||||
expo-constants "~18.0.11"
|
||||
|
||||
expo-privacy-sensitive@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/expo-privacy-sensitive/-/expo-privacy-sensitive-0.1.0.tgz#2177d7a3cb8ed352df94c5806d012dfb7b48bc84"
|
||||
integrity sha512-N0xa8yz+u7HvGY5CqZeo5cwtTOyFQxOxxt15jeW1eAjLKZAcNrtrDGGJP18TX2eh5TfJ3I6OtmECJg9Q8+Yorw==
|
||||
|
||||
expo-pwa@0.0.127:
|
||||
version "0.0.127"
|
||||
resolved "https://registry.yarnpkg.com/expo-pwa/-/expo-pwa-0.0.127.tgz#b8d2fd28efff408a24e0f2539bfb47e09f8e4ebe"
|
||||
|
||||
Reference in New Issue
Block a user