Skip to content

Commit

Permalink
feat(better reconnect): check online status and reconnect when we com…
Browse files Browse the repository at this point in the history
…e back online
  • Loading branch information
rileylnapier committed May 25, 2024
1 parent 00141d1 commit f129c74
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 61 deletions.
1 change: 0 additions & 1 deletion packages/components/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ test("will support onEvent to listen for events on the window", async (done) =>
components: {
inbox: {
onEvent: (params) => {
console.log("params", params);
expect(params).toEqual({
messageId: mockGraphMessage.messageId,
message: mockGraphMessage,
Expand Down
1 change: 0 additions & 1 deletion packages/components/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@
inbox: {
appendTo: "#test",
onEvent: (params) => {
console.log("params", params);
if (
params.event === "read" &&
window.courier.inbox.pinned.find(
Expand Down
9 changes: 7 additions & 2 deletions packages/react-hooks/src/__tests__/use-transport.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { renderHook } from "@testing-library/react-hooks"; // will attempt to auto-detect
import { CourierTransport, Transport } from "@trycourier/transport";
import { CourierTransport } from "@trycourier/transport";
import useTransport from "../use-transport";

jest.mock("@trycourier/transport");

const courierTransportMock = CourierTransport as jest.Mock;
const courierTransport = new CourierTransport({
clientSourceId: "foo",
clientKey: "foobar",
});

const renewSessionMock = courierTransport.renewSession as jest.Mock;
Expand All @@ -17,7 +18,11 @@ describe("useTransport", () => {
});

test("will return the same transport passed in", () => {
const transport = new Transport();
const transport = new CourierTransport({
clientSourceId: "abc123",
clientKey: "foo",
});

const { result } = renderHook(() =>
useTransport({
transport,
Expand Down
28 changes: 24 additions & 4 deletions packages/react-hooks/src/inbox/use-inbox-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ const useInboxActions = (): IInboxActions => {
}
},
markMessageRead: async (messageId: string, fromWS?: boolean) => {
const message = allMessages.find((m) => m.messageId === messageId);
if (message?.read) {
return;
}

dispatch(markMessageRead(messageId));
if (!fromWS) {
await inboxClient.markRead(messageId);
Expand All @@ -198,12 +203,17 @@ const useInboxActions = (): IInboxActions => {
if (onEvent) {
onEvent({
event: "read",
message: allMessages.find((m) => m.messageId === messageId),
message,
messageId,
});
}
},
markMessageUnread: async (messageId, fromWS) => {
const message = allMessages.find((m) => m.messageId === messageId);
if (!message?.read) {
return;
}

dispatch(markMessageUnread(messageId));
if (!fromWS) {
await inboxClient.markUnread(messageId);
Expand All @@ -212,12 +222,17 @@ const useInboxActions = (): IInboxActions => {
if (onEvent) {
onEvent({
messageId,
message: allMessages.find((m) => m.messageId === messageId),
message,
event: "unread",
});
}
},
markMessageOpened: async (messageId, fromWS) => {
const message = allMessages.find((m) => m.messageId === messageId);
if (message?.opened) {
return;
}

dispatch(markMessageOpened(messageId));
if (!fromWS) {
await inboxClient.markOpened(messageId);
Expand All @@ -226,12 +241,17 @@ const useInboxActions = (): IInboxActions => {
if (onEvent) {
onEvent({
messageId,
message: allMessages.find((m) => m.messageId === messageId),
message,
event: "opened",
});
}
},
markMessageArchived: async (messageId, fromWS) => {
const message = allMessages.find((m) => m.messageId === messageId);
if (message?.archived) {
return;
}

dispatch(markMessageArchived(messageId));
if (!fromWS) {
await inboxClient.markArchive(messageId);
Expand All @@ -240,7 +260,7 @@ const useInboxActions = (): IInboxActions => {
if (onEvent) {
onEvent({
messageId,
message: allMessages.find((m) => m.messageId === messageId),
message,
event: "archive",
});
}
Expand Down
76 changes: 51 additions & 25 deletions packages/react-hooks/src/use-transport.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import { useMemo, useRef } from "react";
import {
CourierTransport,
Transport,
ITransportOptions,
} from "@trycourier/transport";
import { useMemo, useRef, useEffect } from "react";
import { CourierTransport, TransportOptions } from "@trycourier/transport";
import jwtDecode from "jwt-decode";
interface DecodedAuth {
scope: string;
tenantId: string;
}

const useTransport = ({
tenantId,
authorization,
clientSourceId,
clientKey,
clientSourceId,
isOnline,
tenantId,
transport,
userSignature,
wsOptions,
}: {
tenantId?: string;
authorization?: string;
clientSourceId?: string;
clientKey?: string;
transport?: CourierTransport | Transport;
clientSourceId?: string;
isOnline?: boolean;
tenantId?: string;
transport?: CourierTransport;
userSignature?: string;
wsOptions?: ITransportOptions["wsOptions"];
}): CourierTransport | Transport | undefined => {
wsOptions?: TransportOptions["wsOptions"];
}): CourierTransport | undefined => {
const transportRef =
useRef<{
authorization: string;
Expand Down Expand Up @@ -71,25 +69,53 @@ const useTransport = ({
return;
}

const newTransport = new CourierTransport({
tenantId,
authorization,
clientSourceId,
clientKey,
userSignature,
wsOptions,
});

// keep track of the transport so we don't reconnect when we don't have to
if (authorization) {
const courierTransport = new CourierTransport({
tenantId,
authorization,
clientSourceId,
wsOptions,
});

// keep track of the transport so we don't reconnect when we don't have to
transportRef.current = {
authorization,
transport: newTransport,
transport: courierTransport,
};

return courierTransport;
}

if (clientKey) {
const courierTransport = new CourierTransport({
tenantId,
clientSourceId,
clientKey,
userSignature,
wsOptions,
});

return courierTransport;
}
return newTransport;
}, [tenantId, authorization, clientKey, transport, userSignature, wsOptions]);

const isConnected = newTransport?.isConnected
? newTransport?.isConnected()
: undefined;
useEffect(() => {
if (!(newTransport instanceof CourierTransport)) {
return;
}

if (!isOnline && isConnected) {
newTransport.closeConnection();
}

if (isOnline && !isConnected) {
newTransport.connect();
}
}, [newTransport, isConnected, isOnline]);

return newTransport;
};

Expand Down
25 changes: 25 additions & 0 deletions packages/react-provider/src/hooks/use-is-online.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useEffect, useState } from "react";

const useIsOnline = (): boolean | undefined => {
if (typeof window === "undefined" || typeof navigator === "undefined") {
return;
}

const [isOnline, setOnlineStatus] = useState(window.navigator.onLine);

useEffect(() => {
const toggleOnlineStatus = () => setOnlineStatus(window.navigator.onLine);

window.addEventListener("online", toggleOnlineStatus);
window.addEventListener("offline", toggleOnlineStatus);

return () => {
window.removeEventListener("online", toggleOnlineStatus);
window.removeEventListener("offline", toggleOnlineStatus);
};
}, [isOnline]);

return isOnline;
};

export { useIsOnline };
9 changes: 6 additions & 3 deletions packages/react-provider/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import useClientSourceId from "./hooks/use-client-source-id";
import deepExtend from "deep-extend";
import { darkVariables, lightVariables } from "./theme";
import { createGlobalStyle } from "styled-components";
import { useIsOnline } from "./hooks/use-is-online";

export * from "./hooks";

Expand Down Expand Up @@ -117,15 +118,17 @@ const CourierProviderInner: React.FunctionComponent<
createReducer<any, Partial<ICourierContext>>(...middleware),
[middleware]
);
const isOnline = useIsOnline();

const transport =
typeof window === "undefined"
? undefined
: useTransport({
tenantId: tenantId,
authorization,
clientSourceId,
clientKey,
clientSourceId,
isOnline,
tenantId: tenantId,
transport: _transport,
userSignature,
wsOptions,
Expand Down Expand Up @@ -224,7 +227,7 @@ const CourierProviderInner: React.FunctionComponent<
try {
parsedLocalStorageState = JSON.parse(localStorageState);
} catch (ex) {
console.log("error", ex);
console.error(ex);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-provider/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CourierTransport, Transport, WSOptions } from "@trycourier/transport";
import { CourierTransport, WSOptions } from "@trycourier/transport";
import { Interceptor } from "@trycourier/core";
import { Brand } from "@trycourier/core";
import { IInboxMessagePreview } from "@trycourier/core";
Expand Down Expand Up @@ -37,7 +37,7 @@ export interface ICourierProviderProps {
onRouteChange?: (route: string) => void;
tenantId?: string;
theme?: ProviderTheme;
transport?: CourierTransport | Transport;
transport?: CourierTransport;
userId?: string;
userSignature?: string;
wsOptions?: WSOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ const MessagesExample: React.FunctionComponent<{
(messageId: string) => async (event: React.MouseEvent) => {
event.preventDefault();
const result = await inboxApi.getMessage(messageId);
console.log("message", message);
if (!result?.message) {
return;
}
Expand Down
1 change: 0 additions & 1 deletion packages/storybook/stories/inbox/with-toast.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ export function AsyncUserId() {

export function OnMessage() {
const handleOnMessage = (message) => {
console.log(message);
return message;
};

Expand Down
29 changes: 17 additions & 12 deletions packages/transport/src/courier/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
import { CourierWS } from "../ws";
import { Transport } from "../base";
import { Interceptor } from "@trycourier/core";
import { ITransportOptions } from "./types";
import { TransportOptions, IClientKeyOptions, IJWTOptions } from "./types";

export class CourierTransport extends Transport {
protected tenantId?: string;
protected authorization?: string;
protected clientSourceId: string;
protected clientSourceId?: string;
protected clientKey?: string;
protected declare interceptor?: Interceptor;
protected userSignature?: string;
protected ws: CourierWS;

constructor(options: ITransportOptions) {
constructor(options: TransportOptions) {
super();

if (!options.clientKey && !options.authorization) {
const clientKeyOptions = options as IClientKeyOptions;
const jwtOptions = options as IJWTOptions;

if (!clientKeyOptions.clientKey && !jwtOptions.authorization) {
throw new Error("Missing Authorization");
}

this.authorization = options.authorization;
this.authorization = jwtOptions.authorization;
this.clientSourceId = options.clientSourceId;
this.clientKey = options.clientKey;
this.userSignature = options.userSignature;
this.clientKey = clientKeyOptions.clientKey;
this.userSignature = clientKeyOptions.userSignature;

this.ws = new CourierWS({
tenantId: options.tenantId,
authorization: options.authorization,
authorization: jwtOptions.authorization,
clientSourceId: options.clientSourceId,
clientKey: options.clientKey,
clientKey: clientKeyOptions.clientKey,
options: options.wsOptions,
userSignature: options.userSignature,
userSignature: clientKeyOptions.userSignature,
});

this.ws.connect();

if (options.wsOptions?.onReconnect) {
this.ws.onReconnection({
id: "propReconnect",
Expand All @@ -51,6 +52,10 @@ export class CourierTransport extends Transport {
this.ws.connect();
}

isConnected(): boolean {
return this.ws.connected;
}

keepAlive(): void {
this.ws.send({
action: "keepAlive",
Expand Down
Loading

0 comments on commit f129c74

Please sign in to comment.