Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
79fb1af
fix(android): drop the default OneSignal large icon — every push show…
abalinda Sep 2, 2026
5de5a0e
fix(avatar): own avatar shows the first letter of the username, not t…
abalinda Sep 2, 2026
82a1e0e
fix(about): the Terms of Service title follows the app language
abalinda Sep 2, 2026
03bb1e4
fix(about): every policy title follows the app language, not only the…
abalinda Sep 2, 2026
d06f79c
fix(avatar): seed the home avatar from the username, whatever showFul…
abalinda Sep 2, 2026
3c08191
Merge origin/dev into native-release-bug-fixes-aleks
abalinda Sep 2, 2026
daf5580
fix(notifications): one opt-in, one subscription — stop re-logging in…
abalinda Sep 3, 2026
53a97ae
fix(notifications): detect a new opt-in from the SDK's previous state…
abalinda Sep 3, 2026
dc45a54
fix(notifications): a new opt-in is the one false → true transition, …
abalinda Sep 3, 2026
d341285
fix(card): keep revealed details across an app switch; copy the expir…
abalinda Sep 3, 2026
b1815ba
Merge origin/dev into native-release-bug-fixes-aleks
abalinda Sep 3, 2026
361d90f
fix(card): expiry/cvv copy icons on the DS icon scale
abalinda Sep 3, 2026
5659eb6
fix(card): cover revealed card details while the app is backgrounded
abalinda Sep 3, 2026
b7bf1b3
fix(notifications): join the in-flight OneSignal login instead of sta…
abalinda Sep 3, 2026
d836cbe
Merge origin/dev into native-release-bug-fixes-aleks
abalinda Sep 3, 2026
624d56b
Update src/components/Card/CardFace.tsx
abalinda Sep 3, 2026
159e0b8
fix(profile): Personal details follows the name-visibility setting (T…
abalinda Sep 4, 2026
8cfa3b7
Merge origin/dev into native-release-bug-fixes-aleks
abalinda Sep 4, 2026
5a77f1a
fix: CI red from the dev merge, plus three review findings
abalinda Sep 4, 2026
c061b17
fix(notifications): guard the login commit and retry a joined login t…
abalinda Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
206 changes: 0 additions & 206 deletions src/components/Global/DotFaceAvatar.tsx

This file was deleted.

42 changes: 0 additions & 42 deletions src/components/Global/__tests__/DotFaceAvatar.test.tsx

This file was deleted.

14 changes: 9 additions & 5 deletions src/components/Profile/AvatarWithBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ interface AvatarWithBadgeProps {
textColor?: string
iconFillColor?: string
logo?: string | StaticImageData
/**
* The user's own avatar (home chip, self profile header) shows the first
* letter of the username only; contacts keep two-letter initials.
*/
firstLetterOnly?: boolean
/**
* Rendered when `logo` fails to load (next/image onError). Lets a parent
* provide a semantic fallback (e.g. bank tx → bank icon on dark bg)
Expand All @@ -42,6 +47,7 @@ const AvatarWithBadge: React.FC<AvatarWithBadgeProps> = ({
iconFillColor,
logo,
fallback,
firstLetterOnly,
}) => {
const [logoFailed, setLogoFailed] = useState(false)
// board 17802:61529 sizes XS/S/M/L are 24/32/48/64 — the boxes here already
Expand All @@ -66,11 +72,9 @@ const AvatarWithBadge: React.FC<AvatarWithBadgeProps> = ({
}

const initials = useMemo(() => {
if (name) {
return getInitialsFromName(name)
}
return ''
}, [name])
if (!name) return ''
return firstLetterOnly ? name.trim().charAt(0).toUpperCase() : getInitialsFromName(name)
}, [name, firstLetterOnly])

if (logo && !logoFailed) {
return (
Expand Down
8 changes: 4 additions & 4 deletions src/components/Profile/components/ProfileHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import posthog from 'posthog-js'
import React, { useEffect, useRef } from 'react'
import { twMerge } from '@/utils/tw'
import AvatarWithBadge from '../AvatarWithBadge'
import DotFaceAvatar from '@/components/Global/DotFaceAvatar'
import { VerifiedUserLabel } from '@/components/UserHeader'
import { useAuth } from '@/context/authContext'
import { useIdentityVerification } from '@/hooks/useIdentityVerification'
Expand Down Expand Up @@ -63,10 +62,11 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = ({
return (
<>
<div className={twMerge('space-y-2 flex flex-col items-center', className)}>
{/* Own profile gets the generated dot face; someone else's
public profile keeps initials (letters identify others). */}
{/* Own profile shows the first letter of the username; someone
else's public profile keeps initials (letters identify others).
The generated face (497ab2a5e) is parked until avatar v2. */}
{isSelfProfile ? (
<DotFaceAvatar username={username} size={88} />
<AvatarWithBadge name={username} firstLetterOnly size="large" />
) : (
<AvatarWithBadge name={name || username} />
)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/UserHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import DotFaceAvatar from '@/components/Global/DotFaceAvatar'
import AvatarWithBadge from '@/components/Profile/AvatarWithBadge'
import Link from 'next/link'
import { Icon } from '../Global/Icons/Icon'
import { twMerge } from '@/utils/tw'
Expand Down Expand Up @@ -28,7 +28,7 @@ export const UserHeader = ({ username }: UserHeaderProps) => {
shadowSize="3"
size="small"
>
<DotFaceAvatar username={username} className="h-[30px] w-[30px]" />
<AvatarWithBadge size="extra-small" name={username} firstLetterOnly className="h-[30px] w-[30px]" />
<span className="pr-1 text-body-xs font-semibold whitespace-nowrap md:text-body-s">{username}</span>
</Button>
</Link>
Expand Down
13 changes: 5 additions & 8 deletions src/features/home/views/HomeTopNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { Icon } from '@/components/Global/Icons/Icon'
import InvitesIcon from '@/components/Home/InvitesIcon'
import DotFaceAvatar from '@/components/Global/DotFaceAvatar'
import AvatarWithBadge from '@/components/Profile/AvatarWithBadge'
import { useAppHaptic } from '@/hooks/useAppHaptic'
import { useAppTranslations } from '@/i18n/app/useAppTranslations'
Expand Down Expand Up @@ -31,14 +30,12 @@ export function HomeTopNav({ avatarName, showRewards }: HomeTopNavProps) {
className="relative block after:absolute after:-inset-1.5"
aria-label={t('openProfile')}
>
{/* The generated face, which is what DotFaceAvatar exists for —
own identity, here and on the profile header. This chip was
rebuilt onto initials during the nav consolidation and the
face was left behind in UserHeader, which nothing renders.
A user with no name string at all still gets an avatar-toned
circle (yellow — the palette's no-name default). */}
{/* Own identity: the first letter of the username, here and on
the profile header (the generated face is parked until avatar
v2). A user with no name string at all still gets an
avatar-toned circle (yellow — the palette's no-name default). */}
{avatarName ? (
<DotFaceAvatar username={avatarName} size={32} />
<AvatarWithBadge size="extra-small" name={avatarName} firstLetterOnly />
Comment thread
abalinda marked this conversation as resolved.
Outdated
) : (
<AvatarWithBadge
size="extra-small"
Expand Down
6 changes: 3 additions & 3 deletions src/features/home/views/__tests__/HomeTopNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ jest.mock('@/hooks/useAppHaptic', () => ({ useAppHaptic: () => ({ triggerHaptic:
jest.mock('@/components/Home/InvitesIcon', () => ({ __esModule: true, default: () => null }))

describe('HomeTopNav', () => {
it('wears the generated face, not initials — this chip is the user own identity', () => {
it('shows the first letter of the username — not two-letter initials, not a generated face', () => {
const { container } = renderWithIntl(<HomeTopNav avatarName="testuser" showRewards={false} />)

// DotFaceAvatar draws an svg; the initials avatar would render the letters
expect(container.querySelector('a[href="/profile"] svg')).toBeInTheDocument()
expect(container.querySelector('a[href="/profile"]')).toHaveTextContent(/^T$/)
expect(screen.queryByText(/^TE$/i)).not.toBeInTheDocument()
expect(container.querySelector('a[href="/profile"] svg')).not.toBeInTheDocument()
})

it('falls back to the no-name circle when there is no username yet', () => {
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/app/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@
"unknown": "Unknown"
},
"exchangeRate": "Exchange rate",
"slideToProceed": "Slide to Proceed",
"userAvatarAlt": "Avatar for {username}"
"slideToProceed": "Slide to Proceed"
},
"navigation": {
"home": "Home",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/app/messages/en.marketing.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@
"unknown": "Unknown"
},
"exchangeRate": "Exchange rate",
"slideToProceed": "Slide to Proceed",
"userAvatarAlt": "Avatar for {username}"
"slideToProceed": "Slide to Proceed"
},
"errors": {
"balanceSettling": "Your balance isn't fully available yet. Please try again in a few seconds.",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/app/messages/es-419.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@
"unknown": "Desconocido"
},
"exchangeRate": "Tipo de cambio",
"slideToProceed": "Desliza para continuar",
"userAvatarAlt": "Avatar de {username}"
"slideToProceed": "Desliza para continuar"
},
"navigation": {
"home": "Inicio",
Expand Down
Loading
Loading