From a784f4c977ab0e3972068af2ad5bc7889c8d785b Mon Sep 17 00:00:00 2001 From: Riley Love Date: Tue, 30 Apr 2024 18:17:28 +0200 Subject: [PATCH 1/2] feat(add params to markallread): add params to mark all read, supports tags and archived --- .../src/inbox/__tests__/mark-all-read.spec.ts | 2 +- .../client-graphql/src/inbox/mark-all-read.ts | 13 +++-- packages/components/README.md | 2 - packages/components/src/index.tsx | 2 +- .../src/inbox/__tests__/reducer.spec.ts | 3 -- .../src/inbox/actions/mark-all-read.ts | 13 +++-- .../src/inbox/actions/reset-last-fetched.ts | 9 ---- packages/react-hooks/src/inbox/middleware.ts | 19 +++++-- packages/react-hooks/src/inbox/reducer.ts | 53 +++++++++++++------ packages/react-hooks/src/inbox/types.ts | 3 +- .../src/inbox/use-inbox-actions.ts | 21 ++++---- packages/react-hooks/src/inbox/use-inbox.ts | 2 +- .../src/components/Messages2.0/Header.tsx | 3 +- .../Messages2.0/actions/MarkAllRead.tsx | 5 +- .../src/hooks/use-local-storage-messages.ts | 10 +--- 15 files changed, 91 insertions(+), 69 deletions(-) delete mode 100644 packages/react-hooks/src/inbox/actions/reset-last-fetched.ts diff --git a/packages/client-graphql/src/inbox/__tests__/mark-all-read.spec.ts b/packages/client-graphql/src/inbox/__tests__/mark-all-read.spec.ts index 8004617a..7eacf35d 100644 --- a/packages/client-graphql/src/inbox/__tests__/mark-all-read.spec.ts +++ b/packages/client-graphql/src/inbox/__tests__/mark-all-read.spec.ts @@ -21,7 +21,7 @@ describe("trackEvent", () => { Array [ "https://inbox.courier.com/q", Object { - "body": "{\\"query\\":\\"mutation TrackEvent {\\\\n markAllRead\\\\n}\\\\n\\",\\"operationName\\":\\"TrackEvent\\",\\"variables\\":{}}", + "body": "{\\"query\\":\\"mutation TrackEvent($params: MarkAllAsReadParamsInput) {\\\\n markAllRead(params: $params)\\\\n}\\\\n\\",\\"operationName\\":\\"TrackEvent\\",\\"variables\\":{\\"params\\":{}}}", "headers": Object { "content-type": "application/json", "x-courier-client-key": "CLIENT_KEY", diff --git a/packages/client-graphql/src/inbox/mark-all-read.ts b/packages/client-graphql/src/inbox/mark-all-read.ts index cede6b87..71c42cd8 100644 --- a/packages/client-graphql/src/inbox/mark-all-read.ts +++ b/packages/client-graphql/src/inbox/mark-all-read.ts @@ -1,12 +1,13 @@ import { Client } from "urql"; +import { IGetInboxMessagesParams } from "./messages"; export const MARK_ALL_READ = ` - mutation TrackEvent { - markAllRead + mutation TrackEvent($params: MarkAllAsReadParamsInput) { + markAllRead(params: $params) } `; -export type MarkAllRead = () => Promise< +export type MarkAllRead = (params?: IGetInboxMessagesParams) => Promise< | { markAllRead: boolean; } @@ -15,12 +16,14 @@ export type MarkAllRead = () => Promise< export const markAllRead = (client?: Client): MarkAllRead => - async () => { + async (params: IGetInboxMessagesParams = {}) => { if (!client) { return Promise.resolve(undefined); } - const results = await client.mutation(MARK_ALL_READ).toPromise(); + const results = await client + .mutation(MARK_ALL_READ, { params }) + .toPromise(); const markAllRead = results?.data?.markAllRead; return { diff --git a/packages/components/README.md b/packages/components/README.md index f1f99f84..66caa792 100644 --- a/packages/components/README.md +++ b/packages/components/README.md @@ -209,8 +209,6 @@ interface IInboxState = { from?: number; isLoading?: boolean; isOpen?: boolean; - lastMarkedAllRead?: number; - lastMessagesFetched?: number; messages?: Array; pinned?: Array; startCursor?: string; diff --git a/packages/components/src/index.tsx b/packages/components/src/index.tsx index 6aaedc5d..6b642829 100644 --- a/packages/components/src/index.tsx +++ b/packages/components/src/index.tsx @@ -48,7 +48,7 @@ declare global { setConfig?: (config: InboxProps) => void; config?: any; }; - preferences?: UsePreferences | {}; + preferences?: UsePreferences | unknown; transport?: any; brand?: Brand; renewSession?: (token: string) => void; diff --git a/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts b/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts index e0f144fe..787b40b1 100644 --- a/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts +++ b/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts @@ -185,7 +185,6 @@ describe("inbox reducer", () => { expect(state).toEqual({ ...initialState, isLoading: false, - lastMessagesFetched: mockDate, messages: [mockGraphMessage], pinned: [], searchParams: { @@ -216,7 +215,6 @@ describe("inbox reducer", () => { expect(state).toEqual({ ...initialState, - lastMessagesFetched: mockDate, messages: [mockGraphMessage, mockGraphMessage2], isLoading: false, searchParams: { @@ -345,7 +343,6 @@ describe("inbox reducer", () => { expect(state).toEqual({ ...initialState, unreadMessageCount: 0, - lastMarkedAllRead: mockDate, messages: [ { ...mockGraphMessage, diff --git a/packages/react-hooks/src/inbox/actions/mark-all-read.ts b/packages/react-hooks/src/inbox/actions/mark-all-read.ts index f13191f9..73cdc6b2 100644 --- a/packages/react-hooks/src/inbox/actions/mark-all-read.ts +++ b/packages/react-hooks/src/inbox/actions/mark-all-read.ts @@ -1,5 +1,8 @@ +import { IGetInboxMessagesParams } from "@trycourier/client-graphql"; + export type MarkAllRead = { type: "inbox/MARK_ALL_READ"; + payload?: IGetInboxMessagesParams; }; export type MarkAllReadPending = { @@ -12,9 +15,10 @@ export type MarkAllReadError = { export type MarkAllReadDone = { type: "inbox/MARK_ALL_READ/DONE"; - payload: { + payload?: { ids: string[]; }; + meta?: IGetInboxMessagesParams; }; export const INBOX_MARK_ALL_READ = "inbox/MARK_ALL_READ"; @@ -22,8 +26,11 @@ export const INBOX_MARK_ALL_READ_PENDING = "inbox/MARK_ALL_READ/PENDING"; export const INBOX_MARK_ALL_READ_DONE = "inbox/MARK_ALL_READ/DONE"; export const INBOX_MARK_ALL_READ_ERROR = "inbox/MARK_ALL_READ/ERROR"; -export const markAllRead = (): MarkAllRead => ({ +export const markAllRead = ( + payload?: IGetInboxMessagesParams +): MarkAllRead => ({ type: INBOX_MARK_ALL_READ, + payload, }); export const markAllReadPending = (): MarkAllReadPending => ({ @@ -39,7 +46,7 @@ interface MarkAllReadDonePayload { } export const markAllReadDone = ( - payload: MarkAllReadDonePayload + payload?: MarkAllReadDonePayload ): MarkAllReadDone => ({ type: INBOX_MARK_ALL_READ_DONE, payload, diff --git a/packages/react-hooks/src/inbox/actions/reset-last-fetched.ts b/packages/react-hooks/src/inbox/actions/reset-last-fetched.ts deleted file mode 100644 index e7896e7d..00000000 --- a/packages/react-hooks/src/inbox/actions/reset-last-fetched.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type ResetLastFetched = { - type: "inbox/RESET_LAST_FETCHED"; -}; - -export const INBOX_RESET_LAST_FETCHED = "inbox/RESET_LAST_FETCHED"; - -export const resetLastFetched = (): ResetLastFetched => ({ - type: INBOX_RESET_LAST_FETCHED, -}); diff --git a/packages/react-hooks/src/inbox/middleware.ts b/packages/react-hooks/src/inbox/middleware.ts index 8c3493fb..3c94a8e6 100644 --- a/packages/react-hooks/src/inbox/middleware.ts +++ b/packages/react-hooks/src/inbox/middleware.ts @@ -1,4 +1,3 @@ -import { resetLastFetched } from "./actions/reset-last-fetched"; import { fetchUnreadMessageCount, fetchUnreadMessageCountDone, @@ -6,13 +5,27 @@ import { INBOX_FETCH_UNREAD_MESSAGE_COUNT, } from "./actions/fetch-unread-message-count"; +import { fetchMessages } from "./actions/fetch-messages"; +import { INBOX_MARK_ALL_READ_DONE } from "./actions/mark-all-read"; + export default (api) => (store) => (next) => async (action) => { const state = store.getState(); switch (action.type) { - //case "root/PAGE_VISIBLE": + case INBOX_MARK_ALL_READ_DONE: { + if (action.meta) { + const state = store.getState(); + + console.log("state", state); + store.dispatch(fetchMessages()); + store.dispatch(fetchUnreadMessageCount()); + } + + next(action); + break; + } + case "root/WS_RECONNECTED": { - store.dispatch(resetLastFetched()); store.dispatch(fetchUnreadMessageCount()); break; } diff --git a/packages/react-hooks/src/inbox/reducer.ts b/packages/react-hooks/src/inbox/reducer.ts index 571a2cd5..9125f638 100644 --- a/packages/react-hooks/src/inbox/reducer.ts +++ b/packages/react-hooks/src/inbox/reducer.ts @@ -4,14 +4,17 @@ import { IInbox } from "./types"; import { InboxInit, INBOX_INIT } from "./actions/init"; import { InboxSetView, INBOX_SET_VIEW } from "./actions/set-view"; import { ToggleInbox, INBOX_TOGGLE } from "./actions/toggle-inbox"; -import { MarkAllRead, INBOX_MARK_ALL_READ } from "./actions/mark-all-read"; +import { + INBOX_MARK_ALL_READ_DONE, + MarkAllReadDone, + MarkAllReadPending, + MarkAllReadError, + INBOX_MARK_ALL_READ_PENDING, + INBOX_MARK_ALL_READ_ERROR, +} from "./actions/mark-all-read"; import { NewMessage, INBOX_NEW_MESSAGE } from "./actions/new-message"; import { UnpinMessage, INBOX_UNPIN_MESSAGE } from "./actions/unpin-message"; -import { - ResetLastFetched, - INBOX_RESET_LAST_FETCHED, -} from "./actions/reset-last-fetched"; import { AddTag, INBOX_ADD_TAG } from "./actions/add-tag"; import { RemoveTag, INBOX_REMOVE_TAG } from "./actions/remove-tag"; @@ -59,14 +62,15 @@ type InboxAction = | FetchUnreadMessageCountDone | InboxInit | InboxSetView - | MarkAllRead + | MarkAllReadPending + | MarkAllReadError + | MarkAllReadDone | MarkMessageArchived | MarkMessageOpened | MarkMessageRead | MarkMessageUnread | NewMessage | RemoveTag - | ResetLastFetched | ToggleInbox | UnpinMessage; @@ -156,13 +160,6 @@ export default (state: IInbox = initialState, action?: InboxAction): IInbox => { }; } - case INBOX_RESET_LAST_FETCHED: { - return { - ...state, - lastMessagesFetched: undefined, - }; - } - case INBOX_FETCH_MESSAGES_DONE: { const newMessages = action.payload?.appendMessages ? [...(state?.messages ?? []), ...(action.payload?.messages ?? [])] @@ -172,7 +169,6 @@ export default (state: IInbox = initialState, action?: InboxAction): IInbox => { ...state, isLoading: false, searchParams: action.meta.searchParams, - lastMessagesFetched: new Date().getTime(), messages: newMessages as IInboxMessagePreview[], pinned: action.payload?.appendMessages ? state.pinned @@ -386,7 +382,16 @@ export default (state: IInbox = initialState, action?: InboxAction): IInbox => { }; } - case INBOX_MARK_ALL_READ: { + case INBOX_MARK_ALL_READ_DONE: { + const markAllAsReadDone = action as MarkAllReadDone; + if (markAllAsReadDone.meta) { + // we had params so we rely on the middleware to refetch count + return { + ...state, + markingAllAsRead: false, + }; + } + const unreadMessageCount = 0; const handleMarkRead = (message) => { @@ -401,10 +406,24 @@ export default (state: IInbox = initialState, action?: InboxAction): IInbox => { return { ...state, - lastMarkedAllRead: new Date().getTime(), messages: newMessages, pinned: newPinned, unreadMessageCount, + markingAllAsRead: false, + }; + } + + case INBOX_MARK_ALL_READ_ERROR: { + return { + ...state, + markingAllAsRead: false, + }; + } + + case INBOX_MARK_ALL_READ_PENDING: { + return { + ...state, + markingAllAsRead: true, }; } } diff --git a/packages/react-hooks/src/inbox/types.ts b/packages/react-hooks/src/inbox/types.ts index 50f492fc..57e90368 100644 --- a/packages/react-hooks/src/inbox/types.ts +++ b/packages/react-hooks/src/inbox/types.ts @@ -7,8 +7,7 @@ export interface IInbox { from?: number; isLoading?: boolean; isOpen?: boolean; - lastMarkedAllRead?: number; - lastMessagesFetched?: number; + markingAllAsRead?: boolean; messages?: Array; onEvent?: OnEvent; searchParams?: IGetInboxMessagesParams; diff --git a/packages/react-hooks/src/inbox/use-inbox-actions.ts b/packages/react-hooks/src/inbox/use-inbox-actions.ts index 33df7365..774541cf 100644 --- a/packages/react-hooks/src/inbox/use-inbox-actions.ts +++ b/packages/react-hooks/src/inbox/use-inbox-actions.ts @@ -9,11 +9,10 @@ import { IInbox } from "./types"; import { IGetInboxMessagesParams, Inbox } from "@trycourier/client-graphql"; import { initInbox } from "./actions/init"; -import { markAllRead } from "./actions/mark-all-read"; +import { markAllReadDone } from "./actions/mark-all-read"; import { markMessageArchived } from "./actions/mark-message-archived"; import { markMessageRead } from "./actions/mark-message-read"; import { markMessageUnread } from "./actions/mark-message-unread"; -import { resetLastFetched } from "./actions/reset-last-fetched"; import { setView } from "./actions/set-view"; import { toggleInbox } from "./actions/toggle-inbox"; import { unpinMessage } from "./actions/unpin-message"; @@ -35,13 +34,12 @@ export interface IInboxActions { fetchMessages: (params?: IFetchMessagesParams) => void; getUnreadMessageCount: (params?: IGetInboxMessagesParams) => void; init: (inbox?: IInbox) => void; - markAllAsRead: (fromWS?: boolean) => void; + markAllAsRead: (params?: IGetInboxMessagesParams, fromWS?: boolean) => void; markMessageArchived: (messageId: string, fromWS?: boolean) => Promise; markMessageOpened: (messageId: string, fromWS?: boolean) => Promise; markMessageRead: (messageId: string, fromWS?: boolean) => Promise; markMessageUnread: (messageId: string, fromWS?: boolean) => Promise; newMessage: (transportMessage: IInboxMessagePreview) => void; - resetLastFetched: () => void; setView: (view: string | "preferences") => void; toggleInbox: (isOpen?: boolean) => void; unpinMessage: (messageId: string, fromWS?: boolean) => Promise; @@ -135,9 +133,6 @@ const useInboxActions = (): IInboxActions => { return { init: handleInit, - resetLastFetched: () => { - dispatch(resetLastFetched()); - }, toggleInbox: (isOpen?: boolean) => { dispatch(toggleInbox(isOpen)); }, @@ -166,15 +161,21 @@ const useInboxActions = (): IInboxActions => { }); }, getUnreadMessageCount: handleGetUnreadMessageCount, - markAllAsRead: async (fromWS) => { - dispatch(markAllRead()); + markAllAsRead: async (params, fromWS) => { if (!fromWS) { - await inboxClient.markAllRead(); + dispatch({ + meta: params, + payload: () => inboxClient.markAllRead(params), + type: "inbox/MARK_ALL_READ", + }); + } else { + dispatch(markAllReadDone()); } if (onEvent) { onEvent({ event: "mark-all-read", + data: params as Record, }); } }, diff --git a/packages/react-hooks/src/inbox/use-inbox.ts b/packages/react-hooks/src/inbox/use-inbox.ts index 88b99130..1c2d97bd 100644 --- a/packages/react-hooks/src/inbox/use-inbox.ts +++ b/packages/react-hooks/src/inbox/use-inbox.ts @@ -51,7 +51,7 @@ const useInbox = (): IInbox & IInboxActions => { } if (data.event === "mark-all-read") { - actions.markAllAsRead(true); + actions.markAllAsRead({}, true); } if (!data?.messageId) { diff --git a/packages/react-inbox/src/components/Messages2.0/Header.tsx b/packages/react-inbox/src/components/Messages2.0/Header.tsx index 2935823e..00f963d6 100644 --- a/packages/react-inbox/src/components/Messages2.0/Header.tsx +++ b/packages/react-inbox/src/components/Messages2.0/Header.tsx @@ -213,7 +213,7 @@ const Header: React.FunctionComponent = ({ const [showDropdown, setShowDropdown] = useState(false); const { brand } = useCourier(); - const { view, setView, toggleInbox } = useInbox(); + const { view, setView, toggleInbox, markingAllAsRead } = useInbox(); const handleSetView = (newView: string | "preferences") => (event: React.MouseEvent) => { event.preventDefault(); @@ -328,6 +328,7 @@ const Header: React.FunctionComponent = ({
{messages.length > 0 && unreadMessageCount ? ( markAllAsRead()} /> diff --git a/packages/react-inbox/src/components/Messages2.0/actions/MarkAllRead.tsx b/packages/react-inbox/src/components/Messages2.0/actions/MarkAllRead.tsx index a1f7dd9c..61e1e62d 100644 --- a/packages/react-inbox/src/components/Messages2.0/actions/MarkAllRead.tsx +++ b/packages/react-inbox/src/components/Messages2.0/actions/MarkAllRead.tsx @@ -19,13 +19,14 @@ const StyledButton = styled.button(({ theme }) => { }); const MarkAllRead: React.FunctionComponent<{ + disabled?: boolean; onClick: React.MouseEventHandler; label?: string; -}> = ({ onClick, label }) => { +}> = ({ onClick, disabled, label }) => { const title = label ?? "Mark All as Read"; return ( - + { const { localStorage } = useCourier(); - const { - lastMessagesFetched, - messages, - pinned, - startCursor, - unreadMessageCount, - } = useInbox(); + const { messages, pinned, startCursor, unreadMessageCount } = useInbox(); useEffect(() => { if (!localStorage) { @@ -56,7 +50,6 @@ const useLocalStorageMessages = ({ localStorage.setItem( localStorageKey, JSON.stringify({ - lastMessagesFetched, messages, pinned, startCursor, @@ -64,7 +57,6 @@ const useLocalStorageMessages = ({ }) ); }, [ - lastMessagesFetched, localStorage, localStorageKey, messages, From 48ca57c7575bf3d848700196522cf59cd6de5936 Mon Sep 17 00:00:00 2001 From: Riley Love Date: Fri, 31 May 2024 15:22:15 -0700 Subject: [PATCH 2/2] feat(mark all as read by tags): mark all as read by tags --- .../client-graphql/src/inbox/mark-all-read.ts | 9 ++++++--- packages/components/src/index.tsx | 2 +- .../src/inbox/__tests__/reducer.spec.ts | 5 +++-- packages/react-hooks/src/inbox/middleware.ts | 5 ----- packages/react-hooks/src/inbox/reducer.ts | 16 ++++++++++++++++ 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/client-graphql/src/inbox/mark-all-read.ts b/packages/client-graphql/src/inbox/mark-all-read.ts index 71c42cd8..5897dcb3 100644 --- a/packages/client-graphql/src/inbox/mark-all-read.ts +++ b/packages/client-graphql/src/inbox/mark-all-read.ts @@ -1,5 +1,4 @@ import { Client } from "urql"; -import { IGetInboxMessagesParams } from "./messages"; export const MARK_ALL_READ = ` mutation TrackEvent($params: MarkAllAsReadParamsInput) { @@ -7,7 +6,11 @@ export const MARK_ALL_READ = ` } `; -export type MarkAllRead = (params?: IGetInboxMessagesParams) => Promise< +export interface IMarkAllAsReadParams { + tags?: string[]; +} + +export type MarkAllRead = (params?: IMarkAllAsReadParams) => Promise< | { markAllRead: boolean; } @@ -16,7 +19,7 @@ export type MarkAllRead = (params?: IGetInboxMessagesParams) => Promise< export const markAllRead = (client?: Client): MarkAllRead => - async (params: IGetInboxMessagesParams = {}) => { + async (params: IMarkAllAsReadParams = {}) => { if (!client) { return Promise.resolve(undefined); } diff --git a/packages/components/src/index.tsx b/packages/components/src/index.tsx index 6b642829..6aaedc5d 100644 --- a/packages/components/src/index.tsx +++ b/packages/components/src/index.tsx @@ -48,7 +48,7 @@ declare global { setConfig?: (config: InboxProps) => void; config?: any; }; - preferences?: UsePreferences | unknown; + preferences?: UsePreferences | {}; transport?: any; brand?: Brand; renewSession?: (token: string) => void; diff --git a/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts b/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts index 787b40b1..76566dd1 100644 --- a/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts +++ b/packages/react-hooks/src/inbox/__tests__/reducer.spec.ts @@ -26,7 +26,7 @@ import { import { INBOX_NEW_MESSAGE, newMessage } from "../actions/new-message"; -import { INBOX_MARK_ALL_READ, markAllRead } from "../actions/mark-all-read"; +import { INBOX_MARK_ALL_READ, markAllReadDone } from "../actions/mark-all-read"; import { INBOX_MARK_MESSAGE_OPENED, @@ -337,12 +337,13 @@ describe("inbox reducer", () => { unreadMessageCount: 2, messages: [mockGraphMessage, mockGraphMessage], }, - markAllRead() + markAllReadDone() ); expect(state).toEqual({ ...initialState, unreadMessageCount: 0, + markingAllAsRead: false, messages: [ { ...mockGraphMessage, diff --git a/packages/react-hooks/src/inbox/middleware.ts b/packages/react-hooks/src/inbox/middleware.ts index 3c94a8e6..09e94d82 100644 --- a/packages/react-hooks/src/inbox/middleware.ts +++ b/packages/react-hooks/src/inbox/middleware.ts @@ -5,7 +5,6 @@ import { INBOX_FETCH_UNREAD_MESSAGE_COUNT, } from "./actions/fetch-unread-message-count"; -import { fetchMessages } from "./actions/fetch-messages"; import { INBOX_MARK_ALL_READ_DONE } from "./actions/mark-all-read"; export default (api) => (store) => (next) => async (action) => { @@ -14,10 +13,6 @@ export default (api) => (store) => (next) => async (action) => { switch (action.type) { case INBOX_MARK_ALL_READ_DONE: { if (action.meta) { - const state = store.getState(); - - console.log("state", state); - store.dispatch(fetchMessages()); store.dispatch(fetchUnreadMessageCount()); } diff --git a/packages/react-hooks/src/inbox/reducer.ts b/packages/react-hooks/src/inbox/reducer.ts index 9125f638..360b0565 100644 --- a/packages/react-hooks/src/inbox/reducer.ts +++ b/packages/react-hooks/src/inbox/reducer.ts @@ -385,9 +385,25 @@ export default (state: IInbox = initialState, action?: InboxAction): IInbox => { case INBOX_MARK_ALL_READ_DONE: { const markAllAsReadDone = action as MarkAllReadDone; if (markAllAsReadDone.meta) { + const handleMarkRead = (message) => { + const tags = markAllAsReadDone?.meta?.tags; + const hasTags = message?.tags?.some((t) => tags?.includes(t)); + + if (hasTags) { + message.read = new Date().toISOString(); + } + + return message; + }; + + const newPinned = state.pinned?.map(handleMarkRead); + const newMessages = state.messages?.map(handleMarkRead); + // we had params so we rely on the middleware to refetch count return { ...state, + messages: newMessages, + pinned: newPinned, markingAllAsRead: false, }; }