Skip to content
Merged

fixed #207

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions components/wallet/ui/OfflineBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
'use client';

import useOffline from '@/hooks/useOffline';
import { WifiOff } from 'lucide-react';
import { WifiOff, Wifi } from 'lucide-react';

/**
* OfflineBanner — Component to display network status fallback.
*/
const OfflineBanner = () => {
const { isOnline } = useOffline();
const { isOnline, showBackOnline } = useOffline();

if (isOnline) return null;
if (isOnline && !showBackOnline) return null;

return (
<div className="fixed top-0 left-0 right-0 z-[9999] animate-in fade-in slide-in-from-top duration-300">
<div className="bg-gradient-to-r from-red-600 to-red-500 text-white px-6 py-2.5 flex items-center justify-center gap-3 shadow-lg backdrop-blur-md bg-opacity-95">
<WifiOff className="w-4 h-4 animate-pulse" />
<div className={`px-6 py-2.5 flex items-center justify-center gap-3 shadow-lg backdrop-blur-md bg-opacity-95 ${showBackOnline ? 'bg-gradient-to-r from-green-600 to-green-500' : 'bg-gradient-to-r from-red-600 to-red-500'} text-white`}>
{showBackOnline ? (
<Wifi className="w-4 h-4 animate-pulse" />
) : (
<WifiOff className="w-4 h-4 animate-pulse" />
)}
<p className="text-sm font-semibold tracking-wide">
You are currently offline.{' '}
<span className="font-normal opacity-90 ml-1.5">
Transactions are disabled until connection is restored.
</span>
{showBackOnline ? (
'Back Online!'
) : (
<>
You are currently offline.{' '}
<span className="font-normal opacity-90 ml-1.5">
Transactions are disabled until connection is restored.
</span>
</>
)}
</p>
</div>
</div>
Expand Down
16 changes: 16 additions & 0 deletions hooks/__tests__/useOffline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { renderHook, act } from '@testing-library/react';
import useOffline from '@/hooks/useOffline';
import { networkService } from '@/services/networkService';

jest.useFakeTimers();

jest.mock('@/services/networkService', () => ({
networkService: {
getIsOnline: jest.fn(),
Expand All @@ -14,12 +16,17 @@ describe('useOffline', () => {

beforeEach(() => {
jest.clearAllMocks();
jest.useFakeTimers();
(networkService.subscribe as jest.Mock).mockImplementation((cb) => {
subscribeCallback = cb;
return jest.fn(); // Unsubscribe mock
});
});

afterEach(() => {
jest.useRealTimers();
});

it('should initialize with the current network status', () => {
(networkService.getIsOnline as jest.Mock).mockReturnValue(true);
const { result } = renderHook(() => useOffline());
Expand All @@ -38,12 +45,21 @@ describe('useOffline', () => {
});

expect(result.current.isOnline).toBe(false);
expect(result.current.showBackOnline).toBe(false);

act(() => {
subscribeCallback(true);
});

expect(result.current.isOnline).toBe(false);
expect(result.current.showBackOnline).toBe(true);

act(() => {
jest.advanceTimersByTime(2000);
});

expect(result.current.isOnline).toBe(true);
expect(result.current.showBackOnline).toBe(false);
});

it('should cleanup subscription on unmount', () => {
Expand Down
31 changes: 27 additions & 4 deletions hooks/useOffline.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';
import { networkService } from '@/services/networkService';

/**
Expand All @@ -8,20 +8,43 @@ const useOffline = () => {
const [isOnline, setIsOnline] = useState<boolean>(
networkService.getIsOnline()
);
const [showBackOnline, setShowBackOnline] = useState<boolean>(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setIsOnline(networkService.getIsOnline());

const unsubscribe = networkService.subscribe((status) => {
setIsOnline(status);
if (status) {
setShowBackOnline(true);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setShowBackOnline(false);
setIsOnline(true);
}, 2000);
} else {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
setShowBackOnline(false);
setIsOnline(false);
}
});

return () => unsubscribe();
return () => {
unsubscribe();
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);

return {
isOnline,
showBackOnline,
};
};

Expand Down
6 changes: 5 additions & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ const config: Config = {
'^.+\\.(ts|tsx)$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.jest.json',
tsconfig: {
jsx: 'react-jsx',
moduleResolution: 'node',
ignoreDeprecations: '5.0',
},
},
],
},
Expand Down
Loading