diff options
Diffstat (limited to 'src/view/screens/Profile.tsx')
-rw-r--r-- | src/view/screens/Profile.tsx | 203 |
1 files changed, 182 insertions, 21 deletions
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 236f8f908..6711f7e04 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -1,52 +1,213 @@ import React, {useState, useEffect} from 'react' -import {View, StyleSheet} from 'react-native' -import {FeedViewModel} from '../../state/models/feed-view' +import {SectionList, StyleSheet, Text, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {ProfileUiModel, SECTION_IDS} from '../../state/models/profile-ui' +import {FeedViewItemModel} from '../../state/models/feed-view' import {useStores} from '../../state' import {ProfileHeader} from '../com/profile/ProfileHeader' -import {Feed} from '../com/feed/Feed' +import {FeedItem} from '../com/posts/FeedItem' +import {Selector} from '../com/util/Selector' +import {ErrorScreen} from '../com/util/ErrorScreen' +import {ErrorMessage} from '../com/util/ErrorMessage' +import {s, colors} from '../lib/styles' import {ScreenParams} from '../routes' -export const Profile = ({visible, params}: ScreenParams) => { +const SECTION_HEADER_ITEM = Symbol('SectionHeaderItem') +const LOADING_ITEM = Symbol('LoadingItem') +const EMPTY_ITEM = Symbol('EmptyItem') +const END_ITEM = Symbol('EndItem') + +interface RenderItemParams { + item: any + index: number + section: Section +} + +interface ErrorItem { + error: string +} + +interface Section { + data: any[] + keyExtractor?: (v: any) => string + renderItem: (params: RenderItemParams) => JSX.Element +} + +export const Profile = observer(({visible, params}: ScreenParams) => { const store = useStores() const [hasSetup, setHasSetup] = useState<boolean>(false) - const [feedView, setFeedView] = useState<FeedViewModel | undefined>() + const [profileUiState, setProfileUiState] = useState< + ProfileUiModel | undefined + >() useEffect(() => { if (!visible) { return } - const author = params.name + const user = params.name if (hasSetup) { - console.log('Updating profile feed for', author) - feedView?.update() + console.log('Updating profile for', user) + profileUiState?.update() } else { - console.log('Fetching profile feed for', author) - const newFeedView = new FeedViewModel(store, {author}) - setFeedView(newFeedView) - newFeedView - .setup() - .catch(err => console.error('Failed to fetch feed', err)) - .then(() => { - setHasSetup(true) - store.nav.setTitle(author) - }) + console.log('Fetching profile for', user) + store.nav.setTitle(user) + const newProfileUiState = new ProfileUiModel(store, {user}) + setProfileUiState(newProfileUiState) + newProfileUiState.setup().then(() => { + setHasSetup(true) + }) } }, [visible, params.name, store]) + // events + // = + + const onSelectViewSelector = (index: number) => + profileUiState?.setSelectedViewIndex(index) + const onRefresh = () => { + profileUiState + ?.refresh() + .catch((err: any) => console.error('Failed to refresh', err)) + } + const onEndReached = () => { + profileUiState + ?.loadMore() + .catch((err: any) => console.error('Failed to load more', err)) + } + const onPressTryAgain = () => { + profileUiState?.setup() + } + + // rendering + // = + + const renderItem = (_params: RenderItemParams) => <View /> + const renderLoadingItem = (_params: RenderItemParams) => ( + <Text style={styles.loading}>Loading...</Text> + ) + const renderErrorItem = ({item}: {item: ErrorItem}) => ( + <View style={s.p5}> + <ErrorMessage message={item.error} onPressTryAgain={onPressTryAgain} /> + </View> + ) + const renderEmptyItem = (_params: RenderItemParams) => ( + <Text style={styles.loading}>No posts yet!</Text> + ) + const renderProfileItem = (_params: RenderItemParams) => { + if (!profileUiState) { + return <View /> + } + return <ProfileHeader view={profileUiState.profile} /> + } + const renderSectionHeader = ({section}: {section: Section}) => { + if (section?.data?.[0] !== SECTION_HEADER_ITEM) { + return ( + <Selector + items={ProfileUiModel.SELECTOR_ITEMS} + style={styles.selector} + onSelect={onSelectViewSelector} + /> + ) + } + return <View /> + } + const renderPostsItem = ({item}: {item: FeedViewItemModel | Symbol}) => { + if (item === END_ITEM || item instanceof Symbol) { + return <Text style={styles.endItem}>- end of feed -</Text> + } + return <FeedItem item={item} /> + } + const renderBadgesItem = ({item}: {item: any}) => <Text>todo</Text> + + const sections = [ + {data: [SECTION_HEADER_ITEM], renderItem: renderProfileItem}, + ] + if (profileUiState) { + if (profileUiState.selectedViewIndex === SECTION_IDS.POSTS) { + if (profileUiState.isInitialLoading) { + sections.push({ + data: [LOADING_ITEM], + renderItem: renderLoadingItem, + } as Section) + } else if (profileUiState.feed.hasError) { + sections.push({ + data: [{error: profileUiState.feed.error}], + renderItem: renderErrorItem, + } as Section) + } else if (profileUiState.currentView.hasContent) { + const items: (FeedViewItemModel | Symbol)[] = + profileUiState.feed.feed.slice() + if (profileUiState.feed.hasReachedEnd) { + items.push(END_ITEM) + } + sections.push({ + data: items, + renderItem: renderPostsItem, + keyExtractor: (item: FeedViewItemModel) => item._reactKey, + } as Section) + } else if (profileUiState.currentView.isEmpty) { + sections.push({ + data: [EMPTY_ITEM], + renderItem: renderEmptyItem, + }) + } + } + if (profileUiState.selectedViewIndex === SECTION_IDS.BADGES) { + sections.push({ + data: [{}], + renderItem: renderBadgesItem, + } as Section) + } + } + return ( <View style={styles.container}> - <ProfileHeader user={params.name} /> - <View style={styles.feed}>{feedView && <Feed feed={feedView} />}</View> + <View style={styles.feed}> + {profileUiState && + (profileUiState.profile.hasError ? ( + <ErrorScreen + title="Failed to load profile" + message={`There was an issue when attempting to load ${params.name}`} + details={profileUiState.profile.error} + onPressTryAgain={onPressTryAgain} + /> + ) : ( + <SectionList + sections={sections} + renderSectionHeader={renderSectionHeader} + renderItem={renderItem} + refreshing={profileUiState.isRefreshing} + onRefresh={onRefresh} + onEndReached={onEndReached} + /> + ))} + </View> </View> ) -} +}) const styles = StyleSheet.create({ container: { flexDirection: 'column', height: '100%', }, + selector: { + paddingTop: 8, + backgroundColor: colors.white, + borderBottomWidth: 1, + borderColor: colors.gray2, + }, feed: { flex: 1, }, + loading: { + paddingVertical: 10, + paddingHorizontal: 14, + }, + endItem: { + paddingTop: 20, + paddingBottom: 30, + color: colors.gray5, + textAlign: 'center', + }, }) |