@sberid/js-sdk
Version:
Javascript SDK для партнеров Сбер ID, упрощающая подключение SberbankID на сайте.
351 lines (282 loc) • 12.4 kB
text/typescript
import equal from 'fast-deep-equal';
import {initSA, sendSberAnalytics} from '../sberid-analytics';
import {Browser} from '../browser';
import {CssObject, OidcParams, User} from '../interfaces/common';
import {SberidNotificationBannerButton} from '../sberid-button';
import {Cookies} from '../services/cookies';
import {buildUrl, hideLoader, log, setCssStyle, showLoader} from '../utils/common';
import {NotificationConfig, NotificationPosition} from './interfaces';
import {defaultNotificationConfig} from './constants';
import {UserHelper} from '../helper';
import {fadeIn, fadeOut} from './utils';
import {getAuthUrl} from '../constants/common';
import {SberidUniversalLink} from '../universal-link';
import {SberidSDKErrorResult, SberidSDKSuccessResult, SberidSDKProps} from '../sberid-sdk';
import {FastAuthorizationResponse} from '../fast-login/interfaces';
import {FastLogin} from '../fast-login';
export class NotificationBanner {
private config: NotificationConfig;
private button!: SberidNotificationBannerButton;
private notification: HTMLDivElement;
private readonly parser = Browser.getParser(window.navigator.userAgent);
private userHelper: UserHelper;
universalLinkDetect: SberidUniversalLink;
fastLogin: FastLogin;
private user: User | undefined;
private oidcParams!: OidcParams;
onSuccessCallback: (data?: SberidSDKSuccessResult) => void = () => {};
onErrorCallback: (data?: SberidSDKErrorResult) => void = () => {};
constructor(config: SberidSDKProps) {
this.notification = document.createElement('div');
this.config = {
...defaultNotificationConfig,
...config.notification,
position: NotificationBanner.getDefaultPosition(
config.notification && config.notification.position,
),
fastLogin: !!config.fastLogin?.enable,
sa: !!config.sa?.enable,
debug: !!config.debug,
};
this.userHelper = UserHelper.getInstance();
this.userHelper.setConfig({
baseUrl: config.baseUrl,
clientId: config.oidc.client_id,
});
this.userHelper.setListener(this.handleUserChange.bind(this));
this.universalLinkDetect = new SberidUniversalLink(config);
this.fastLogin = new FastLogin(config);
if (this.config.sa) {
if (config?.sa?.init === 'auto') {
initSA(config);
}
}
if (config.onSuccessCallback) {
this.onSuccessCallback = config.onSuccessCallback;
}
if (config.onErrorCallback) {
this.onErrorCallback = config.onErrorCallback;
}
this.create(config);
this.setOIDCParams(config);
}
handleUserChange(user?: User): void {
if (!equal(this.user, user)) {
this.user = user;
if (!user) {
this.hide();
}
}
}
async setOIDCParams(config: SberidSDKProps): Promise<void> {
const params = this.universalLinkDetect.buildOidcParams(config.oidc);
const paramsKeys = Object.keys(params);
const isForceUniversalLinkDisable =
paramsKeys.includes('app_redirect_uri') ||
paramsKeys.includes('authApp') ||
paramsKeys['is_universal_link'] === 'false';
if (config.mweb2app && !isForceUniversalLinkDisable) {
const universalLinkResponse = await this.universalLinkDetect.run(params);
this.button.setUrl(universalLinkResponse.link);
} else {
this.button.setUrl(buildUrl(getAuthUrl(config.baseUrl), params));
}
this.oidcParams = {...params};
}
async silentAuthorization(): Promise<FastAuthorizationResponse> {
return await this.fastLogin.authorization(this.oidcParams);
}
async create(config: SberidSDKProps): Promise<void> {
if (!this.notification) {
this.notification = document.createElement('div');
}
const body = document.getElementsByTagName('body')[0];
const container = document.createElement('div');
container.className = 'sbid-notification-banner';
/*if (this.isMobile()) {
container.classList.add('sbid-notification-banner--mobile');
}*/
container.id = 'i-sbid-notification-banner';
setCssStyle(container, this.getStyle());
const label = document.createElement('div');
label.innerHTML = 'Вход по Сбер ID';
label.className = 'sbid-notification-banner__label';
container.appendChild(label);
const closeButton = document.createElement('a');
closeButton.innerHTML =
'<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 4.65094L10.3675 0.282877C10.7431 -0.0927631 11.3485 -0.0942703 11.721 0.27826C12.0961 0.653395 12.0914 1.25686 11.7164 1.63195L7.34886 6L11.7164 10.368C12.0914 10.7431 12.0961 11.3466 11.721 11.7217C11.3485 12.0943 10.7431 12.0928 10.3675 11.7171L6 7.34906L1.63247 11.7171C1.25688 12.0928 0.651469 12.0943 0.278986 11.7217C-0.0960822 11.3466 -0.0914182 10.7431 0.283602 10.368L4.65114 6L0.283602 1.63195C-0.0914182 1.25686 -0.0960822 0.653395 0.278986 0.27826C0.651469 -0.0942703 1.25688 -0.0927631 1.63247 0.282877L6 4.65094Z" fill="#767676"/></svg>';
closeButton.className = 'sbid-notification-banner__close';
container.appendChild(closeButton);
closeButton.addEventListener('click', this.onClose.bind(this));
this.button = new SberidNotificationBannerButton(
config,
container,
config.onButtonClick || this.handleButtonClick.bind(this),
);
body.appendChild(container);
this.notification = container;
}
async handleButtonClick(e: Event): Promise<boolean> {
e.preventDefault();
e.stopPropagation();
if ((<HTMLElement>e.target)?.getAttribute('disabled') === 'true') {
return false;
}
showLoader();
this.button.disable();
if (this.config.sa) {
sendSberAnalytics({
eventCategory: 'Login',
eventAction: 'sberid_Banner_Login_Button_Click',
eventType: 'Click',
});
}
const url =
(<HTMLAnchorElement>e.target).closest('.sbid-button')?.getAttribute('href') || '';
if (this.user && this.config.fastLogin) {
const response = await this.silentAuthorization();
if (response.success) {
if (this.config.debug) {
log(['Успешный быстрый вход', response.data], 'success');
}
this.onSuccessCallback(response.data as SberidSDKSuccessResult);
this.onClose();
} else {
if (this.config.debug) {
log(['Ошибка быстрого входа', response.data], 'error');
}
window.location.href = url;
}
hideLoader();
this.button.enable();
} else {
window.location.href = url;
}
return false;
}
private isMobile(): boolean {
return this.parser.getPlatformType(true) === 'mobile';
}
isNotification(): boolean {
return !!this.notification;
}
async show(user?: User): Promise<void> {
if (!this.user && !user) {
const u = await this.userHelper.getUser().catch((e) => {
if (this.config.debug) {
log(['Ошибка при получении данных о пользователе', e], 'error');
}
});
if (!u) {
return;
}
this.user = u;
}
const animationDelay = this.config.animation ? 500 : 12;
this.notification.classList.add('sbid-notification-banner--show');
fadeIn(this.notification, animationDelay);
if (this.config.sa) {
sendSberAnalytics({
eventCategory: 'Login',
eventAction: 'sberid_Notification_Banner_Show',
eventType: 'Show',
description: 'success',
});
}
if (this.isMobile() && this.config.autoClose) {
if (this.config.debug) {
log(`Баннер будет скрыт через ${this.config.autoCloseDelay} секунд.`);
}
setTimeout(this.onClose.bind(this), this.config.autoCloseDelay * 1000);
}
if (typeof this.config.onOpen === 'function') {
this.config.onOpen();
}
}
hide(): void {
if (this.notification) {
const animationDelay = this.config.animation ? 500 : 12;
fadeOut(this.notification, animationDelay).then(() => {
this.notification.remove();
});
this.notification.classList.remove('sbid-notification-banner--show');
}
}
onClose(): void {
if (this.notification) {
this.hide();
if (this.config.sa) {
sendSberAnalytics({
eventCategory: 'Login',
eventAction: 'sberid_Notification_Banner_Close',
eventType: 'Close',
description: 'success',
});
}
}
const closeCount = +(<string>Cookies.get('sbid_notification_banner_close_count')) || 0;
const currentDate = new Date();
Cookies.set('sbid_notification_banner_closed', '1', {
path: '/',
expires: this.getCookieExpires(closeCount),
});
Cookies.set('sbid_notification_banner_close_count', `${closeCount + 1}`, {
path: '/',
expires: new Date(
currentDate.getFullYear() + 1,
currentDate.getMonth(),
currentDate.getDate(),
),
});
if (this.config.onClose && typeof this.config.onClose === 'function') {
this.config.onClose();
}
}
getStyle(): CssObject {
const defaultStyle: CssObject = {};
if (!this.isMobile()) {
if (['bottom-left', 'top-left'].includes(this.config.position)) {
defaultStyle.left = `${this.config.offset.left}px`;
}
if (['bottom-right', 'top-right'].includes(this.config.position)) {
defaultStyle.right = `${this.config.offset.right}px`;
}
if (['bottom-right', 'bottom-left'].includes(this.config.position)) {
defaultStyle.bottom = `${this.config.offset.bottom}px`;
}
if (['top-right', 'top-left'].includes(this.config.position)) {
defaultStyle.top = `${this.config.offset.top}px`;
}
}
return defaultStyle;
}
getCookieExpires(countClose: number): number {
const times = [7200, 86400, 604800, 2419200];
let count = countClose || 0;
if (countClose >= times.length) {
count = times.length - 1;
}
return times[count];
}
destroy(): void {
this.notification.remove();
}
static getDefaultPosition(pos?: NotificationPosition): NotificationPosition {
let position: NotificationPosition = 'bottom-right';
switch (pos) {
case 'left':
position = 'bottom-left';
break;
case 'right':
position = 'bottom-right';
break;
default:
break;
}
position =
pos && ['bottom-left', 'bottom-right', 'top-left', 'top-right'].includes(pos)
? pos
: position;
return position;
}
}