diff --git a/packages/chat-app/src/components/Chat/Conversations/Sidenav/ProfileHeader.tsx b/packages/chat-app/src/components/Chat/Conversations/Sidenav/NavHeader.tsx
similarity index 56%
rename from packages/chat-app/src/components/Chat/Conversations/Sidenav/ProfileHeader.tsx
rename to packages/chat-app/src/components/Chat/Conversations/Sidenav/NavHeader.tsx
index c17ebb85d..be5464597 100644
--- a/packages/chat-app/src/components/Chat/Conversations/Sidenav/ProfileHeader.tsx
+++ b/packages/chat-app/src/components/Chat/Conversations/Sidenav/NavHeader.tsx
@@ -1,10 +1,25 @@
import { ActionButton, ActionLink, Plus, Times, t } from '@youfoundation/common-app';
import { CHAT_ROOT } from '../../../../templates/Chat/ChatHome';
-export const ProfileHeader = ({ closeSideNav }: { closeSideNav: (() => void) | undefined }) => {
+export const NavHeader = ({
+ closeSideNav,
+ isOnline,
+}: {
+ closeSideNav: (() => void) | undefined;
+ isOnline: boolean;
+}) => {
return (
-
Homebase Chat
+
{t('New')}
diff --git a/packages/chat-app/src/hooks/chat/useLiveChatProcessor.ts b/packages/chat-app/src/hooks/chat/useLiveChatProcessor.ts
index 5047d2402..025c8c77a 100644
--- a/packages/chat-app/src/hooks/chat/useLiveChatProcessor.ts
+++ b/packages/chat-app/src/hooks/chat/useLiveChatProcessor.ts
@@ -25,17 +25,18 @@ import { processCommand } from '../../providers/ChatCommandProvider';
const MINUTE_IN_MS = 60000;
-// We first setup the websocket, and then trigger processing of the inbox
-// So that new message will be detected by the websocket;
+// We first process the inbox, then we connect for live updates;
export const useLiveChatProcessor = () => {
- // Setup websocket, so that we get notified instantly when a new message is received
- const connected = useChatWebsocket(true);
+ // Process the inbox on startup; As we want to cover the backlog of messages first
+ const { status: inboxStatus } = useInboxProcessor(true);
- // Process the inbox on startup (once the socket is connected)
- const { status: inboxStatus } = useInboxProcessor(connected);
+ // Only after the inbox is processed, we connect for live updates; So we avoid clearing the cache on each fileAdded update
+ const isOnline = useChatWebsocket(inboxStatus === 'success');
// Only after the inbox is processed, we process commands as new ones might have been added via the inbox
useChatCommandProcessor(inboxStatus === 'success');
+
+ return isOnline;
};
// Process the inbox on startup
diff --git a/packages/chat-app/src/templates/Chat/ChatHome.tsx b/packages/chat-app/src/templates/Chat/ChatHome.tsx
index 81b3b5e3d..98667c7b6 100644
--- a/packages/chat-app/src/templates/Chat/ChatHome.tsx
+++ b/packages/chat-app/src/templates/Chat/ChatHome.tsx
@@ -8,7 +8,7 @@ import { useState } from 'react';
import { NewConversation } from '../../components/Chat/Conversations/Sidenav/NewConversation';
import { NewConversationGroup } from '../../components/Chat/Conversations/Sidenav/NewConversationGroup';
import { ConversationsSidebar } from '../../components/Chat/Conversations/Sidenav/ConversationsSidenav';
-import { ProfileHeader } from '../../components/Chat/Conversations/Sidenav/ProfileHeader';
+import { NavHeader } from '../../components/Chat/Conversations/Sidenav/NavHeader';
import {
CHAT_APP_ID,
ExtendPermissionDialog,
@@ -25,7 +25,7 @@ export const ChatHome = () => {
const { conversationKey } = useParams();
const [isSidenavOpen, setIsSidenavOpen] = useState(false);
- useLiveChatProcessor();
+ const isOnline = useLiveChatProcessor();
useMarkAllAsRead({ appId: CHAT_APP_ID });
return (
@@ -42,7 +42,11 @@ export const ChatHome = () => {
// needsAllConnected={true}
/>
-
+
{
const ChatSideNav = ({
isOpen,
setIsSidenavOpen,
+ isOnline,
}: {
isOpen: boolean;
setIsSidenavOpen: (newIsOpen: boolean) => void;
+ isOnline: boolean;
}) => {
const { conversationKey } = useParams();
const { logout } = useAuth();
@@ -93,7 +99,10 @@ const ChatSideNav = ({
) : (
<>
- setIsSidenavOpen(false)} />
+ setIsSidenavOpen(false)}
+ isOnline={isOnline}
+ />
{
diff --git a/packages/common-app/src/hooks/transitProcessor/useNotificationSubscriber.ts b/packages/common-app/src/hooks/transitProcessor/useNotificationSubscriber.ts
index 9795fc772..8b80ba477 100644
--- a/packages/common-app/src/hooks/transitProcessor/useNotificationSubscriber.ts
+++ b/packages/common-app/src/hooks/transitProcessor/useNotificationSubscriber.ts
@@ -15,14 +15,13 @@ export const useNotificationSubscriber = (
subscriber: ((notification: TypedConnectionNotification) => void) | undefined,
types: NotificationType[],
drives: TargetDrive[] = [BlogConfig.FeedDrive, BlogConfig.PublicChannelDrive],
- onDisconnect?: () => void
+ onDisconnect?: () => void,
+ onReconnect?: () => void
) => {
const [isActive, setIsActive] = useState(false);
const isConnected = useRef(false);
const dotYouClient = useDotYouClient().getDotYouClient();
- const [shouldReconnect, setShouldReconnect] = useState(false);
-
const localHandler = subscriber
? (notification: TypedConnectionNotification) => {
if (types?.length >= 1 && !types.includes(notification.notificationType)) return;
@@ -40,16 +39,20 @@ export const useNotificationSubscriber = (
if (!isConnected.current && localHandler) {
isConnected.current = true;
(async () => {
- await Subscribe(dotYouClient, drives, localHandler, () => {
- isConnected.current = false;
- setIsActive(false);
-
- setShouldReconnect(true);
-
- onDisconnect && onDisconnect();
- });
+ await Subscribe(
+ dotYouClient,
+ drives,
+ localHandler,
+ () => {
+ setIsActive(false);
+ onDisconnect && onDisconnect();
+ },
+ () => {
+ setIsActive(true);
+ onReconnect && onReconnect();
+ }
+ );
setIsActive(true);
- setShouldReconnect(false);
})();
}
@@ -64,7 +67,7 @@ export const useNotificationSubscriber = (
}
}
};
- }, [subscriber, shouldReconnect]);
+ }, [subscriber]);
return isActive;
};
diff --git a/packages/js-lib/src/core/WebsocketData/WebsocketProvider.ts b/packages/js-lib/src/core/WebsocketData/WebsocketProvider.ts
index 823a72a6d..d58f87cdb 100644
--- a/packages/js-lib/src/core/WebsocketData/WebsocketProvider.ts
+++ b/packages/js-lib/src/core/WebsocketData/WebsocketProvider.ts
@@ -24,9 +24,12 @@ const PING_INTERVAL = 1000 * 5 * 1;
let pingInterval: NodeJS.Timeout | undefined;
let lastPong: number | undefined;
+let reconnectTimeout: NodeJS.Timeout | undefined;
+
const subscribers: {
handler: (data: TypedConnectionNotification) => void;
onDisconnect?: () => void;
+ onReconnect?: () => void;
}[] = [];
interface RawClientNotification {
@@ -120,9 +123,9 @@ const ConnectSocket = async (
lastPong = Date.now();
pingInterval = setInterval(() => {
if (lastPong && Date.now() - lastPong > PING_INTERVAL * 2) {
- // 2 ping intervals have passed without a pong, force disconnect
+ // 2 ping intervals have passed without a pong, reconnect
if (isDebug) console.debug(`[NotificationProvider] Ping timeout`);
- DisconnectSocket();
+ ReconnectSocket(dotYouClient, drives, args);
return;
}
Notify({
@@ -154,21 +157,42 @@ const ConnectSocket = async (
webSocketClient.onerror = (e) => {
console.error('[NotificationProvider]', e);
- DisconnectSocket();
};
webSocketClient.onclose = (e) => {
if (isDebug) console.debug('[NotificationProvider] Connection closed', e);
- DisconnectSocket();
+
+ subscribers.map((subscriber) => subscriber.onDisconnect && subscriber.onDisconnect());
+ ReconnectSocket(dotYouClient, drives, args);
};
});
};
-const DisconnectSocket = async () => {
- if (!webSocketClient) throw new Error('No active client to disconnect');
+const ReconnectSocket = async (
+ dotYouClient: DotYouClient,
+ drives: TargetDrive[],
+ args?: unknown // Extra parameters to pass to WebSocket constructor; Only applicable for React Native...; TODO: Remove this
+) => {
+ if (reconnectTimeout) return;
+
+ reconnectTimeout = setTimeout(async () => {
+ reconnectTimeout = undefined;
+ webSocketClient = undefined;
+ lastPong = undefined;
+ isConnected = false;
+ clearInterval(pingInterval);
+
+ if (isDebug) console.debug('[NotificationProvider] Reconnecting');
+ await ConnectSocket(dotYouClient, drives, args);
+ subscribers.map((subscriber) => subscriber.onReconnect && subscriber.onReconnect());
+ }, 5000);
+};
+
+const DisconnectSocket = async () => {
try {
- webSocketClient.close(1000, 'Normal Disconnect');
+ if (!webSocketClient) console.warn('No active client to disconnect');
+ else webSocketClient.close(1000, 'Normal Disconnect');
} catch (e) {
// Ignore any errors on close, as we always want to clean up
}
@@ -188,6 +212,7 @@ export const Subscribe = async (
drives: TargetDrive[],
handler: (data: TypedConnectionNotification) => void,
onDisconnect?: () => void,
+ onReconnect?: () => void,
args?: unknown // Extra parameters to pass to WebSocket constructor; Only applicable for React Native...; TODO: Remove this
) => {
const apiType = dotYouClient.getType();
@@ -197,7 +222,7 @@ export const Subscribe = async (
}
activeSs = sharedSecret;
- subscribers.push({ handler, onDisconnect });
+ subscribers.push({ handler, onDisconnect, onReconnect });
if (isDebug) console.debug(`[NotificationProvider] New subscriber (${subscribers.length})`);