diff --git a/src/App.tsx b/src/App.tsx index e327c7f1..154aca70 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,7 +23,7 @@ import { getDeviceId } from './utils/FingerPrintJS'; import { syncApiErrors } from './service/SyncApiErrorsService'; import { MILLI_IN_MIN } from './utils/DateHelper'; import { getFCMToken } from './firebase/firebase'; -import { setFcmToken, setIsLonghornThemeEnabled } from './reducers/commonSlice'; +import { setFcmToken } from './reducers/commonSlice'; import NotificationsBlockerOverlay from './NotificationBlockerOverlay'; import { LOCAL_STORAGE_KEYS, SESSION_STORAGE_KEYS } from './constants/StorageKeys'; import { pushToSessionStorage } from './utils/StorageUtils'; @@ -32,8 +32,11 @@ import useLocalStorageObserver from './hooks/useLocalStorageObserver'; import { AMEYO_AVAILABILITY_KEY, HEARTBEAT_TYPE } from './constants/Common.constants'; import heartbeatService from './service/heartbeatService'; import cx from 'classnames'; -import isLitmusExperimentEnabled from './utils/isLitmusExperimentEnabled'; -import { LITMUS_EXPERIMENT_NAMES } from './constants/litmusExperimentNames'; +import useInternetAvailability from './hooks/useInternetAvailability'; +import { WifiOnline } from './assets/icons/WifiOnline'; +import { WifiOffline } from './assets/icons/WifiOffline'; +import useUpdateEffect from '@cp/hooks/useUpdateEffect'; + declare global { interface Window { config: AppConfig; @@ -74,16 +77,52 @@ const shouldBlockUserForExtension = (phoneNumber: any) => { }; const TIME_TO_SEND_HEARTBEAT = 30 * 1000; // 30 seconds + +const InternetStatus = ({ enableStickyNote = false, stickyNoteComponent }: any) => { + const HIDE_TIMEOUT = 3e3; + const [hide, setHide] = useState(true); + const { isOnline } = useInternetAvailability(); + + useUpdateEffect(() => { + setHide(false); + if (isOnline) { + const timeout = setTimeout(() => { + setHide(true); + }, HIDE_TIMEOUT); + return () => clearTimeout(timeout); + } + }, [isOnline]); + + if (hide) { + if (enableStickyNote && stickyNoteComponent) { + return stickyNoteComponent(); + } + return null; + } + + return ( +
+ {!isOnline && ( + <> + You’re offline! Some features will be unavailable. + + )} + {isOnline && ( + <> + You’re back online. + + )} +
+ ); +}; + const App = () => { const { phoneNumber, naviUser, userImpersonated } = useSelector( (store: RootState) => store?.common?.userData ); const showStickyNote = useSelector((store: RootState) => store?.common?.showStickyNote); - const ameyoCampaignId = useSelector((store: RootState) => store?.common?.userData?.campaignId); - const authData = useSelector((state: RootState) => state?.common?.userData); - const [pluginFound, setPluginFound] = useState(false); const [userFoundInBlockList, setUserFoundInBlockList] = useState(false); const [notificationPermission, setNotificationPermission] = useState(Notification.permission); @@ -220,6 +259,14 @@ const App = () => { // } /> // // + + const stickyNoteComponent = () => { + return ( + + An update of the app is available, please click this to refresh + + ); + }; return (
{notificationPermission === 'granted' || isMobilePhone || import.meta.env.DEV ? ( @@ -238,11 +285,10 @@ const App = () => { > - {showStickyNote && ( - - An update of the app is available, please click this to refresh - - )} +
)} diff --git a/src/assets/icons/WifiOffline.tsx b/src/assets/icons/WifiOffline.tsx new file mode 100644 index 00000000..1ee77308 --- /dev/null +++ b/src/assets/icons/WifiOffline.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { IconProps } from './types'; + +export const WifiOffline: React.FC = props => { + const { className, size = 18, fillColor = '#BA2323', onClick } = props; + return ( + + + + ); +}; diff --git a/src/assets/icons/WifiOnline.tsx b/src/assets/icons/WifiOnline.tsx new file mode 100644 index 00000000..fe1b0237 --- /dev/null +++ b/src/assets/icons/WifiOnline.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { IconProps } from './types'; + +export const WifiOnline: React.FC = props => { + const { className, size = 18, fillColor = '#00952A', onClick } = props; + return ( + + + + ); +}; diff --git a/src/hooks/useInternetAvailability.ts b/src/hooks/useInternetAvailability.ts new file mode 100644 index 00000000..e1c5d768 --- /dev/null +++ b/src/hooks/useInternetAvailability.ts @@ -0,0 +1,22 @@ +import React, { useState, useEffect } from 'react'; + +const useInternetAvailability = () => { + const [isOnline, setIsOnline] = useState(true); + + useEffect(() => { + const handleOnline = () => setIsOnline(true); + const handleOffline = () => setIsOnline(false); + + window.addEventListener('online', handleOnline); + window.addEventListener('offline', handleOffline); + + return () => { + window.removeEventListener('online', handleOnline); + window.removeEventListener('offline', handleOffline); + }; + }, []); + + return { isOnline }; +}; + +export default useInternetAvailability; diff --git a/src/hooks/useUpdateEffect.tsx b/src/hooks/useUpdateEffect.tsx new file mode 100644 index 00000000..d43fda9d --- /dev/null +++ b/src/hooks/useUpdateEffect.tsx @@ -0,0 +1,17 @@ +import { DependencyList, EffectCallback, useEffect, useRef } from 'react'; + +function useUpdateEffect(effect: EffectCallback, dependencies: DependencyList) { + const isInitialRender = useRef(true); + + useEffect(() => { + // Skip the first render + if (isInitialRender.current) { + isInitialRender.current = false; + } else { + // run the effect on updates + return effect(); + } + }, dependencies); +} + +export default useUpdateEffect; diff --git a/src/index.scss b/src/index.scss index 1dcaf503..7fd3cc5a 100644 --- a/src/index.scss +++ b/src/index.scss @@ -61,17 +61,58 @@ code { font-weight: 500; transition: animation 300ms; // background-color: rgb(249, 0, 0); - background-color: rgb(240, 50, 50); box-shadow: 2px 8px 16px 0px rgba(0, 0, 0, 0.2); color: #fff; text-decoration: none; + background-color: rgb(240, 50, 50); + transition: background-color 300ms; + + &.green { + background-color: rgb(33 208 129); + } transition: transform 300ms, box-shadow 300ms; - &:hover { + &:hover:not(.nobounce) { animation: none; transform: translateX(-50%) scale(1.3); } + + &.nobounce { + animation: none; + } +} + +.internetStatus { + animation: fadeIn 300ms; + display: flex; + position: fixed; + top: 56px; + left: 50%; + transform: translateX(-50%); + z-index: var(--z-index-sticky-note); + padding: 6px 24px; + border-radius: 4px; + cursor: default; + transition: background-color 0.5s; + font-weight: 400; + font-size: 14px; + + svg { + margin-right: 8px; + } + + &.offline { + color: #ba2323; + border: 1px solid; + background-color: #fbd5d5; + } + + &.online { + color: #00952a; + border: 1px solid; + background-color: #ccf1d6; + } } .errorMessage { @@ -135,27 +176,32 @@ code { padding-right: 5px !important; } } + .ag-header-row { .ag-header-cell { padding-left: 16px; padding-right: 0px; } } + .ag-row { .ag-cell { padding-left: 16px; padding-right: 0px; } } + .ag-pinned-left-header { .ag-header-cell { padding-right: 5px; } + .ag-header-group-cell { background-color: unset !important; padding-left: 5px; } } + .ag-center-cols-clipper { min-height: unset !important; } @@ -177,6 +223,7 @@ code { .ag-header-group-text { font-size: 14px !important; } + :nth-child(1) { display: inline-block; align-items: center; @@ -194,6 +241,7 @@ code { color: var(--green-base); pointer-events: none; } + > div:nth-child(3) { background-color: var(--pale-yellow); color: var(--navi-color-yellow-dark); @@ -210,11 +258,13 @@ code { color: var(--green-base); pointer-events: none; } + > div:nth-child(2) { background-color: var(--orange-oldlase); color: var(--orange-dark); pointer-events: none; } + > div:nth-child(3) { background-color: var(--pale-yellow); color: var(--navi-color-yellow-dark); @@ -242,6 +292,7 @@ code { .paginationWrapperClasses { overflow: unset; z-index: 1; + > div:nth-child(1) { border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; diff --git a/web-ui-library b/web-ui-library index 300885d1..66993786 160000 --- a/web-ui-library +++ b/web-ui-library @@ -1 +1 @@ -Subproject commit 300885d14dde199b834ff703c289e7839d687f36 +Subproject commit 669937867d24956dae4f58e34b3afa88ebf4ee7a