Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-edge committed Nov 2, 2024
1 parent c404f6b commit b13b97e
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 26 deletions.
10 changes: 10 additions & 0 deletions src/actions/DeviceSettingsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ export const modifyDeviceNotifInfo = async (deviceNotifStateKey: string, deviceN
})
}

/** Returns 0 if any priority notifications exist, number of incomplete
* notifications otherwise. */
export const getNotifNumber = (): number | undefined => {
const { deviceNotifState } = getDeviceSettings()
const priorityNotifs = Object.values(deviceNotifState).filter(deviceNotifInfo => deviceNotifInfo.isPriority).length
const incompleteNotifs = Object.values(deviceNotifState).filter(deviceNotifInfo => !deviceNotifInfo.isCompleted).length

return priorityNotifs > 0 ? 0 : incompleteNotifs
}

/**
* Track the state of whether the "How did you Discover Edge" modal was shown.
**/
Expand Down
10 changes: 10 additions & 0 deletions src/components/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import { ManageTokensScene as ManageTokensSceneComponent } from './scenes/Manage
import { MigrateWalletCalculateFeeScene as MigrateWalletCalculateFeeSceneComponent } from './scenes/MigrateWalletCalculateFeeScene'
import { MigrateWalletCompletionScene as MigrateWalletCompletionSceneComponent } from './scenes/MigrateWalletCompletionScene'
import { MigrateWalletSelectCryptoScene as MigrateWalletSelectCryptoSceneComponent } from './scenes/MigrateWalletSelectCryptoScene'
import { NotificationCenterScene as NotificationCenterSceneComponent } from './scenes/NotificationCenterScene'
import { NotificationScene as NotificationSceneComponent } from './scenes/NotificationScene'
import { OtpRepairScene as OtpRepairSceneComponent } from './scenes/OtpRepairScene'
import { OtpSettingsScene as OtpSettingsSceneComponent } from './scenes/OtpSettingsScene'
Expand Down Expand Up @@ -195,6 +196,7 @@ const SweepPrivateKeyProcessingScene = ifLoggedIn(SweepPrivateKeyProcessingScene
const MigrateWalletCalculateFeeScene = ifLoggedIn(MigrateWalletCalculateFeeSceneComponent)
const MigrateWalletCompletionScene = ifLoggedIn(MigrateWalletCompletionSceneComponent)
const MigrateWalletSelectCryptoScene = ifLoggedIn(MigrateWalletSelectCryptoSceneComponent)
const NotificationCenterScene = ifLoggedIn(NotificationCenterSceneComponent)
const NotificationScene = ifLoggedIn(NotificationSceneComponent)
const OtpRepairScene = ifLoggedIn(OtpRepairSceneComponent)
const OtpSettingsScene = ifLoggedIn(OtpSettingsSceneComponent)
Expand Down Expand Up @@ -685,6 +687,14 @@ const EdgeAppStack = () => {
}}
/>
<AppStack.Screen name="migrateWalletSelectCrypto" component={MigrateWalletSelectCryptoScene} />
<AppStack.Screen
name="notificationCenter"
component={NotificationCenterScene}
options={{
title: "TODO",
headerRight: () => null
}}
/>
<AppStack.Screen
name="notificationSettings"
component={NotificationScene}
Expand Down
42 changes: 18 additions & 24 deletions src/components/icons/IconBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import * as React from 'react'
import { Platform, StyleProp, View, ViewStyle } from 'react-native'

import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { EdgeText } from '../themed/EdgeText'

const SCALE = 0.9

export interface Props {
children: React.ReactNode
sizeRem: number
onPress: () => void | Promise<void>
/**
* - If undefined, renders only the children, without a badge.
* - If 0, renders a red dot with a white circle inside.
* - All other cases: renders a red dot with a white number inside.
*/
number?: number
testID?: string
}

