diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-09-05 16:57:20 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-09-05 16:57:20 -0500 |
commit | 2ec09ba54574f5e05f0bbec9c864dacd2092edd4 (patch) | |
tree | dfa5960c0ede277260ddb6456523033ea4226ff9 /src | |
parent | cb310ab1c1e4d2beb6f39b39e4972b9ae21f9849 (diff) | |
download | voidsky-2ec09ba54574f5e05f0bbec9c864dacd2092edd4.tar.zst |
Rework profile header
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/profile/ProfileHeader.tsx | 177 | ||||
-rw-r--r-- | src/view/com/util/Selector.tsx | 73 | ||||
-rw-r--r-- | src/view/index.ts | 2 | ||||
-rw-r--r-- | src/view/lib/assets.native.ts | 2 | ||||
-rw-r--r-- | src/view/lib/assets.ts | 2 | ||||
-rw-r--r-- | src/view/lib/styles.ts | 4 |
6 files changed, 222 insertions, 38 deletions
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 4ad84b32a..08d895554 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -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, }, + banner: { + width: '100%', + height: 120, + }, avi: { - width: 60, - height: 60, - borderRadius: 30, + 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, }, - nameLine: { + displayNameLine: { + paddingLeft: 86, + marginBottom: 14, + }, + displayName: { + fontSize: 24, + fontWeight: 'bold', + }, + badgesLine: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 10, + }, + buttonsLine: { + flexDirection: 'row', + marginBottom: 12, + }, + followBtn: { flexDirection: 'row', - alignItems: 'flex-end', + 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, }, }) diff --git a/src/view/com/util/Selector.tsx b/src/view/com/util/Selector.tsx new file mode 100644 index 000000000..ef7c65d59 --- /dev/null +++ b/src/view/com/util/Selector.tsx @@ -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, + }, +}) diff --git a/src/view/index.ts b/src/view/index.ts index af24030c8..c4b8fa9f9 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -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, diff --git a/src/view/lib/assets.native.ts b/src/view/lib/assets.native.ts index af5ee2bac..a341e02a9 100644 --- a/src/view/lib/assets.native.ts +++ b/src/view/lib/assets.native.ts @@ -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') diff --git a/src/view/lib/assets.ts b/src/view/lib/assets.ts index 7d0584a41..1575101d2 100644 --- a/src/view/lib/assets.ts +++ b/src/view/lib/assets.ts @@ -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'} diff --git a/src/view/lib/styles.ts b/src/view/lib/styles.ts index 82fe41546..17a24707b 100644 --- a/src/view/lib/styles.ts +++ b/src/view/lib/styles.ts @@ -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}, |