From c6e28f88e5e96453005ff6f11194f3ccf091f4b6 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 18 Oct 2023 20:08:12 +0100 Subject: [PATCH 1/8] Fix four gallery images for Firefox (#1710) --- src/view/com/util/images/ImageLayoutGrid.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx index 2e352d0864..4aa6f28de5 100644 --- a/src/view/com/util/images/ImageLayoutGrid.tsx +++ b/src/view/com/util/images/ImageLayoutGrid.tsx @@ -63,8 +63,8 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { case 4: return ( - - + <> + @@ -72,7 +72,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { - + @@ -80,7 +80,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { - + ) default: From 32fbb9dba7a0f27993ac4b3d76975ec577464e2d Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 25 Oct 2023 04:57:34 +0100 Subject: [PATCH 2/8] Simplify the avatar component (#1744) * Copypaste UserAvatar to EditableUserAvatar * Swap usages with onSelectNewAvatar to EditableUserAvatar * Split prop types into UserAvatarProps and EditableUserAvatarProps * Remove dead branches from EditableUserAvatar * Remove dead branches from UserAvatar --- src/view/com/modals/CreateOrEditMuteList.tsx | 4 +- src/view/com/modals/EditProfile.tsx | 4 +- src/view/com/util/UserAvatar.tsx | 108 ++++++++++++------- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/view/com/modals/CreateOrEditMuteList.tsx b/src/view/com/modals/CreateOrEditMuteList.tsx index 3f3cfc5f01..4a440afebe 100644 --- a/src/view/com/modals/CreateOrEditMuteList.tsx +++ b/src/view/com/modals/CreateOrEditMuteList.tsx @@ -18,7 +18,7 @@ import {ListModel} from 'state/models/content/list' import {s, colors, gradients} from 'lib/styles' import {enforceLen} from 'lib/strings/helpers' import {compressIfNeeded} from 'lib/media/manip' -import {UserAvatar} from '../util/UserAvatar' +import {EditableUserAvatar} from '../util/UserAvatar' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {useAnalytics} from 'lib/analytics/analytics' @@ -148,7 +148,7 @@ export function Component({ )} List Avatar - - void + moderation?: ModerationUI +} + +interface EditableUserAvatarProps extends BaseUserAvatarProps { + onSelectNewAvatar: (img: RNImage | null) => void } interface PreviewableUserAvatarProps extends BaseUserAvatarProps { + moderation?: ModerationUI did: string handle: string } @@ -106,8 +110,65 @@ export function UserAvatar({ size, avatar, moderation, - onSelectNewAvatar, }: UserAvatarProps) { + const pal = usePalette('default') + + const aviStyle = useMemo(() => { + if (type === 'algo' || type === 'list') { + return { + width: size, + height: size, + borderRadius: size > 32 ? 8 : 3, + } + } + return { + width: size, + height: size, + borderRadius: Math.floor(size / 2), + } + }, [type, size]) + + const alert = useMemo(() => { + if (!moderation?.alert) { + return null + } + return ( + + + + ) + }, [moderation?.alert, size, pal]) + + return avatar && + !((moderation?.blur && isAndroid) /* android crashes with blur */) ? ( + + + {alert} + + ) : ( + + + {alert} + + ) +} + +export function EditableUserAvatar({ + type = 'user', + size, + avatar, + onSelectNewAvatar, +}: EditableUserAvatarProps) { const store = useStores() const pal = usePalette('default') const {requestCameraAccessIfNeeded} = useCameraPermission() @@ -146,7 +207,7 @@ export function UserAvatar({ return } - onSelectNewAvatar?.( + onSelectNewAvatar( await openCamera(store, { width: 1000, height: 1000, @@ -186,7 +247,7 @@ export function UserAvatar({ path: item.path, }) - onSelectNewAvatar?.(croppedImage) + onSelectNewAvatar(croppedImage) }, }, !!avatar && { @@ -203,7 +264,7 @@ export function UserAvatar({ web: 'trash', }, onPress: async () => { - onSelectNewAvatar?.(null) + onSelectNewAvatar(null) }, }, ].filter(Boolean) as DropdownItem[], @@ -216,23 +277,7 @@ export function UserAvatar({ ], ) - const alert = useMemo(() => { - if (!moderation?.alert) { - return null - } - return ( - - - - ) - }, [moderation?.alert, size, pal]) - - // onSelectNewAvatar is only passed as prop on the EditProfile component - return onSelectNewAvatar ? ( + return ( - ) : avatar && - !((moderation?.blur && isAndroid) /* android crashes with blur */) ? ( - - - {alert} - - ) : ( - - - {alert} - ) } From 4a4be1de1a3475d034fe9a95493c50222ef90e7e Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 25 Oct 2023 16:47:21 +0100 Subject: [PATCH 3/8] Remove MobX from Link (#1745) * Remove MobX from Link * Keep memo() though We previously had observer() apply it automatically, so re-add it --- src/view/com/util/Link.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx index 6915d3e083..35524bcc65 100644 --- a/src/view/com/util/Link.tsx +++ b/src/view/com/util/Link.tsx @@ -1,5 +1,4 @@ -import React, {ComponentProps, useMemo} from 'react' -import {observer} from 'mobx-react-lite' +import React, {ComponentProps, memo, useMemo} from 'react' import { Linking, GestureResponderEvent, @@ -50,7 +49,7 @@ interface Props extends ComponentProps { anchorNoUnderline?: boolean } -export const Link = observer(function Link({ +export const Link = memo(function Link({ testID, style, href, @@ -136,7 +135,7 @@ export const Link = observer(function Link({ ) }) -export const TextLink = observer(function TextLink({ +export const TextLink = memo(function TextLink({ testID, type = 'md', style, @@ -236,7 +235,7 @@ interface DesktopWebTextLinkProps extends TextProps { accessibilityHint?: string title?: string } -export const DesktopWebTextLink = observer(function DesktopWebTextLink({ +export const DesktopWebTextLink = memo(function DesktopWebTextLink({ testID, type = 'md', style, From 3426b24f67c571b697064d79ea83894f9a2f76e1 Mon Sep 17 00:00:00 2001 From: elliot Date: Wed, 25 Oct 2023 12:41:56 -0400 Subject: [PATCH 4/8] Fixed CropImage modal aspect ratio buttons visibility in dark mode (#1730) --- src/view/com/modals/crop-image/CropImage.web.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx index c5959cf4c1..8e35201d13 100644 --- a/src/view/com/modals/crop-image/CropImage.web.tsx +++ b/src/view/com/modals/crop-image/CropImage.web.tsx @@ -100,7 +100,7 @@ export function Component({ accessibilityHint="Sets image aspect ratio to wide"> From 1781b3aab737a83898dac450daa1d19beb96bb38 Mon Sep 17 00:00:00 2001 From: Ansh Date: Wed, 25 Oct 2023 09:54:04 -0700 Subject: [PATCH 5/8] allow enter key to trigger waitlist signup (#1723) --- src/view/com/modals/Waitlist.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/view/com/modals/Waitlist.tsx b/src/view/com/modals/Waitlist.tsx index 1104c0a397..0fb371fe4d 100644 --- a/src/view/com/modals/Waitlist.tsx +++ b/src/view/com/modals/Waitlist.tsx @@ -77,6 +77,8 @@ export function Component({}: {}) { keyboardAppearance={theme.colorScheme} value={email} onChangeText={setEmail} + onSubmitEditing={onPressSignup} + enterKeyHint="done" accessible={true} accessibilityLabel="Email" accessibilityHint="Input your email to get on the Bluesky waitlist" From ee80cae75f25da33f6527c3df16c90319eeaf0bc Mon Sep 17 00:00:00 2001 From: Ansh Date: Wed, 25 Oct 2023 09:54:52 -0700 Subject: [PATCH 6/8] add scroll indicator on web to profile suggested follows card (#1724) --- .../com/profile/ProfileHeaderSuggestedFollows.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx index c5b187fb3d..cf759ddd17 100644 --- a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx +++ b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {View, StyleSheet, ScrollView, Pressable} from 'react-native' +import {View, StyleSheet, Pressable, ScrollView} from 'react-native' import Animated, { useSharedValue, withTiming, @@ -26,6 +26,7 @@ import {sanitizeHandle} from 'lib/strings/handles' import {makeProfileLink} from 'lib/routes/links' import {Link} from 'view/com/util/Link' import {useAnalytics} from 'lib/analytics/analytics' +import {isWeb} from 'platform/detection' const OUTER_PADDING = 10 const INNER_PADDING = 14 @@ -100,7 +101,6 @@ export function ProfileHeaderSuggestedFollows({ backgroundColor: pal.viewLight.backgroundColor, height: '100%', paddingTop: INNER_PADDING / 2, - paddingBottom: INNER_PADDING, }}> {isLoading ? ( <> From c13b6946bab9600cba670478c2f64771cc75f1af Mon Sep 17 00:00:00 2001 From: Ansh Date: Wed, 25 Oct 2023 10:00:46 -0700 Subject: [PATCH 7/8] 1721 Update support link (#1722) * change support page copy * fix grammar --- src/view/screens/Support.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/view/screens/Support.tsx b/src/view/screens/Support.tsx index de1b38b848..dc00d473d5 100644 --- a/src/view/screens/Support.tsx +++ b/src/view/screens/Support.tsx @@ -9,6 +9,7 @@ import {TextLink} from 'view/com/util/Link' import {CenteredView} from 'view/com/util/Views' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' +import {HELP_DESK_URL} from 'lib/constants' type Props = NativeStackScreenProps export const SupportScreen = (_props: Props) => { @@ -29,14 +30,13 @@ export const SupportScreen = (_props: Props) => { Support - If you need help, email us at{' '} + The support form has been moved. If you need help, please {' '} - with a description of your issue and information about how we can help - you. + or visit {HELP_DESK_URL} to get in touch with us. From 927cee18d9c4425398e90811d4ed24098bf67396 Mon Sep 17 00:00:00 2001 From: Ansh Date: Thu, 26 Oct 2023 08:37:14 -0700 Subject: [PATCH 8/8] Update Analytics (#1743) --- src/lib/analytics/analytics.tsx | 6 +++--- src/lib/analytics/analytics.web.tsx | 6 +++--- src/state/models/feeds/post.ts | 14 ++++++++------ src/view/com/profile/ProfileHeader.tsx | 11 +++++------ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/lib/analytics/analytics.tsx b/src/lib/analytics/analytics.tsx index d1eb50f8a9..b3db9149c9 100644 --- a/src/lib/analytics/analytics.tsx +++ b/src/lib/analytics/analytics.tsx @@ -51,10 +51,10 @@ export function init(store: RootStoreModel) { store.onSessionLoaded(() => { const sess = store.session.currentSession if (sess) { - if (sess.email) { + if (sess.did) { + const did_hashed = sha256(sess.did) + segmentClient.identify(did_hashed, {did_hashed}) store.log.debug('Ping w/hash') - const email_hashed = sha256(sess.email) - segmentClient.identify(email_hashed, {email_hashed}) } else { store.log.debug('Ping w/o hash') segmentClient.identify() diff --git a/src/lib/analytics/analytics.web.tsx b/src/lib/analytics/analytics.web.tsx index db9d86e3cc..78bd9b42bb 100644 --- a/src/lib/analytics/analytics.web.tsx +++ b/src/lib/analytics/analytics.web.tsx @@ -46,10 +46,10 @@ export function init(store: RootStoreModel) { store.onSessionLoaded(() => { const sess = store.session.currentSession if (sess) { - if (sess.email) { + if (sess.did) { + const did_hashed = sha256(sess.did) + segmentClient.identify(did_hashed, {did_hashed}) store.log.debug('Ping w/hash') - const email_hashed = sha256(sess.email) - segmentClient.identify(email_hashed, {email_hashed}) } else { store.log.debug('Ping w/o hash') segmentClient.identify() diff --git a/src/state/models/feeds/post.ts b/src/state/models/feeds/post.ts index ae4f291054..d46cced756 100644 --- a/src/state/models/feeds/post.ts +++ b/src/state/models/feeds/post.ts @@ -116,6 +116,7 @@ export class PostsFeedItemModel { }, () => this.rootStore.agent.deleteLike(url), ) + track('Post:Unlike') } else { // like await updateDataOptimistically( @@ -129,11 +130,10 @@ export class PostsFeedItemModel { this.post.viewer!.like = res.uri }, ) + track('Post:Like') } } catch (error) { this.rootStore.log.error('Failed to toggle like', error) - } finally { - track(this.post.viewer.like ? 'Post:Unlike' : 'Post:Like') } } @@ -141,6 +141,7 @@ export class PostsFeedItemModel { this.post.viewer = this.post.viewer || {} try { if (this.post.viewer?.repost) { + // unrepost const url = this.post.viewer.repost await updateDataOptimistically( this.post, @@ -150,7 +151,9 @@ export class PostsFeedItemModel { }, () => this.rootStore.agent.deleteRepost(url), ) + track('Post:Unrepost') } else { + // repost await updateDataOptimistically( this.post, () => { @@ -162,11 +165,10 @@ export class PostsFeedItemModel { this.post.viewer!.repost = res.uri }, ) + track('Post:Repost') } } catch (error) { this.rootStore.log.error('Failed to toggle repost', error) - } finally { - track(this.post.viewer.repost ? 'Post:Unrepost' : 'Post:Repost') } } @@ -174,13 +176,13 @@ export class PostsFeedItemModel { try { if (this.isThreadMuted) { this.rootStore.mutedThreads.uris.delete(this.rootUri) + track('Post:ThreadUnmute') } else { this.rootStore.mutedThreads.uris.add(this.rootUri) + track('Post:ThreadMute') } } catch (error) { this.rootStore.log.error('Failed to toggle thread mute', error) - } finally { - track(this.isThreadMuted ? 'Post:ThreadUnmute' : 'Post:ThreadMute') } } diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index df19ecad57..5514bf98e9 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -132,20 +132,19 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ }, [store, view]) const onPressToggleFollow = React.useCallback(() => { - track( - view.viewer.following - ? 'ProfileHeader:FollowButtonClicked' - : 'ProfileHeader:UnfollowButtonClicked', - ) view?.toggleFollowing().then( () => { setShowSuggestedFollows(Boolean(view.viewer.following)) - Toast.show( `${ view.viewer.following ? 'Following' : 'No longer following' } ${sanitizeDisplayName(view.displayName || view.handle)}`, ) + track( + view.viewer.following + ? 'ProfileHeader:FollowButtonClicked' + : 'ProfileHeader:UnfollowButtonClicked', + ) }, err => store.log.error('Failed to toggle follow', err), )