Skip to content

Commit

Permalink
Merge pull request #23 from rossbulat/rb-offline-monitor
Browse files Browse the repository at this point in the history
feat: Offline monitor
  • Loading branch information
Ross Bulat authored Mar 10, 2024
2 parents 68e9c12 + 0b67b59 commit 3b5644e
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ContextMenu } from 'library/ContextMenu';
import { Tabs } from 'library/Tabs';
import { Tooltip } from 'library/Tooltip';
import { Notifications } from 'library/Notifications';
import { Offline } from 'library/Offline';

export const Router = () => (
<>
Expand All @@ -27,5 +28,6 @@ export const Router = () => (
{/* Fallback route to chain */}
<Route key="route_fallback" path="*" element={<Navigate to="/" />} />
</Routes>
<Offline />
</>
);
7 changes: 6 additions & 1 deletion src/contexts/Api/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useTabs } from 'contexts/Tabs';
import { useEventListener } from 'usehooks-ts';
import { isCustomEvent } from 'Utils';
import type { APIChainSpec, ApiStatus } from 'model/Api/types';
import { OnlineStatusController } from 'controllers/OnlineStatusController';

export const Api = createContext<ApiContextInterface>(defaultApiContext);

Expand Down Expand Up @@ -148,8 +149,12 @@ export const ApiProvider = ({ children }: { children: ReactNode }) => {
// Listen for new chain spec updates.
useEventListener('new-chain-spec', handleNewChainSpec, documentRef);

// Initialisation of Api instances.
// Initialisation of Api.
useEffect(() => {
// Start listening for online / offline events.
OnlineStatusController.initOnlineEvents();

// Instantiate Api instances from tabs.
tabs.forEach((tab) => {
if (tab.autoConnect) {
instantiateApiFromTab(tab.id);
Expand Down
25 changes: 25 additions & 0 deletions src/controllers/OnlineStatusController/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2024 @rossbulat/console authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

export class OnlineStatusController {
// Set up online / offline event listeners. Relays information to `document` for the UI to handle.
static initOnlineEvents() {
window.addEventListener('offline', async () => {
this.dispatchEvent(false);
});
window.addEventListener('online', () => {
this.dispatchEvent(true);
});
}

// Dispatch an `online-status` event.
static dispatchEvent(online: boolean) {
document.dispatchEvent(
new CustomEvent('online-status', {
detail: {
online,
},
})
);
}
}
6 changes: 6 additions & 0 deletions src/controllers/OnlineStatusController/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2024 @rossbulat/console authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

export interface OnlineStatusEvent {
online: boolean;
}
29 changes: 29 additions & 0 deletions src/library/Offline/Wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 @rossbulat/console authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import styled from 'styled-components';

export const Wrapper = styled.div`
background: var(--accent-color-primary);
border: 0.5px solid var(--border-primary-color);
/* TODO: make theme variable + dark mode support */
box-shadow:
0 2px 3px -1px rgba(0, 0, 0, 0.05),
0 1px 4px -2px rgba(0, 0, 0, 0.05);
position: fixed;
bottom: 0.5rem;
right: 0.5rem;
z-index: 999;
padding: 0.6rem 0.9rem;
border-radius: 0.6rem;
display: flex;
> h3,
svg {
color: var(--text-color-invert);
}
> svg {
margin-right: 0.5rem;
}
`;
36 changes: 36 additions & 0 deletions src/library/Offline/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2024 @rossbulat/console authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { Wrapper } from './Wrapper';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faWarning } from '@fortawesome/free-solid-svg-icons';
import { useRef, useState } from 'react';
import { useEventListener } from 'usehooks-ts';
import { isCustomEvent } from 'Utils';

export const Offline = () => {
// Whether the app is offline.
const [offline, setOffline] = useState<boolean>(false);

// Handle incoming online status updates.
const handleOnlineStatus = (e: Event): void => {
if (isCustomEvent(e)) {
const { online } = e.detail;
setOffline(!online);
}
};

// Listen for online status updates.
const documentRef = useRef<Document>(document);
useEventListener('online-status', handleOnlineStatus, documentRef);

if (!offline) {
return null;
}
return (
<Wrapper>
<FontAwesomeIcon icon={faWarning} />
<h3>Connection appears to be offline</h3>
</Wrapper>
);
};
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only

import type { NotificationItem } from 'controllers/NotificationsController/types';
import type { OnlineStatusEvent } from 'controllers/OnlineStatusController/types';
import type {
APIChainSpecEventDetail,
APIStatusEventDetail,
Expand All @@ -12,6 +13,7 @@ declare global {
interface DocumentEventMap {
'api-status': CustomEvent<APIStatusEventDetail>;
'new-chain-spec': CustomEvent<APIChainSpecEventDetail>;
'online-status': CustomEvent<OnlineStatusEvent>;
notification: CustomEvent<NotificationItem>;
}
}
Expand Down

0 comments on commit 3b5644e

Please sign in to comment.