108 lines
3.4 KiB
TypeScript
Executable File
108 lines
3.4 KiB
TypeScript
Executable File
import {useCallback, useState} from 'react';
|
|
import {toast} from '../../ui/toast/toast';
|
|
import {useDisconnectSocial} from './disconnect-social';
|
|
import {useTrans} from '../../i18n/use-trans';
|
|
import {getBootstrapData} from '../../core/bootstrap-data/use-backend-bootstrap-data';
|
|
import {useBootstrapData} from '../../core/bootstrap-data/bootstrap-data-context';
|
|
|
|
export type SocialService = 'google' | 'twitter' | 'facebook' | 'envato';
|
|
|
|
interface SocialMessageEvent {
|
|
status?: 'SUCCESS' | 'ALREADY_LOGGED_IN' | 'REQUEST_PASSWORD' | 'ERROR';
|
|
callbackData?: {
|
|
bootstrapData?: string;
|
|
errorMessage?: string;
|
|
};
|
|
}
|
|
|
|
export function useSocialLogin() {
|
|
const {trans} = useTrans();
|
|
const {setBootstrapData} = useBootstrapData();
|
|
const disconnectSocial = useDisconnectSocial();
|
|
|
|
const [requestingPassword, setIsRequestingPassword] = useState(false);
|
|
|
|
const handleSocialLoginCallback = useCallback(
|
|
(e: SocialMessageEvent) => {
|
|
const {status, callbackData} = e;
|
|
if (!status) return;
|
|
switch (status.toUpperCase()) {
|
|
case 'SUCCESS':
|
|
if (callbackData?.bootstrapData) {
|
|
setBootstrapData(callbackData.bootstrapData);
|
|
}
|
|
return e;
|
|
case 'REQUEST_PASSWORD':
|
|
setIsRequestingPassword(true);
|
|
return e;
|
|
case 'ERROR':
|
|
const message: string =
|
|
callbackData?.errorMessage ||
|
|
trans({
|
|
message: 'An error occurred. Please try again later',
|
|
});
|
|
toast.danger(message);
|
|
return e;
|
|
default:
|
|
return e;
|
|
}
|
|
},
|
|
[trans, setBootstrapData],
|
|
);
|
|
|
|
return {
|
|
requestingPassword,
|
|
setIsRequestingPassword,
|
|
loginWithSocial: async (serviceName: SocialService) => {
|
|
const event = await openNewSocialAuthWindow(
|
|
`secure/auth/social/${serviceName}/login`,
|
|
);
|
|
return handleSocialLoginCallback(event);
|
|
},
|
|
connectSocial: async (serviceNameOrUrl: SocialService | string) => {
|
|
const url = serviceNameOrUrl.includes('/')
|
|
? serviceNameOrUrl
|
|
: `secure/auth/social/${serviceNameOrUrl}/connect`;
|
|
const event = await openNewSocialAuthWindow(url);
|
|
return handleSocialLoginCallback(event);
|
|
},
|
|
disconnectSocial,
|
|
};
|
|
}
|
|
|
|
const windowHeight = 550;
|
|
const windowWidth = 650;
|
|
let win: Window | null;
|
|
|
|
function openNewSocialAuthWindow(url: string): Promise<SocialMessageEvent> {
|
|
const left = window.screen.width / 2 - windowWidth / 2;
|
|
const top = window.screen.height / 2 - windowHeight / 2;
|
|
|
|
return new Promise(resolve => {
|
|
win = window.open(
|
|
url,
|
|
'Authenticate Account',
|
|
`menubar=0, location=0, toolbar=0, titlebar=0, status=0, scrollbars=1, width=${windowWidth}, height=${windowHeight}, left=${left}, top=${top}`,
|
|
);
|
|
|
|
const messageListener = (e: MessageEvent) => {
|
|
const baseUrl = getBootstrapData().settings.base_url;
|
|
if (e.data.type === 'social-auth' && baseUrl.indexOf(e.origin) > -1) {
|
|
resolve(e.data);
|
|
window.removeEventListener('message', messageListener);
|
|
}
|
|
};
|
|
|
|
window.addEventListener('message', messageListener);
|
|
|
|
// if user closes social login callback without interacting with it, remove message event listener
|
|
const timer = setInterval(() => {
|
|
if (!win || win.closed) {
|
|
clearInterval(timer);
|
|
resolve({});
|
|
window.removeEventListener('message', messageListener);
|
|
}
|
|
}, 1000);
|
|
});
|
|
}
|