Skip to content

Commit 5020076

Browse files
committed
Add userId support across wallet APIs and UI
Introduces support for the userId field throughout wallet-related APIs and dashboard components. Updates search, display, and transformation logic to handle userId in addition to existing identifiers, and improves UI to distinguish between 'User Identifier' (userId) and 'Auth Identifier'. Updates API client types and documentation to reflect the new userId field. Closes BLD-447
1 parent 29d05a5 commit 5020076

File tree

9 files changed

+131
-20
lines changed

9 files changed

+131
-20
lines changed

.changeset/breezy-heads-crash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/api": patch
3+
---
4+
5+
added support for userId across wallet apis

apps/dashboard/src/@/components/in-app-wallet-users-content/AdvancedSearchInput.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ import {
1414
} from "@/components/ui/select";
1515
import type { SearchType } from "./types";
1616

17+
const searchTypeLabels: Record<SearchType, string> = {
18+
email: "Email",
19+
phone: "Phone",
20+
id: "Auth Identifier",
21+
address: "Address",
22+
externalWallet: "External Wallet",
23+
userId: "User Identifier",
24+
};
25+
1726
export function AdvancedSearchInput(props: {
1827
onSearch: (searchType: SearchType, query: string) => void;
1928
onClear: () => void;
@@ -52,7 +61,8 @@ export function AdvancedSearchInput(props: {
5261
<SelectContent>
5362
<SelectItem value="email">Email</SelectItem>
5463
<SelectItem value="phone">Phone</SelectItem>
55-
<SelectItem value="id">ID</SelectItem>
64+
<SelectItem value="id">Auth Identifier</SelectItem>
65+
<SelectItem value="userId">User Identifier</SelectItem>
5666
<SelectItem value="address">Address</SelectItem>
5767
<SelectItem value="externalWallet">External Wallet</SelectItem>
5868
</SelectContent>
@@ -62,7 +72,7 @@ export function AdvancedSearchInput(props: {
6272
<div className="relative flex-1">
6373
<Input
6474
className="bg-background pl-9 border-r-0 rounded-r-none rounded-l-full"
65-
placeholder={`Search by ${searchType}...`}
75+
placeholder={`Search by ${searchTypeLabels[searchType]}...`}
6676
value={query}
6777
onChange={(e) => setQuery(e.target.value)}
6878
onKeyDown={handleKeyDown}

apps/dashboard/src/@/components/in-app-wallet-users-content/SearchResults.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import {
1212
TooltipProvider,
1313
TooltipTrigger,
1414
} from "@/components/ui/tooltip";
15+
import { CopyTextButton } from "../ui/CopyTextButton";
1516

16-
const getUserIdentifier = (user: WalletUser) => {
17+
const getAuthIdentifier = (user: WalletUser) => {
1718
const mainDetail = user.linkedAccounts[0]?.details;
1819
return (
1920
mainDetail?.email ??
@@ -24,6 +25,13 @@ const getUserIdentifier = (user: WalletUser) => {
2425
);
2526
};
2627

28+
const truncateIdentifier = (value: string) => {
29+
if (value.length <= 18) {
30+
return value;
31+
}
32+
return `${value.slice(0, 8)}...${value.slice(-4)}`;
33+
};
34+
2735
export function SearchResults(props: {
2836
results: WalletUser[];
2937
client: ThirdwebClient;
@@ -51,6 +59,8 @@ export function SearchResults(props: {
5159
const mainDetail = user.linkedAccounts?.[0]?.details;
5260
const email = mainDetail?.email as string | undefined;
5361
const phone = mainDetail?.phone as string | undefined;
62+
const authIdentifier = getAuthIdentifier(user);
63+
const userIdentifier = user.id;
5464

5565
// Get external wallet addresses from linkedAccounts where type is 'siwe'
5666
const externalWalletAccounts =
@@ -68,7 +78,34 @@ export function SearchResults(props: {
6878
<p className="text-sm font-medium text-muted-foreground">
6979
User Identifier
7080
</p>
71-
<p className="text-sm">{getUserIdentifier(user)}</p>
81+
{userIdentifier ? (
82+
<CopyTextButton
83+
textToShow={truncateIdentifier(userIdentifier)}
84+
textToCopy={userIdentifier}
85+
tooltip="Copy User Identifier"
86+
copyIconPosition="left"
87+
variant="ghost"
88+
/>
89+
) : (
90+
<p className="text-sm">N/A</p>
91+
)}
92+
</div>
93+
94+
<div>
95+
<p className="text-sm font-medium text-muted-foreground">
96+
Auth Identifier
97+
</p>
98+
{authIdentifier ? (
99+
<CopyTextButton
100+
textToShow={truncateIdentifier(authIdentifier)}
101+
textToCopy={authIdentifier}
102+
tooltip="Copy Auth Identifier"
103+
copyIconPosition="left"
104+
variant="ghost"
105+
/>
106+
) : (
107+
<p className="text-sm">N/A</p>
108+
)}
72109
</div>
73110

74111
{walletAddress && (

apps/dashboard/src/@/components/in-app-wallet-users-content/in-app-wallet-users-content.tsx

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { SearchResults } from "./SearchResults";
2828
import { searchUsers } from "./searchUsers";
2929
import type { SearchType } from "./types";
3030

31-
const getUserIdentifier = (accounts: WalletUser["linkedAccounts"]) => {
31+
const getAuthIdentifier = (accounts: WalletUser["linkedAccounts"]) => {
3232
const mainDetail = accounts[0]?.details;
3333
return (
3434
mainDetail?.email ??
@@ -44,6 +44,13 @@ const getExternalWallets = (accounts: WalletUser["linkedAccounts"]) => {
4444

4545
const columnHelper = createColumnHelper<WalletUser>();
4646

47+
const truncateIdentifier = (value: string) => {
48+
if (value.length <= 18) {
49+
return value;
50+
}
51+
return `${value.slice(0, 8)}...${value.slice(-4)}`;
52+
};
53+
4754
export function InAppWalletUsersPageContent(
4855
props: {
4956
authToken: string;
@@ -56,31 +63,48 @@ export function InAppWalletUsersPageContent(
5663
) {
5764
const columns = useMemo(() => {
5865
return [
66+
columnHelper.accessor("id", {
67+
cell: (cell) => {
68+
const userId = cell.getValue();
69+
70+
if (!userId) {
71+
return "N/A";
72+
}
73+
74+
return (
75+
<CopyTextButton
76+
textToShow={truncateIdentifier(userId)}
77+
textToCopy={userId}
78+
tooltip="Copy User Identifier"
79+
copyIconPosition="left"
80+
variant="ghost"
81+
/>
82+
);
83+
},
84+
header: "User Identifier",
85+
id: "user_identifier",
86+
}),
5987
columnHelper.accessor("linkedAccounts", {
6088
cell: (cell) => {
61-
const identifier = getUserIdentifier(cell.getValue());
89+
const identifier = getAuthIdentifier(cell.getValue());
6290

6391
if (!identifier) {
6492
return "N/A";
6593
}
6694

6795
return (
6896
<CopyTextButton
69-
textToShow={
70-
identifier.length > 30
71-
? `${identifier.slice(0, 30)}...`
72-
: identifier
73-
}
97+
textToShow={truncateIdentifier(identifier)}
7498
textToCopy={identifier}
75-
tooltip="Copy User Identifier"
99+
tooltip="Copy Auth Identifier"
76100
copyIconPosition="left"
77101
variant="ghost"
78102
/>
79103
);
80104
},
81105
enableColumnFilter: true,
82-
header: "User Identifier",
83-
id: "user_identifier",
106+
header: "Auth Identifier",
107+
id: "auth_identifier",
84108
}),
85109
columnHelper.accessor("wallets", {
86110
cell: (cell) => {
@@ -243,7 +267,8 @@ export function InAppWalletUsersPageContent(
243267
: "Wallet not created yet",
244268
external_wallets: externalWalletAddresses || "None",
245269
login_methods: row.linkedAccounts.map((acc) => acc.type).join(", "),
246-
user_identifier: getUserIdentifier(row.linkedAccounts),
270+
auth_identifier: getAuthIdentifier(row.linkedAccounts) || "N/A",
271+
user_identifier: row.id || "N/A",
247272
};
248273
}),
249274
);

apps/dashboard/src/@/components/in-app-wallet-users-content/searchUsers.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ configure({
1818
type APIWallet = ListUserWalletsResponses[200]["result"]["wallets"][0];
1919
type APIProfile = APIWallet["profiles"][0];
2020

21-
// Transform API response to match existing WalletUser format
21+
// Transform API response to match wallet user format
2222
function transformToWalletUser(apiWallet: APIWallet): WalletUser {
2323
return {
24-
id: getProfileId(apiWallet.profiles[0]) || "",
24+
id: apiWallet.userId || getProfileId(apiWallet.profiles[0]) || "",
2525
linkedAccounts: apiWallet.profiles.map((profile) => {
2626
// Create details object based on the profile data
2727
let details:
@@ -105,6 +105,9 @@ export async function searchUsers(
105105
case "externalWallet":
106106
queryParams.externalWalletAddress = query;
107107
break;
108+
case "userId":
109+
queryParams.userId = query;
110+
break;
108111
}
109112

110113
// Use the generated API function with Bearer authentication

apps/dashboard/src/@/components/in-app-wallet-users-content/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export type SearchType =
33
| "phone"
44
| "id"
55
| "address"
6-
| "externalWallet";
6+
| "externalWallet"
7+
| "userId";

apps/dashboard/src/@/hooks/useEmbeddedWallets.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ const fetchAccountList = ({
7676
};
7777
};
7878

79-
// Transform API response to match existing WalletUser format
79+
// Transform API response to match the wallet user format
8080
function transformToWalletUser(apiWallet: APIWallet): WalletUser {
8181
return {
82-
id: getProfileId(apiWallet.profiles[0]) || "",
82+
id: apiWallet.userId || getProfileId(apiWallet.profiles[0]) || "",
8383
linkedAccounts: apiWallet.profiles.map((profile) => {
8484
// Create details object based on the profile data
8585
let details:

packages/api/src/client/sdk.gen.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ export const initiateAuthentication = <ThrowOnError extends boolean = false>(
244244
* - `isNewUser` - Whether this is a new wallet creation
245245
* - `token` - JWT token for authenticated API requests
246246
* - `type` - The authentication method used
247+
* - `userId` - Unique identifier for the authenticated user
247248
* - `walletAddress` - Your new or existing wallet address
248249
*
249250
* **Next step - Verify your token:**
@@ -442,6 +443,7 @@ export const socialAuthentication = <ThrowOnError extends boolean = false>(
442443
* Retrieve the authenticated user's wallet information including wallet addresses and linked authentication wallets. This endpoint provides comprehensive user data for the currently authenticated session.
443444
*
444445
* **Returns:**
446+
* - userId - Unique identifier for this wallet in thirdweb auth
445447
* - Primary wallet address
446448
* - Smart wallet address (if available)
447449
* - Wallet creation timestamp

packages/api/src/client/types.gen.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,10 @@ export type CompleteAuthenticationResponses = {
330330
* Type of authentication completed
331331
*/
332332
type: string;
333+
/**
334+
* Unique identifier for the authenticated user
335+
*/
336+
userId: string;
333337
/**
334338
* The wallet address
335339
*/
@@ -875,6 +879,10 @@ export type GetMyWalletResponses = {
875879
*/
876880
200: {
877881
result: {
882+
/**
883+
* Unique identifier for the user wallet within the thirdweb auth system.
884+
*/
885+
userId?: string;
878886
/**
879887
* The EOA (Externally Owned Wallet) address of the wallet. This is the traditional wallet address.
880888
*/
@@ -1076,6 +1084,10 @@ export type ListUserWalletsData = {
10761084
address?: string;
10771085
externalWalletAddress?: string;
10781086
id?: string;
1087+
/**
1088+
* Filter results by the unique user identifier returned by auth flows.
1089+
*/
1090+
userId?: string;
10791091
};
10801092
url: "/v1/wallets/user";
10811093
};
@@ -1122,6 +1134,10 @@ export type ListUserWalletsResponses = {
11221134
* Array of user wallets
11231135
*/
11241136
wallets: Array<{
1137+
/**
1138+
* Unique identifier for the user wallet within the thirdweb auth system.
1139+
*/
1140+
userId?: string;
11251141
/**
11261142
* The EOA (Externally Owned Wallet) address of the wallet. This is the traditional wallet address.
11271143
*/
@@ -1362,6 +1378,10 @@ export type CreateUserWalletResponses = {
13621378
*/
13631379
200: {
13641380
result: {
1381+
/**
1382+
* Unique identifier for the user wallet within the thirdweb auth system.
1383+
*/
1384+
userId?: string;
13651385
/**
13661386
* The EOA (Externally Owned Wallet) address of the wallet. This is the traditional wallet address.
13671387
*/
@@ -1604,6 +1624,10 @@ export type ListServerWalletsResponses = {
16041624
* Array of server wallets
16051625
*/
16061626
wallets: Array<{
1627+
/**
1628+
* Unique identifier for the user wallet within the thirdweb auth system.
1629+
*/
1630+
userId?: string;
16071631
/**
16081632
* The EOA (Externally Owned Wallet) address of the wallet. This is the traditional wallet address.
16091633
*/
@@ -1831,6 +1855,10 @@ export type CreateServerWalletResponses = {
18311855
*/
18321856
200: {
18331857
result: {
1858+
/**
1859+
* Unique identifier for the user wallet within the thirdweb auth system.
1860+
*/
1861+
userId?: string;
18341862
/**
18351863
* The EOA (Externally Owned Wallet) address of the wallet. This is the traditional wallet address.
18361864
*/

0 commit comments

Comments
 (0)