diff --git a/src/lib/analytics/types.ts b/src/lib/analytics/types.ts
index 7caa9b3577..5858846326 100644
--- a/src/lib/analytics/types.ts
+++ b/src/lib/analytics/types.ts
@@ -129,6 +129,7 @@ interface ScreenPropertiesMap {
Feed: {}
Notifications: {}
Profile: {}
+ 'Profile:Preview': {}
Settings: {}
AppPasswords: {}
Moderation: {}
diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts
index ba03fe1b58..c6e7289df0 100644
--- a/src/state/models/ui/shell.ts
+++ b/src/state/models/ui/shell.ts
@@ -31,6 +31,11 @@ export interface EditProfileModal {
onUpdate?: () => void
}
+export interface ProfilePreviewModal {
+ name: 'profile-preview'
+ did: string
+}
+
export interface ServerInputModal {
name: 'server-input'
initialService: string
@@ -128,6 +133,7 @@ export type Modal =
| ChangeHandleModal
| DeleteAccountModal
| EditProfileModal
+ | ProfilePreviewModal
// Curation
| ContentFilteringSettingsModal
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index b276dabc04..ad8794e89e 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -9,6 +9,7 @@ import {usePalette} from 'lib/hooks/usePalette'
import * as ConfirmModal from './Confirm'
import * as EditProfileModal from './EditProfile'
+import * as ProfilePreviewModal from './ProfilePreview'
import * as ServerInputModal from './ServerInput'
import * as ReportPostModal from './report/ReportPost'
import * as RepostModal from './Repost'
@@ -62,6 +63,9 @@ export const ModalsContainer = observer(function ModalsContainer() {
} else if (activeModal?.name === 'edit-profile') {
snapPoints = EditProfileModal.snapPoints
element =
+ } else if (activeModal?.name === 'profile-preview') {
+ snapPoints = ProfilePreviewModal.snapPoints
+ element =
} else if (activeModal?.name === 'server-input') {
snapPoints = ServerInputModal.snapPoints
element =
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 77842d3e1e..20312fe6bd 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -8,6 +8,7 @@ import {isMobileWeb} from 'platform/detection'
import * as ConfirmModal from './Confirm'
import * as EditProfileModal from './EditProfile'
+import * as ProfilePreviewModal from './ProfilePreview'
import * as ServerInputModal from './ServerInput'
import * as ReportPostModal from './report/ReportPost'
import * as ReportAccountModal from './report/ReportAccount'
@@ -68,6 +69,8 @@ function Modal({modal}: {modal: ModalIface}) {
element =
} else if (modal.name === 'edit-profile') {
element =
+ } else if (modal.name === 'profile-preview') {
+ element =
} else if (modal.name === 'server-input') {
element =
} else if (modal.name === 'report-post') {
diff --git a/src/view/com/modals/ProfilePreview.tsx b/src/view/com/modals/ProfilePreview.tsx
new file mode 100644
index 0000000000..d3267644b6
--- /dev/null
+++ b/src/view/com/modals/ProfilePreview.tsx
@@ -0,0 +1,89 @@
+import React, {useState, useEffect, useCallback} from 'react'
+import {StyleSheet, View} from 'react-native'
+import {observer} from 'mobx-react-lite'
+import {useNavigation, StackActions} from '@react-navigation/native'
+import {Text} from '../util/text/Text'
+import {useStores} from 'state/index'
+import {ProfileModel} from 'state/models/content/profile'
+import {usePalette} from 'lib/hooks/usePalette'
+import {useAnalytics} from 'lib/analytics/analytics'
+import {ProfileHeader} from '../profile/ProfileHeader'
+import {Button} from '../util/forms/Button'
+import {NavigationProp} from 'lib/routes/types'
+
+export const snapPoints = [560]
+
+export const Component = observer(({did}: {did: string}) => {
+ const store = useStores()
+ const pal = usePalette('default')
+ const palInverted = usePalette('inverted')
+ const navigation = useNavigation()
+ const [model] = useState(new ProfileModel(store, {actor: did}))
+ const {screen} = useAnalytics()
+
+ useEffect(() => {
+ screen('Profile:Preview')
+ model.setup()
+ }, [model, screen])
+
+ const onPressViewProfile = useCallback(() => {
+ navigation.dispatch(StackActions.push('Profile', {name: model.handle}))
+ store.shell.closeModal()
+ }, [navigation, store, model])
+
+ return (
+
+
+ {}} />
+
+
+
+
+
+
+
+
+ )
+})
+
+const styles = StyleSheet.create({
+ headerWrapper: {
+ height: 440,
+ },
+ buttonsContainer: {
+ height: 120,
+ },
+ buttons: {
+ flexDirection: 'row',
+ gap: 8,
+ paddingHorizontal: 14,
+ paddingTop: 16,
+ },
+ button: {
+ flex: 2,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ paddingVertical: 12,
+ },
+ buttonWide: {
+ flex: 3,
+ },
+})
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index e1c73c0d5f..133d38421a 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -13,7 +13,7 @@ import {RichText} from '../util/text/RichText'
import {Text} from '../util/text/Text'
import {PostDropdownBtn} from '../util/forms/DropdownButton'
import * as Toast from '../util/Toast'
-import {UserAvatar} from '../util/UserAvatar'
+import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
import {ago, niceDate} from 'lib/strings/time'
import {sanitizeDisplayName} from 'lib/strings/display-names'
@@ -163,22 +163,17 @@ export const PostThreadItem = observer(function PostThreadItem({
-
-
-
+
-
+
-
-
-
+
{replyAuthorDid !== '' && (
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index 921f231907..5035d345d5 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -28,7 +28,6 @@ const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
export const Feed = observer(function Feed({
feed,
style,
- showPostFollowBtn,
scrollElRef,
onPressTryAgain,
onScroll,
@@ -41,7 +40,6 @@ export const Feed = observer(function Feed({
}: {
feed: PostsFeedModel
style?: StyleProp
- showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject | null>
onPressTryAgain?: () => void
onScroll?: OnScrollCb
@@ -138,15 +136,9 @@ export const Feed = observer(function Feed({
} else if (item === LOADING_ITEM) {
return
}
- return
+ return
},
- [
- feed,
- onPressTryAgain,
- onPressRetryLoadMore,
- showPostFollowBtn,
- renderEmptyState,
- ],
+ [feed, onPressTryAgain, onPressRetryLoadMore, renderEmptyState],
)
const FeedFooter = React.useCallback(
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index 6ec2c80f44..e1b160dcb2 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -21,7 +21,7 @@ import {ImageHider} from '../util/moderation/ImageHider'
import {RichText} from '../util/text/RichText'
import {PostSandboxWarning} from '../util/PostSandboxWarning'
import * as Toast from '../util/Toast'
-import {UserAvatar} from '../util/UserAvatar'
+import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
import {useStores} from 'state/index'
import {usePalette} from 'lib/hooks/usePalette'
@@ -33,14 +33,12 @@ export const FeedItem = observer(function ({
item,
isThreadChild,
isThreadParent,
- showFollowBtn,
ignoreMuteFor,
}: {
item: PostsFeedItemModel
isThreadChild?: boolean
isThreadParent?: boolean
showReplyLine?: boolean
- showFollowBtn?: boolean
ignoreMuteFor?: string
}) {
const store = useStores()
@@ -55,7 +53,6 @@ export const FeedItem = observer(function ({
return `/profile/${item.post.author.handle}/post/${urip.rkey}`
}, [item.post.uri, item.post.author.handle])
const itemTitle = `Post by ${item.post.author.handle}`
- const authorHref = `/profile/${item.post.author.handle}`
const replyAuthorDid = useMemo(() => {
if (!record?.reply) {
return ''
@@ -214,13 +211,13 @@ export const FeedItem = observer(function ({
-
-
-
+
{!isThreadChild && replyAuthorDid !== '' && (
@@ -357,9 +352,9 @@ const styles = StyleSheet.create({
layout: {
flexDirection: 'row',
marginTop: 1,
+ gap: 10,
},
layoutAvi: {
- width: 70,
paddingLeft: 8,
},
layoutContent: {
diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx
index d75ff13850..8ac813b922 100644
--- a/src/view/com/posts/FeedSlice.tsx
+++ b/src/view/com/posts/FeedSlice.tsx
@@ -11,11 +11,9 @@ import {ModerationBehaviorCode} from 'lib/labeling/types'
export function FeedSlice({
slice,
- showFollowBtn,
ignoreMuteFor,
}: {
slice: PostsFeedSliceModel
- showFollowBtn?: boolean
ignoreMuteFor?: string
}) {
if (slice.moderation.list.behavior === ModerationBehaviorCode.Hide) {
@@ -32,7 +30,6 @@ export function FeedSlice({
item={slice.items[0]}
isThreadParent={slice.isThreadParentAt(0)}
isThreadChild={slice.isThreadChildAt(0)}
- showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
@@ -49,7 +45,6 @@ export function FeedSlice({
item={slice.items[last]}
isThreadParent={slice.isThreadParentAt(last)}
isThreadChild={slice.isThreadChildAt(last)}
- showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
>
@@ -64,7 +59,6 @@ export function FeedSlice({
item={item}
isThreadParent={slice.isThreadParentAt(i)}
isThreadChild={slice.isThreadChildAt(i)}
- showFollowBtn={showFollowBtn}
ignoreMuteFor={ignoreMuteFor}
/>
))}
diff --git a/src/view/com/posts/MultiFeed.tsx b/src/view/com/posts/MultiFeed.tsx
index 466a7a47d2..97899e554e 100644
--- a/src/view/com/posts/MultiFeed.tsx
+++ b/src/view/com/posts/MultiFeed.tsx
@@ -28,7 +28,6 @@ import {CogIcon} from 'lib/icons'
export const MultiFeed = observer(function Feed({
multifeed,
style,
- showPostFollowBtn,
scrollElRef,
onScroll,
scrollEventThrottle,
@@ -38,7 +37,6 @@ export const MultiFeed = observer(function Feed({
}: {
multifeed: PostsMultiFeedModel
style?: StyleProp
- showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject | null>
onPressTryAgain?: () => void
onScroll?: OnScrollCb
@@ -105,9 +103,7 @@ export const MultiFeed = observer(function Feed({
)
} else if (item.type === 'feed-slice') {
- return (
-
- )
+ return
} else if (item.type === 'feed-loading') {
return
} else if (item.type === 'feed-error') {
@@ -139,7 +135,7 @@ export const MultiFeed = observer(function Feed({
}
return null
},
- [showPostFollowBtn, pal],
+ [pal],
)
const ListFooter = React.useCallback(
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index b142e7616b..46ff3d9794 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -6,10 +6,7 @@ import {
TouchableWithoutFeedback,
View,
} from 'react-native'
-import {
- FontAwesomeIcon,
- FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native'
import {BlurView} from '../util/BlurView'
import {ProfileModel} from 'state/models/content/profile'
@@ -102,6 +99,7 @@ export const ProfileHeader = observer(
const ProfileHeaderLoaded = observer(
({view, onRefreshAll, hideBackButton = false}: Props) => {
const pal = usePalette('default')
+ const palInverted = usePalette('inverted')
const store = useStores()
const navigation = useNavigation()
const {track} = useAnalytics()
@@ -351,15 +349,15 @@ const ProfileHeaderLoaded = observer(
-
+
Follow
@@ -609,7 +607,6 @@ const styles = StyleSheet.create({
},
description: {
- flex: 1,
marginBottom: 8,
},
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index 1dec97e78d..454fd7c21c 100644
--- a/src/view/com/util/Link.tsx
+++ b/src/view/com/util/Link.tsx
@@ -6,6 +6,7 @@ import {
Platform,
StyleProp,
TextStyle,
+ TextProps,
View,
ViewStyle,
TouchableOpacity,
@@ -144,7 +145,7 @@ export const TextLink = observer(function TextLink({
numberOfLines?: number
lineHeight?: number
dataSet?: any
-}) {
+} & TextProps) {
const {...props} = useLinkProps({to: sanitizeUrl(href)})
const store = useStores()
const navigation = useNavigation()
@@ -186,16 +187,7 @@ export const TextLink = observer(function TextLink({
/**
* Only acts as a link on desktop web
*/
-export const DesktopWebTextLink = observer(function DesktopWebTextLink({
- testID,
- type = 'md',
- style,
- href,
- text,
- numberOfLines,
- lineHeight,
- ...props
-}: {
+interface DesktopWebTextLinkProps extends TextProps {
testID?: string
type?: TypographyVariant
style?: StyleProp
@@ -206,7 +198,17 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({
accessible?: boolean
accessibilityLabel?: string
accessibilityHint?: string
-}) {
+}
+export const DesktopWebTextLink = observer(function DesktopWebTextLink({
+ testID,
+ type = 'md',
+ style,
+ href,
+ text,
+ numberOfLines,
+ lineHeight,
+ ...props
+}: DesktopWebTextLinkProps) {
if (isDesktopWeb) {
return (
{
- setDidFollow(true)
- }, [setDidFollow])
-
- if (
- opts.showFollowBtn &&
- !isMe &&
- (followState === FollowState.NotFollowing || didFollow) &&
- opts.did
- ) {
- // two-liner with follow button
- return (
-
-
-
-
-
- ·
-
-
-
-
-
-
-
-
-
-
- )
- }
-
- // one-liner
return (
-
+
{typeof opts.authorAvatar !== 'undefined' && (
-
+
)}
-
+
-
- ·
-
+ {!isAndroid && (
+
+ ·
+
+ )}
void
+}
+
+interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
+ did: string
+ handle: string
+}
+
const BLUR_AMOUNT = isWeb ? 5 : 100
function DefaultAvatar({type, size}: {type: Type; size: number}) {
@@ -91,13 +109,7 @@ export function UserAvatar({
avatar,
moderation,
onSelectNewAvatar,
-}: {
- type?: Type
- size: number
- avatar?: string | null
- moderation?: AvatarModeration
- onSelectNewAvatar?: (img: RNImage | null) => void
-}) {
+}: UserAvatarProps) {
const store = useStores()
const pal = usePalette('default')
const {requestCameraAccessIfNeeded} = useCameraPermission()
@@ -244,6 +256,32 @@ export function UserAvatar({
)
}
+export function PreviewableUserAvatar(props: PreviewableUserAvatarProps) {
+ const store = useStores()
+
+ if (isDesktopWeb) {
+ return (
+
+
+
+ )
+ }
+ return (
+
+ store.shell.openModal({
+ name: 'profile-preview',
+ did: props.did,
+ })
+ }
+ accessibilityRole="button"
+ accessibilityLabel={props.handle}
+ accessibilityHint="">
+
+
+ )
+}
+
const styles = StyleSheet.create({
editButtonContainer: {
position: 'absolute',
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index 7d44523841..1ab59f7362 100644
--- a/src/view/screens/Feeds.tsx
+++ b/src/view/screens/Feeds.tsx
@@ -106,7 +106,6 @@ export const FeedsScreen = withAuthRequired(
onScroll={onMainScroll}
scrollEventThrottle={100}
headerOffset={HEADER_OFFSET}
- showPostFollowBtn
/>