about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-10-26 10:40:35 -0700
committerPaul Frazee <pfrazee@gmail.com>2023-10-26 10:40:35 -0700
commita1a61ef2e5d9d36877c7078580572ae70e468a4a (patch)
tree90878708e958bf268d86b2fa653017b141637e67
parent01e25c912cb156aa4a2c34ddef8206d045ee2b89 (diff)
parent927cee18d9c4425398e90811d4ed24098bf67396 (diff)
downloadvoidsky-a1a61ef2e5d9d36877c7078580572ae70e468a4a.tar.zst
Merge branch 'main' of github.com:bluesky-social/social-app into main
-rw-r--r--src/lib/analytics/analytics.tsx6
-rw-r--r--src/lib/analytics/analytics.web.tsx6
-rw-r--r--src/state/models/feeds/post.ts14
-rw-r--r--src/view/com/modals/CreateOrEditMuteList.tsx4
-rw-r--r--src/view/com/modals/EditProfile.tsx4
-rw-r--r--src/view/com/modals/Waitlist.tsx2
-rw-r--r--src/view/com/modals/crop-image/CropImage.web.tsx6
-rw-r--r--src/view/com/profile/ProfileHeader.tsx11
-rw-r--r--src/view/com/profile/ProfileHeaderSuggestedFollows.tsx12
-rw-r--r--src/view/com/util/Link.tsx9
-rw-r--r--src/view/com/util/UserAvatar.tsx108
-rw-r--r--src/view/com/util/images/ImageLayoutGrid.tsx8
-rw-r--r--src/view/screens/Support.tsx10
13 files changed, 117 insertions, 83 deletions
diff --git a/src/lib/analytics/analytics.tsx b/src/lib/analytics/analytics.tsx
index d1eb50f8a..b3db9149c 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 db9d86e3c..78bd9b42b 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 ae4f29105..d46cced75 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/modals/CreateOrEditMuteList.tsx b/src/view/com/modals/CreateOrEditMuteList.tsx
index 3f3cfc5f0..4a440afeb 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({
         )}
         <Text style={[styles.label, pal.text]}>List Avatar</Text>
         <View style={[styles.avi, {borderColor: pal.colors.background}]}>
-          <UserAvatar
+          <EditableUserAvatar
             type="list"
             size={80}
             avatar={avatar}
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx
index 620aad9fc..58d0857ad 100644
--- a/src/view/com/modals/EditProfile.tsx
+++ b/src/view/com/modals/EditProfile.tsx
@@ -20,7 +20,7 @@ import {enforceLen} from 'lib/strings/helpers'
 import {MAX_DISPLAY_NAME, MAX_DESCRIPTION} from 'lib/constants'
 import {compressIfNeeded} from 'lib/media/manip'
 import {UserBanner} from '../util/UserBanner'
