diff --git a/.gitignore b/.gitignore index 0895fdc..72e3631 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ yarn-debug.log* yarn-error.log* .idea + +CLAUDE.md diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 8cb81e0..03ad5a1 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dimo-network/login-with-dimo", - "version": "0.0.27", + "version": "0.0.32", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dimo-network/login-with-dimo", - "version": "0.0.27", + "version": "0.0.32", "license": "ISC", "dependencies": { "jwt-decode": "^4.0.0" diff --git a/sdk/package.json b/sdk/package.json index ce39e51..e92f12e 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,11 +1,11 @@ { "name": "@dimo-network/login-with-dimo", - "version": "0.0.31", + "version": "0.0.32", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "tsc --noEmit -p tsconfig.json", "build": "webpack --config webpack.config.js", "watch": "webpack --config webpack.config.js --watch" }, diff --git a/sdk/src/components/ShareAccountWithDimo.tsx b/sdk/src/components/ShareAccountWithDimo.tsx new file mode 100644 index 0000000..d448b28 --- /dev/null +++ b/sdk/src/components/ShareAccountWithDimo.tsx @@ -0,0 +1,72 @@ +import React from 'react'; + +import { EntryState, EventTypes } from '@enums/index'; +import { + BaseButtonProps, + ButtonLabels, + InternalDimoActionParams, + LoginButtonProps, +} from '@dimo-types/index'; +import { getPermissionsBinary } from '@utils/index'; +import { useResolvedBrand, formatBrandedLabel } from '@utils/brand'; +import { BaseDimoButton } from './BaseDimoButton'; + +type ShareAccountWithDimoProps = BaseButtonProps & + LoginButtonProps & + ButtonLabels; + +const ShareAccountWithDimo: React.FC = ({ + mode, + onSuccess, + onError, + permissionTemplateId, + permissions, + expirationDate, + authenticatedLabel, + unAuthenticatedLabel, + utm = null, + altTitle, + brandOverride, +}) => { + const brand = useResolvedBrand(brandOverride); + + const resolvedAuthLabel = + authenticatedLabel ?? + formatBrandedLabel( + 'Share Documents with {name}', + brand.name, + 'Share Documents with DIMO' + ); + const resolvedUnAuthLabel = + unAuthenticatedLabel ?? + formatBrandedLabel( + 'Sign in to Share Documents with {name}', + brand.name, + 'Sign in to Share Documents with DIMO' + ); + + const payload: InternalDimoActionParams & { eventType: EventTypes } = { + permissionTemplateId, + expirationDate, + eventType: EventTypes.SHARE_ACCOUNT_DATA, + utm, + ...getPermissionsBinary(permissions, permissionTemplateId), + }; + + return ( + + authenticated ? resolvedAuthLabel : resolvedUnAuthLabel + } + altTitle={altTitle} + payload={payload} + brand={brand} + /> + ); +}; + +export default ShareAccountWithDimo; diff --git a/sdk/src/components/__tests__/ShareAccountWithDimo.test.tsx b/sdk/src/components/__tests__/ShareAccountWithDimo.test.tsx new file mode 100644 index 0000000..8a5f08b --- /dev/null +++ b/sdk/src/components/__tests__/ShareAccountWithDimo.test.tsx @@ -0,0 +1,56 @@ +/** + * Smoke test for ShareAccountWithDimo. + * + * The SDK ships no test runner (jest is an intentional no-op stub and there are + * no testing-library deps — see CLAUDE.md "No tests exist for the SDK itself"). + * To stay aligned with the existing toolchain (tsc + webpack path aliases) this + * smoke test is type-checked via `npm test` (`tsc --noEmit`). It asserts that: + * 1. the component is exported from the package root, + * 2. it can be instantiated as a React element with its public props, + * 3. the SHARE_ACCOUNT_DATA event type / ACCOUNT_MANAGER entry state exist. + */ +import React from 'react'; +import { DimoSDKModes, EntryState, EventTypes } from '@enums/index'; +import ShareAccountWithDimo from '../ShareAccountWithDimo'; +import { ShareAccountWithDimo as ShareAccountWithDimoFromRoot } from '../../index'; + +// 1. Exported from the package root. +console.assert( + ShareAccountWithDimoFromRoot === ShareAccountWithDimo, + 'ShareAccountWithDimo should be re-exported from the package root' +); + +// 2. Renders / mounts as a valid React element with its public props. +const element = ( + { + // accountGranted + transactionHash are surfaced on success. + void authData.accountGranted; + void authData.transactionHash; + void authData.token; + }} + onError={(e) => console.error(e)} + brandOverride={{ name: 'Acme' }} + /> +); + +console.assert( + React.isValidElement(element), + 'ShareAccountWithDimo should produce a valid React element' +); + +// 3. The enum members the component relies on exist. +console.assert( + EventTypes.SHARE_ACCOUNT_DATA === 'SHARE_ACCOUNT_DATA', + 'SHARE_ACCOUNT_DATA event type should exist' +); +console.assert( + EntryState.ACCOUNT_MANAGER === 'ACCOUNT_MANAGER', + 'ACCOUNT_MANAGER entry state should exist' +); + +export {}; diff --git a/sdk/src/enums/auth.enums.ts b/sdk/src/enums/auth.enums.ts index 505ddcc..e91ec68 100644 --- a/sdk/src/enums/auth.enums.ts +++ b/sdk/src/enums/auth.enums.ts @@ -3,6 +3,7 @@ export enum EntryState { OTP_INPUT = 'OTP_INPUT', SUCCESS = 'SUCCESS', VEHICLE_MANAGER = 'VEHICLE_MANAGER', + ACCOUNT_MANAGER = 'ACCOUNT_MANAGER', ADVANCED_TRANSACTION = 'ADVANCED_TRANSACTION', SIGN_MESSAGE = 'SIGN_MESSAGE', ERROR = 'ERROR', diff --git a/sdk/src/enums/events.enums.ts b/sdk/src/enums/events.enums.ts index 58ada80..a137d9c 100644 --- a/sdk/src/enums/events.enums.ts +++ b/sdk/src/enums/events.enums.ts @@ -11,6 +11,7 @@ export enum MessageEventType { export enum EventTypes { EXECUTE_ADVANCED_TRANSACTION = 'EXECUTE_ADVANCED_TRANSACTION', SHARE_VEHICLES_DATA = 'SHARE_VEHICLES_DATA', + SHARE_ACCOUNT_DATA = 'SHARE_ACCOUNT_DATA', SIGN_MESSAGE = 'SIGN_MESSAGE', LOGOUT = 'LOGOUT', } diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 84889e2..54ffc65 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -1,6 +1,7 @@ // Export components export { default as LoginWithDimo } from './components/LoginWithDimo'; export { default as ShareVehiclesWithDimo } from './components/ShareVehiclesWithDimo'; +export { default as ShareAccountWithDimo } from './components/ShareAccountWithDimo'; export { default as ExecuteAdvancedTransactionWithDimo } from './components/ExecuteAdvancedTransactionWithDimo'; export { default as SignMessageWithDimo } from './components/SignMessageWithDimo'; export { default as LogoutWithDimo } from './components/LogoutWithDimo'; diff --git a/sdk/src/types/common.types.ts b/sdk/src/types/common.types.ts index 2b32a6c..ee01c44 100644 --- a/sdk/src/types/common.types.ts +++ b/sdk/src/types/common.types.ts @@ -6,6 +6,7 @@ export interface AuthData { transactionHash?: string; transactionReceipt?: TransactionReceipt; sharedVehicles?: string[]; + accountGranted?: boolean; signature?: `0x${string}`; signer?: `0x${string}`; } diff --git a/sdk/src/types/events.types.ts b/sdk/src/types/events.types.ts index 6d09f89..86aa3ee 100644 --- a/sdk/src/types/events.types.ts +++ b/sdk/src/types/events.types.ts @@ -11,6 +11,7 @@ export interface MessageData { transactionHash?: string; transactionReceipt?: TransactionReceipt; sharedVehicles?: string[]; + accountGranted?: boolean; message?: string; signature?: `0x${string}`; signer?: `0x${string}`; diff --git a/sdk/src/utils/authUtils.ts b/sdk/src/utils/authUtils.ts index 95fbb24..5503405 100644 --- a/sdk/src/utils/authUtils.ts +++ b/sdk/src/utils/authUtils.ts @@ -6,9 +6,13 @@ import { } from '@storage/storageManager'; export const processAuthResponse = ( - { token, walletAddress, email, sharedVehicles }: any, + { token, walletAddress, email, sharedVehicles, accountGranted }: any, setAuthenticated: (status: boolean) => void, - onSuccess: (data: { token: string; sharedVehicles: string[] }) => void + onSuccess: (data: { + token: string; + sharedVehicles: string[]; + accountGranted?: boolean; + }) => void ) => { // This auth response may be triggered for a coupled or decoupled flow // If decoupled, it will only return token @@ -18,7 +22,11 @@ export const processAuthResponse = ( if (token) { storeJWTInCookies(token); setAuthenticated(true); - onSuccess({ token, sharedVehicles }); + onSuccess({ + token, + sharedVehicles, + ...(accountGranted !== undefined && { accountGranted }), + }); } }; diff --git a/sdk/src/utils/eventHandler.ts b/sdk/src/utils/eventHandler.ts index 9945def..7eb0b70 100644 --- a/sdk/src/utils/eventHandler.ts +++ b/sdk/src/utils/eventHandler.ts @@ -74,7 +74,7 @@ const handleAuthResponse = ( }; const handleTransactionResponse = ( - { token, transactionHash, transactionReceipt }: MessageData, + { token, transactionHash, transactionReceipt, accountGranted }: MessageData, handlers: EventHandlers ): void => { if (!token) { @@ -82,11 +82,12 @@ const handleTransactionResponse = ( return; } - if (transactionHash || transactionReceipt) { + if (transactionHash || transactionReceipt || accountGranted) { const responseData: AuthData = { token: token, ...(transactionHash && { transactionHash }), ...(transactionReceipt && { transactionReceipt }), + ...(accountGranted !== undefined && { accountGranted }), }; handlers.onSuccess(responseData); } else { @@ -189,7 +190,10 @@ export const createMessageHandler = ( messageData, { onSuccess, onError, setAuthenticated }, mode === DimoSDKModes.POPUP - ? { sharedVehicles: messageData.sharedVehicles } + ? { + sharedVehicles: messageData.sharedVehicles, + accountGranted: messageData.accountGranted, + } : {} ); };