0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-20 21:32:31 -05:00

refactor(ui): add preview social connectors filter (#932)

* refactor(ui): add preview social connectors filter

add preview social connectors filter

* fix(ui): typo fix

typo fix
This commit is contained in:
simeng-li 2022-05-24 16:43:14 +08:00 committed by GitHub
parent cf360b9c15
commit 8060ec4941
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 31 deletions

View file

@ -4,16 +4,17 @@ import i18next from 'i18next';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Context } from '@/hooks/use-page-context'; import { Context } from '@/hooks/use-page-context';
import { filterPreviewSocialConnectors } from '@/hooks/utils';
import initI18n from '@/i18n/init'; import initI18n from '@/i18n/init';
import { SignInExperienceSettingsResponse } from '@/types'; import { SignInExperienceSettingsResponse, Platform } from '@/types';
import { parseQueryParameters } from '@/utils'; import { parseQueryParameters } from '@/utils';
import { parseSignInExperienceSettings } from '@/utils/sign-in-experience'; import { getPrimarySignInMethod, getSecondarySignInMethods } from '@/utils/sign-in-experience';
type PreviewConfig = { type PreviewConfig = {
signInExperience: SignInExperienceSettingsResponse; signInExperience: SignInExperienceSettingsResponse;
language: Language; language: Language;
mode: AppearanceMode.LightMode | AppearanceMode.DarkMode; mode: AppearanceMode.LightMode | AppearanceMode.DarkMode;
platform: 'web' | 'mobile'; platform: Platform;
}; };
const usePreview = (context: Context): [boolean, PreviewConfig?] => { const usePreview = (context: Context): [boolean, PreviewConfig?] => {
@ -53,20 +54,31 @@ const usePreview = (context: Context): [boolean, PreviewConfig?] => {
return; return;
} }
const { signInExperience, language, mode, platform } = previewConfig; const {
const experienceSettings = parseSignInExperienceSettings(signInExperience); 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); void i18next.changeLanguage(language);
setTheme(mode); setTheme(mode);
setPlatform(platform); setPlatform(platform);
setExperienceSettings({
...experienceSettings, setExperienceSettings(experienceSettings);
branding: {
...experienceSettings.branding,
isDarkModeEnabled: false, // Disable theme mode auto detection on preview
},
});
}, [isPreview, previewConfig, setExperienceSettings, setPlatform, setTheme]); }, [isPreview, previewConfig, setExperienceSettings, setPlatform, setTheme]);
return [isPreview, previewConfig]; return [isPreview, previewConfig];

View file

@ -1,6 +1,6 @@
import { ConnectorData } from '@/types'; import { ConnectorData } from '@/types';
import { filterSocialConnectors } from './utils'; import { filterSocialConnectors, filterPreviewSocialConnectors } from './utils';
const mockConnectors = [ const mockConnectors = [
{ platform: 'Web', target: 'facebook' }, { 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' },
]);
});
});

View file

@ -1,4 +1,4 @@
import { ConnectorData } from '@/types'; import { ConnectorData, Platform } from '@/types';
import { generateRandomString } from '@/utils'; import { generateRandomString } from '@/utils';
/** /**
@ -126,3 +126,63 @@ export const filterSocialConnectors = (socialConnectors?: ConnectorData[]) => {
return Array.from(connectorMap.values()); 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<string, 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.
* 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());
};

View file

@ -9,7 +9,7 @@ import { getSignInExperience } from '@/apis/settings';
import { filterSocialConnectors } from '@/hooks/utils'; import { filterSocialConnectors } from '@/hooks/utils';
import { SignInMethod, SignInExperienceSettingsResponse, SignInExperienceSettings } from '@/types'; import { SignInMethod, SignInExperienceSettingsResponse, SignInExperienceSettings } from '@/types';
const getPrimarySignInMethod = (signInMethods: SignInMethods) => { export const getPrimarySignInMethod = (signInMethods: SignInMethods) => {
for (const [key, value] of Object.entries(signInMethods)) { for (const [key, value] of Object.entries(signInMethods)) {
if (value === 'primary') { if (value === 'primary') {
return key as keyof SignInMethods; return key as keyof SignInMethods;
@ -19,7 +19,7 @@ const getPrimarySignInMethod = (signInMethods: SignInMethods) => {
return 'username'; return 'username';
}; };
const getSecondarySignInMethods = (signInMethods: SignInMethods) => export const getSecondarySignInMethods = (signInMethods: SignInMethods) =>
Object.entries(signInMethods).reduce<SignInMethod[]>((methods, [key, value]) => { Object.entries(signInMethods).reduce<SignInMethod[]>((methods, [key, value]) => {
if (value === 'secondary') { if (value === 'secondary') {
return [...methods, key as SignInMethod]; return [...methods, key as SignInMethod];
@ -28,21 +28,16 @@ const getSecondarySignInMethods = (signInMethods: SignInMethods) =>
return methods; return methods;
}, []); }, []);
export const parseSignInExperienceSettings = ({ const getSignInExperienceSettings = async (): Promise<SignInExperienceSettings> => {
signInMethods, const { signInMethods, socialConnectors, ...rest } =
socialConnectors, await getSignInExperience<SignInExperienceSettingsResponse>();
...rest
}: SignInExperienceSettingsResponse) => ({ return {
...rest, ...rest,
primarySignInMethod: getPrimarySignInMethod(signInMethods), primarySignInMethod: getPrimarySignInMethod(signInMethods),
secondarySignInMethods: getSecondarySignInMethods(signInMethods), secondarySignInMethods: getSecondarySignInMethods(signInMethods),
socialConnectors: filterSocialConnectors(socialConnectors), socialConnectors: filterSocialConnectors(socialConnectors),
}); };
const getSignInExperienceSettings = async (): Promise<SignInExperienceSettings> => {
const response = await getSignInExperience<SignInExperienceSettingsResponse>();
return parseSignInExperienceSettings(response);
}; };
export default getSignInExperienceSettings; export default getSignInExperienceSettings;