-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'
@@ -153,7 +153,7 @@ export function Component({
             onSelectNewBanner={onSelectNewBanner}
           />
           <View style={[styles.avi, {borderColor: pal.colors.background}]}>
-            <UserAvatar
+            <EditableUserAvatar
               size={80}
               avatar={userAvatar}
               onSelectNewAvatar={onSelectNewAvatar}
diff --git a/src/view/com/modals/Waitlist.tsx b/src/view/com/modals/Waitlist.tsx
index 1104c0a39..0fb371fe4 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"
diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx
index c5959cf4c..8e35201d1 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">
           <RectWideIcon
             size={24}
-            style={as === AspectRatio.Wide ? s.blue3 : undefined}
+            style={as === AspectRatio.Wide ? s.blue3 : pal.text}
           />
         </TouchableOpacity>
         <TouchableOpacity
@@ -110,7 +110,7 @@ export function Component({
           accessibilityHint="Sets image aspect ratio to tall">
           <RectTallIcon
             size={24}
-            style={as === AspectRatio.Tall ? s.blue3 : undefined}
+            style={as === AspectRatio.Tall ? s.blue3 : pal.text}
           />
         </TouchableOpacity>
         <TouchableOpacity
@@ -120,7 +120,7 @@ export function Component({
           accessibilityHint="Sets image aspect ratio to square">
           <SquareIcon
             size={24}
-            style={as === AspectRatio.Square ? s.blue3 : undefined}
+            style={as === AspectRatio.Square ? s.blue3 : pal.text}
           />
         </TouchableOpacity>
       </View>
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index df19ecad5..5514bf98e 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),
     )
diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
index c5b187fb3..cf759ddd1 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,
           }}>
           <View
             style={{
@@ -130,11 +130,15 @@ export function ProfileHeaderSuggestedFollows({
           </View>
 
           <ScrollView
-            horizontal
-            showsHorizontalScrollIndicator={false}
+            horizontal={true}
+            showsHorizontalScrollIndicator={isWeb}
+            persistentScrollbar={true}
+            scrollIndicatorInsets={{bottom: 0}}
+            scrollEnabled={true}
             contentContainerStyle={{
               alignItems: 'flex-start',
               paddingLeft: INNER_PADDING / 2,
+              paddingBottom: INNER_PADDING,
             }}>
             {isLoading ? (
               <>
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index 6915d3e08..35524bcc6 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<typeof TouchableOpacity> {
   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,
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index d24e47499..fbc0b5e11 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -23,14 +23,18 @@ interface BaseUserAvatarProps {
   type?: Type
   size: number
   avatar?: string | null
-  moderation?: ModerationUI
 }
 
 interface UserAvatarProps extends BaseUserAvatarProps {
-  onSelectNewAvatar?: (img: RNImage | null) => 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 (
+      <View style={[styles.alertIconContainer, pal.view]}>
+        <FontAwesomeIcon
+          icon="exclamation-circle"
+          style={styles.alertIcon}
+          size={Math.floor(size / 3)}
+        />
+      </View>
+    )
+  }, [moderation?.alert, size, pal])
+
+  return avatar &&
+    !((moderation?.blur && isAndroid) /* android crashes with blur */) ? (
+    <View style={{width: size, height: size}}>
+      <HighPriorityImage
+        testID="userAvatarImage"
+        style={aviStyle}
+        contentFit="cover"
+        source={{uri: avatar}}
+        blurRadius={moderation?.blur ? BLUR_AMOUNT : 0}
+      />
+      {alert}
+    </View>
+  ) : (
+    <View style={{width: size, height: size}}>
+      <DefaultAvatar type={type} size={size} />
+      {alert}
+    </View>
+  )
+}
+
+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 (
-      <View style={[styles.alertIconContainer, pal.view]}>
-        <FontAwesomeIcon
-          icon="exclamation-circle"
-          style={styles.alertIcon}
-          size={Math.floor(size / 3)}
-        />
-      </View>
-    )
-  }, [moderation?.alert, size, pal])
-
-  // onSelectNewAvatar is only passed as prop on the EditProfile component
-  return onSelectNewAvatar ? (
+  return (
     <NativeDropdown
       testID="changeAvatarBtn"
       items={dropdownItems}
@@ -256,23 +301,6 @@ export function UserAvatar({
         />
       </View>
     </NativeDropdown>
-  ) : avatar &&
-    !((moderation?.blur && isAndroid) /* android crashes with blur */) ? (
-    <View style={{width: size, height: size}}>
-      <HighPriorityImage
-        testID="userAvatarImage"
-        style={aviStyle}
-        contentFit="cover"
-        source={{uri: avatar}}
-        blurRadius={moderation?.blur ? BLUR_AMOUNT : 0}
-      />
-      {alert}
-    </View>
-  ) : (
-    <View style={{width: size, height: size}}>
-      <DefaultAvatar type={type} size={size} />
-      {alert}
-    </View>
   )
 }
 
diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx
index 2e352d086..4aa6f28de 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 (
-        <View style={styles.flexRow}>
-          <View style={{flex: 1}}>
+        <>
+          <View style={styles.flexRow}>
             <View style={styles.smallItem}>
               <GalleryItem {...props} index={0} imageStyle={styles.image} />
             </View>
@@ -72,7 +72,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
               <GalleryItem {...props} index={2} imageStyle={styles.image} />
             </View>
           </View>
-          <View style={{flex: 1}}>
+          <View style={styles.flexRow}>
             <View style={styles.smallItem}>
               <GalleryItem {...props} index={1} imageStyle={styles.image} />
             </View>
@@ -80,7 +80,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
               <GalleryItem {...props} index={3} imageStyle={styles.image} />
             </View>
           </View>
-        </View>
+        </>
       )
 
     default:
diff --git a/src/view/screens/Support.tsx b/src/view/screens/Support.tsx
index de1b38b84..dc00d473d 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<CommonNavigatorParams, 'Support'>
 export const SupportScreen = (_props: Props) => {
@@ -29,14 +30,13 @@ export const SupportScreen = (_props: Props) => {
           Support
         </Text>
         <Text style={[pal.text, s.p20]}>
-          If you need help, email us at{' '}
+          The support form has been moved. If you need help, please
           <TextLink
-            href="mailto:support@bsky.app"
-            text="support@bsky.app"
+            href={HELP_DESK_URL}
+            text=" click here"
             style={pal.link}
           />{' '}
-          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.
         </Text>
       </CenteredView>
     </View>