Skip to content

Commit

Permalink
feat: notifications history
Browse files Browse the repository at this point in the history
  • Loading branch information
sandipndev committed Apr 23, 2024
1 parent 826b229 commit 658f31b
Show file tree
Hide file tree
Showing 12 changed files with 481 additions and 1 deletion.
35 changes: 35 additions & 0 deletions app/graphql/generated.gql
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ fragment TransactionList on TransactionConnection {
__typename
}

mutation StatefulNotificationAcknowledge($input: StatefulNotificationAcknowledgeInput!) {
statefulNotificationAcknowledge(input: $input) {
notification {
acknowledgedAt
__typename
}
__typename
}
}

mutation accountDelete {
accountDelete {
errors {
Expand Down Expand Up @@ -840,6 +850,31 @@ query SettingsScreen {
}
}

query StatefulNotifications($after: String) {
me {
statefulNotifications(first: 10, after: $after) {
nodes {
id
title
body
deepLink
createdAt
acknowledgedAt
__typename
}
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
__typename
}
__typename
}
__typename
}
}

query accountDefaultWallet($walletCurrency: WalletCurrency, $username: Username!) {
accountDefaultWallet(walletCurrency: $walletCurrency, username: $username) {
id
Expand Down
203 changes: 203 additions & 0 deletions app/graphql/generated.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions app/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2886,6 +2886,10 @@ const en: BaseTranslation = {
PROCESSING: "Processing",
REVIEW: "Review",
},
NotificationHistory: {
title: "Notifications",
noNotifications: "You don't have any notifications right now"
}
}

export default en
20 changes: 20 additions & 0 deletions app/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9099,6 +9099,16 @@ type RootTranslation = {
*/
REVIEW: string
}
NotificationHistory: {
/**
* N​o​t​i​f​i​c​a​t​i​o​n​s
*/
title: string
/**
* Y​o​u​ ​d​o​n​'​t​ ​h​a​v​e​ ​a​n​y​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​ ​r​i​g​h​t​ ​n​o​w
*/
noNotifications: string
}
}

export type TranslationFunctions = {
Expand Down Expand Up @@ -18081,6 +18091,16 @@ export type TranslationFunctions = {
*/
REVIEW: () => LocalizedString
}
NotificationHistory: {
/**
* Notifications
*/
title: () => LocalizedString
/**
* You don't have any notifications right now
*/
noNotifications: () => LocalizedString
}
}

export type Formatters = {
Expand Down
4 changes: 4 additions & 0 deletions app/i18n/raw-i18n/source/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2766,5 +2766,9 @@
"ERROR": "Error",
"PROCESSING": "Processing",
"REVIEW": "Review"
},
"NotificationHistory": {
"title": "Notifications",
"noNotifications": "You don't have any notifications right now"
}
}
6 changes: 6 additions & 0 deletions app/navigation/root-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import { LanguageScreen } from "../screens/settings-screen/language-screen"
import { SecurityScreen } from "../screens/settings-screen/security-screen"
import { TransactionDetailScreen } from "../screens/transaction-detail-screen"
import { TransactionHistoryScreen } from "../screens/transaction-history/transaction-history-screen"
import { NotificationHistoryScreen } from "@app/screens/notification-history-screen/notification-history-screen"
import {
PeopleStackParamList,
PhoneValidationStackParamList,
Expand Down Expand Up @@ -417,6 +418,11 @@ export const RootStack = () => {
title: LL.support.chatbot(),
}}
/>
<RootNavigator.Screen
name="notificationHistory"
component={NotificationHistoryScreen}
options={{ title: LL.NotificationHistory.title() }}
/>
</RootNavigator.Navigator>
)
}
Expand Down
1 change: 1 addition & 0 deletions app/navigation/stack-param-lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export type RootStackParamList = {
webView: { url: string; initialTitle?: string }
fullOnboardingFlow: undefined
chatbot: undefined
notificationHistory: undefined
}

export type PeopleStackParamList = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { gql } from "@apollo/client"
import { Screen } from "@app/components/screen"
import { useStatefulNotificationsQuery } from "@app/graphql/generated"
import { useIsAuthed } from "@app/graphql/is-authed-context"
import { useI18nContext } from "@app/i18n/i18n-react"
import { testProps } from "@app/utils/testProps"
import { useIsFocused } from "@react-navigation/native"
import { Text, makeStyles, useTheme } from "@rneui/themed"
import { FlatList, RefreshControl } from "react-native-gesture-handler"
import { Notification } from "./notification"

gql`
query StatefulNotifications($after: String) {
me {
statefulNotifications(first: 10, after: $after) {
nodes {
id
title
body
deepLink
createdAt
acknowledgedAt
}
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
}
`

