Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(better reconnect): check online status and reconnect when we com… #577

Merged
merged 1 commit into from
May 28, 2024
Merged
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
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();
rileylnapier marked this conversation as resolved.
Show resolved Hide resolved

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
Loading