diff --git a/package-lock.json b/package-lock.json index 6cf781cd..1521e276 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@babel/preset-env": "^7.24.5", "@deriv-com/api-hooks": "^0.1.24", "@deriv-com/translations": "^1.2.4", - "@deriv-com/ui": "^1.26.0", + "@deriv-com/ui": "^1.27.5", "@deriv-com/utils": "latest", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-design": "^1.2.24", @@ -2723,11 +2723,10 @@ } }, "node_modules/@deriv-com/ui": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.26.0.tgz", - "integrity": "sha512-nk6NVpuqE/ibvuJ82+Eu5pqfaMVWTVOMj0lyMouD7oTcI1etkat2TdQ0qObDp9KGrLRhhNKrTlC6OT2gQpBgog==", + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.27.5.tgz", + "integrity": "sha512-jn1M0uowKOGfvEjadGRfLfUOUK/OkQjEPskRedpZJl6a8xFyJQpCG44nMxIyPK28pz4DRnNkbugReRwRzP5jfw==", "dependencies": { - "@deriv/quill-icons": "^1.22.5", "@types/react-modal": "^3.16.3", "react-tiny-popover": "^8.0.4" }, @@ -22702,11 +22701,10 @@ } }, "@deriv-com/ui": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.26.0.tgz", - "integrity": "sha512-nk6NVpuqE/ibvuJ82+Eu5pqfaMVWTVOMj0lyMouD7oTcI1etkat2TdQ0qObDp9KGrLRhhNKrTlC6OT2gQpBgog==", + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.27.5.tgz", + "integrity": "sha512-jn1M0uowKOGfvEjadGRfLfUOUK/OkQjEPskRedpZJl6a8xFyJQpCG44nMxIyPK28pz4DRnNkbugReRwRzP5jfw==", "requires": { - "@deriv/quill-icons": "^1.22.5", "@rollup/rollup-linux-x64-gnu": "^4.13.0", "@types/react-modal": "^3.16.3", "react-tiny-popover": "^8.0.4" diff --git a/package.json b/package.json index 29ace1be..6fd2399f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@babel/preset-env": "^7.24.5", "@deriv-com/api-hooks": "^0.1.24", "@deriv-com/translations": "^1.2.4", - "@deriv-com/ui": "^1.26.0", + "@deriv-com/ui": "^1.27.5", "@deriv-com/utils": "latest", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-design": "^1.2.24", diff --git a/src/components/AppFooter/AppFooter.scss b/src/components/AppFooter/AppFooter.scss index a4ab0a7e..07c5f083 100644 --- a/src/components/AppFooter/AppFooter.scss +++ b/src/components/AppFooter/AppFooter.scss @@ -36,8 +36,19 @@ width: 0.8rem; height: 0.8rem; border-radius: 100%; - background-color: #4bb4b3; margin-left: 2rem; + + &-online { + background-color: #4bb4b3; + } + + &-offline { + background-color: #ff444f; + } + + &-blinking { + animation: blink 1s infinite alternate; + } } &__endpoint { @@ -45,3 +56,12 @@ text-decoration: underline; } } + +@keyframes blink { + 0% { + opacity: 1; + } + 100% { + opacity: 0.2; + } +} diff --git a/src/components/AppFooter/AppFooter.tsx b/src/components/AppFooter/AppFooter.tsx index df92dfe2..176a1d09 100644 --- a/src/components/AppFooter/AppFooter.tsx +++ b/src/components/AppFooter/AppFooter.tsx @@ -10,7 +10,7 @@ import FullScreen from './FullScreen'; import HelpCentre from './HelpCentre'; import LanguageSettings from './LanguageSettings'; import Livechat from './Livechat'; -import { NetworkStatus } from './NetworkStatus'; +import NetworkStatus from './NetworkStatus'; import ResponsibleTrading from './ResponsibleTrading'; import { ServerTime } from './ServerTime'; import WhatsApp from './WhatsApp'; diff --git a/src/components/AppFooter/NetworkStatus.tsx b/src/components/AppFooter/NetworkStatus.tsx index df87c97b..3b29efb2 100644 --- a/src/components/AppFooter/NetworkStatus.tsx +++ b/src/components/AppFooter/NetworkStatus.tsx @@ -1,17 +1,33 @@ +import { useMemo } from 'react'; +import clsx from 'clsx'; +import { useNetworkStatus } from '@/hooks'; import { useTranslations } from '@deriv-com/translations'; import { TooltipMenuIcon } from '@deriv-com/ui'; -export const NetworkStatus = () => { +const statusConfigs = { + blinking: { + className: 'app-footer__network-status-online app-footer__network-status-blinking', + tooltip: 'Connecting to server', + }, + offline: { className: 'app-footer__network-status-offline', tooltip: 'Offline' }, + online: { className: 'app-footer__network-status-online', tooltip: 'Online' }, +}; + +const NetworkStatus = () => { + const status = useNetworkStatus(); const { localize } = useTranslations(); + const { className, tooltip } = useMemo(() => statusConfigs[status], [status]); return ( -
+
); }; + +export default NetworkStatus; diff --git a/src/hooks/custom-hooks/index.ts b/src/hooks/custom-hooks/index.ts index dd9862cc..d86e52e5 100644 --- a/src/hooks/custom-hooks/index.ts +++ b/src/hooks/custom-hooks/index.ts @@ -8,6 +8,8 @@ export { default as useFullScreen } from './useFullScreen'; export { default as useIsAdvertiser } from './useIsAdvertiser'; export { default as useIsAdvertiserBarred } from './useIsAdvertiserBarred'; export { default as useModalManager } from './useModalManager'; +export { default as useNavigatorOnline } from './useNavigatorOnline'; +export { default as useNetworkStatus } from './useNetworkStatus'; export { default as usePoiPoaStatus } from './usePoiPoaStatus'; export { default as useQueryString } from './useQueryString'; export { default as useSendbird } from './useSendbird'; diff --git a/src/hooks/custom-hooks/useNavigatorOnline.ts b/src/hooks/custom-hooks/useNavigatorOnline.ts new file mode 100644 index 00000000..753e5cf6 --- /dev/null +++ b/src/hooks/custom-hooks/useNavigatorOnline.ts @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; + +/** + * Retrieves the current online status of the browser. + * @returns {boolean} The online status of the browser. + */ +const getOnlineStatus = () => + typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean' ? navigator.onLine : true; + +/** + * A custom React hook that tracks the online status of the browser. + * @returns {boolean} The current online status of the browser. + */ +const useNavigatorOnline = () => { + const [status, setStatus] = useState(getOnlineStatus()); + + const setOnline = () => setStatus(true); + const setOffline = () => setStatus(false); + + useEffect(() => { + window.addEventListener('online', setOnline); + window.addEventListener('offline', setOffline); + + return () => { + window.removeEventListener('online', setOnline); + window.removeEventListener('offline', setOffline); + }; + }, []); + + return status; +}; + +export default useNavigatorOnline; diff --git a/src/hooks/custom-hooks/useNetworkStatus.ts b/src/hooks/custom-hooks/useNetworkStatus.ts new file mode 100644 index 00000000..59432698 --- /dev/null +++ b/src/hooks/custom-hooks/useNetworkStatus.ts @@ -0,0 +1,22 @@ +import { useEffect, useState } from 'react'; +import useNavigatorOnline from './useNavigatorOnline'; + +type TStatus = 'blinking' | 'offline' | 'online'; + +const useNetworkStatus = () => { + const [status, setStatus] = useState('online'); + const networkStatus = useNavigatorOnline(); + + // TODO we need socket connection state from api-hooks whenever it finished we can update this part and check + // both navigatorStatus and socket Status + // for now we just check the user network status. + + useEffect(() => { + if (networkStatus) setStatus('online'); + else setStatus('offline'); + }, [networkStatus]); + + return status; +}; + +export default useNetworkStatus;