/**
* Maybe renders a red dot badge on top of the supplied `children,` with a white
* number or dot inside. Visibility of the red dot depends on the `number` prop.
*
* For backwards compatibility, takes a style prop and provides no built-in margins.
*/
export const IconBadge = (props: Props) => {
const { number, children, sizeRem, testID, onPress } = props
const { number, children, sizeRem } = props
const theme = useTheme()
const styles = getStyles(theme)

Expand All @@ -42,25 +39,25 @@ export const IconBadge = (props: Props) => {
)

return (
<EdgeTouchableOpacity accessible={false} style={containerStyle} onPress={onPress} testID={testID}>
<View style={containerStyle}>
{children}
{number == null ? null : (
<View style={styles.badgeContainer}>
{number === 0 ? (
<View style={styles.circle} />
) : (
<EdgeText style={[styles.superscriptLabel, Platform.OS === 'android' ? styles.androidAdjust : null]} disableFontScaling>
<EdgeText style={Platform.OS === 'android' ? styles.textAndroid : styles.textIos} disableFontScaling>
{number}
</EdgeText>
)}
</View>
)}
</EdgeTouchableOpacity>
</View>
)
}

const getStyles = cacheStyles((theme: Theme) => {
const badgeSize = theme.rem(0.75) * SCALE
const badgeSize = theme.rem(0.75)

return {
iconContainer: {
Expand All @@ -71,30 +68,27 @@ const getStyles = cacheStyles((theme: Theme) => {
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
top: 0,
right: 0,
top: -badgeSize / 2,
right: -badgeSize / 2,
height: badgeSize,
minWidth: badgeSize,
borderRadius: badgeSize / 2,
paddingLeft: theme.rem(0.25) / 2,
paddingRight: theme.rem(0.25) / 2,
backgroundColor: 'red'
},
label: {
textAlign: 'center',
marginBottom: theme.rem(0.5)
},
superscriptLabel: {
// TODO: Adjust platform-specific styles
textIos: {
fontSize: theme.rem(0.5)
},
androidAdjust: {
textAndroid: {
fontSize: theme.rem(0.5),
marginTop: 2,
marginLeft: 1
marginLeft: 1,
marginRight: 1
},
circle: {
width: theme.rem(0.25),
height: theme.rem(0.25),
borderRadius: theme.rem(0.125),
width: theme.rem(0.2),
height: theme.rem(0.2),
borderRadius: theme.rem(0.1),
backgroundColor: 'white',
alignSelf: 'center'
}
Expand Down
6 changes: 5 additions & 1 deletion src/components/navigation/SideMenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { DrawerActions, useNavigation } from '@react-navigation/native'
import * as React from 'react'
import { Keyboard } from 'react-native'

import { getNotifNumber } from '../../actions/DeviceSettingsActions'
import { Fontello } from '../../assets/vector/index'
import { useHandler } from '../../hooks/useHandler'
import { triggerHaptic } from '../../util/haptic'
import { IconBadge } from '../icons/IconBadge'
import { useTheme } from '../services/ThemeContext'
import { NavigationButton } from './NavigationButton'

Expand All @@ -20,7 +22,9 @@ export const SideMenuButton = () => {

return (
<NavigationButton paddingRem={[0, 1]} onPress={handlePress}>
<Fontello name="hamburgerButton" size={theme.rem(1)} testID="sideMenuButton" color={theme.icon} />
<IconBadge number={getNotifNumber()} sizeRem={1}>
<Fontello name="hamburgerButton" size={theme.rem(1)} testID="sideMenuButton" color={theme.icon} />
</IconBadge>
</NavigationButton>
)
}
48 changes: 48 additions & 0 deletions src/components/notification/NotificationCenterCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react'
import { View } from 'react-native'
import { cacheStyles } from 'react-native-patina'

import { getThemedIconUri } from '../../util/CdnUris'
import { Theme, useTheme } from '../services/ThemeContext'
import { NotificationCard } from './NotificationCard'

interface Props {
message: string
title: string
type: 'warning' | 'info'
isComplete: boolean
iconUri?: string

onPress: () => void | Promise<void>
}

export const NotificationCenterCard = (props: Props) => {
const theme = useTheme()
const styles = getStyles(theme)

const { title, type, message, isComplete, onPress } = props
const { iconUri = type === 'warning' ? getThemedIconUri(theme, 'notifications/icon-warning') : getThemedIconUri(theme, 'notifications/icon-info') } = props

return (
<View style={styles.container}>
<View style={[styles.dot, isComplete ? styles.noDot : null]} />
<NotificationCard message={message} title={title} type={type} iconUri={iconUri} onPress={onPress} />
</View>
)
}

const getStyles = cacheStyles((theme: Theme) => ({
container: {
flexDirection: 'row',
flexShrink: 1,
flexGrow: 1
},
dot: {
width: theme.rem(0.75),
height: theme.rem(0.75),
backgroundColor: 'red'
},
noDot: {
backgroundColor: 'transparent'
}
}))
5 changes: 5 additions & 0 deletions src/components/scenes/DevTestScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export function DevTestScene(props: Props) {
})
}

const handleNotificationCenterPress = () => {
navigation2.navigate('notificationCenter')
}

const coreWallet = selectedWallet?.wallet
let balance = coreWallet?.balanceMap.get(tokenId) ?? ''
if (eq(balance, '0')) balance = ''
Expand All @@ -158,6 +162,7 @@ export function DevTestScene(props: Props) {
<>
<SectionHeader leftTitle="Scenes" />
<EdgeButton label="AddressFormScene" onPress={handleAddressFormPress} marginRem={0.5} />
<EdgeButton label="NotificationCenterScene" onPress={handleNotificationCenterPress} marginRem={0.5} />
</>
<>
<SectionHeader leftTitle="Modals" rightNode={<EdgeText>Galore</EdgeText>} />
Expand Down
32 changes: 32 additions & 0 deletions src/components/scenes/NotificationCenterScene.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react'

import { getDeviceSettings } from '../../actions/DeviceSettingsActions'
import { getLocalAccountSettings } from '../../actions/LocalSettingsActions'
import { useAsyncEffect } from '../../hooks/useAsyncEffect'
import { lstrings } from '../../locales/strings'
import { useSelector } from '../../types/reactRedux'
import { EdgeAppSceneProps } from '../../types/routerTypes'
import { DeviceNotifInfo } from '../../types/types'
import { SceneWrapper } from '../common/SceneWrapper'
import { SectionHeader } from '../common/SectionHeader'
import { useTheme } from '../services/ThemeContext'

interface Props extends EdgeAppSceneProps<'notificationCenter'> {}

export const NotificationCenterScene = (props: Props) => {
const { navigation } = props
const theme = useTheme()
const { deviceNotifState } = getDeviceSettings()
const accountNotifDismissInfo = getLocalAccountSettings().accountNotifDismissInfo

const pinnedNotifInfos = Object.values(deviceNotifState).filter(deviceNotifInfo => deviceNotifInfo.isPriority)
const otherNotifInfos = Object.values(deviceNotifState).filter(deviceNotifInfo => !deviceNotifInfo.isPriority)

return (
<SceneWrapper>
<SectionHeader leftTitle={lstrings.pinned_notifications} />
<></>
<SectionHeader leftTitle={lstrings.other_notifications} />
</SceneWrapper>
)
}
22 changes: 21 additions & 1 deletion src/components/themed/SideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'
import Share from 'react-native-share'
import Feather from 'react-native-vector-icons/Feather'
import FontAwesome5Icon from 'react-native-vector-icons/FontAwesome5'
import Ionicons from 'react-native-vector-icons/Ionicons'
import MaterialIcon from 'react-native-vector-icons/MaterialIcons'
import { sprintf } from 'sprintf-js'

import { showBackupModal } from '../../actions/BackupModalActions'
import { launchDeepLink } from '../../actions/DeepLinkingActions'
import { getNotifNumber } from '../../actions/DeviceSettingsActions'
import { getRootNavigation, logoutRequest } from '../../actions/LoginActions'
import { executePluginAction } from '../../actions/PluginActions'
import { Fontello } from '../../assets/vector'
Expand All @@ -32,6 +34,7 @@ import { getDisplayUsername } from '../../util/utils'
import { IONIA_SUPPORTED_FIATS } from '../cards/VisaCardCard'
import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity'
import { styled } from '../hoc/styled'
import { IconBadge } from '../icons/IconBadge'
import { ButtonsModal } from '../modals/ButtonsModal'
import { ScanModal } from '../modals/ScanModal'
import { Airship, showError } from '../services/AirshipInstance'
Expand Down Expand Up @@ -216,6 +219,14 @@ export function SideMenu(props: DrawerContentComponentProps) {
iconNameFontAwesome?: string
title: string
}> = [
{
pressHandler: () => {
navigation.navigate('edgeAppStack', { screen: 'notificationCenter' })
navigation.dispatch(DrawerActions.closeDrawer())
},
iconName: 'notifications',
title: lstrings.notifications
},
{
pressHandler: () => {
navigation.navigate('edgeAppStack', { screen: 'fioAddressList' })
Expand Down Expand Up @@ -349,7 +360,16 @@ export function SideMenu(props: DrawerContentComponentProps) {
{rowDatas.map(rowData => (
<EdgeTouchableOpacity accessible={false} onPress={rowData.pressHandler} key={rowData.title} style={styles.rowContainer}>
<View style={styles.leftIconContainer}>
{rowData.iconName != null ? <Fontello name={rowData.iconName} style={styles.icon} size={theme.rem(1.5)} color={theme.iconTappable} /> : null}
{
// TODO: No red dot ever renders for some reason...
rowData.iconName === 'notifications' ? (
<IconBadge number={getNotifNumber()} sizeRem={theme.rem(1.5)}>
<Ionicons name="notifications-outline" style={styles.icon} size={theme.rem(1.5)} color={theme.iconTappable} />
</IconBadge>
) : rowData.iconName != null ? (
<Fontello name={rowData.iconName} style={styles.icon} size={theme.rem(1.5)} color={theme.iconTappable} />
) : null
}
{rowData.iconNameFontAwesome != null ? (
<FontAwesome5Icon name={rowData.iconNameFontAwesome} style={styles.icon} size={theme.rem(1.25)} color={theme.iconTappable} />
) : null}
Expand Down
3 changes: 3 additions & 0 deletions src/locales/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1648,6 +1648,9 @@ const strings = {
split_description: 'This action creates wallets from pre-existing wallets.',
add_custom_token: 'Add Custom Token',
choose_custom_token_wallet: 'Select Wallet for Custom Token',
notifications: 'Notifications',
pinned_notifications: 'Pinned',
other_notifications: 'Other',

// Currency Labels
currency_label_AFN: 'Afghani',
Expand Down
1 change: 1 addition & 0 deletions src/locales/strings/enUS.json
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,7 @@
"split_description": "This action creates wallets from pre-existing wallets.",
"add_custom_token": "Add Custom Token",
"choose_custom_token_wallet": "Select Wallet for Custom Token",
"notifications": "Notifications",
"currency_label_AFN": "Afghani",
"currency_label_ALL": "Lek",
"currency_label_DZD": "Algerian Dinar",
Expand Down
1 change: 1 addition & 0 deletions src/types/routerTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export type EdgeAppStackParamList = {} & {
migrateWalletCompletion: MigrateWalletCompletionParams
migrateWalletSelectCrypto: MigrateWalletSelectCryptoParams
notificationSettings: undefined
notificationCenter: undefined
otpRepair: OtpRepairParams
otpSetup: undefined
passwordRecovery: undefined
Expand Down

0 comments on commit b13b97e

Please sign in to comment.