Skip to content

Commit a48022f

Browse files
authored
Merge pull request #151 from Unshut-Labs/saulmc/user-default-avatar
feat: simple first letter default profile image
2 parents b13a506 + 68aea79 commit a48022f

File tree

17 files changed

+126
-65
lines changed

17 files changed

+126
-65
lines changed

assets/default-pfp-color.png

-148 Bytes
Binary file not shown.

assets/default-pfp-dark.png

-453 Bytes
Binary file not shown.

assets/default-pfp-light.png

-148 Bytes
Binary file not shown.

components/Avatar.tsx

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,70 @@
1-
import { Image, ImageStyle } from "expo-image";
2-
import { StyleProp, useColorScheme } from "react-native";
3-
4-
import PFPPlaceholderColor from "../assets/default-pfp-color.png";
5-
import PFPPlaceholderDark from "../assets/default-pfp-dark.png";
6-
import PFPPlaceholderLight from "../assets/default-pfp-light.png";
1+
import { actionSecondaryColor, textSecondaryColor } from "@styles/colors";
2+
import { AvatarSizes } from "@styles/sizes";
3+
import {
4+
ColorSchemeName,
5+
Image,
6+
ImageStyle,
7+
StyleProp,
8+
StyleSheet,
9+
Text,
10+
useColorScheme,
11+
View,
12+
} from "react-native";
713