export const NotificationHistoryScreen = () => {
const styles = useStyles()
const {
theme: { colors },
} = useTheme()
const isFocused = useIsFocused()

const { LL } = useI18nContext()

const { data, fetchMore, refetch, loading } = useStatefulNotificationsQuery({
skip: !useIsAuthed(),
})
const notifications = data?.me?.statefulNotifications

const fetchNextNotificationsPage = () => {
const pageInfo = notifications?.pageInfo

if (pageInfo?.hasNextPage) {
fetchMore({
variables: {
after: pageInfo.endCursor,
},
})
}
}

return (
<Screen>
<FlatList
{...testProps("notification-screen")}
contentContainerStyle={styles.scrollViewContainer}
refreshControl={
<RefreshControl
refreshing={loading && isFocused}
onRefresh={refetch}
colors={[colors.primary]} // Android refresh indicator colors
tintColor={colors.primary} // iOS refresh indicator color
/>
}
data={notifications?.nodes}
renderItem={({ item }) => <Notification {...item} />}
onEndReached={fetchNextNotificationsPage}
onEndReachedThreshold={0.5}
onRefresh={refetch}
refreshing={loading}
ListEmptyComponent={
loading ? <></> : <Text>{LL.NotificationHistory.noNotifications()}</Text>
}
></FlatList>
</Screen>
)
}

const useStyles = makeStyles(() => ({
scrollViewContainer: {},
}))
78 changes: 78 additions & 0 deletions app/screens/notification-history-screen/notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
StatefulNotification,
StatefulNotificationsDocument,
useStatefulNotificationAcknowledgeMutation,
} from "@app/graphql/generated"
import { Text, makeStyles } from "@rneui/themed"
import { View } from "react-native"
import { timeAgo } from "./utils"
import { gql } from "@apollo/client"
import { TouchableWithoutFeedback } from "react-native-gesture-handler"
import { useLinkTo } from "@react-navigation/native"
import { useState } from "react"

gql`
mutation StatefulNotificationAcknowledge(
$input: StatefulNotificationAcknowledgeInput!
) {
statefulNotificationAcknowledge(input: $input) {
notification {
acknowledgedAt
}
}
}
`

export const Notification: React.FC<StatefulNotification> = ({
id,
title,
body,
createdAt,
acknowledgedAt,
deepLink,
}) => {
const [isAcknowledged, setIsAcknowledged] = useState(Boolean(acknowledgedAt))
const styles = useStyles({ isAcknowledged })

const [ack, _] = useStatefulNotificationAcknowledgeMutation({
variables: { input: { notificationId: id } },
refetchQueries: [StatefulNotificationsDocument],
})

const linkTo = useLinkTo()

return (
<TouchableWithoutFeedback
onPress={() => {
setIsAcknowledged(true)
!isAcknowledged && ack()
deepLink && linkTo(deepLink)
}}
>
<View style={styles.container}>
<Text type="p2" style={styles.text}>
{title}
</Text>
<Text type="p3" style={styles.text}>
{body}
</Text>
<Text type="p4" style={styles.text}>
{timeAgo(createdAt)}
</Text>
</View>
</TouchableWithoutFeedback>
)
}

const useStyles = makeStyles(
({ colors }, { isAcknowledged }: { isAcknowledged: boolean }) => ({
container: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: colors.grey5,
},
text: {
color: isAcknowledged ? colors.grey3 : colors.black,
},
}),
)
19 changes: 19 additions & 0 deletions app/screens/notification-history-screen/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const timeAgo = (pastDate: number): string => {
const now = new Date()
const past = new Date(pastDate * 1000)
const diff = Number(now) - Number(past)

const seconds = Math.floor(diff / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
const days = Math.floor(hours / 24)

if (seconds < 60) {
return `a few seconds ago`
} else if (minutes < 60) {
return `${minutes} minute${minutes > 1 ? "s" : ""} ago`
} else if (hours < 24) {
return `${hours} hour${hours > 1 ? "s" : ""} ago`
}
return `${days} day${days > 1 ? "s" : ""} ago`
}
22 changes: 21 additions & 1 deletion app/screens/settings-screen/settings-screen.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { ScrollView } from "react-native-gesture-handler"
import { useEffect } from "react"
import { TouchableOpacity } from "react-native"

import { useNavigation } from "@react-navigation/native"
import { StackNavigationProp } from "@react-navigation/stack"
import { RootStackParamList } from "@app/navigation/stack-param-lists"

import { gql } from "@apollo/client"
import { Screen } from "@app/components/screen"
import { VersionComponent } from "@app/components/version"
import { AccountLevel, useLevel } from "@app/graphql/level-context"
import { useI18nContext } from "@app/i18n/i18n-react"
import { makeStyles } from "@rneui/themed"
import { Icon, makeStyles } from "@rneui/themed"

import { AccountBanner } from "./account/banner"
import { EmailSetting } from "./account/settings/email"
Expand Down Expand Up @@ -79,6 +85,17 @@ export const SettingsScreen: React.FC = () => {
community: [NeedHelpSetting, JoinCommunitySetting],
}

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={() => navigation.navigate("notificationHistory")}>
<Icon style={styles.headerRight} name="notifications" type="ionicon" />
</TouchableOpacity>
),
})
})

return (
<Screen keyboardShouldPersistTaps="handled">
<ScrollView contentContainerStyle={styles.outer}>
Expand Down Expand Up @@ -116,4 +133,7 @@ const useStyles = makeStyles(() => ({
flexDirection: "column",
rowGap: 18,
},
headerRight: {
marginRight: 12,
},
}))
Empty file added logs
Empty file.

0 comments on commit 658f31b

Please sign in to comment.