diff --git a/packages/ui/src/hooks/use-preview.ts b/packages/ui/src/hooks/use-preview.ts index 4801e6b42..8d513f80a 100644 --- a/packages/ui/src/hooks/use-preview.ts +++ b/packages/ui/src/hooks/use-preview.ts @@ -4,16 +4,17 @@ import i18next from 'i18next'; import { useEffect, useState } from 'react'; import { Context } from '@/hooks/use-page-context'; +import { filterPreviewSocialConnectors } from '@/hooks/utils'; import initI18n from '@/i18n/init'; -import { SignInExperienceSettingsResponse } from '@/types'; +import { SignInExperienceSettingsResponse, Platform } from '@/types'; import { parseQueryParameters } from '@/utils'; -import { parseSignInExperienceSettings } from '@/utils/sign-in-experience'; +import { getPrimarySignInMethod, getSecondarySignInMethods } from '@/utils/sign-in-experience'; type PreviewConfig = { signInExperience: SignInExperienceSettingsResponse; language: Language; mode: AppearanceMode.LightMode | AppearanceMode.DarkMode; - platform: 'web' | 'mobile'; + platform: Platform; }; const usePreview = (context: Context): [boolean, PreviewConfig?] => { @@ -53,20 +54,31 @@ const usePreview = (context: Context): [boolean, PreviewConfig?] => { return; } - const { signInExperience, language, mode, platform } = previewConfig; - const experienceSettings = parseSignInExperienceSettings(signInExperience); + const { + signInExperience: { signInMethods, socialConnectors, branding, ...rest }, + language, + mode, + platform, + } = previewConfig; + + const experienceSettings = { + ...rest, + branding: { + ...branding, + isDarkModeEnabled: false, // Disable theme mode auto detection on preview + }, + primarySignInMethod: getPrimarySignInMethod(signInMethods), + secondarySignInMethods: getSecondarySignInMethods(signInMethods), + socialConnectors: filterPreviewSocialConnectors(platform, socialConnectors), + }; void i18next.changeLanguage(language); setTheme(mode); + setPlatform(platform); - setExperienceSettings({ - ...experienceSettings, - branding: { - ...experienceSettings.branding, - isDarkModeEnabled: false, // Disable theme mode auto detection on preview - }, - }); + + setExperienceSettings(experienceSettings); }, [isPreview, previewConfig, setExperienceSettings, setPlatform, setTheme]); return [isPreview, previewConfig]; diff --git a/packages/ui/src/hooks/utils.test.ts b/packages/ui/src/hooks/utils.test.ts index bb52f65e2..9afe11c6a 100644 --- a/packages/ui/src/hooks/utils.test.ts +++ b/packages/ui/src/hooks/utils.test.ts @@ -1,6 +1,6 @@ import { ConnectorData } from '@/types'; -import { filterSocialConnectors } from './utils'; +import { filterSocialConnectors, filterPreviewSocialConnectors } from './utils'; const mockConnectors = [ { platform: 'Web', target: 'facebook' }, @@ -63,3 +63,30 @@ describe('filterSocialConnectors', () => { ]); }); }); + +describe('filterPreviewSocialConnectors', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('undefined input should return empty list', () => { + expect(filterPreviewSocialConnectors('web')).toEqual([]); + expect(filterPreviewSocialConnectors('mobile')).toEqual([]); + }); + + it('filter Web Connectors', () => { + expect(filterPreviewSocialConnectors('web', mockConnectors)).toEqual([ + { platform: 'Web', target: 'facebook' }, + { platform: 'Web', target: 'google' }, + { platform: 'Universal', target: 'WeChat' }, + ]); + }); + + it('filter Native Connectors', () => { + expect(filterPreviewSocialConnectors('mobile', mockConnectors)).toEqual([ + { platform: 'Universal', target: 'facebook' }, + { platform: 'Native', target: 'WeChat' }, + { platform: 'Native', target: 'Alipay' }, + ]); + }); +}); diff --git a/packages/ui/src/hooks/utils.ts b/packages/ui/src/hooks/utils.ts index 902c1607f..4878e67f3 100644 --- a/packages/ui/src/hooks/utils.ts +++ b/packages/ui/src/hooks/utils.ts @@ -1,4 +1,4 @@ -import { ConnectorData } from '@/types'; +import { ConnectorData, Platform } from '@/types'; import { generateRandomString } from '@/utils'; /** @@ -71,7 +71,7 @@ export const filterSocialConnectors = (socialConnectors?: ConnectorData[]) => { /** * Browser Environment * Accepts both web and universal platform connectors. - * Insert universal connectors only if there is no web platform connector provided with the same target. + * Insert universal connectors only if there is no web platform connector provided with the same target. * Web platform has higher priority. **/ @@ -95,7 +95,7 @@ export const filterSocialConnectors = (socialConnectors?: ConnectorData[]) => { /** * Native Webview Environment * Accepts both native and universal platform connectors. - * Insert universal connectors only if there is no native platform connector provided with the same target. + * Insert universal connectors only if there is no native platform connector provided with the same target. * Native platform has higher priority. **/ @@ -126,3 +126,63 @@ export const filterSocialConnectors = (socialConnectors?: ConnectorData[]) => { return Array.from(connectorMap.values()); }; + +/** + * Social Connectors Filter Utility Methods used in preview mode only + */ +export const filterPreviewSocialConnectors = ( + previewPlatform: Platform, + socialConnectors?: ConnectorData[] +) => { + if (!socialConnectors) { + return []; + } + + const connectorMap = new Map(); + + /** + * Browser Environment + * Accepts both web and universal platform connectors. + * Insert universal connectors only if there is no web platform connector provided with the same target. + * Web platform has higher priority. + **/ + + if (previewPlatform === 'web') { + for (const connector of socialConnectors) { + const { platform, target } = connector; + + if (platform === 'Native') { + continue; + } + + if (platform === 'Web' || !connectorMap.get(target)) { + connectorMap.set(target, connector); + continue; + } + } + + return Array.from(connectorMap.values()); + } + + /** + * Native Webview Environment + * Accepts both native and universal platform connectors. + * Insert universal connectors only if there is no native platform connector provided with the same target. + * Native platform has higher priority. + **/ + + for (const connector of socialConnectors) { + const { platform, target } = connector; + + if (platform === 'Web') { + continue; + } + + if (platform === 'Native' || !connectorMap.get(target)) { + connectorMap.set(target, connector); + continue; + } + } + + return Array.from(connectorMap.values()); +}; diff --git a/packages/ui/src/utils/sign-in-experience.ts b/packages/ui/src/utils/sign-in-experience.ts index 054172a97..2c866193e 100644 --- a/packages/ui/src/utils/sign-in-experience.ts +++ b/packages/ui/src/utils/sign-in-experience.ts @@ -9,7 +9,7 @@ import { getSignInExperience } from '@/apis/settings'; import { filterSocialConnectors } from '@/hooks/utils'; import { SignInMethod, SignInExperienceSettingsResponse, SignInExperienceSettings } from '@/types'; -const getPrimarySignInMethod = (signInMethods: SignInMethods) => { +export const getPrimarySignInMethod = (signInMethods: SignInMethods) => { for (const [key, value] of Object.entries(signInMethods)) { if (value === 'primary') { return key as keyof SignInMethods; @@ -19,7 +19,7 @@ const getPrimarySignInMethod = (signInMethods: SignInMethods) => { return 'username'; }; -const getSecondarySignInMethods = (signInMethods: SignInMethods) => +export const getSecondarySignInMethods = (signInMethods: SignInMethods) => Object.entries(signInMethods).reduce((methods, [key, value]) => { if (value === 'secondary') { return [...methods, key as SignInMethod]; @@ -28,21 +28,16 @@ const getSecondarySignInMethods = (signInMethods: SignInMethods) => return methods; }, []); -export const parseSignInExperienceSettings = ({ - signInMethods, - socialConnectors, - ...rest -}: SignInExperienceSettingsResponse) => ({ - ...rest, - primarySignInMethod: getPrimarySignInMethod(signInMethods), - secondarySignInMethods: getSecondarySignInMethods(signInMethods), - socialConnectors: filterSocialConnectors(socialConnectors), -}); - const getSignInExperienceSettings = async (): Promise => { - const response = await getSignInExperience(); + const { signInMethods, socialConnectors, ...rest } = + await getSignInExperience(); - return parseSignInExperienceSettings(response); + return { + ...rest, + primarySignInMethod: getPrimarySignInMethod(signInMethods), + secondarySignInMethods: getSecondarySignInMethods(signInMethods), + socialConnectors: filterSocialConnectors(socialConnectors), + }; }; export default getSignInExperienceSettings;