Skip to content

Commit

Permalink
feat(ONYX-620): Activity List: Implement management action sheet (#9716)
Browse files Browse the repository at this point in the history
* feat: add ActionSheet

* chore: address review comments
  • Loading branch information
dariakoko authored Jan 10, 2024
1 parent 8d68a62 commit f128bff
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 97 deletions.
16 changes: 8 additions & 8 deletions src/app/Scenes/Activity/Activity.tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ describe("Activity", () => {

await flushPromiseQueue()

expect(screen.queryByText("Notification One")).toBeTruthy()
expect(screen.queryByText("Notification Two")).toBeTruthy()
expect(screen.getByText("Notification One")).toBeTruthy()
expect(screen.getByText("Notification Two")).toBeTruthy()
})

it("renders tabs", async () => {
Expand Down Expand Up @@ -75,8 +75,8 @@ describe("Activity", () => {
})
await flushPromiseQueue()

expect(screen.queryByText("Notification One")).toBeTruthy()
expect(screen.queryByText("Notification Two")).toBeTruthy()
expect(screen.getByText("Notification One")).toBeTruthy()
expect(screen.getByText("Notification Two")).toBeTruthy()
})

it("should hide artworks based notifications that don't have artworks", async () => {
Expand All @@ -100,15 +100,15 @@ describe("Activity", () => {
})
await flushPromiseQueue()

expect(screen.queryByText("Notification One")).toBeTruthy()
expect(screen.queryByText("Notification Two")).toBeTruthy()
expect(screen.getByText("Notification One")).toBeTruthy()
expect(screen.getByText("Notification Two")).toBeTruthy()
expect(screen.queryByText("Notification Three")).toBeNull()
})

it("should track event when the tab is tapped", () => {
const { getByText } = renderWithHookWrappersTL(<Activity />, mockEnvironment)
renderWithHookWrappersTL(<Activity />, mockEnvironment)

fireEvent.press(getByText("Alerts"))
fireEvent.press(screen.getByText("Alerts"))

expect(mockTrackEvent.mock.calls[0]).toMatchInlineSnapshot(`
[
Expand Down
44 changes: 40 additions & 4 deletions src/app/Scenes/Activity/Activity.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ActionType, OwnerType } from "@artsy/cohesion"
import { ClickedActivityPanelTab } from "@artsy/cohesion/dist/Schema/Events/ActivityPanel"
import { Tabs } from "@artsy/palette-mobile"
import { MoreIcon, Tabs, Touchable } from "@artsy/palette-mobile"
import { useActionSheet } from "@expo/react-native-action-sheet"
import { ActivityQuery } from "__generated__/ActivityQuery.graphql"

import { useMarkAllNotificationsAsRead } from "app/Scenes/Activity/hooks/useMarkAllNotificationsAsRead"
import { useMarkNotificationsAsSeen } from "app/Scenes/Activity/hooks/useMarkNotificationsAsSeen"
import { goBack } from "app/system/navigation/navigate"
import { goBack, navigate } from "app/system/navigation/navigate"
import { useFeatureFlag } from "app/utils/hooks/useFeatureFlag"
import { ProvideScreenTrackingWithCohesionSchema } from "app/utils/track"
import { screen } from "app/utils/track/helpers"
import { Suspense } from "react"
Expand Down Expand Up @@ -47,7 +49,11 @@ export const ActivityContent: React.FC<ActivityProps> = ({ type }) => {
}

export const Activity = () => {
const enableNavigateToASingleNotification = useFeatureFlag("AREnableSingleActivityPanelScreen")

const tracking = useTracking()
const { showActionSheetWithOptions } = useActionSheet()
const { markAllNotificationsAsRead } = useMarkAllNotificationsAsRead()

const handleTabPress: OnTabChangeCallback = (data) => {
tracking.trackEvent(tracks.clickedActivityPanelTab(data.tabName))
Expand All @@ -60,7 +66,37 @@ export const Activity = () => {
<Tabs.TabsWithHeader
title="Activity"
onTabChange={handleTabPress}
headerProps={{ onBack: goBack }}
headerProps={{
onBack: goBack,
rightElements: enableNavigateToASingleNotification && (
<Touchable
onPress={() => {
showActionSheetWithOptions(
{
options: ["Mark all as read", "Edit Alerts", "Edit Follows", "Cancel"],
cancelButtonIndex: 3,
useModal: true,
},
(buttonIndex) => {
switch (buttonIndex) {
case 0:
markAllNotificationsAsRead()
break
case 1:
navigate("settings/alerts")
break
case 2:
navigate("favorites")
break
}
}
)
}}
>
<MoreIcon fill="black100" accessibilityLabel="Notifications menu" />
</Touchable>
),
}}
>
<Tabs.Tab name="All" label="All">
<Tabs.Lazy>
Expand Down
88 changes: 3 additions & 85 deletions src/app/Scenes/Activity/ActivityMarkAllAsReadSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { Button, Flex, FlexProps, Text } from "@artsy/palette-mobile"
import { captureMessage } from "@sentry/react-native"
import { ActivityMarkAllAsReadSectionMutation } from "__generated__/ActivityMarkAllAsReadSectionMutation.graphql"
import { ConnectionHandler, UseMutationConfig, graphql, useMutation } from "react-relay"
import { notificationTypes } from "./types"
import { getNotificationTypes } from "./utils/getNotificationTypes"
import { useMarkAllNotificationsAsRead } from "app/Scenes/Activity/hooks/useMarkAllNotificationsAsRead"

interface ActivityMarkAllAsReadSectionProps extends FlexProps {
hasUnreadNotifications: boolean
Expand All @@ -14,37 +10,7 @@ export const ActivityMarkAllAsReadSection: React.FC<ActivityMarkAllAsReadSection
...flexProps
}) => {
const label = hasUnreadNotifications ? "New notifications" : "No new notifications"
const [commit, mutationInProgress] =
useMutation<ActivityMarkAllAsReadSectionMutation>(MarkAllAsReadMutation)

const handleMarkAllAsReadPress = () => {
try {
commit({
variables: {},
updater: (store) => {
markAllAsReadMutationUpdater(store)
},
optimisticUpdater: (store) => {
markAllAsReadMutationUpdater(store)
},
onCompleted: (response) => {
const errorMessage =
response.markAllNotificationsAsRead?.responseOrError?.mutationError?.message

if (errorMessage) {
throw new Error(errorMessage)
}
},
})
} catch (e) {
if (__DEV__) {
console.error(e)
} else {
captureMessage(`ActivityMarkAllAsReadSection ${JSON.stringify(e)}`)
}
}
}

const { markAllNotificationsAsRead, mutationInProgress } = useMarkAllNotificationsAsRead()
return (
<Flex
flexDirection="row"
Expand All @@ -60,58 +26,10 @@ export const ActivityMarkAllAsReadSection: React.FC<ActivityMarkAllAsReadSection
disabled={!hasUnreadNotifications || mutationInProgress}
size="small"
variant="outline"
onPress={handleMarkAllAsReadPress}
onPress={markAllNotificationsAsRead}
>
Mark all as read
</Button>
</Flex>
)
}

const MarkAllAsReadMutation = graphql`
mutation ActivityMarkAllAsReadSectionMutation {
markAllNotificationsAsRead(input: {}) {
responseOrError {
... on MarkAllNotificationsAsReadSuccess {
success
}
... on MarkAllNotificationsAsReadFailure {
mutationError {
message
}
}
}
}
}
`

const markAllAsReadMutationUpdater = (
store: Parameters<
NonNullable<UseMutationConfig<ActivityMarkAllAsReadSectionMutation>["updater"]>
>[0]
) => {
const root = store.getRoot()
const me = root.getLinkedRecord("me")
const viewer = root.getLinkedRecord("viewer")

if (!me || !viewer) {
return
}

notificationTypes.forEach((type) => {
const key = "ActivityList_notificationsConnection"
const connection = ConnectionHandler.getConnection(viewer, key, {
notificationTypes: getNotificationTypes(type),
})
const edges = connection?.getLinkedRecords("edges")

// Set unread notifications count to 0
me.setValue(0, "unreadNotificationsCount")

// Mark all notifications as read
edges?.forEach((edge) => {
const node = edge.getLinkedRecord("node")
node?.setValue(false, "isUnread")
})
})
}
87 changes: 87 additions & 0 deletions src/app/Scenes/Activity/hooks/useMarkAllNotificationsAsRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { captureMessage } from "@sentry/react-native"
import { useMarkAllNotificationsAsReadMutation } from "__generated__/useMarkAllNotificationsAsReadMutation.graphql"
import { notificationTypes } from "app/Scenes/Activity/types"
import { getNotificationTypes } from "app/Scenes/Activity/utils/getNotificationTypes"
import { ConnectionHandler, UseMutationConfig, graphql, useMutation } from "react-relay"

export const useMarkAllNotificationsAsRead = () => {
const [commit, mutationInProgress] =
useMutation<useMarkAllNotificationsAsReadMutation>(MarkAllAsReadMutation)

const markAllNotificationsAsRead = () => {
try {
commit({
variables: {},
updater: (store) => {
markAllAsReadMutationUpdater(store)
},
optimisticUpdater: (store) => {
markAllAsReadMutationUpdater(store)
},
onCompleted: (response) => {
const errorMessage =
response.markAllNotificationsAsRead?.responseOrError?.mutationError?.message
if (errorMessage) {
throw new Error(errorMessage)
}
},
})
} catch (e) {
if (__DEV__) {
console.error(e)
} else {
captureMessage(`ActivityMarkAllAsReadSection ${JSON.stringify(e)}`)
}
}
}

return { markAllNotificationsAsRead, mutationInProgress }
}

const markAllAsReadMutationUpdater = (
store: Parameters<
NonNullable<UseMutationConfig<useMarkAllNotificationsAsReadMutation>["updater"]>
>[0]
) => {
const root = store.getRoot()
const me = root.getLinkedRecord("me")
const viewer = root.getLinkedRecord("viewer")

if (!me || !viewer) {
return
}

notificationTypes.forEach((type) => {
const key = "ActivityList_notificationsConnection"
const connection = ConnectionHandler.getConnection(viewer, key, {
notificationTypes: getNotificationTypes(type),
})
const edges = connection?.getLinkedRecords("edges")

// Set unread notifications count to 0
me.setValue(0, "unreadNotificationsCount")

// Mark all notifications as read
edges?.forEach((edge) => {
const node = edge.getLinkedRecord("node")
node?.setValue(false, "isUnread")
})
})
}

const MarkAllAsReadMutation = graphql`
mutation useMarkAllNotificationsAsReadMutation {
markAllNotificationsAsRead(input: {}) {
responseOrError {
... on MarkAllNotificationsAsReadSuccess {
success
}
... on MarkAllNotificationsAsReadFailure {
mutationError {
message
}
}
}
}
}
`

0 comments on commit f128bff

Please sign in to comment.