Skip to content

Commit

Permalink
feat(native-app): Notifications final improvements (#15240)
Browse files Browse the repository at this point in the history
* feat: buttons at top should not be sticky

* feat: add empty state for notifications

* fix: make sure to update unreadCount in inbox when fetching next page

* feat: add fallback icon for notifications

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
2 people authored and lodmfjord committed Jun 18, 2024
1 parent 56f72cc commit 3b25846
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 55 deletions.
3 changes: 3 additions & 0 deletions apps/native/app/src/messages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ export const en: TranslatedMessages = {
'notifications.markAllAsRead': 'Mark all as read',
'notifications.settings': 'My settings',
'notifications.errorUnknown': 'Error occurred while loading notifications',
'notifications.emptyListTitle': 'No notifications',
'notifications.emptyListDescription':
'When you receive notifications, they will appear here.',

// profile
'profile.screenTitle': 'More',
Expand Down
3 changes: 3 additions & 0 deletions apps/native/app/src/messages/is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ export const is = {
'notifications.markAllAsRead': 'Merkja allt lesið',
'notifications.settings': 'Mínar stillingar',
'notifications.errorUnknown': 'Villa kom upp við að sækja tilkynningar',
'notifications.emptyListTitle': 'Engar tilkynningar',
'notifications.emptyListDescription':
'Þegar þú færð sendar tilkynningar þá birtast þær hér.',

// applications screen
'applications.title': 'Umsóknir',
Expand Down
1 change: 1 addition & 0 deletions apps/native/app/src/screens/inbox/inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ function useInboxQuery(incomingFilters?: Filters) {
...(data.documentsV2?.data ?? []),
],
totalCount: data.documentsV2?.totalCount ?? 0,
unreadCount: data.documentsV2?.unreadCount ?? 0,
}))
} else {
setData(data.documentsV2)
Expand Down
125 changes: 84 additions & 41 deletions apps/native/app/src/screens/notifications/notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ import {
NotificationCard,
Problem,
ListItemSkeleton,
EmptyList,
} from '@ui'
import { useApolloClient } from '@apollo/client'

import { dismissAllNotificationsAsync } from 'expo-notifications'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useIntl } from 'react-intl'
import { ActivityIndicator, FlatList, SafeAreaView } from 'react-native'
import {
ActivityIndicator,
FlatList,
SafeAreaView,
View,
Image,
} from 'react-native'
import {
Navigation,
NavigationFunctionComponent,
Expand All @@ -37,6 +44,7 @@ import { isAndroid } from '../../utils/devices'
import { testIDs } from '../../utils/test-ids'
import settings from '../../assets/icons/settings.png'
import inboxRead from '../../assets/icons/inbox-read.png'
import emptyIllustrationSrc from '../../assets/illustrations/le-company-s3.png'

const LoadingWrapper = styled.View`
padding-vertical: ${({ theme }) => theme.spacing[3]}px;
Expand All @@ -47,10 +55,14 @@ const ButtonWrapper = styled.View`
flex-direction: row;
margin-horizontal: ${({ theme }) => theme.spacing[2]}px;
margin-top: ${({ theme }) => theme.spacing[2]}px;
margin-bottom: ${({ theme }) => theme.spacing[2]}px;
`

const DEFAULT_PAGE_SIZE = 50

const FALLBACK_ICON_URL =
'https://images.ctfassets.net/8k0h54kbe6bj/6XhCz5Ss17OVLxpXNVDxAO/d3d6716bdb9ecdc5041e6baf68b92ba6/coat_of_arms.svg'

const { getNavigationOptions, useNavigationOptions } =
createNavigationOptionHooks(() => ({
topBar: {
Expand All @@ -62,7 +74,12 @@ type NotificationItem = NonNullable<
NonNullable<GetUserNotificationsQuery['userNotifications']>['data']
>[0]

type ListItem = SkeletonItem | NotificationItem
export type EmptyItem = {
id: string
__typename: 'Empty'
}

type ListItem = SkeletonItem | NotificationItem | EmptyItem

export const NotificationsScreen: NavigationFunctionComponent = ({
componentId,
Expand Down Expand Up @@ -123,6 +140,10 @@ export const NotificationsScreen: NavigationFunctionComponent = ({
return createSkeletonArr(9)
}

if (data?.userNotifications?.data?.length === 0) {
return [{ id: '0', __typename: 'Empty' }]
}

return data?.userNotifications?.data || []
}, [data, loading])

Expand Down Expand Up @@ -185,18 +206,38 @@ export const NotificationsScreen: NavigationFunctionComponent = ({
return <ListItemSkeleton multilineMessage />
}

if (item.__typename === 'Empty') {
return (
<View style={{ marginTop: 80 }}>
<EmptyList
title={intl.formatMessage({ id: 'notifications.emptyListTitle' })}
description={intl.formatMessage({
id: 'notifications.emptyListDescription',
})}
image={
<Image
source={emptyIllustrationSrc}
style={{ width: 134, height: 176 }}
resizeMode="contain"
/>
}
/>
</View>
)
}

return (
<NotificationCard
key={item.id}
id={item.id}
title={item.message.title}
message={item.message.displayBody}
date={new Date(item.metadata.sent)}
icon={
item.sender?.logoUrl && {
uri: `${item.sender.logoUrl}?w=64&h=64&fit=pad&fm=png`,
}
}
icon={{
uri: `${
item.sender.logoUrl ?? FALLBACK_ICON_URL
}?w=64&h=64&fit=pad&fm=png`,
}}
unread={!item.metadata.read}
onPress={() => onNotificationPress(item)}
testID={testIDs.NOTIFICATION_CARD_BUTTON}
Expand All @@ -219,40 +260,6 @@ export const NotificationsScreen: NavigationFunctionComponent = ({
showLoading={loading && !!data}
/>
<SafeAreaView style={{ flex: 1 }} testID={testIDs.SCREEN_NOTIFICATIONS}>
<ButtonWrapper>
<Button
isOutlined
isUtilityButton
title={intl.formatMessage({
id: 'notifications.markAllAsRead',
defaultMessage: 'Merkja allt lesið',
})}
style={{
marginRight: theme.spacing[2],
maxWidth: 145,
}}
icon={inboxRead}
iconStyle={{ tintColor: theme.color.blue400 }}
onPress={() => markAllUserNotificationsAsRead()}
/>
<Button
isOutlined
isUtilityButton
title={intl.formatMessage({
id: 'notifications.settings',
defaultMessage: 'Mínar stillingar',
})}
onPress={() => navigateTo('/settings', componentId)}
icon={settings}
style={{
maxWidth: 145,
}}
iconStyle={{
tintColor: theme.color.blue400,
resizeMode: 'contain',
}}
/>
</ButtonWrapper>
{showError ? (
<Problem type="error" withContainer />
) : (
Expand All @@ -265,6 +272,42 @@ export const NotificationsScreen: NavigationFunctionComponent = ({
onEndReached={handleEndReached}
scrollEventThrottle={16}
scrollToOverflowEnabled
ListHeaderComponent={
<ButtonWrapper>
<Button
isOutlined
isUtilityButton
title={intl.formatMessage({
id: 'notifications.markAllAsRead',
defaultMessage: 'Merkja allt lesið',
})}
style={{
marginRight: theme.spacing[2],
maxWidth: 145,
}}
icon={inboxRead}
iconStyle={{ tintColor: theme.color.blue400 }}
onPress={() => markAllUserNotificationsAsRead()}
/>
<Button
isOutlined
isUtilityButton
title={intl.formatMessage({
id: 'notifications.settings',
defaultMessage: 'Mínar stillingar',
})}
onPress={() => navigateTo('/settings', componentId)}
icon={settings}
style={{
maxWidth: 145,
}}
iconStyle={{
tintColor: theme.color.blue400,
resizeMode: 'contain',
}}
/>
</ButtonWrapper>
}
ListFooterComponent={
loadingMore && !error ? (
<LoadingWrapper>
Expand Down
20 changes: 6 additions & 14 deletions apps/native/app/src/ui/lib/empty-state/empty-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react'
import { View } from 'react-native'
import styled from 'styled-components/native'

import { font } from '../../utils/font'
import { dynamicColor } from '@ui/utils'
import { Typography } from '../typography/typography'

const Host = styled.View`
display: flex;
Expand All @@ -12,6 +12,7 @@ const Host = styled.View`
justify-content: center;
align-items: center;
padding: 0 53px;
margin-top: ${({ theme }) => theme.spacing[3]}px;
`

const HostWithBorder = styled.View`
Expand All @@ -36,21 +37,12 @@ const ImageWrap = styled.View`
margin-bottom: 50px;
`

const Title = styled.Text`
margin-bottom: ${({ theme }) => theme.spacing[2]}px;
${font({
fontWeight: '600',
})}
const Title = styled(Typography)`
margin-bottom: ${({ theme }) => theme.spacing[1]}px;
text-align: center;
`

const Description = styled.Text`
${font({
fontWeight: '300',
lineHeight: 24,
})}
const Description = styled(Typography)`
text-align: center;
`

Expand All @@ -74,7 +66,7 @@ export function EmptyList({ title, description, image, small }: HeadingProps) {
return (
<Host>
<ImageWrap>{image}</ImageWrap>
<Title>{title}</Title>
<Title variant={'heading3'}>{title}</Title>
<Description>{description}</Description>
</Host>
)
Expand Down

0 comments on commit 3b25846

Please sign in to comment.