Skip to content

Commit 0ede90e

Browse files
authored
Merge pull request #5979 from EdgeApp/jon/fix/remove-contacts-modal-first-receive
Fix contacts permission prompt timing
2 parents 1f1a8b1 + 43bea65 commit 0ede90e

6 files changed

Lines changed: 39 additions & 44 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- added: Debug settings scene (Developer Mode only) with nodes/servers inspection, engine `dataDump` viewer, and log viewer
1010
- fixed: Swap quote timeout error interrupting user after cancelling a slow swap search
1111
- fixed: Disable "Migrate Wallets" button when no assets are available to migrate
12+
- fixed: Contacts permission prompt no longer appears on first receive and only shows from transaction-list or payee edit flows
1213

1314
## 4.45.0 (2025-03-10)
1415

eslint.config.mjs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,6 @@ export default [
174174

175175
'src/components/modals/CategoryModal.tsx',
176176

177-
'src/components/modals/ContactListModal.tsx',
178-
'src/components/modals/ContactsPermissionModal.tsx',
179177
'src/components/modals/CountryListModal.tsx',
180178
'src/components/modals/DateModal.tsx',
181179
'src/components/modals/FiatListModal.tsx',
@@ -405,7 +403,7 @@ export default [
405403
'src/controllers/loan-manager/redux/actions.ts',
406404
'src/controllers/loan-manager/util/waitForLoanAccountSync.ts',
407405
'src/hooks/animations/useFadeAnimation.ts',
408-
'src/hooks/redux/useContactThumbnail.ts',
406+
409407
'src/hooks/useAbortable.ts',
410408
'src/hooks/useAccountSyncRatio.tsx',
411409
'src/hooks/useAsyncEffect.ts',

src/components/modals/ContactListModal.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import { lstrings } from '../../locales/strings'
99
import { useDispatch, useSelector } from '../../types/reactRedux'
1010
import type { GuiContact } from '../../types/types'
1111
import { normalizeForSearch } from '../../util/utils'
12-
import { requestContactsPermission } from '../services/PermissionsManager'
1312
import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext'
1413
import { SelectableRow } from '../themed/SelectableRow'
15-
import { maybeShowContactsPermissionModal } from './ContactsPermissionModal'
14+
import { promptForContactsPermission } from './ContactsPermissionModal'
1615
import { ListModal } from './ListModal'
1716

1817
export interface ContactModalResult {
@@ -26,11 +25,11 @@ interface Props {
2625
contactName: string
2726
}
2827

29-
export function ContactListModal({
28+
export const ContactListModal: React.FC<Props> = ({
3029
bridge,
3130
contactType,
3231
contactName
33-
}: Props): React.ReactElement {
32+
}) => {
3433
const theme = useTheme()
3534
const styles = getStyles(theme)
3635
const contacts = useSelector(state => state.contacts)
@@ -96,10 +95,7 @@ export function ContactListModal({
9695

9796
useAsyncEffect(
9897
async () => {
99-
const result = await dispatch(maybeShowContactsPermissionModal())
100-
if (result === 'allow') {
101-
await requestContactsPermission(true)
102-
}
98+
await dispatch(promptForContactsPermission())
10399
},
104100
[],
105101
'ContactListModal'

src/components/modals/ContactsPermissionModal.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { config } from '../../theme/appConfig'
1212
import type { ThunkAction } from '../../types/reduxTypes'
1313
import { ButtonsModal } from '../modals/ButtonsModal'
1414
import { Airship } from '../services/AirshipInstance'
15+
import { requestContactsPermission } from '../services/PermissionsManager'
1516
import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext'
1617
import { EdgeText } from '../themed/EdgeText'
1718

@@ -47,8 +48,9 @@ export function maybeShowContactsPermissionModal(): ThunkAction<
4748

4849
// Bail if we already have permission:
4950
const contactsPermissionOn =
50-
(await check(permissionNames.contacts).catch(_error => 'denied')) ===
51-
'granted'
51+
(await check(permissionNames.contacts).catch(
52+
(_error: unknown) => 'denied'
53+
)) === 'granted'
5254
if (contactsPermissionOn) return
5355

5456
// Show the modal:
@@ -61,11 +63,20 @@ export function maybeShowContactsPermissionModal(): ThunkAction<
6163
}
6264
}
6365

66+
export function promptForContactsPermission(): ThunkAction<Promise<void>> {
67+
return async dispatch => {
68+
const result = await dispatch(maybeShowContactsPermissionModal())
69+
if (result === 'allow') {
70+
await requestContactsPermission(true)
71+
}
72+
}
73+
}
74+
6475
/**
6576
* Shows the modal if it hasn't been shown before, and attempts to set the
6677
* system contacts permission setting
6778
*/
68-
function ContactsPermissionModal(props: Props) {
79+
const ContactsPermissionModal: React.FC<Props> = props => {
6980
const { bridge } = props
7081
const theme = useTheme()
7182
const styles = getStyles(theme)

src/components/scenes/TransactionListScene.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { getWalletName } from '../../util/CurrencyWalletHelpers'
3636
import { calculateSpamThreshold, unixToLocaleDateTime } from '../../util/utils'
3737
import { SceneWrapper } from '../common/SceneWrapper'
3838
import { withWallet } from '../hoc/withWallet'
39+
import { promptForContactsPermission } from '../modals/ContactsPermissionModal'
3940
import { HeaderTitle } from '../navigation/HeaderTitle'
4041
import { cacheStyles, useTheme } from '../services/ThemeContext'
4142
import { ExplorerCard } from '../themed/ExplorerCard'
@@ -131,6 +132,13 @@ const TransactionListComponent: React.FC<Props> = props => {
131132
return out
132133
}, [atEnd, isTransactionListUnsupported, transactions])
133134

135+
const hasNamedTransactions = React.useMemo(() => {
136+
return transactions.some(transaction => {
137+
const metadataName = transaction.metadata?.name
138+
return metadataName != null && metadataName.trim() !== ''
139+
})
140+
}, [transactions])
141+
134142
// ---------------------------------------------------------------------------
135143
// Side-Effects
136144
// ---------------------------------------------------------------------------
@@ -142,6 +150,15 @@ const TransactionListComponent: React.FC<Props> = props => {
142150
}
143151
}, [enabledTokenIds, navigation, tokenId])
144152

153+
useAsyncEffect(
154+
async () => {
155+
if (!hasNamedTransactions) return
156+
await dispatch(promptForContactsPermission())
157+
},
158+
[hasNamedTransactions],
159+
'TransactionListScene contacts permission'
160+
)
161+
145162
// Automatically navigate to the token activation confirmation scene if
146163
// the token appears in the unactivatedTokenIds list once the wallet loads
147164
// this state.

src/hooks/redux/useContactThumbnail.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,14 @@
11
import * as React from 'react'
2-
import { check } from 'react-native-permissions'
32

4-
import { maybeShowContactsPermissionModal } from '../../components/modals/ContactsPermissionModal'
5-
import { requestContactsPermission } from '../../components/services/PermissionsManager'
63
import { MERCHANT_CONTACTS } from '../../constants/MerchantContacts'
7-
import { permissionNames } from '../../reducers/PermissionsReducer'
8-
import { useDispatch, useSelector } from '../../types/reactRedux'
4+
import { useSelector } from '../../types/reactRedux'
95
import { normalizeForSearch } from '../../util/utils'
10-
import { useAsyncEffect } from '../useAsyncEffect'
116

127
/**
13-
* Looks up a thumbnail image for a contact. Will show a contacts permission
14-
* request modal if we haven't shown it before and the system contacts
15-
* permission is not granted.
8+
* Looks up a thumbnail image for a contact using existing contacts data.
169
*/
1710
export const useContactThumbnail = (name?: string): string | undefined => {
1811
const contacts = useSelector(state => state.contacts)
19-
const dispatch = useDispatch()
20-
21-
useAsyncEffect(
22-
async () => {
23-
const contactsPermission = await check(permissionNames.contacts).catch(
24-
(_error: unknown) => 'denied'
25-
)
26-
27-
if (
28-
contactsPermission !== 'granted' &&
29-
contactsPermission !== 'limited'
30-
) {
31-
const result = await dispatch(maybeShowContactsPermissionModal())
32-
if (result === 'allow') {
33-
await requestContactsPermission(true)
34-
}
35-
}
36-
},
37-
[],
38-
'useContactThumbnail'
39-
)
4012

4113
return React.useMemo(() => {
4214
if (name == null) return

0 commit comments

Comments
 (0)