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 (
)}
>
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