TP-54271 | Internet online/offline nudge | Herik (#818)
* TP-54271 | Internet online/offline nudge | Herik * TP-54271|Kunal|added useUpdateEffect hook * TP-54271|Kunal|added useUpdateEffect hook * TP-1|Kunal|submodule update --------- Co-authored-by: kunalsharma <kunal.sharma@navi.com>
This commit is contained in:
committed by
GitHub
parent
d340a6b1bc
commit
ee86650415
68
src/App.tsx
68
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 (
|
||||
<div className={cx('internetStatus', isOnline ? 'online' : 'offline')}>
|
||||
{!isOnline && (
|
||||
<>
|
||||
<WifiOffline /> You’re offline! Some features will be unavailable.
|
||||
</>
|
||||
)}
|
||||
{isOnline && (
|
||||
<>
|
||||
<WifiOnline /> You’re back online.
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
// <Route path="*" element={<DefaultLayout />} />
|
||||
// </Routes>
|
||||
// </Suspense>
|
||||
|
||||
const stickyNoteComponent = () => {
|
||||
return (
|
||||
<a onClick={refresh} className="stickyNote" href={window?.location?.href}>
|
||||
An update of the app is available, please click this to refresh
|
||||
</a>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{notificationPermission === 'granted' || isMobilePhone || import.meta.env.DEV ? (
|
||||
@@ -238,11 +285,10 @@ const App = () => {
|
||||
>
|
||||
<ToastContainer style={{ width: 'auto' }} />
|
||||
<AppRouter />
|
||||
{showStickyNote && (
|
||||
<a onClick={refresh} className="stickyNote" href={window?.location?.href}>
|
||||
An update of the app is available, please click this to refresh
|
||||
</a>
|
||||
)}
|
||||
<InternetStatus
|
||||
enableStickyNote={showStickyNote}
|
||||
stickyNoteComponent={stickyNoteComponent}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
20
src/assets/icons/WifiOffline.tsx
Normal file
20
src/assets/icons/WifiOffline.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { IconProps } from './types';
|
||||
|
||||
export const WifiOffline: React.FC<IconProps> = props => {
|
||||
const { className, size = 18, fillColor = '#BA2323', onClick } = props;
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox={`0 0 ${size} ${size}`}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.8125 16.9501L7.7625 9.8626C7.175 10.0001 6.62813 10.2063 6.12188 10.4813C5.61563 10.7563 5.1625 11.1001 4.7625 11.5126L3.1875 9.9001C3.5875 9.5001 4.01875 9.1501 4.48125 8.8501C4.94375 8.5501 5.4375 8.2876 5.9625 8.0626L4.275 6.3751C3.7625 6.6376 3.28438 6.92822 2.84063 7.24697C2.39688 7.56572 1.975 7.9251 1.575 8.3251L0 6.7126C0.4 6.3126 0.815625 5.95322 1.24688 5.63447C1.67813 5.31572 2.1375 5.0126 2.625 4.7251L1.05 3.1501L2.1 2.1001L15.9 15.9001L14.8125 16.9501ZM9 15.7501C8.475 15.7501 8.03125 15.5657 7.66875 15.197C7.30625 14.8282 7.125 14.3876 7.125 13.8751C7.125 13.3501 7.30625 12.9063 7.66875 12.5438C8.03125 12.1813 8.475 12.0001 9 12.0001C9.525 12.0001 9.96875 12.1813 10.3313 12.5438C10.6938 12.9063 10.875 13.3501 10.875 13.8751C10.875 14.3876 10.6938 14.8282 10.3313 15.197C9.96875 15.5657 9.525 15.7501 9 15.7501ZM13.425 11.2876L9.6375 7.5001C10.65 7.6001 11.5969 7.85635 12.4781 8.26885C13.3594 8.68135 14.1375 9.2251 14.8125 9.9001L13.425 11.2876ZM16.425 8.3251C15.4625 7.3626 14.3469 6.60947 13.0781 6.06572C11.8094 5.52197 10.45 5.2501 9 5.2501C8.7375 5.2501 8.48438 5.25947 8.24063 5.27822C7.99688 5.29697 7.75 5.3251 7.5 5.3626L5.5875 3.4501C6.1375 3.3001 6.69687 3.1876 7.26562 3.1126C7.83438 3.0376 8.4125 3.0001 9 3.0001C10.775 3.0001 12.4313 3.33135 13.9688 3.99385C15.5062 4.65635 16.85 5.5626 18 6.7126L16.425 8.3251Z"
|
||||
fill={fillColor}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
20
src/assets/icons/WifiOnline.tsx
Normal file
20
src/assets/icons/WifiOnline.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { IconProps } from './types';
|
||||
|
||||
export const WifiOnline: React.FC<IconProps> = props => {
|
||||
const { className, size = 18, fillColor = '#00952A', onClick } = props;
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox={`0 0 ${size} ${size}`}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9 15.75C8.475 15.75 8.03125 15.5687 7.66875 15.2062C7.30625 14.8437 7.125 14.4 7.125 13.875C7.125 13.35 7.30625 12.9063 7.66875 12.5438C8.03125 12.1813 8.475 12 9 12C9.525 12 9.96875 12.1813 10.3313 12.5438C10.6938 12.9063 10.875 13.35 10.875 13.875C10.875 14.4 10.6938 14.8437 10.3313 15.2062C9.96875 15.5687 9.525 15.75 9 15.75ZM4.7625 11.5125L3.1875 9.9C3.925 9.1625 4.79063 8.57813 5.78438 8.14688C6.77813 7.71563 7.85 7.5 9 7.5C10.15 7.5 11.2219 7.71875 12.2156 8.15625C13.2094 8.59375 14.075 9.1875 14.8125 9.9375L13.2375 11.5125C12.6875 10.9625 12.05 10.5312 11.325 10.2188C10.6 9.90625 9.825 9.75 9 9.75C8.175 9.75 7.4 9.90625 6.675 10.2188C5.95 10.5312 5.3125 10.9625 4.7625 11.5125ZM1.575 8.325L0 6.75C1.15 5.575 2.49375 4.65625 4.03125 3.99375C5.56875 3.33125 7.225 3 9 3C10.775 3 12.4313 3.33125 13.9688 3.99375C15.5062 4.65625 16.85 5.575 18 6.75L16.425 8.325C15.4625 7.3625 14.3469 6.60938 13.0781 6.06563C11.8094 5.52188 10.45 5.25 9 5.25C7.55 5.25 6.19062 5.52188 4.92188 6.06563C3.65313 6.60938 2.5375 7.3625 1.575 8.325Z"
|
||||
fill={fillColor}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
22
src/hooks/useInternetAvailability.ts
Normal file
22
src/hooks/useInternetAvailability.ts
Normal file
@@ -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;
|
||||
17
src/hooks/useUpdateEffect.tsx
Normal file
17
src/hooks/useUpdateEffect.tsx
Normal file
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
Submodule web-ui-library updated: 300885d14d...669937867d
Reference in New Issue
Block a user