Skip to content

Akmal / Enable skeleton loader and fix bugs for login buttons #1138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions src/javascript/app/base/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,38 @@ const Client = (() => {
applyToAllElements('.is-logout', (el) => {
el.style.display = 'none';
});

// Only hide skeleton loaders if not on a loading trading page and header is ready
const trading_init_progress = getElementById('trading_init_progress');
const is_trading_loading = trading_init_progress && trading_init_progress.style.display !== 'none';

// Check if header is ready via SmartTrader namespace to avoid circular dependency
let is_header_ready = false;
if (typeof window !== 'undefined' && window.SmartTrader?.Header?.isHeaderReady) {
is_header_ready = window.SmartTrader.Header.isHeaderReady();
} else {
// If header readiness check not available, assume ready
is_header_ready = true;
}

if (!is_trading_loading && is_header_ready) {
// Hide skeleton loaders for logged-in state (login buttons not needed)
applyToAllElements('.skeleton-btn-login, .skeleton-btn-signup', (el) => {
el.classList.add('hidden');
});
} else if (!is_header_ready) {
// If header not ready, register callback via SmartTrader namespace
if (typeof window !== 'undefined' && window.SmartTrader?.Header?.addHeaderReadyCallback) {
window.SmartTrader.Header.addHeaderReadyCallback(() => {
// Re-run the skeleton loader logic when header is ready
if (!is_trading_loading) {
applyToAllElements('.skeleton-btn-login, .skeleton-btn-signup', (el) => {
el.style.display = 'none';
});
}
});
}
}
});
} else {
// applyToAllElements('.client_logged_in', (el) => {
Expand All @@ -79,9 +111,57 @@ const Client = (() => {
applyToAllElements('.is-login', (el) => {
el.style.display = 'none';
});
applyToAllElements('.is-logout', (el) => {
el.style.display = 'inline-flex';
// .is-logout container is already visible by default, no need to show it again

// EXPLICIT CLEANUP: Always remove skeleton loaders first for logout scenarios
applyToAllElements('.skeleton-btn-login, .skeleton-btn-signup', (el) => {
el.style.display = 'none';
});

// Check if header is ready via SmartTrader namespace to avoid circular dependency
let is_header_ready = false;
if (typeof window !== 'undefined' && window.SmartTrader?.Header?.isHeaderReady) {
is_header_ready = window.SmartTrader.Header.isHeaderReady();
} else {
// If header readiness check not available, assume ready
is_header_ready = true;
}

const logoutHeaderModule = window.SmartTrader?.Header;
if (is_header_ready) {
// Header is ready, coordinate with header to show login buttons
try {
if (logoutHeaderModule && logoutHeaderModule.updateLoginButtonsDisplay) {
logoutHeaderModule.updateLoginButtonsDisplay();
}
} catch (error) {
// Fallback: directly show login buttons if header module not available
applyToAllElements('#btn__login, #btn__signup', (el) => {
el.classList.remove('hidden');
});
}
} else if (typeof window !== 'undefined' && logoutHeaderModule && logoutHeaderModule.addHeaderReadyCallback) {
// If header not ready, register callback via SmartTrader namespace
logoutHeaderModule.addHeaderReadyCallback(() => {
// Coordinate with header when ready
try {
const callbackHeaderModule = window.SmartTrader?.Header;
if (callbackHeaderModule && callbackHeaderModule.updateLoginButtonsDisplay) {
callbackHeaderModule.updateLoginButtonsDisplay();
}
} catch (error) {
// Fallback: directly show login buttons
applyToAllElements('#btn__login, #btn__signup', (el) => {
el.style.display = '';
});
}
});
} else {
// Final fallback: directly show login buttons
applyToAllElements('#btn__login, #btn__signup', (el) => {
el.style.display = '';
});
}
}
};

Expand Down
159 changes: 131 additions & 28 deletions src/javascript/app/base/header.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// const BinaryPjax = require('./binary_pjax');
// const Cookies = require('js-cookie');
const Cookies = require('js-cookie');
const requestOidcAuthentication = require('@deriv-com/auth-client').requestOidcAuthentication;
const Client = require('./client');
const BinarySocket = require('./socket');
Expand Down Expand Up @@ -41,6 +41,8 @@ const Header = (() => {
const notifications = [];
let is_language_popup_on = false;
let is_full_screen = false;
let is_header_ready = false;
const header_ready_callbacks = [];
const ext_platform_url = encodeURIComponent(window.location.href);
const fullscreen_map = {
event : ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'],
Expand All @@ -49,9 +51,43 @@ const Header = (() => {
fnc_exit : ['exitFullscreen', 'webkitExitFullscreen', 'mozCancelFullScreen', 'msExitFullscreen'],
};

const addHeaderReadyCallback = (callback) => {
if (is_header_ready) {
callback();
} else {
header_ready_callbacks.push(callback);
}
};

const triggerHeaderReadyCallbacks = () => {
is_header_ready = true;
header_ready_callbacks.forEach(callback => {
try {
callback();
} catch (error) {
// eslint-disable-next-line no-console
console.error('Header ready callback error:', error);
}
});
header_ready_callbacks.length = 0; // Clear callbacks array
};

const isHeaderReady = () => is_header_ready;

// Expose functions via SmartTrader namespace to avoid circular dependencies
if (typeof window !== 'undefined') {
// Initialize SmartTrader namespace
window.SmartTrader = window.SmartTrader || {};
window.SmartTrader.Header = {
addHeaderReadyCallback,
isHeaderReady,
updateLoginButtonsDisplay,
};
}

const onLoad = async () => {
bindSvg();
// updateLoginButtonsDisplay();
updateLoginButtonsDisplay();

await BinarySocket.wait('authorize', 'landing_company');

Expand Down Expand Up @@ -85,6 +121,9 @@ const Header = (() => {
});

applyFeatureFlags();

// Mark header as ready and trigger any waiting callbacks
triggerHeaderReadyCallbacks();
};

const applyFeatureFlags = () => {
Expand Down Expand Up @@ -373,37 +412,98 @@ const Header = (() => {
mobile_platform_list.appendChild(platform_dropdown_cta_container);
};

// const updateLoginButtonsDisplay = () => {
// // Check if we should show skeleton loading state
// const logged_state = typeof Cookies !== 'undefined' ? Cookies.get('logged_state') : null;
// const client_accounts = typeof window !== 'undefined' ? JSON.parse(window.localStorage.getItem('client.accounts') || '{}') : {};
// const is_client_accounts_populated = Object.keys(client_accounts).length > 0;
// const is_silent_login_excluded = window.location.pathname.includes('callback') || window.location.pathname.includes('endpoint');
// const will_eventually_sso = logged_state === 'true' && !is_client_accounts_populated;
const updateLoginButtonsDisplay = () => {
// Check if we should show skeleton loading state
const logged_state = Cookies.get('logged_state');
const client_accounts = typeof window !== 'undefined' ? JSON.parse(window.localStorage.getItem('client.accounts') || '{}') : {};
const is_client_accounts_populated = Object.keys(client_accounts).length > 0;
const is_silent_login_excluded = window.location.pathname.includes('callback') || window.location.pathname.includes('endpoint');
const will_eventually_sso = logged_state === 'true' && !is_client_accounts_populated;

// Check if user is logged out - this takes priority over trading page logic
const is_logged_out = !Client.isLoggedIn();

// Check if we're on a trading page and if trading is still loading
const is_trading_page = window.location.pathname.includes('/trading');
const trading_init_progress = getElementById('trading_init_progress');
const is_trading_loading = trading_init_progress && trading_init_progress.style.display !== 'none';

// // Get login and signup buttons
// const btn_login = getElementById('btn__login');
// const btn_signup = getElementById('btn__signup');
// const header_btn_container = btn_login ? btn_login.parentElement : null;
// Get login and signup buttons
const btn_login = getElementById('btn__login');
const btn_signup = getElementById('btn__signup');
const header_btn_container = btn_login ? btn_login.parentElement : null;

if (!header_btn_container) return;

// if (btn_login) btn_login.style.display = 'none';
// if (btn_signup) btn_signup.style.display = 'none';
// Hide buttons initially
if (btn_login) btn_login.style.display = 'none';
if (btn_signup) btn_signup.style.display = 'none';

// if (!will_eventually_sso || is_silent_login_excluded) {
// // Show regular buttons
// if (btn_login) btn_login.style.display = 'flex';
// if (btn_signup) btn_signup.style.display = 'flex';

// // Remove skeleton squares if they exist
// const skeleton1 = document.querySelector('.skeleton-btn-login');
// const skeleton2 = document.querySelector('.skeleton-btn-signup');
// if (skeleton1) header_btn_container.removeChild(skeleton1);
// if (skeleton2) header_btn_container.removeChild(skeleton2);
// }
// };
// PRIORITY 1: If user is logged out, always show login buttons (override trading logic)
if (is_logged_out) {
removeHeaderSkeletonLoaders();
if (btn_login) btn_login.style.display = 'flex';
if (btn_signup) btn_signup.style.display = 'flex';
return;
}

// PRIORITY 2: On trading pages, sync with purchase container loading state (only for logged-in users)
if (is_trading_page && is_trading_loading) {
// Show skeleton loaders while trading is initializing
showHeaderSkeletonLoaders();
return;
}

// Remove skeleton loaders first
removeHeaderSkeletonLoaders();

if (!will_eventually_sso || is_silent_login_excluded) {
// Show regular buttons
if (btn_login) btn_login.style.display = 'flex';
if (btn_signup) btn_signup.style.display = 'flex';
} else {
// Show skeleton loaders for silent login
showHeaderSkeletonLoaders();
}
};

const showHeaderSkeletonLoaders = () => {
const btn_login = getElementById('btn__login');
const btn_signup = getElementById('btn__signup');
const header_btn_container = btn_login ? btn_login.parentElement : null;

if (!header_btn_container) return;

// Remove existing skeleton loaders first
removeHeaderSkeletonLoaders();

// Hide actual buttons
if (btn_login) btn_login.style.display = 'none';
if (btn_signup) btn_signup.style.display = 'none';

// Create and add skeleton loaders
const skeleton_login = document.createElement('div');
skeleton_login.className = 'skeleton-btn-login';
skeleton_login.style.cssText = 'width: 60px; height: 32px; background: #f0f0f0; border-radius: 4px; margin-right: 8px; animation: skeleton-loading 1.5s infinite ease-in-out;';

const skeleton_signup = document.createElement('div');
skeleton_signup.className = 'skeleton-btn-signup';
skeleton_signup.style.cssText = 'width: 80px; height: 32px; background: #f0f0f0; border-radius: 4px; animation: skeleton-loading 1.5s infinite ease-in-out;';

header_btn_container.appendChild(skeleton_login);
header_btn_container.appendChild(skeleton_signup);
};

const removeHeaderSkeletonLoaders = () => {
const skeleton_login = document.querySelector('.skeleton-btn-login');
const skeleton_signup = document.querySelector('.skeleton-btn-signup');

if (skeleton_login) skeleton_login.remove();
if (skeleton_signup) skeleton_signup.remove();
};

const bindClick = () => {
// updateLoginButtonsDisplay();
updateLoginButtonsDisplay();
const btn_login = getElementById('btn__login');
btn_login.addEventListener('click', loginOnClick);

Expand Down Expand Up @@ -1778,6 +1878,9 @@ const Header = (() => {
hideNotification,
displayAccountStatus,
loginOnClick,
updateLoginButtonsDisplay,
addHeaderReadyCallback,
isHeaderReady,
};
})();

Expand Down
50 changes: 49 additions & 1 deletion src/javascript/app/pages/trade/process.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const refreshDropdown = require('@binary-com/binary-style').selectDropdown;
const Cookies = require('js-cookie');
const moment = require('moment');
const TradingAnalysis = require('./analysis');
const commonTrading = require('./common');
Expand All @@ -14,7 +15,7 @@ const StartDates = require('./starttime').StartDates;
const Symbols = require('./symbols');
const Tick = require('./tick');
const NotAvailable = require('./not-available.jsx');
const BinarySocket = require('../../base/socket');
const BinarySocket = require('../../base/socket');
const dataManager = require('../../common/data_manager.js').default;
const getMinPayout = require('../../common/currency').getMinPayout;
const isCryptocurrency = require('../../common/currency').isCryptocurrency;
Expand Down Expand Up @@ -134,6 +135,53 @@ const Process = (() => {

if (init_logo && init_logo.style.display !== 'none') {
init_logo.style.display = 'none';

// Synchronize header skeleton loaders with trading completion
// Wait for header to be ready before updating skeleton loaders
// Use SmartTrader namespace to avoid circular dependency
if (typeof window !== 'undefined' && window.SmartTrader?.Header?.addHeaderReadyCallback) {
window.SmartTrader.Header.addHeaderReadyCallback(() => {
try {
if (window.SmartTrader?.Header?.updateLoginButtonsDisplay) {
window.SmartTrader.Header.updateLoginButtonsDisplay();
}
} catch (error) {
// console.warn('Header module not available in callback:', error);
}
});
} else {
// Fallback if callback system not available via SmartTrader namespace
try {
if (window.SmartTrader?.Header?.updateLoginButtonsDisplay) {
window.SmartTrader.Header.updateLoginButtonsDisplay();
}
} catch (error) {
// console.warn('Header module not available for fallback:', error);
}
}

// Additional fallback: directly update header skeleton loaders if Header module not available
if (typeof window === 'undefined' || !window.SmartTrader?.Header) {
const skeleton_login = document.querySelector('.skeleton-btn-login');
const skeleton_signup = document.querySelector('.skeleton-btn-signup');
const btn_login = document.getElementById('btn__login');
const btn_signup = document.getElementById('btn__signup');

if (skeleton_login) skeleton_login.remove();
if (skeleton_signup) skeleton_signup.remove();

// Show appropriate buttons based on login state
const logged_state = Cookies.get('logged_state');
const client_accounts = typeof window !== 'undefined' ? JSON.parse(window.localStorage.getItem('client.accounts') || '{}') : {};
const is_client_accounts_populated = Object.keys(client_accounts).length > 0;
const will_eventually_sso = logged_state === 'true' && !is_client_accounts_populated;

if (!will_eventually_sso) {
if (btn_login) btn_login.style.display = 'flex';
if (btn_signup) btn_signup.style.display = 'flex';
}
}

Defaults.update();
}
};
Expand Down
5 changes: 5 additions & 0 deletions src/sass/_common/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ body {
border: 1px solid var(--border-normal);
}

/* Utility Classes */
.hidden {
display: none !important;
}

@mixin modal-container {
min-width: 480px;
min-height: 176px;
Expand Down
5 changes: 4 additions & 1 deletion src/sass/_common/header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,12 @@ body .header, .wallet__header {
width: 1px;
margin-left: 8px;
}
.is-login, .is-logout {
.is-login {
display: none;
}
.is-logout {
display: inline-flex; // Show initially to display skeleton loaders
}
&__btn {
display: flex;
align-items: center;
Expand Down
Loading
Loading