diff options
Diffstat (limited to 'src/view/com')
-rw-r--r-- | src/view/com/profile/ProfileFollowers.tsx | 141 | ||||
-rw-r--r-- | src/view/com/profile/ProfileFollows.tsx | 141 | ||||
-rw-r--r-- | src/view/com/profile/ProfileHeader.tsx | 21 |
3 files changed, 297 insertions, 6 deletions
diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx new file mode 100644 index 000000000..06cc0c14d --- /dev/null +++ b/src/view/com/profile/ProfileFollowers.tsx @@ -0,0 +1,141 @@ +import React, {useState, useEffect} from 'react' +import {observer} from 'mobx-react-lite' +import { + ActivityIndicator, + FlatList, + Image, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native' +import {OnNavigateContent} from '../../routes/types' +import { + UserFollowersViewModel, + FollowerItem, +} from '../../../state/models/user-followers-view' +import {useStores} from '../../../state' +import {s} from '../../lib/styles' +import {AVIS} from '../../lib/assets' + +export const ProfileFollowers = observer(function ProfileFollowers({ + name, + onNavigateContent, +}: { + name: string + onNavigateContent: OnNavigateContent +}) { + const store = useStores() + const [view, setView] = useState<UserFollowersViewModel | undefined>() + + useEffect(() => { + if (view?.params.user === name) { + console.log('User followers doing nothing') + return // no change needed? or trigger refresh? + } + console.log('Fetching user followers', name) + const newView = new UserFollowersViewModel(store, {user: name}) + setView(newView) + newView + .setup() + .catch(err => console.error('Failed to fetch user followers', err)) + }, [name, view?.params.user, store]) + + // loading + // = + if ( + !view || + (view.isLoading && !view.isRefreshing) || + view.params.user !== name + ) { + return ( + <View> + <ActivityIndicator /> + </View> + ) + } + + // error + // = + if (view.hasError) { + return ( + <View> + <Text>{view.error}</Text> + </View> + ) + } + + // loaded + // = + const renderItem = ({item}: {item: FollowerItem}) => ( + <User item={item} onNavigateContent={onNavigateContent} /> + ) + return ( + <View> + <FlatList + data={view.followers} + keyExtractor={item => item._reactKey} + renderItem={renderItem} + /> + </View> + ) +}) + +const User = ({ + item, + onNavigateContent, +}: { + item: FollowerItem + onNavigateContent: OnNavigateContent +}) => { + const onPressOuter = () => { + onNavigateContent('Profile', { + name: item.name, + }) + } + return ( + <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <View style={styles.layout}> + <View style={styles.layoutAvi}> + <Image + style={styles.avi} + source={AVIS[item.name] || AVIS['alice.com']} + /> + </View> + <View style={styles.layoutContent}> + <Text style={[s.f15, s.bold]}>{item.displayName}</Text> + <Text style={[s.f14, s.gray]}>@{item.name}</Text> + </View> + </View> + </TouchableOpacity> + ) +} + +const styles = StyleSheet.create({ + outer: { + borderTopWidth: 1, + borderTopColor: '#e8e8e8', + backgroundColor: '#fff', + }, + layout: { + flexDirection: 'row', + }, + layoutAvi: { + width: 60, + paddingLeft: 10, + paddingTop: 10, + paddingBottom: 10, + }, + avi: { + width: 40, + height: 40, + borderRadius: 30, + resizeMode: 'cover', + }, + layoutContent: { + flex: 1, + paddingRight: 10, + paddingTop: 10, + paddingBottom: 10, + }, +}) diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx new file mode 100644 index 000000000..bb5859852 --- /dev/null +++ b/src/view/com/profile/ProfileFollows.tsx @@ -0,0 +1,141 @@ +import React, {useState, useEffect} from 'react' +import {observer} from 'mobx-react-lite' +import { + ActivityIndicator, + FlatList, + Image, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native' +import {OnNavigateContent} from '../../routes/types' +import { + UserFollowsViewModel, + FollowItem, +} from '../../../state/models/user-follows-view' +import {useStores} from '../../../state' +import {s} from '../../lib/styles' +import {AVIS} from '../../lib/assets' + +export const ProfileFollows = observer(function ProfileFollows({ + name, + onNavigateContent, +}: { + name: string + onNavigateContent: OnNavigateContent +}) { + const store = useStores() + const [view, setView] = useState<UserFollowsViewModel | undefined>() + + useEffect(() => { + if (view?.params.user === name) { + console.log('User follows doing nothing') + return // no change needed? or trigger refresh? + } + console.log('Fetching user follows', name) + const newView = new UserFollowsViewModel(store, {user: name}) + setView(newView) + newView + .setup() + .catch(err => console.error('Failed to fetch user follows', err)) + }, [name, view?.params.user, store]) + + // loading + // = + if ( + !view || + (view.isLoading && !view.isRefreshing) || + view.params.user !== name + ) { + return ( + <View> + <ActivityIndicator /> + </View> + ) + } + + // error + // = + if (view.hasError) { + return ( + <View> + <Text>{view.error}</Text> + </View> + ) + } + + // loaded + // = + const renderItem = ({item}: {item: FollowItem}) => ( + <User item={item} onNavigateContent={onNavigateContent} /> + ) + return ( + <View> + <FlatList + data={view.follows} + keyExtractor={item => item._reactKey} + renderItem={renderItem} + /> + </View> + ) +}) + +const User = ({ + item, + onNavigateContent, +}: { + item: FollowItem + onNavigateContent: OnNavigateContent +}) => { + const onPressOuter = () => { + onNavigateContent('Profile', { + name: item.name, + }) + } + return ( + <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <View style={styles.layout}> + <View style={styles.layoutAvi}> + <Image + style={styles.avi} + source={AVIS[item.name] || AVIS['alice.com']} + /> + </View> + <View style={styles.layoutContent}> + <Text style={[s.f15, s.bold]}>{item.displayName}</Text> + <Text style={[s.f14, s.gray]}>@{item.name}</Text> + </View> + </View> + </TouchableOpacity> + ) +} + +const styles = StyleSheet.create({ + outer: { + borderTopWidth: 1, + borderTopColor: '#e8e8e8', + backgroundColor: '#fff', + }, + layout: { + flexDirection: 'row', + }, + layoutAvi: { + width: 60, + paddingLeft: 10, + paddingTop: 10, + paddingBottom: 10, + }, + avi: { + width: 40, + height: 40, + borderRadius: 30, + resizeMode: 'cover', + }, + layoutContent: { + flex: 1, + paddingRight: 10, + paddingTop: 10, + paddingBottom: 10, + }, +}) diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 1eace94aa..6a6d04140 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -6,6 +6,7 @@ import { Image, StyleSheet, Text, + TouchableOpacity, View, } from 'react-native' import {OnNavigateContent} from '../../routes/types' @@ -18,8 +19,8 @@ import Toast from '../util/Toast' export const ProfileHeader = observer(function ProfileHeader({ user, -}: // onNavigateContent, -{ + onNavigateContent, +}: { user: string onNavigateContent: OnNavigateContent }) { @@ -53,6 +54,12 @@ export const ProfileHeader = observer(function ProfileHeader({ err => console.error('Failed to toggle follow', err), ) } + const onPressFollowers = () => { + onNavigateContent('ProfileFollowers', {name: user}) + } + const onPressFollows = () => { + onNavigateContent('ProfileFollows', {name: user}) + } // loading // = @@ -91,16 +98,18 @@ export const ProfileHeader = observer(function ProfileHeader({ <Text style={[s.mb5, s.f15, s['lh15-1.3']]}>{view.description}</Text> )} <View style={s.flexRow}> - <View style={[s.flexRow, s.mr10]}> + <TouchableOpacity + style={[s.flexRow, s.mr10]} + onPress={onPressFollowers}> <Text style={[s.bold, s.mr2]}>{view.followersCount}</Text> <Text style={s.gray}> {pluralize(view.followersCount, 'follower')} </Text> - </View> - <View style={[s.flexRow, s.mr10]}> + </TouchableOpacity> + <TouchableOpacity style={[s.flexRow, s.mr10]} onPress={onPressFollows}> <Text style={[s.bold, s.mr2]}>{view.followsCount}</Text> <Text style={s.gray}>following</Text> - </View> + </TouchableOpacity> <View style={[s.flexRow, s.mr10]}> <Text style={[s.bold, s.mr2]}>{view.postsCount}</Text> <Text style={s.gray}>{pluralize(view.postsCount, 'post')}</Text> |