Update profile header to use design system

zio/stable
Paul Frazee 2022-12-30 13:13:10 -06:00
parent 5e07b5bbc3
commit 10f613475a
2 changed files with 70 additions and 58 deletions

View File

@ -14,7 +14,7 @@ import {
ProfileImageLightbox, ProfileImageLightbox,
} from '../../../state/models/shell-ui' } from '../../../state/models/shell-ui'
import {pluralize} from '../../../lib/strings' import {pluralize} from '../../../lib/strings'
import {s, colors} from '../../lib/styles' import {s} from '../../lib/styles'
import {getGradient} from '../../lib/asset-gen' import {getGradient} from '../../lib/asset-gen'
import {DropdownButton, DropdownItem} from '../util/forms/DropdownButton' import {DropdownButton, DropdownItem} from '../util/forms/DropdownButton'
import * as Toast from '../util/Toast' import * as Toast from '../util/Toast'
@ -24,6 +24,7 @@ import {RichText} from '../util/text/RichText'
import {UserAvatar} from '../util/UserAvatar' import {UserAvatar} from '../util/UserAvatar'
import {UserBanner} from '../util/UserBanner' import {UserBanner} from '../util/UserBanner'
import {UserInfoText} from '../util/UserInfoText' import {UserInfoText} from '../util/UserInfoText'
import {usePalette} from '../../lib/hooks/usePalette'
export const ProfileHeader = observer(function ProfileHeader({ export const ProfileHeader = observer(function ProfileHeader({
view, view,
@ -32,6 +33,7 @@ export const ProfileHeader = observer(function ProfileHeader({
view: ProfileViewModel view: ProfileViewModel
onRefreshAll: () => void onRefreshAll: () => void
}) { }) {
const pal = usePalette('default')
const store = useStores() const store = useStores()
const isMember = useMemo( const isMember = useMemo(
() => view.isScene && view.myState.member, () => view.isScene && view.myState.member,
@ -95,9 +97,10 @@ export const ProfileHeader = observer(function ProfileHeader({
// = // =
if (!view || !view.hasLoaded) { if (!view || !view.hasLoaded) {
return ( return (
<View style={styles.outer}> <View style={pal.view}>
<LoadingPlaceholder width="100%" height={120} /> <LoadingPlaceholder width="100%" height={120} />
<View style={styles.avi}> <View
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
<LoadingPlaceholder <LoadingPlaceholder
width={80} width={80}
height={80} height={80}
@ -113,7 +116,7 @@ export const ProfileHeader = observer(function ProfileHeader({
/> />
</View> </View>
<View style={styles.displayNameLine}> <View style={styles.displayNameLine}>
<Text style={styles.displayName}> <Text type="h2" style={[pal.text, {lineHeight: 38}]}>
{view.displayName || view.handle} {view.displayName || view.handle}
</Text> </Text>
</View> </View>
@ -161,24 +164,32 @@ export const ProfileHeader = observer(function ProfileHeader({
} }
} }
return ( return (
<View style={styles.outer}> <View style={pal.view}>
<UserBanner handle={view.handle} banner={view.banner} /> <UserBanner handle={view.handle} banner={view.banner} />
<View style={styles.content}> <View style={styles.content}>
<View style={[styles.buttonsLine]}> <View style={[styles.buttonsLine]}>
{isMe ? ( {isMe ? (
<TouchableOpacity <TouchableOpacity
onPress={onPressEditProfile} onPress={onPressEditProfile}
style={[styles.btn, styles.mainBtn]}> style={[styles.btn, styles.mainBtn, pal.btn]}>
<Text style={[s.fw400, s.f14, s.black]}>Edit Profile</Text> <Text type="button" style={pal.text}>
Edit Profile
</Text>
</TouchableOpacity> </TouchableOpacity>
) : ( ) : (
<> <>
{view.myState.follow ? ( {view.myState.follow ? (
<TouchableOpacity <TouchableOpacity
onPress={onPressToggleFollow} onPress={onPressToggleFollow}
style={[styles.btn, styles.mainBtn]}> style={[styles.btn, styles.mainBtn, pal.btn]}>
<FontAwesomeIcon icon="check" style={[s.mr5]} size={14} /> <FontAwesomeIcon
<Text style={[s.fw400, s.f14, s.black]}>Following</Text> icon="check"
style={[pal.text, s.mr5]}
size={14}
/>
<Text type="button" style={pal.text}>
Following
</Text>
</TouchableOpacity> </TouchableOpacity>
) : ( ) : (
<TouchableOpacity onPress={onPressToggleFollow}> <TouchableOpacity onPress={onPressToggleFollow}>
@ -188,7 +199,9 @@ export const ProfileHeader = observer(function ProfileHeader({
end={{x: 1, y: 1}} end={{x: 1, y: 1}}
style={[styles.btn, styles.gradientBtn]}> style={[styles.btn, styles.gradientBtn]}>
<FontAwesomeIcon icon="plus" style={[s.white, s.mr5]} /> <FontAwesomeIcon icon="plus" style={[s.white, s.mr5]} />
<Text style={[s.white, s.fw600, s.f16]}>Follow</Text> <Text type="button" style={[s.white, s.bold]}>
Follow
</Text>
</LinearGradient> </LinearGradient>
</TouchableOpacity> </TouchableOpacity>
)} )}
@ -196,33 +209,38 @@ export const ProfileHeader = observer(function ProfileHeader({
)} )}
{dropdownItems?.length ? ( {dropdownItems?.length ? (
<DropdownButton <DropdownButton
type="bare"
items={dropdownItems} items={dropdownItems}
style={[styles.btn, styles.secondaryBtn]}> style={[styles.btn, styles.secondaryBtn, pal.btn]}>
<FontAwesomeIcon icon="ellipsis" style={[s.gray5]} /> <FontAwesomeIcon icon="ellipsis" style={[pal.text]} />
</DropdownButton> </DropdownButton>
) : undefined} ) : undefined}
</View> </View>
<View style={styles.displayNameLine}> <View style={styles.displayNameLine}>
<Text style={[styles.displayName, s.black]}> <Text type="h2" style={[pal.text, {lineHeight: 38}]}>
{view.displayName || view.handle} {view.displayName || view.handle}
</Text> </Text>
</View> </View>
<View style={styles.handleLine}> <View style={styles.handleLine}>
{view.isScene ? ( {view.isScene ? (
<View style={styles.typeLabelWrapper}> <View
<Text style={styles.typeLabel}>Scene</Text> style={[
styles.typeLabelWrapper,
{backgroundColor: pal.colors.backgroundLight},
]}>
<Text style={[styles.typeLabel, pal.textLight]}>Scene</Text>
</View> </View>
) : undefined} ) : undefined}
<Text style={styles.handle}>@{view.handle}</Text> <Text style={pal.textLight}>@{view.handle}</Text>
</View> </View>
<View style={styles.metricsLine}> <View style={styles.metricsLine}>
<TouchableOpacity <TouchableOpacity
style={[s.flexRow, s.mr10]} style={[s.flexRow, s.mr10]}
onPress={onPressFollowers}> onPress={onPressFollowers}>
<Text style={[s.bold, s.mr2, styles.metricsText, s.black]}> <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
{view.followersCount} {view.followersCount}
</Text> </Text>
<Text style={[s.gray5, styles.metricsText]}> <Text type="body2" style={[pal.textLight]}>
{pluralize(view.followersCount, 'follower')} {pluralize(view.followersCount, 'follower')}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
@ -230,36 +248,38 @@ export const ProfileHeader = observer(function ProfileHeader({
<TouchableOpacity <TouchableOpacity
style={[s.flexRow, s.mr10]} style={[s.flexRow, s.mr10]}
onPress={onPressFollows}> onPress={onPressFollows}>
<Text style={[s.bold, s.mr2, styles.metricsText, s.black]}> <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
{view.followsCount} {view.followsCount}
</Text> </Text>
<Text style={[s.gray5, styles.metricsText]}>following</Text> <Text type="body2" style={[pal.textLight]}>
following
</Text>
</TouchableOpacity> </TouchableOpacity>
) : undefined} ) : undefined}
{view.isScene ? ( {view.isScene ? (
<TouchableOpacity <TouchableOpacity
style={[s.flexRow, s.mr10]} style={[s.flexRow, s.mr10]}
onPress={onPressMembers}> onPress={onPressMembers}>
<Text style={[s.bold, s.mr2, styles.metricsText, s.black]}> <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
{view.membersCount} {view.membersCount}
</Text> </Text>
<Text style={[s.gray5, styles.metricsText]}> <Text type="body2" style={[pal.textLight]}>
{pluralize(view.membersCount, 'member')} {pluralize(view.membersCount, 'member')}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
) : undefined} ) : undefined}
<View style={[s.flexRow, s.mr10]}> <View style={[s.flexRow, s.mr10]}>
<Text style={[s.bold, s.mr2, styles.metricsText, s.black]}> <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
{view.postsCount} {view.postsCount}
</Text> </Text>
<Text style={[s.gray5, styles.metricsText]}> <Text type="body2" style={[pal.textLight]}>
{pluralize(view.postsCount, 'post')} {pluralize(view.postsCount, 'post')}
</Text> </Text>
</View> </View>
</View> </View>
{view.description ? ( {view.description ? (
<RichText <RichText
style={styles.description} style={[styles.description, pal.text]}
numberOfLines={3} numberOfLines={3}
text={view.description} text={view.description}
entities={view.descriptionEntities} entities={view.descriptionEntities}
@ -267,10 +287,16 @@ export const ProfileHeader = observer(function ProfileHeader({
) : undefined} ) : undefined}
{view.isScene && view.creator ? ( {view.isScene && view.creator ? (
<View style={styles.relationshipsLine}> <View style={styles.relationshipsLine}>
<FontAwesomeIcon icon={['far', 'user']} style={[s.gray5, s.mr5]} /> <FontAwesomeIcon
<Text style={[s.mr2, s.gray5, s.f15]}>Created by</Text> icon={['far', 'user']}
style={[pal.textLight, s.mr5]}
/>
<Text type="body2" style={[s.mr2, pal.textLight]}>
Created by
</Text>
<UserInfoText <UserInfoText
style={[s.blue3, s.f15]} type="body2"
style={[pal.link]}
did={view.creator} did={view.creator}
prefix="@" prefix="@"
asLink asLink
@ -281,14 +307,16 @@ export const ProfileHeader = observer(function ProfileHeader({
<View style={styles.relationshipsLine}> <View style={styles.relationshipsLine}>
<FontAwesomeIcon <FontAwesomeIcon
icon={['far', 'circle-check']} icon={['far', 'circle-check']}
style={[s.gray5, s.mr5]} style={[pal.textLight, s.mr5]}
/> />
<Text style={[s.mr2, s.gray5, s.f15]}>You are a member</Text> <Text type="body2" style={[s.mr2, pal.textLight]}>
You are a member
</Text>
</View> </View>
) : undefined} ) : undefined}
</View> </View>
{view.isScene && view.creator === store.me.did ? ( {view.isScene && view.creator === store.me.did ? (
<View style={styles.sceneAdminContainer}> <View style={[styles.sceneAdminContainer, pal.border]}>
<TouchableOpacity onPress={onPressInviteMembers}> <TouchableOpacity onPress={onPressInviteMembers}>
<LinearGradient <LinearGradient
colors={[gradient[1], gradient[0]]} colors={[gradient[1], gradient[0]]}
@ -300,12 +328,16 @@ export const ProfileHeader = observer(function ProfileHeader({
style={[s.mr5, s.white]} style={[s.mr5, s.white]}
size={15} size={15}
/> />
<Text style={[s.bold, s.f15, s.white]}>Invite Members</Text> <Text type="button" style={[s.bold, s.white]}>
Invite Members
</Text>
</LinearGradient> </LinearGradient>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
) : undefined} ) : undefined}
<TouchableOpacity style={styles.avi} onPress={onPressAvi}> <TouchableOpacity
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}
onPress={onPressAvi}>
<UserAvatar <UserAvatar
size={80} size={80}
handle={view.handle} handle={view.handle}
@ -318,9 +350,6 @@ export const ProfileHeader = observer(function ProfileHeader({
}) })
const styles = StyleSheet.create({ const styles = StyleSheet.create({
outer: {
backgroundColor: colors.white,
},
banner: { banner: {
width: '100%', width: '100%',
height: 120, height: 120,
@ -333,8 +362,6 @@ const styles = StyleSheet.create({
height: 84, height: 84,
borderRadius: 42, borderRadius: 42,
borderWidth: 2, borderWidth: 2,
borderColor: colors.white,
backgroundColor: colors.white,
}, },
content: { content: {
paddingTop: 8, paddingTop: 8,
@ -363,7 +390,6 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
paddingVertical: 7, paddingVertical: 7,
borderRadius: 50, borderRadius: 50,
backgroundColor: colors.gray1,
marginLeft: 6, marginLeft: 6,
}, },
@ -371,22 +397,12 @@ const styles = StyleSheet.create({
// paddingLeft: 86, // paddingLeft: 86,
// marginBottom: 14, // marginBottom: 14,
}, },
displayName: {
fontSize: 28,
fontWeight: 'bold',
},
handleLine: { handleLine: {
flexDirection: 'row', flexDirection: 'row',
marginBottom: 8, marginBottom: 8,
}, },
handle: {
fontSize: 15,
fontWeight: 'bold',
color: colors.gray5,
},
typeLabelWrapper: { typeLabelWrapper: {
backgroundColor: colors.gray1,
paddingHorizontal: 4, paddingHorizontal: 4,
borderRadius: 4, borderRadius: 4,
marginRight: 5, marginRight: 5,
@ -394,22 +410,15 @@ const styles = StyleSheet.create({
typeLabel: { typeLabel: {
fontSize: 15, fontSize: 15,
fontWeight: 'bold', fontWeight: 'bold',
color: colors.gray5,
}, },
metricsLine: { metricsLine: {
flexDirection: 'row', flexDirection: 'row',
marginBottom: 8, marginBottom: 8,
}, },
metricsText: {
fontSize: 15,
},
description: { description: {
marginBottom: 8, marginBottom: 8,
fontSize: 16,
lineHeight: 20.8, // 1.3 of 16px
color: colors.black,
}, },
relationshipsLine: { relationshipsLine: {
@ -419,7 +428,6 @@ const styles = StyleSheet.create({
}, },
sceneAdminContainer: { sceneAdminContainer: {
borderColor: colors.gray1,
borderTopWidth: 1, borderTopWidth: 1,
borderBottomWidth: 1, borderBottomWidth: 1,
paddingVertical: 12, paddingVertical: 12,

View File

@ -4,6 +4,7 @@ import {useTheme, PaletteColorName, PaletteColor} from '../ThemeContext'
export interface UsePaletteValue { export interface UsePaletteValue {
colors: PaletteColor colors: PaletteColor
view: ViewStyle view: ViewStyle
btn: ViewStyle
border: ViewStyle border: ViewStyle
text: TextStyle text: TextStyle
textLight: TextStyle textLight: TextStyle
@ -17,6 +18,9 @@ export function usePalette(color: PaletteColorName): UsePaletteValue {
view: { view: {
backgroundColor: palette.background, backgroundColor: palette.background,
}, },
btn: {
backgroundColor: palette.backgroundLight,
},
border: { border: {
borderColor: palette.border, borderColor: palette.border,
}, },