From 99078bbff2282ad18594a4d5a4ed5fe207c394e4 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 12 Jun 2024 08:51:08 -0700 Subject: [PATCH 01/11] Don't show warning when sharing your own post in PWI opt-out mode (#4495) --- src/view/com/profile/ProfileMenu.tsx | 7 +++++-- src/view/com/util/forms/PostDropdownBtn.tsx | 5 ++++- src/view/com/util/post-ctrls/PostCtrls.tsx | 10 ++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/view/com/profile/ProfileMenu.tsx b/src/view/com/profile/ProfileMenu.tsx index 5d39e5f0f3..efc2497600 100644 --- a/src/view/com/profile/ProfileMenu.tsx +++ b/src/view/com/profile/ProfileMenu.tsx @@ -71,8 +71,11 @@ let ProfileMenu = ({ const loggedOutWarningPromptControl = Prompt.usePromptControl() const showLoggedOutWarning = React.useMemo(() => { - return !!profile.labels?.find(label => label.val === '!no-unauthenticated') - }, [profile.labels]) + return ( + profile.did !== currentAccount?.did && + !!profile.labels?.find(label => label.val === '!no-unauthenticated') + ) + }, [currentAccount, profile]) const invalidateProfileQuery = React.useCallback(() => { queryClient.invalidateQueries({ diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index b6873ff8ad..2486b73d58 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -196,6 +196,9 @@ let PostDropdownBtn = ({ ) }, [postAuthor]) + const showLoggedOutWarning = + postAuthor.did !== currentAccount?.did && hideInPWI + const onSharePost = React.useCallback(() => { const url = toShareUrl(href) shareUrl(url) @@ -296,7 +299,7 @@ let PostDropdownBtn = ({ testID="postDropdownShareBtn" label={isWeb ? _(msg`Copy link to post`) : _(msg`Share`)} onPress={() => { - if (hideInPWI) { + if (showLoggedOutWarning) { loggedOutWarningPromptControl.open() } else { onSharePost() diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index d42590e905..c389855e3d 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -27,7 +27,7 @@ import { usePostLikeMutationQueue, usePostRepostMutationQueue, } from '#/state/queries/post' -import {useRequireAuth} from '#/state/session' +import {useRequireAuth, useSession} from '#/state/session' import {useComposerControls} from '#/state/shell/composer' import {atoms as a, useTheme} from '#/alf' import {useDialogControl} from '#/components/Dialog' @@ -64,6 +64,7 @@ let PostCtrls = ({ const t = useTheme() const {_} = useLingui() const {openComposer} = useComposerControls() + const {currentAccount} = useSession() const [queueLike, queueUnlike] = usePostLikeMutationQueue(post, logContext) const [queueRepost, queueUnrepost] = usePostRepostMutationQueue( post, @@ -75,10 +76,11 @@ let PostCtrls = ({ const playHaptic = useHaptics() const shouldShowLoggedOutWarning = React.useMemo(() => { - return !!post.author.labels?.find( - label => label.val === '!no-unauthenticated', + return ( + post.author.did !== currentAccount?.did && + !!post.author.labels?.find(label => label.val === '!no-unauthenticated') ) - }, [post]) + }, [currentAccount, post]) const defaultCtrlColor = React.useMemo( () => ({ From a55f924639605709d436474073e75f9f39a84276 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 12 Jun 2024 16:52:00 -0500 Subject: [PATCH 02/11] Just use server count (#4499) * Just use server count * Fix count --- src/components/KnownFollowers.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/KnownFollowers.tsx b/src/components/KnownFollowers.tsx index b99fe3398e..2b8dcc866d 100644 --- a/src/components/KnownFollowers.tsx +++ b/src/components/KnownFollowers.tsx @@ -82,15 +82,6 @@ function KnownFollowersInner({ t.atoms.text_contrast_medium, ] - // list of users, minus blocks - const returnedCount = cachedKnownFollowers.followers.length - // db count, includes blocks - const fullCount = cachedKnownFollowers.count - // knownFollowers can return up to 5 users, but will exclude blocks - // therefore, if we have less 5 users, use whichever count is lower - const count = - returnedCount < 5 ? Math.min(fullCount, returnedCount) : fullCount - const slice = cachedKnownFollowers.followers.slice(0, 3).map(f => { const moderation = moderateProfile(f, moderationOpts) return { @@ -104,6 +95,7 @@ function KnownFollowersInner({ moderation, } }) + const count = cachedKnownFollowers.count - Math.min(slice.length, 2) return ( Date: Thu, 13 Jun 2024 04:23:37 +0200 Subject: [PATCH 03/11] Show social proof in hovercards (#4502) * Add social proof to hovercards * Close it more reliably --- src/components/KnownFollowers.tsx | 8 +++++++- src/components/ProfileHoverCard/index.web.tsx | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/components/KnownFollowers.tsx b/src/components/KnownFollowers.tsx index 2b8dcc866d..3d4d362ef2 100644 --- a/src/components/KnownFollowers.tsx +++ b/src/components/KnownFollowers.tsx @@ -8,7 +8,7 @@ import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' -import {Link} from '#/components/Link' +import {Link, LinkProps} from '#/components/Link' import {Text} from '#/components/Typography' const AVI_SIZE = 30 @@ -29,9 +29,11 @@ export function shouldShowKnownFollowers( export function KnownFollowers({ profile, moderationOpts, + onLinkPress, }: { profile: AppBskyActorDefs.ProfileViewDetailed moderationOpts: ModerationOpts + onLinkPress?: LinkProps['onPress'] }) { const cache = React.useRef>( new Map(), @@ -56,6 +58,7 @@ export function KnownFollowers({ profile={profile} cachedKnownFollowers={cachedKnownFollowers} moderationOpts={moderationOpts} + onLinkPress={onLinkPress} /> ) } @@ -67,10 +70,12 @@ function KnownFollowersInner({ profile, moderationOpts, cachedKnownFollowers, + onLinkPress, }: { profile: AppBskyActorDefs.ProfileViewDetailed moderationOpts: ModerationOpts cachedKnownFollowers: AppBskyActorDefs.KnownFollowers + onLinkPress?: LinkProps['onPress'] }) { const t = useTheme() const {_} = useLingui() @@ -102,6 +107,7 @@ function KnownFollowersInner({ label={_( msg`Press to view followers of this account that you also follow`, )} + onPress={onLinkPress} to={makeProfileLink(profile, 'known-followers')} style={[ a.flex_1, diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx index 024867b1a3..1fd74fd195 100644 --- a/src/components/ProfileHoverCard/index.web.tsx +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -22,6 +22,10 @@ import {useFollowMethods} from '#/components/hooks/useFollowMethods' import {useRichText} from '#/components/hooks/useRichText' import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import { + KnownFollowers, + shouldShowKnownFollowers, +} from '#/components/KnownFollowers' import {InlineLinkText, Link} from '#/components/Link' import {Loader} from '#/components/Loader' import {Portal} from '#/components/Portal' @@ -473,6 +477,17 @@ function Inner({ /> ) : undefined} + + {!isMe && + shouldShowKnownFollowers(profile.viewer?.knownFollowers) && ( + + + + )} )} From 247af5aee99ba5020b8bee9ccfa0f02bb8c4084a Mon Sep 17 00:00:00 2001 From: Mary <148872143+mary-ext@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:37:49 +0700 Subject: [PATCH 04/11] Prevent rich-formatting paste (#4327) * fix: prevent rich-formatting paste * fix: return true instead of preventDefault --- .../com/composer/text-input/TextInput.web.tsx | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx index 7f8dc2ed5e..a91524974e 100644 --- a/src/view/com/composer/text-input/TextInput.web.tsx +++ b/src/view/com/composer/text-input/TextInput.web.tsx @@ -150,16 +150,27 @@ export const TextInput = React.forwardRef(function TextInputImpl( attributes: { class: modeClass, }, - handlePaste: (_, event) => { - const items = event.clipboardData?.items + handlePaste: (view, event) => { + const clipboardData = event.clipboardData - if (items === undefined) { - return + if (clipboardData) { + if (clipboardData.types.includes('text/html')) { + // Rich-text formatting is pasted, try retrieving plain text + const text = clipboardData.getData('text/plain') + + // `pasteText` will invoke this handler again, but `clipboardData` will be null. + view.pasteText(text) + + // Return `true` to prevent ProseMirror's default paste behavior. + return true + } else { + // Otherwise, try retrieving images from the clipboard + + getImageFromUri(clipboardData.items, (uri: string) => { + textInputWebEmitter.emit('photo-pasted', uri) + }) + } } - - getImageFromUri(items, (uri: string) => { - textInputWebEmitter.emit('photo-pasted', uri) - }) }, handleKeyDown: (_, event) => { if ((event.metaKey || event.ctrlKey) && event.code === 'Enter') { From d989128e5b086fc60aea01ba991039c4e66165c6 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 12 Jun 2024 21:44:06 -0500 Subject: [PATCH 05/11] Set profile hover prefetch stale time to 30s (#4417) * Set prefetch stale time to 30s * Run prefetch on mouseOver * Only prefetch once on mousemove --- src/state/queries/profile.ts | 1 + src/view/com/util/PostMeta.tsx | 75 +++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index 7cc9f69116..6f7f2de792 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -94,6 +94,7 @@ export function usePrefetchProfileQuery() { const prefetchProfileQuery = useCallback( async (did: string) => { await queryClient.prefetchQuery({ + staleTime: STALE.SECONDS.THIRTY, queryKey: RQKEY(did), queryFn: async () => { const res = await agent.getProfile({actor: did || ''}) diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index b6fe6d374d..df45174b9a 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -11,6 +11,7 @@ import {sanitizeHandle} from 'lib/strings/handles' import {niceDate} from 'lib/strings/time' import {TypographyVariant} from 'lib/ThemeContext' import {isAndroid, isWeb} from 'platform/detection' +import {atoms as a} from '#/alf' import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {TextLinkOnWebOnly} from './Link' import {Text} from './text/Text' @@ -39,9 +40,13 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { const prefetchProfileQuery = usePrefetchProfileQuery() const profileLink = makeProfileLink(opts.author) - const onPointerEnter = isWeb - ? () => prefetchProfileQuery(opts.author.did) - : undefined + const prefetchedProfile = React.useRef(false) + const onPointerMove = React.useCallback(() => { + if (!prefetchedProfile.current) { + prefetchedProfile.current = true + prefetchProfileQuery(opts.author.did) + } + }, [opts.author.did, prefetchProfileQuery]) const queryClient = useQueryClient() const onOpenAuthor = opts.onOpenAuthor @@ -66,37 +71,39 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { )} - - - {sanitizeDisplayName( - displayName, - opts.moderation?.ui('displayName'), - )} - - } - href={profileLink} - onBeforePress={onBeforePressAuthor} - onPointerEnter={onPointerEnter} - /> - - + + + + {sanitizeDisplayName( + displayName, + opts.moderation?.ui('displayName'), + )} + + } + href={profileLink} + onBeforePress={onBeforePressAuthor} + /> + + + {!isAndroid && ( Date: Wed, 12 Jun 2024 21:53:32 -0500 Subject: [PATCH 06/11] Fix profile hover card blocked state (#4480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: mini profile on hover allows following a blocker/blocked user (#4423) (#4440) * Tweaks --------- Co-authored-by: Michał Gołda --- src/components/ProfileHoverCard/index.web.tsx | 79 +++++++++++++------ .../moderation/ProfileHeaderAlerts.tsx | 13 ++- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx index 1fd74fd195..e17977af43 100644 --- a/src/components/ProfileHoverCard/index.web.tsx +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -5,6 +5,7 @@ import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom' import {msg, plural} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {getModerationCauseKey} from '#/lib/moderation' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' @@ -31,6 +32,7 @@ import {Loader} from '#/components/Loader' import {Portal} from '#/components/Portal' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' +import {ProfileLabel} from '../moderation/ProfileHeaderAlerts' import {ProfileHoverCardProps} from './types' const floatingMiddlewares = [ @@ -374,7 +376,10 @@ function Inner({ profile: profileShadow, logContext: 'ProfileHoverCard', }) - const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy + const isBlockedUser = + profile.viewer?.blocking || + profile.viewer?.blockedBy || + profile.viewer?.blockingByList const following = formatCount(profile.followsCount || 0) const followers = formatCount(profile.followersCount || 0) const pluralizedFollowers = plural(profile.followersCount || 0, { @@ -405,29 +410,41 @@ function Inner({ /> - {!isMe && ( - - )} + {!isMe && + (isBlockedUser ? ( + + {_(msg`View profile`)} + + ) : ( + + ))} @@ -443,7 +460,19 @@ function Inner({ - {!blockHide && ( + {isBlockedUser && ( + + {moderation.ui('profileView').alerts.map(cause => ( + + ))} + + )} + + {!isBlockedUser && ( <> - + {!disableDetailsDialog && ( + + )} ) } From 7faa1d9131fc28e1c0acd4b4c7b7c2bbeabc2281 Mon Sep 17 00:00:00 2001 From: Minseo Lee Date: Thu, 13 Jun 2024 11:58:25 +0900 Subject: [PATCH 07/11] KnownFollowers localization (#4494) * Update KnownFollowers.tsx * Update KnownFollowers.tsx * Update KnownFollowers.tsx --- src/components/KnownFollowers.tsx | 52 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/components/KnownFollowers.tsx b/src/components/KnownFollowers.tsx index 3d4d362ef2..a8bdb763d7 100644 --- a/src/components/KnownFollowers.tsx +++ b/src/components/KnownFollowers.tsx @@ -1,7 +1,7 @@ import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' -import {msg, plural, Trans} from '@lingui/macro' +import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {makeProfileLink} from '#/lib/routes/links' @@ -164,31 +164,37 @@ function KnownFollowersInner({ }, ]} numberOfLines={2}> - Followed by{' '} {count > 2 ? ( - <> - {slice.slice(0, 2).map(({profile: prof}, i) => ( - - {prof.displayName} - {i === 0 && ', '} - - ))} - {', '} - {plural(count - 2, { - one: 'and # other', - other: 'and # others', - })} - - ) : count === 2 ? ( - slice.map(({profile: prof}, i) => ( - - {prof.displayName} {i === 0 ? _(msg`and`) + ' ' : ''} + + Followed by{' '} + + {slice[0].profile.displayName} - )) + ,{' '} + + {slice[1].profile.displayName} + + , and{' '} + + + ) : count === 2 ? ( + + Followed by{' '} + + {slice[0].profile.displayName} + {' '} + and{' '} + + {slice[1].profile.displayName} + + ) : ( - - {slice[0].profile.displayName} - + + Followed by{' '} + + {slice[0].profile.displayName} + + )} From 498e46ae4e698e5a75dec9972209fe2bb8a2a603 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 13 Jun 2024 11:03:31 +0200 Subject: [PATCH 08/11] Hide bio and social proof for blocked users (#4504) --- src/screens/Profile/Header/ProfileHeaderStandard.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index f8a87a68e4..4ad84ac633 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -75,6 +75,10 @@ let ProfileHeaderStandard = ({ const [_queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) const unblockPromptControl = Prompt.usePromptControl() const requireAuth = useRequireAuth() + const isBlockedUser = + profile.viewer?.blocking || + profile.viewer?.blockedBy || + profile.viewer?.blockingByList const onPressEditProfile = React.useCallback(() => { track('ProfileHeader:EditProfileButtonClicked') @@ -257,7 +261,7 @@ let ProfileHeaderStandard = ({ - {!isPlaceholderProfile && ( + {!isPlaceholderProfile && !isBlockedUser && ( <> {descriptionRT && !moderation.ui('profileView').blur ? ( @@ -274,6 +278,7 @@ let ProfileHeaderStandard = ({ ) : undefined} {!isMe && + !isBlockedUser && shouldShowKnownFollowers(profile.viewer?.knownFollowers) && ( Date: Thu, 13 Jun 2024 15:58:56 +0100 Subject: [PATCH 09/11] Calculate correct keyboard offset in composer (#4500) * calculate correct keyboard offset * give viewHeight a default value * much simpler approach --- src/view/com/composer/Composer.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 9bb704012e..80bce5351c 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -415,12 +415,14 @@ export const ComposePost = observer(function ComposePost({ bottomBarAnimatedStyle, } = useAnimatedBorders() + const keyboardVerticalOffset = useKeyboardVerticalOffset() + return ( + keyboardVerticalOffset={keyboardVerticalOffset} + style={a.flex_1}> @@ -741,6 +743,19 @@ function useAnimatedBorders() { } } +function useKeyboardVerticalOffset() { + const {top} = useSafeAreaInsets() + + // Android etc + if (!isIOS) return 0 + + // iPhone SE + if (top === 20) return 40 + + // all other iPhones + return top + 10 +} + const styles = StyleSheet.create({ topbarInner: { flexDirection: 'row', From d9066a6beb4a84ab42d638810d25c002601678a4 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 13 Jun 2024 14:31:19 -0700 Subject: [PATCH 10/11] add `document.referrer` to statsig custom (#4509) * add referrer to statsig custom dont include referrer if hostname is bsky.app save add `document.referrer` to statsig custom * add a hostname field * account for ssr * account for ssr --- src/lib/statsig/statsig.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index 166d468a1b..d57f1c0aa6 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -26,6 +26,8 @@ type StatsigUser = { bundleDate: number refSrc: string refUrl: string + referrer: string + referrerHostname: string appLanguage: string contentLanguages: string[] } @@ -33,12 +35,22 @@ type StatsigUser = { let refSrc = '' let refUrl = '' +let referrer = '' +let referrerHostname = '' if (isWeb && typeof window !== 'undefined') { const params = new URLSearchParams(window.location.search) refSrc = params.get('ref_src') ?? '' refUrl = decodeURIComponent(params.get('ref_url') ?? '') } +if (isWeb && typeof document !== 'undefined' && document != null) { + const url = new URL(document.referrer) + if (url.hostname !== 'bsky.app') { + referrer = document.referrer + referrerHostname = url.hostname + } +} + export type {LogEvents} function createStatsigOptions(prefetchUsers: StatsigUser[]) { @@ -198,6 +210,8 @@ function toStatsigUser(did: string | undefined): StatsigUser { custom: { refSrc, refUrl, + referrer, + referrerHostname, platform: Platform.OS as 'ios' | 'android' | 'web', bundleIdentifier: BUNDLE_IDENTIFIER, bundleDate: BUNDLE_DATE, From bdeac28d74abd54b0373663cbd57b7858888280f Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 13 Jun 2024 23:06:22 +0100 Subject: [PATCH 11/11] Try/catch URL parsing of referrer (#4511) --- src/lib/statsig/statsig.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index d57f1c0aa6..f6aed999f9 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -43,12 +43,19 @@ if (isWeb && typeof window !== 'undefined') { refUrl = decodeURIComponent(params.get('ref_url') ?? '') } -if (isWeb && typeof document !== 'undefined' && document != null) { - const url = new URL(document.referrer) - if (url.hostname !== 'bsky.app') { - referrer = document.referrer - referrerHostname = url.hostname - } +if ( + isWeb && + typeof document !== 'undefined' && + document != null && + document.referrer +) { + try { + const url = new URL(document.referrer) + if (url.hostname !== 'bsky.app') { + referrer = document.referrer + referrerHostname = url.hostname + } + } catch {} } export type {LogEvents}