Rework profile header
parent
cb310ab1c1
commit
2ec09ba545
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -2,19 +2,22 @@ import React, {useState, useEffect} from 'react'
|
|||
import {observer} from 'mobx-react-lite'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||
import {useStores} from '../../../state'
|
||||
import {pluralize} from '../../lib/strings'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
import {s, gradients, colors} from '../../lib/styles'
|
||||
import {AVIS, BANNER} from '../../lib/assets'
|
||||
import Toast from '../util/Toast'
|
||||
import {Link} from '../util/Link'
|
||||
import {Selector, SelectorItem} from '../util/Selector'
|
||||
|
||||
export const ProfileHeader = observer(function ProfileHeader({
|
||||
user,
|
||||
|
@ -35,6 +38,11 @@ export const ProfileHeader = observer(function ProfileHeader({
|
|||
newView.setup().catch(err => console.error('Failed to fetch profile', err))
|
||||
}, [user, view?.params.user, store])
|
||||
|
||||
const selectorItems: SelectorItem[] = [
|
||||
{label: 'Posts', onSelect() {}},
|
||||
{label: 'Badges', onSelect() {}},
|
||||
]
|
||||
|
||||
const onPressToggleFollow = () => {
|
||||
view?.toggleFollowing().then(
|
||||
() => {
|
||||
|
@ -51,6 +59,12 @@ export const ProfileHeader = observer(function ProfileHeader({
|
|||
err => console.error('Failed to toggle follow', err),
|
||||
)
|
||||
}
|
||||
const onPressEditProfile = () => {
|
||||
// TODO
|
||||
}
|
||||
const onPressMenu = () => {
|
||||
// TODO
|
||||
}
|
||||
const onPressFollowers = () => {
|
||||
store.nav.navigate(`/profile/${user}/followers`)
|
||||
}
|
||||
|
@ -84,40 +98,79 @@ export const ProfileHeader = observer(function ProfileHeader({
|
|||
|
||||
// loaded
|
||||
// =
|
||||
const isMe = store.me.did === view.did
|
||||
return (
|
||||
<View style={styles.outer}>
|
||||
<Image style={styles.banner} source={BANNER} />
|
||||
<Image style={styles.avi} source={AVIS[view.name] || AVIS['alice.com']} />
|
||||
<View style={[styles.nameLine, s.mb2]}>
|
||||
<Text style={[s.bold, s.f18, s.mr2]}>{view.displayName}</Text>
|
||||
<Text style={[s.gray5]}>@{view.name}</Text>
|
||||
</View>
|
||||
{view.description && (
|
||||
<Text style={[s.mb5, s.f15, s['lh15-1.3']]}>{view.description}</Text>
|
||||
)}
|
||||
<View style={s.flexRow}>
|
||||
<TouchableOpacity
|
||||
style={[s.flexRow, s.mr10]}
|
||||
onPress={onPressFollowers}>
|
||||
<Text style={[s.bold, s.mr2]}>{view.followersCount}</Text>
|
||||
<Text style={s.gray5}>
|
||||
{pluralize(view.followersCount, 'follower')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={[s.flexRow, s.mr10]} onPress={onPressFollows}>
|
||||
<Text style={[s.bold, s.mr2]}>{view.followsCount}</Text>
|
||||
<Text style={s.gray5}>following</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={[s.flexRow, s.mr10]}>
|
||||
<Text style={[s.bold, s.mr2]}>{view.postsCount}</Text>
|
||||
<Text style={s.gray5}>{pluralize(view.postsCount, 'post')}</Text>
|
||||
<View style={styles.content}>
|
||||
<View style={[styles.displayNameLine]}>
|
||||
<Text style={styles.displayName}>{view.displayName}</Text>
|
||||
</View>
|
||||
<View style={styles.badgesLine}>
|
||||
<FontAwesomeIcon icon="shield" style={s.mr5} size={12} />
|
||||
<Link href="/" title="Badge TODO">
|
||||
<Text style={[s.f12, s.bold]}>
|
||||
Employee <Text style={[s.blue3]}>@blueskyweb.xyz</Text>
|
||||
</Text>
|
||||
</Link>
|
||||
</View>
|
||||
<View style={[styles.buttonsLine]}>
|
||||
{isMe ? (
|
||||
<TouchableOpacity
|
||||
onPress={onPressEditProfile}
|
||||
style={[styles.mainBtn, styles.btn]}>
|
||||
<Text style={[s.fw600, s.f16]}>Edit Profile</Text>
|
||||
</TouchableOpacity>
|
||||
) : view.myState.hasFollowed ? (
|
||||
<TouchableOpacity
|
||||
onPress={onPressToggleFollow}
|
||||
style={[styles.mainBtn, styles.btn]}>
|
||||
<Text style={[s.fw600, s.f16]}>Following</Text>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<TouchableOpacity onPress={onPressToggleFollow}>
|
||||
<LinearGradient
|
||||
colors={[gradients.primary.start, gradients.primary.end]}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[styles.followBtn]}>
|
||||
<FontAwesomeIcon icon="plus" style={[s.white, s.mr5]} />
|
||||
<Text style={[s.white, s.fw600, s.f16]}>Follow</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
onPress={onPressMenu}
|
||||
style={[styles.btn, styles.secondaryBtn, s.ml10]}>
|
||||
<FontAwesomeIcon icon="ellipsis" style={[s.gray5]} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={[s.flexRow, s.mb10]}>
|
||||
<TouchableOpacity
|
||||
style={[s.flexRow, s.mr10]}
|
||||
onPress={onPressFollowers}>
|
||||
<Text style={[s.bold, s.mr2]}>{view.followersCount}</Text>
|
||||
<Text style={s.gray5}>
|
||||
{pluralize(view.followersCount, 'follower')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[s.flexRow, s.mr10]}
|
||||
onPress={onPressFollows}>
|
||||
<Text style={[s.bold, s.mr2]}>{view.followsCount}</Text>
|
||||
<Text style={s.gray5}>following</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={[s.flexRow, s.mr10]}>
|
||||
<Text style={[s.bold, s.mr2]}>{view.postsCount}</Text>
|
||||
<Text style={s.gray5}>{pluralize(view.postsCount, 'post')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
{view.description && (
|
||||
<Text style={[s.mb10, s.f15, s['lh15-1.3']]}>{view.description}</Text>
|
||||
)}
|
||||
</View>
|
||||
<View>
|
||||
<Button
|
||||
title={view.myState.hasFollowed ? 'Unfollow' : 'Follow'}
|
||||
onPress={onPressToggleFollow}
|
||||
/>
|
||||
</View>
|
||||
<Selector items={selectorItems} />
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
@ -125,18 +178,66 @@ export const ProfileHeader = observer(function ProfileHeader({
|
|||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
backgroundColor: colors.white,
|
||||
padding: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: colors.gray2,
|
||||
},
|
||||
avi: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 30,
|
||||
resizeMode: 'cover',
|
||||
banner: {
|
||||
width: '100%',
|
||||
height: 120,
|
||||
},
|
||||
nameLine: {
|
||||
avi: {
|
||||
position: 'absolute',
|
||||
top: 80,
|
||||
left: 10,
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: 40,
|
||||
resizeMode: 'cover',
|
||||
borderWidth: 2,
|
||||
borderColor: colors.white,
|
||||
},
|
||||
content: {
|
||||
paddingTop: 8,
|
||||
paddingHorizontal: 14,
|
||||
paddingBottom: 4,
|
||||
},
|
||||
displayNameLine: {
|
||||
paddingLeft: 86,
|
||||
marginBottom: 14,
|
||||
},
|
||||
displayName: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
badgesLine: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-end',
|
||||
alignItems: 'center',
|
||||
marginBottom: 10,
|
||||
},
|
||||
buttonsLine: {
|
||||
flexDirection: 'row',
|
||||
marginBottom: 12,
|
||||
},
|
||||
followBtn: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 60,
|
||||
borderRadius: 30,
|
||||
},
|
||||
btn: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 8,
|
||||
borderRadius: 30,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.gray2,
|
||||
},
|
||||
mainBtn: {
|
||||
paddingHorizontal: 40,
|
||||
},
|
||||
secondaryBtn: {
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import React, {useState} from 'react'
|
||||
import {
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {colors} from '../../lib/styles'
|
||||
|
||||
export interface SelectorItem {
|
||||
label: string
|
||||
}
|
||||
|
||||
export function Selector({
|
||||
style,
|
||||
items,
|
||||
onSelect,
|
||||
}: {
|
||||
style?: StyleProp<ViewStyle>
|
||||
items: SelectorItem[]
|
||||
onSelect?: (index: number) => void
|
||||
}) {
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(0)
|
||||
const onPressItem = (index: number) => {
|
||||
setSelectedIndex(index)
|
||||
onSelect?.(index)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.outer, style]}>
|
||||
{items.map((item, i) => {
|
||||
const selected = i === selectedIndex
|
||||
return (
|
||||
<TouchableWithoutFeedback key={i} onPress={() => onPressItem(i)}>
|
||||
<View style={selected ? styles.itemSelected : styles.item}>
|
||||
<Text style={selected ? styles.labelSelected : styles.label}>
|
||||
{item.label}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 14,
|
||||
},
|
||||
item: {
|
||||
paddingBottom: 12,
|
||||
marginRight: 20,
|
||||
},
|
||||
label: {
|
||||
fontWeight: '600',
|
||||
fontSize: 16,
|
||||
color: colors.gray5,
|
||||
},
|
||||
itemSelected: {
|
||||
paddingBottom: 8,
|
||||
marginRight: 20,
|
||||
borderBottomWidth: 4,
|
||||
borderBottomColor: colors.purple3,
|
||||
},
|
||||
labelSelected: {
|
||||
fontWeight: '600',
|
||||
fontSize: 16,
|
||||
},
|
||||
})
|
|
@ -26,6 +26,7 @@ import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib'
|
|||
import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus'
|
||||
import {faShare} from '@fortawesome/free-solid-svg-icons/faShare'
|
||||
import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSquare'
|
||||
import {faShield} from '@fortawesome/free-solid-svg-icons/faShield'
|
||||
import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet'
|
||||
import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
|
||||
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
|
||||
|
@ -60,6 +61,7 @@ export function setup() {
|
|||
faRetweet,
|
||||
faShare,
|
||||
faShareFromSquare,
|
||||
faShield,
|
||||
faUser,
|
||||
faUsers,
|
||||
faX,
|
||||
|
|
|
@ -5,3 +5,5 @@ export const AVIS: Record<string, ImageSourcePropType> = {
|
|||
'bob.com': require('../../../public/img/bob.jpg'),
|
||||
'carla.com': require('../../../public/img/carla.jpg'),
|
||||
}
|
||||
|
||||
export const BANNER: ImageSourcePropType = require('../../../public/img/banner.jpg')
|
||||
|
|
|
@ -5,3 +5,5 @@ export const AVIS: Record<string, ImageSourcePropType> = {
|
|||
'bob.com': {uri: '/img/bob.jpg'},
|
||||
'carla.com': {uri: '/img/carla.jpg'},
|
||||
}
|
||||
|
||||
export const BANNER: ImageSourcePropType = {uri: '/img/banner.jpg'}
|
||||
|
|
|
@ -62,6 +62,10 @@ export const s = StyleSheet.create({
|
|||
fw200: {fontWeight: '200'},
|
||||
|
||||
// font sizes
|
||||
f9: {fontSize: 9},
|
||||
f10: {fontSize: 10},
|
||||
f11: {fontSize: 11},
|
||||
f12: {fontSize: 12},
|
||||
f13: {fontSize: 13},
|
||||
f14: {fontSize: 14},
|
||||
f15: {fontSize: 15},
|
||||
|
|
Loading…
Reference in New Issue