@salla.sa/twilight-components
Version:
Salla Web Component
502 lines (501 loc) • 18.8 kB
JavaScript
/*!
* Crafted with ❤ by Salla
*/
import { h } from "@stencil/core";
export class SallaLoginModal {
constructor() {
/**
* Does the merchant/current location for visitor allow to login using mobile, By default outside KSA is `false`
*/
this.isMobileAllowed = true;
/**
* Does the merchant require registration with email & mobile
*/
this.isEmailRequired = false;
/**
* Once the api verify success, it will be login the customer in web pages
*/
this.supportWebAuth = true;
/**
* Reload after a successful login
*/
this.withoutReload = false;
this.htmlElement = document.documentElement;
this.messages = [];
this.isClosable = true;
this.iframeLoaded = false;
this.scrolling = 'no';
this.direction = document.dir;
this.canRenderIframe = false;
salla.event.on('login::open', (data) => {
var _a;
if (!this.isEmailAllowed && !this.isMobileAllowed) {
salla.logger.warn('Please enable atleast one login type to use this component.');
return;
}
this.withoutReload = (_a = data === null || data === void 0 ? void 0 : data.withoutReload) !== null && _a !== void 0 ? _a : this.withoutReload;
this.open(data);
this.openModal();
});
salla.event.on('modal::close', (data) => {
if (this.iframe)
this.sendMessageToIframe({
source: 'login',
type: 'modal_closed',
data,
});
});
salla.onReady(() => {
this.api = salla.config.get('store.api');
this.storeId = salla.config.get('store.id');
});
}
componentDidLoad() {
window.addEventListener('message', (event) => {
if (event.data.source != "login") {
return;
}
salla.log(`salla-account:: recived an event`, event.data);
let action = event.data.type.replace('iframe::', '');
let value = event.data.message;
//if we need skip some events
// if (action === 'dispatchEvent' && [].includes(value.event)) {
// return;
// }
// if (action == "dispatchEvent") {
// return this.host.dispatchEvent(new CustomEvent(value.name, {
// cancelable: true,
// bubbles: true,
// detail: value.payload
// }));
// }
if (action == 'ready') {
if (value.isPasskeyPage) {
this.createPassKey.postMessage({ source: "login", type: "init_passkey_page", data: {
token: salla.storage.get('token'),
store_id: salla.config.get('store.id'),
locale: salla.config.get('user.language_code'),
redirect: salla.config.get('store.url')
} }, salla.config.get('login.url'));
return;
}
this.handleIframeReadyAction();
return;
}
if (action == 'height') {
return (this.iframe.height = (value === null || value === void 0 ? void 0 : value.height) + 'px');
}
//share the iframe storage, so login too
if (action == "storage") {
return this.handleIframeStorageAction(value);
}
// if (action == "success") {
// this.isClosable = true;
// confetti.startConfetti(1200, 50, 150);
// }
if (action == "scrolling") {
this.scrolling = value;
this.isClosable = false;
return;
}
if (action == "direction") {
return document.dir = this.direction = value ? "rtl" : "ltr";
}
if (action == 'notify') {
return salla.notify[value.type](value.message);
}
// temporary condition until we handle both cases separately
if (action == 'authenticated' || action == 'authenticated_web_auth') {
!(value === null || value === void 0 ? void 0 : value.withoutClose) && this.close();
return salla.auth.api.handlAfterAuthenticatedResponse(value, this.supportWebAuth)
.then(() => !((value === null || value === void 0 ? void 0 : value.withoutReload) || this.withoutReload) && setTimeout(() => window.location.reload(), 1000));
}
if (action == 'dispatchEvent' && value.event == 'auth::verified') {
salla.log('Ignored dispatchEvent `auth::verified` from iframe');
//we don't need to fire this event because it have listeners for it, and already it's fired/firing from `salla.auth.api.handlAfterAuthenticatedResponse` already we are handling it
return;
}
if (action == 'dispatchEvent') {
return salla.event.emit(value.event, ...value.payload);
}
if (action == 'reload') {
if (value === null || value === void 0 ? void 0 : value.withClose)
this.modal.close();
return window.location.reload();
}
if (action === 'open_page') {
return window.location.href = value.url;
}
if (action === 'open_passkey_page') {
this.createPassKey = window.open(`${salla.config.get('login.url')}/create-passkey`);
return;
}
if (action == "close") {
this.close();
}
});
this.observeDarkModeChanges();
setTimeout(() => Salla.event.emit('salla-login::ready'), 1);
}
/**
* Open login component
*/
async open(_event = null) {
/**
* This is a workaround until we remove all the user objet from twig files
* When the user is already login, let's reload the page
*/
if (!salla.config.isGuest()) {
if (!this.withoutReload) {
return location.reload();
}
// Fire an event if withoutReload is true
return;
}
return this.modal.open();
}
close() {
Salla.event.emit('salla-login::closing');
this.modal.close();
this.iframeLoaded = false;
this.canRenderIframe = false;
this.host.dispatchEvent(new CustomEvent('salla-login:closed', {
cancelable: true,
bubbles: true,
detail: null
}));
Salla.event.emit('salla-login::closed');
}
handleIframeReadyAction() {
this.sendInitMessage();
this.sendMessageToIframe({
source: 'login',
type: 'width',
data: document.body.clientWidth,
});
}
observeDarkModeChanges() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
this.sendMessageToIframe({
source: 'login',
type: 'darkMode',
data: { isDark: this.htmlElement.classList.contains('dark') ? true : false },
});
}
});
});
// observing the HTML element for attribute changes
observer.observe(this.htmlElement, { attributes: true });
}
async handleIframeStorageAction(value) {
let token, cartUserId = salla.storage.get('cart.user_id');
Object.entries(value).filter(item => !salla.storage.get(item[0]))
.forEach(([key, value]) => {
salla.log('Shared storage from accounts domain', key, value);
let cleanedValue = (typeof value === 'string') ? value.replace(/^"|"$/g, '') : value;
salla.storage.set(key, cleanedValue);
if (key === 'token') {
token = cleanedValue;
}
if (key === 'user' && cartUserId && cleanedValue.id !== cartUserId) {
salla.cart.reset();
}
});
if (token) {
this.modal.close();
salla.auth.event.tokenFetched(token);
!salla.auth.api.isSessionless() && await salla.auth.api.request('auth/jwt');
setTimeout(() => window.location.reload(), 100);
return;
}
}
sendMessageToIframe(message) {
if (!this.iframe)
return;
return this.iframe.contentWindow.postMessage(message, '*');
}
sendInitMessage() {
let headers = this.headers;
try {
headers = typeof headers === 'string' ? JSON.parse(headers) : headers;
}
catch (error) {
console.error('Failed to parse headers!', this.headers, error);
headers = undefined;
}
const storageObject = {
user: salla.storage.get('user'),
s_translations: salla.storage.get('s_translations'),
token: salla.storage.get('token'),
};
storageObject.user || (delete storageObject.user);
storageObject.s_translations || (delete storageObject.s_translations);
storageObject.token || (delete storageObject.token);
this.sendMessageToIframe({
source: "login",
type: "init",
data: {
id: this.storeId,
lang: salla.lang.locale,
isDark: this.htmlElement.classList.contains('dark') ? true : false,
apiUrl: this.api.replace(/\/+$/, ''),
supportWebAuth: this.supportWebAuth,
withoutReload: this.withoutReload,
inline: this.inline,
settings: salla.config.all(),
headers: headers,
storage: storageObject,
}
});
}
openModal() {
if (!this.canRenderIframe) {
this.canRenderIframe = true;
}
this.modal.open();
}
render() {
if (this.inline) {
return this.getLoginDom();
}
return (h("salla-modal", { class: "s-login-modal", ref: modal => this.modal = modal, width: "xs" }, this.getLoginDom()));
}
getLoginDom() {
if (!this.canRenderIframe)
return '';
return h("iframe", { src: salla.config.get('login.url', 'https://accounts.salla.com'), title: "Salla Login", class: "hide-scroll", loading: 'lazy', ref: iframe => { this.iframe = iframe; }, allow: "publickey-credentials-get; publickey-credentials-create", scrolling: this.scrolling, frameborder: "0", width: "100%" });
}
static get is() { return "salla-login-modal"; }
static get originalStyleUrls() {
return {
"$": ["salla-login-modal.scss"]
};
}
static get styleUrls() {
return {
"$": ["salla-login-modal.css"]
};
}
static get properties() {
return {
"isEmailAllowed": {
"type": "boolean",
"attribute": "is-email-allowed",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Does the merchant allow to login using email"
},
"getter": false,
"setter": false,
"reflect": false
},
"isMobileAllowed": {
"type": "boolean",
"attribute": "is-mobile-allowed",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Does the merchant/current location for visitor allow to login using mobile, By default outside KSA is `false`"
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "true"
},
"isEmailRequired": {
"type": "boolean",
"attribute": "is-email-required",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Does the merchant require registration with email & mobile"
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "false"
},
"supportWebAuth": {
"type": "boolean",
"attribute": "support-web-auth",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Once the api verify success, it will be login the customer in web pages"
},
"getter": false,
"setter": false,
"reflect": true,
"defaultValue": "true"
},
"inline": {
"type": "boolean",
"attribute": "inline",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Display the login inline"
},
"getter": false,
"setter": false,
"reflect": true
},
"storeId": {
"type": "any",
"attribute": "store-id",
"mutable": true,
"complexType": {
"original": "string | number",
"resolved": "number | string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "The store ID for authentication"
},
"getter": false,
"setter": false,
"reflect": false
},
"api": {
"type": "string",
"attribute": "api",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "The API endpoint for authentication"
},
"getter": false,
"setter": false,
"reflect": false
},
"headers": {
"type": "string",
"attribute": "headers",
"mutable": true,
"complexType": {
"original": "{ [key: string]: string } | string",
"resolved": "string | { [key: string]: string; }",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Custom headers to be sent with API requests"
},
"getter": false,
"setter": false,
"reflect": false
},
"withoutReload": {
"type": "boolean",
"attribute": "without-reload",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Reload after a successful login"
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "false"
}
};
}
static get states() {
return {
"messages": {},
"isClosable": {},
"iframeLoaded": {},
"scrolling": {},
"direction": {},
"canRenderIframe": {}
};
}
static get methods() {
return {
"open": {
"complexType": {
"signature": "(_event?: any) => Promise<void | HTMLElement>",
"parameters": [{
"name": "_event",
"type": "any",
"docs": ""
}],
"references": {
"Promise": {
"location": "global",
"id": "global::Promise"
},
"HTMLElement": {
"location": "global",
"id": "global::HTMLElement"
}
},
"return": "Promise<void | HTMLElement>"
},
"docs": {
"text": "Open login component",
"tags": []
}
}
};
}
static get elementRef() { return "host"; }
}
//# sourceMappingURL=salla-login-modal.js.map