diff --git a/src/state/models/profile-ui.ts b/src/state/models/profile-ui.ts
index 830dc22b..0ad893dd 100644
--- a/src/state/models/profile-ui.ts
+++ b/src/state/models/profile-ui.ts
@@ -3,19 +3,21 @@ import {RootStoreModel} from './root-store'
import {ProfileViewModel} from './profile-view'
import {FeedModel} from './feed-view'
-export const SECTION_IDS = {
- POSTS: 0,
- BADGES: 1,
+export enum Sections {
+ Posts = 'Posts',
+ Scenes = 'Scenes',
+ Trending = 'Trending',
+ Members = 'Members',
}
+const USER_SELECTOR_ITEMS = [Sections.Posts, Sections.Scenes]
+const SCENE_SELECTOR_ITEMS = [Sections.Trending, Sections.Members]
+
export interface ProfileUiParams {
user: string
}
export class ProfileUiModel {
- // constants
- static SELECTOR_ITEMS = ['Posts', 'Scenes']
-
// data
profile: ProfileViewModel
feed: FeedModel
@@ -43,7 +45,10 @@ export class ProfileUiModel {
}
get currentView(): FeedModel {
- if (this.selectedViewIndex === SECTION_IDS.POSTS) {
+ if (
+ this.selectedView === Sections.Posts ||
+ this.selectedView === Sections.Trending
+ ) {
return this.feed
}
throw new Error(`Invalid selector value: ${this.selectedViewIndex}`)
@@ -58,6 +63,28 @@ export class ProfileUiModel {
return this.profile.isRefreshing || this.currentView.isRefreshing
}
+ get isUser() {
+ return this.profile.isUser
+ }
+
+ get isScene() {
+ return this.profile.isScene
+ }
+
+ get selectorItems() {
+ if (this.isUser) {
+ return USER_SELECTOR_ITEMS
+ } else if (this.isScene) {
+ return SCENE_SELECTOR_ITEMS
+ } else {
+ return USER_SELECTOR_ITEMS
+ }
+ }
+
+ get selectedView() {
+ return this.selectorItems[this.selectedViewIndex]
+ }
+
// public api
// =
diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts
index ebb75bdb..09f1991e 100644
--- a/src/state/models/profile-view.ts
+++ b/src/state/models/profile-view.ts
@@ -4,6 +4,9 @@ import * as Profile from '../../third-party/api/src/client/types/app/bsky/actor/
import {RootStoreModel} from './root-store'
import * as apilib from '../lib/api'
+export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser'
+export const ACTOR_TYPE_SCENE = 'app.bsky.system.actorScene'
+
export class ProfileViewMyStateModel {
follow?: string
@@ -23,6 +26,7 @@ export class ProfileViewModel {
// data
did: string = ''
handle: string = ''
+ actorType = ACTOR_TYPE_USER
displayName?: string
description?: string
followersCount: number = 0
@@ -57,6 +61,14 @@ export class ProfileViewModel {
return this.hasLoaded && !this.hasContent
}
+ get isUser() {
+ return this.actorType === ACTOR_TYPE_USER
+ }
+
+ get isScene() {
+ return this.actorType === ACTOR_TYPE_SCENE
+ }
+
// public api
// =
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index e1b46f4c..d492aa1f 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -102,9 +102,88 @@ export const ProfileHeader = observer(function ProfileHeader({
/>
-
+
+ {isMe ? (
+
+ Edit Profile
+
+ ) : (
+ <>
+ {view.myState.follow ? (
+
+
+ Following
+
+ ) : (
+
+
+
+ Follow
+
+
+ )}
+ >
+ )}
+
+
+
+
+
{view.displayName}
+
+ {view.isScene ? (
+
+ Scene
+
+ ) : undefined}
+ @{view.handle}
+
+
+
+ {view.followersCount}
+
+ {pluralize(view.followersCount, 'follower')}
+
+
+ {view.isUser ? (
+
+ {view.followsCount}
+ following
+
+ ) : undefined}
+ {view.isScene ? (
+
+ {view.followsCount}
+
+ {pluralize(view.followsCount, 'member')}
+
+
+ ) : undefined}
+
+ {view.postsCount}
+ {pluralize(view.postsCount, 'post')}
+
+
+ {view.description && (
+ {view.description}
+ )}
{
undefined /*
@@ -115,71 +194,6 @@ export const ProfileHeader = observer(function ProfileHeader({
*/
}
-
- {isMe ? (
-
- Edit Profile
-
- ) : (
- <>
- {view.myState.follow ? (
-
-
- Following
-
- ) : (
-
-
- Follow
-
- )}
-
-
-
-
-
-
- >
- )}
-
-
-
-
-
-
- {view.followersCount}
-
- {pluralize(view.followersCount, 'follower')}
-
-
-
- {view.followsCount}
- following
-
-
- {view.postsCount}
- {pluralize(view.postsCount, 'post')}
-
-
- {view.description && (
- {view.description}
- )}
)
@@ -222,46 +236,70 @@ const styles = StyleSheet.create({
paddingHorizontal: 14,
paddingBottom: 4,
},
+
+ buttonsLine: {
+ flexDirection: 'row',
+ marginLeft: 'auto',
+ marginBottom: 12,
+ },
+ gradientBtn: {
+ paddingHorizontal: 24,
+ paddingVertical: 6,
+ },
+ mainBtn: {
+ paddingHorizontal: 24,
+ },
+ secondaryBtn: {
+ paddingHorizontal: 14,
+ },
+ btn: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ paddingVertical: 7,
+ borderRadius: 50,
+ backgroundColor: colors.gray1,
+ marginLeft: 6,
+ },
+
displayNameLine: {
- paddingLeft: 86,
- marginBottom: 14,
+ // paddingLeft: 86,
+ // marginBottom: 14,
},
displayName: {
fontSize: 24,
fontWeight: 'bold',
},
+
+ handleLine: {
+ flexDirection: 'row',
+ marginBottom: 8,
+ },
+ handle: {
+ fontSize: 14,
+ fontWeight: 'bold',
+ color: colors.gray5,
+ },
+ typeLabelWrapper: {
+ backgroundColor: colors.gray1,
+ paddingHorizontal: 4,
+ borderRadius: 4,
+ marginRight: 5,
+ },
+ typeLabel: {
+ fontSize: 14,
+ fontWeight: 'bold',
+ color: colors.gray5,
+ },
+
+ metricsLine: {
+ flexDirection: 'row',
+ marginBottom: 8,
+ },
+
badgesLine: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
},
- buttonsLine: {
- flexDirection: 'row',
- marginBottom: 12,
- },
- followBtn: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- paddingVertical: 6,
- borderRadius: 6,
- marginRight: 6,
- },
- btn: {
- flex: 1,
- alignItems: 'center',
- justifyContent: 'center',
- paddingVertical: 7,
- borderRadius: 4,
- backgroundColor: colors.gray1,
- marginRight: 6,
- },
- mainBtn: {
- flexDirection: 'row',
- },
- secondaryBtn: {
- flex: 0,
- paddingHorizontal: 14,
- marginRight: 0,
- },
})
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index f5f4f553..6f7281bd 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -1,9 +1,9 @@
-import React, {useEffect, useState} from 'react'
+import React, {useEffect, useState, useMemo} from 'react'
import {StyleSheet, Text, View} from 'react-native'
import {observer} from 'mobx-react-lite'
import {ViewSelector} from '../com/util/ViewSelector'
import {ScreenParams} from '../routes'
-import {ProfileUiModel, SECTION_IDS} from '../../state/models/profile-ui'
+import {ProfileUiModel, Sections} from '../../state/models/profile-ui'
import {useStores} from '../../state'
import {ProfileHeader} from '../com/profile/ProfileHeader'
import {FeedItem} from '../com/posts/FeedItem'
@@ -18,25 +18,23 @@ const EMPTY_ITEM = {_reactKey: '__empty__'}
export const Profile = observer(({visible, params}: ScreenParams) => {
const store = useStores()
const [hasSetup, setHasSetup] = useState(false)
- const [profileUiState, setProfileUiState] = useState<
- ProfileUiModel | undefined
- >()
+ const uiState = useMemo(
+ () => new ProfileUiModel(store, {user: params.name}),
+ [params.user],
+ )
useEffect(() => {
let aborted = false
if (!visible) {
return
}
- const user = params.name
if (hasSetup) {
- console.log('Updating profile for', user)
- profileUiState?.update()
+ console.log('Updating profile for', params.name)
+ uiState.update()
} else {
- console.log('Fetching profile for', user)
- store.nav.setTitle(user)
- const newProfileUiState = new ProfileUiModel(store, {user})
- setProfileUiState(newProfileUiState)
- newProfileUiState.setup().then(() => {
+ console.log('Fetching profile for', params.name)
+ store.nav.setTitle(params.name)
+ uiState.setup().then(() => {
if (aborted) return
setHasSetup(true)
})
@@ -50,42 +48,45 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
// =
const onSelectView = (index: number) => {
- profileUiState?.setSelectedViewIndex(index)
+ uiState.setSelectedViewIndex(index)
}
const onRefresh = () => {
- profileUiState
- ?.refresh()
+ uiState
+ .refresh()
.catch((err: any) => console.error('Failed to refresh', err))
}
const onEndReached = () => {
- profileUiState
- ?.loadMore()
+ uiState
+ .loadMore()
.catch((err: any) => console.error('Failed to load more', err))
}
const onPressTryAgain = () => {
- profileUiState?.setup()
+ uiState.setup()
}
// rendering
// =
const renderHeader = () => {
- if (!profileUiState) {
+ if (!uiState) {
return
}
- return
+ return
}
let renderItem
let items: any[] = []
- if (profileUiState) {
- if (profileUiState.selectedViewIndex === SECTION_IDS.POSTS) {
- if (profileUiState.isInitialLoading) {
+ if (uiState) {
+ if (
+ uiState.selectedView === Sections.Posts ||
+ uiState.selectedView === Sections.Trending
+ ) {
+ if (uiState.isInitialLoading) {
items.push(LOADING_ITEM)
renderItem = () => Loading...
- } else if (profileUiState.feed.hasError) {
+ } else if (uiState.feed.hasError) {
items.push({
_reactKey: '__error__',
- error: profileUiState.feed.error,
+ error: uiState.feed.error,
})
renderItem = (item: any) => (
@@ -95,9 +96,9 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
/>
)
- } else if (profileUiState.currentView.hasContent) {
- items = profileUiState.feed.feed.slice()
- if (profileUiState.feed.hasReachedEnd) {
+ } else if (uiState.currentView.hasContent) {
+ items = uiState.feed.feed.slice()
+ if (uiState.feed.hasReachedEnd) {
items.push(END_ITEM)
}
renderItem = (item: any) => {
@@ -106,12 +107,11 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
}
return
}
- } else if (profileUiState.currentView.isEmpty) {
+ } else if (uiState.currentView.isEmpty) {
items.push(EMPTY_ITEM)
renderItem = () => No posts yet!
}
- }
- if (profileUiState.selectedViewIndex === SECTION_IDS.BADGES) {
+ } else {
items.push(EMPTY_ITEM)
renderItem = () => TODO
}
@@ -122,20 +122,20 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
return (
- {profileUiState?.profile.hasError ? (
+ {uiState.profile.hasError ? (
) : (