814
type Props = {
915
uri?: string | undefined;
1016
size?: number | undefined;
1117
style?: StyleProp<ImageStyle>;
1218
color?: boolean;
19+
name?: string | undefined;
1320
};
14-
export default function Avatar({ uri, size, style, color }: Props) {
21+
22+
export default function Avatar({
23+
uri,
24+
size = AvatarSizes.default,
25+
style,
26+
color,
27+
name,
28+
}: Props) {
1529
const colorScheme = useColorScheme();
16-
const PFPPlaceholder = color
17-
? PFPPlaceholderColor
18-
: colorScheme === "dark"
19-
? PFPPlaceholderDark
20-
: PFPPlaceholderLight;
21-
return (
30+
const styles = getStyles(colorScheme, size);
31+
const firstLetter = name ? name.charAt(0).toUpperCase() : "";
32+
33+
return uri ? (
2234
<Image
2335
key={`${uri}-${color}-${colorScheme}`}
24-
source={uri ? { uri } : PFPPlaceholder}
25-
placeholder={PFPPlaceholder}
26-
placeholderContentFit="cover"
27-
contentFit="cover"
28-
style={[
29-
{
30-
width: size || 121,
31-
height: size || 121,
32-
borderRadius: size || 121,
33-
},
34-
style,
35-
]}
36+
source={{ uri }}
37+
style={[styles.image, style]}
3638
/>
39+
) : (
40+
<View style={[styles.placeholder, style]}>
41+
<Text style={styles.text}>{firstLetter}</Text>
42+
</View>
3743
);
3844
}
45+
46+
const getStyles = (colorScheme: ColorSchemeName, size: number) =>
47+
StyleSheet.create({
48+
image: {
49+
width: size,
50+
height: size,
51+
borderRadius: size,
52+
},
53+
placeholder: {
54+
width: size,
55+
height: size,
56+
borderRadius: size / 2,
57+
backgroundColor:
58+
colorScheme === "dark"
59+
? textSecondaryColor(colorScheme)
60+
: actionSecondaryColor(colorScheme),
61+
justifyContent: "center",
62+
alignItems: "center",
63+
},
64+
text: {
65+
fontSize: size / 2,
66+
fontWeight: "500",
67+
color: "white",
68+
textAlign: "center",
69+
},
70+
});

components/Chat/Message/Message.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
textPrimaryColor,
66
textSecondaryColor,
77
} from "@styles/colors";
8-
import { sizes } from "@styles/sizes";
8+
import { AvatarSizes } from "@styles/sizes";
99
import * as Haptics from "expo-haptics";
1010
import { ReactNode, useCallback, useMemo, useRef } from "react";
1111
import {
@@ -96,7 +96,11 @@ const MessageSenderAvatar = ({ message }: { message: MessageToDisplay }) => {
9696
return (
9797
<View style={styles.groupSenderAvatarWrapper}>
9898
{!message.hasNextMessageInSeries ? (
99-
<Avatar size={sizes.avatar} uri={getPreferredAvatar(senderSocials)} />
99+
<Avatar
100+
size={AvatarSizes.messageSender}
101+
uri={getPreferredAvatar(senderSocials)}
102+
name={getPreferredName(senderSocials, message.senderAddress)}
103+
/>
100104
) : (
101105
<View style={styles.avatarPlaceholder} />
102106
)}
@@ -533,8 +537,8 @@ const useStyles = () => {
533537
marginVertical: 6,
534538
},
535539
avatarPlaceholder: {
536-
width: sizes.avatar,
537-
height: sizes.avatar,
540+
width: AvatarSizes.messageSender,
541+
height: AvatarSizes.messageSender,
538542
},
539543
});
540544
};

components/Conversation/ConversationTitle.tsx

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Clipboard from "@react-native-clipboard/clipboard";
22
import { NativeStackScreenProps } from "@react-navigation/native-stack";
33
import { headerTitleStyle, textPrimaryColor } from "@styles/colors";
4+
import { AvatarSizes } from "@styles/sizes";
45
import { MutableRefObject, useEffect, useRef, useState } from "react";
56
import {
67
Alert,
@@ -21,7 +22,6 @@ import { getPreferredAvatar } from "../../utils/profile";
2122
import { conversationName, getTitleFontScale } from "../../utils/str";
2223
import Avatar from "../Avatar";
2324
import { useEnableDebug } from "../DebugButton";
24-
import Picto from "../Picto/Picto";
2525

2626
type Props = {
2727
isBlockedPeer: boolean;
@@ -132,27 +132,15 @@ export default function ConversationTitle({
132132
paddingRight: 40,
133133
}}
134134
>
135-
{avatar ? (
136-
<Avatar
137-
uri={avatar}
138-
size={30}
139-
style={{
140-
marginRight: Platform.OS === "android" ? 24 : 7,
141-
marginLeft: Platform.OS === "ios" ? 0 : -9,
142-
}}
143-
/>
144-
) : (
145-
<Picto
146-
picto="info.circle"
147-
// Reason this is smaller than avatar is when it's the same size, looks like more of an error icon than an avatar placeholder
148-
size={Platform.OS === "android" ? 20 : 16}
149-
color={textPrimaryColor(colorScheme)}
150-
style={{
151-
marginRight: Platform.OS === "android" ? 24 : 16,
152-
marginLeft: Platform.OS === "ios" ? 0 : -9,
153-
}}
154-
/>
155-
)}
135+
<Avatar
136+
uri={avatar}
137+
size={AvatarSizes.conversationTitle}
138+
style={{
139+
marginRight: Platform.OS === "android" ? 24 : 7,
140+
marginLeft: Platform.OS === "ios" ? 0 : -9,
141+
}}
142+
name={conversationName(conversation)}
143+
/>
156144
<Text
157145
style={{
158146
color: textPrimaryColor(colorScheme),

components/ConversationList/ProfileSettingsButton.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AvatarSizes } from "@styles/sizes";
12
import React, { useCallback, useEffect, useState } from "react";
23
import { Platform, TouchableOpacity } from "react-native";
34

@@ -10,7 +11,7 @@ import {
1011
} from "../../data/store/accountsStore";
1112
import { evmHelpers } from "../../utils/evm/helpers";
1213
import { navigate } from "../../utils/navigation";
13-
import { getPreferredAvatar } from "../../utils/profile";
14+
import { getPreferredAvatar, getPreferredName } from "../../utils/profile";
1415
import Avatar from "../Avatar";
1516
import Button from "../Button/Button";
1617

@@ -81,13 +82,14 @@ export default function ProfileSettingsButton() {
8182
<TouchableOpacity activeOpacity={0.2} onPress={openProfile}>
8283
<Avatar
8384
uri={getPreferredAvatar(socials)}
84-
size={Platform.OS === "ios" ? 16 : 24}
85+
size={AvatarSizes.profileSettings}
8586
style={{
8687
width: Platform.OS === "android" ? undefined : 32,
8788
height: Platform.OS === "android" ? undefined : 32,
8889
marginRight: Platform.OS === "android" ? 0 : 10,
8990
marginTop: Platform.OS === "ios" ? 3 : 0,
9091
}}
92+
name={getPreferredName(socials, account)}
9193
/>
9294
</TouchableOpacity>
9395
);

components/ConversationListItem.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
textPrimaryColor,
1010
textSecondaryColor,
1111
} from "@styles/colors";
12+
import { AvatarSizes } from "@styles/sizes";
1213
import * as Haptics from "expo-haptics";
1314
import React, { memo, useCallback, useEffect, useRef, useState } from "react";
1415
import {
@@ -120,9 +121,10 @@ const ConversationListItem = memo(function ConversationListItem({
120121
const listItemContent = (
121122
<View style={styles.conversationListItem}>
122123
<Avatar
123-
size={Platform.OS === "ios" ? 56 : 47}
124+
size={AvatarSizes.conversationListItem}
124125
style={styles.avatarWrapper}
125126
uri={conversationPeerAvatar}
127+
name={conversationName}
126128
/>
127129
<View style={styles.conversationListItemContent}>
128130
<Text style={styles.conversationName} numberOfLines={1}>

components/Onboarding/UserProfile.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ export const UserProfile = ({ onboarding, navigation }: Props) => {
259259
uri={profile?.avatar}
260260
style={styles.avatar}
261261
color={intermediaryScreenShown}
262+
name={profile.displayName || "👋"}
262263
/>
263264
{intermediaryScreenShown && (
264265
<>

components/PinnedConversations/PinnedConversation.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { backgroundColor, textSecondaryColor } from "@styles/colors";
2+
import { AvatarSizes } from "@styles/sizes";
23
import { FC, useCallback } from "react";
34
import {
45
StyleSheet,
@@ -12,7 +13,7 @@ import { XmtpConversation } from "../../data/store/chatStore";
1213
import { useGroupName } from "../../hooks/useGroupName";
1314
import { useGroupPhoto } from "../../hooks/useGroupPhoto";
1415
import { navigate } from "../../utils/navigation";
15-
import { getPreferredAvatar } from "../../utils/profile";
16+
import { getPreferredAvatar, getPreferredName } from "../../utils/profile";
1617
import Avatar from "../Avatar";
1718
interface Props {
1819
conversation: XmtpConversation;
@@ -48,8 +49,9 @@ export const PinnedConversation: FC<Props> = ({ conversation }) => {
4849
<Avatar
4950
key={conversation.topic}
5051
uri={avatar}
51-
size={80}
52+
size={AvatarSizes.pinnedConversation}
5253
style={styles.avatar}
54+
name={getPreferredName(socials, conversation.peerAddress || "")}
5355
/>
5456
<Text style={styles.text}>{title}</Text>
5557
</TouchableOpacity>

components/Recommendations/Recommendation.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
textPrimaryColor,
55
textSecondaryColor,
66
} from "@styles/colors";
7+
import { AvatarSizes } from "@styles/sizes";
78
import {
89
Image,
910
Platform,
@@ -68,8 +69,9 @@ export function Recommendation({
6869
{!embedInChat && (
6970
<Avatar
7071
uri={getPreferredAvatar(profile)}
71-
size={40}
72+
size={AvatarSizes.listItemDisplay}
7273
style={styles.avatar}
74+
name={preferredName}
7375
/>
7476
)}
7577
<View style={styles.recommendationLeft}>

components/Search/ProfileSearchItem.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
textPrimaryColor,
55
textSecondaryColor,
66
} from "@styles/colors";
7+
import { AvatarSizes } from "@styles/sizes";
78
import { Platform, StyleSheet, Text, View, useColorScheme } from "react-native";
89

910
import { ProfileSocials } from "../../data/store/profilesStore";
@@ -41,7 +42,12 @@ export function ProfileSearchItem({
4142
return (
4243
<View key={address} style={styles.container}>
4344
<View style={styles.left}>
44-
<Avatar uri={preferredAvatar} size={40} style={styles.avatar} />
45+
<Avatar
46+
uri={preferredAvatar}
47+
size={AvatarSizes.listItemDisplay}
48+
style={styles.avatar}
49+
name={preferredName}
50+
/>
4551
<View>
4652
<Text style={styles.title}>
4753
{preferredName || shortAddress(address)}

ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1873,7 +1873,7 @@ SPEC CHECKSUMS:
18731873
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
18741874
XMTP: 7d69afe403538056efb6caf01db7248b01221c48
18751875
XMTPReactNative: b5b2efeb9ad30341896b60fbb2a8373dd3d0dd20
1876-
Yoga: d17d2cc8105eed528474683b42e2ea310e1daf61
1876+
Yoga: 805bf71192903b20fc14babe48080582fee65a80
18771877

18781878
PODFILE CHECKSUM: 2cf1857df00ddf0c58944ef41304afd85ca07ba7
18791879

screens/Group.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,12 @@ export default function GroupScreen({
294294
>
295295
<List.Section>
296296
<View style={{ alignItems: "center" }}>
297-
<Avatar uri={localGroupPhoto} style={styles.avatar} color={false} />
297+
<Avatar
298+
uri={localGroupPhoto}
299+
style={styles.avatar}
300+
color={false}
301+
name={formattedGroupName}
302+
/>
298303
{canEditGroupMetadata && (
299304
<Button
300305
variant="text"

screens/NewConversation/NewGroupSummary.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,12 @@ export default function NewGroupSummary({
161161
>
162162
<List.Section>
163163
<View style={styles.listSectionContainer}>
164-
<Avatar uri={groupPhoto} style={styles.avatar} color={false} />
164+
<Avatar
165+
uri={groupPhoto}
166+
style={styles.avatar}
167+
color={false}
168+
name={groupName}
169+
/>
165170
<Button
166171
variant="text"
167172
title={

screens/Profile.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,11 @@ export default function ProfileScreen({
500500
style={styles.profile}
501501
contentContainerStyle={styles.profileContent}
502502
>
503-
<Avatar uri={getPreferredAvatar(socials)} style={styles.avatar} />
503+
<Avatar
504+
uri={getPreferredAvatar(socials)}
505+
style={styles.avatar}
506+
name={getPreferredName(socials, peerAddress)}
507+
/>
504508
<Text style={styles.title}>{getPreferredName(socials, peerAddress)}</Text>
505509
{isMyProfile && (
506510
<TableView

styles/sizes/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import { Platform } from "react-native";
22

3+
export enum AvatarSizes {
4+
default = 121,
5+
conversationListItem = Platform.OS === "ios" ? 56 : 47,
6+
messageSender = Platform.OS === "ios" ? 24 : 21,
7+
conversationTitle = 30,
8+
profileSettings = Platform.OS === "ios" ? 16 : 24,
9+
pinnedConversation = 80,
10+
listItemDisplay = 40,
11+
}
12+
313
// Sizes, spaces, paddings, margins, etc.
4-
export const sizes = {
5-
avatar: Platform.OS === "ios" ? 24 : 21,
6-
};
14+
export const sizes = {};
715

816
// Font sizes
917
export const fontSizes = {};

0 commit comments

Comments
 (0)