Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 9 additions & 4 deletions src/components/common/CreatorCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
import showToast from '@/utils/toast.util';
import { formatCompactNumber } from '@/utils/numberFormat.utils';
import { formatCreatorKeyPriceDisplay } from '@/utils/keyPriceDisplay.utils';
import { formatCreatorHandle } from '@/utils/handleDisplay.utils';
import {
formatCreatorHandle,
truncateHandle,
} from '@/utils/handleDisplay.utils';
import { normalizeCreatorDisplayName } from '@/utils/creatorDisplayName.utils';
import { getCreatorPriceChartAccessibilityCopy } from '@/utils/creatorPriceChartAccessibility.utils';
import { formatJoinDate } from '@/utils/formatJoinDate';
Expand Down Expand Up @@ -78,6 +81,8 @@
const displayInstructorHandle =
formatCreatorHandle(creator.instructorId) || '@creator';
const displaySocialHandle = formatCreatorHandle(creator.socialHandle);
const truncatedInstructorHandle = truncateHandle(displayInstructorHandle);
const truncatedSocialHandle = truncateHandle(displaySocialHandle);
const displayCreatorName =
normalizeCreatorDisplayName(creator.title) || 'Unnamed creator';
const priceChartAccessibility = getCreatorPriceChartAccessibilityCopy({
Expand Down Expand Up @@ -117,7 +122,7 @@
setTransactionState('failed');
setFailureDetails({
errorMessage:
'Transaction failed: Insufficient balance to complete the purchase.',

Check warning on line 125 in src/components/common/CreatorCard.tsx

View workflow job for this annotation

GitHub Actions / verify

React Hook useEffect has a missing dependency: 'handleBuy'. Either include it or remove the dependency array
errorCode: 'ERR_INSUFFICIENT_BALANCE',
txHash: '0xabcd1234...failed',
developerDetails: {
Expand Down Expand Up @@ -300,7 +305,7 @@
creatorShareSupply={creator.creatorShareSupply}
isVerified={creator.isVerified}
>
{displayInstructorHandle}
{truncatedInstructorHandle}
</CreatorHandleHoverCard>
</p>

Expand All @@ -324,7 +329,7 @@
creatorShareSupply={creator.creatorShareSupply}
isVerified={creator.isVerified}
>
<span className="truncate">{displaySocialHandle}</span>
<span className="truncate">{truncatedSocialHandle}</span>
</CreatorHandleHoverCard>
</div>
) : (
Expand Down Expand Up @@ -410,7 +415,7 @@
}
value={
creator.socialHandle
? displaySocialHandle
? truncatedSocialHandle
: 'No public handle'
}
valueTitle={
Expand Down
38 changes: 36 additions & 2 deletions src/utils/__tests__/handleDisplay.utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { formatCreatorHandle } from '../handleDisplay.utils';
import { formatCreatorHandle, truncateHandle } from '../handleDisplay.utils';

describe('formatCreatorHandle', () => {
it('lowercases mixed-case handles and prepends @', () => {
Expand Down Expand Up @@ -32,7 +32,9 @@ describe('formatCreatorHandle', () => {
});

it('is idempotent: formatting an already-formatted handle is a no-op', () => {
expect(formatCreatorHandle(formatCreatorHandle('ARivers'))).toBe('@arivers');
expect(formatCreatorHandle(formatCreatorHandle('ARivers'))).toBe(
'@arivers'
);
});

it('does not modify the underlying string the caller passes in', () => {
Expand All @@ -44,3 +46,35 @@ describe('formatCreatorHandle', () => {
expect(raw).toBe('ARivers');
});
});

describe('truncateHandle', () => {
it('returns short handle unchanged (under max length)', () => {
expect(truncateHandle('@short', 20)).toBe('@short');
expect(truncateHandle('abc', 5)).toBe('abc');
});

it('returns exact max handle unchanged', () => {
expect(truncateHandle('12345678901234567890', 20)).toBe(
'12345678901234567890'
);
expect(truncateHandle('abcde', 5)).toBe('abcde');
});

it('truncates one over max handle with ellipsis', () => {
expect(truncateHandle('123456789012345678901', 20)).toBe(
'12345678901234567890...'
);
expect(truncateHandle('abcdef', 5)).toBe('abcde...');
});

it('uses default max of 20 characters when maxLength is not specified', () => {
// Exactly 20 chars should not be truncated
expect(truncateHandle('12345678901234567890')).toBe(
'12345678901234567890'
);
// 21 chars (one over max) should be truncated
expect(truncateHandle('123456789012345678901')).toBe(
'12345678901234567890...'
);
});
});
19 changes: 18 additions & 1 deletion src/utils/handleDisplay.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,25 @@ export const formatCreatorHandle = (raw: string | null | undefined): string => {
if (raw == null) return '';
const trimmed = raw.trim();
if (trimmed === '') return '';
const withoutLeadingAt = trimmed.startsWith('@') ? trimmed.slice(1) : trimmed;
const withoutLeadingAt = trimmed.startsWith('@')
? trimmed.slice(1)
: trimmed;
const normalised = withoutLeadingAt.trim().toLowerCase();
if (normalised === '') return '';
return `@${normalised}`;
};

/**
* Truncates a creator handle to a maximum length, appending an ellipsis
* only when truncation occurs.
*/
export const truncateHandle = (
handle: string,
maxLength: number = 20
): string => {
if (!handle) return '';
if (handle.length <= maxLength) {
return handle;
}
return `${handle.slice(0, maxLength)}...`;
};
Loading