1
0
Fork 0
mirror of https://github.com/BlueWallet/BlueWallet.git synced 2025-03-26 08:55:56 +01:00
This commit is contained in:
Marcos Rodriguez Velez 2024-11-11 21:42:38 -04:00
parent ae15a4a77f
commit 28e325afa5
4 changed files with 102 additions and 20 deletions

View file

@ -1,8 +1,8 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import { findNodeHandle, Platform } from 'react-native';
import { AppState, findNodeHandle, Platform } from 'react-native';
import { getApplicationName, getSystemName, getSystemVersion, getVersion, hasGmsSync, hasHmsSync } from 'react-native-device-info';
import { requestNotifications } from 'react-native-permissions';
import { checkNotifications, requestNotifications } from 'react-native-permissions';
import PushNotification from 'react-native-push-notification';
import loc from '../loc';
@ -16,6 +16,37 @@ const NOTIFICATIONS_NO_AND_DONT_ASK_FLAG = 'NOTIFICATIONS_NO_AND_DONT_ASK_FLAG';
let alreadyConfigured = false;
let baseURI = groundControlUri;
// Function to check notification permission status at the system level
export const checkNotificationPermissionStatus = async () => {
try {
const { status } = await checkNotifications();
return status;
} catch (error) {
console.error('Failed to check notification permissions:', error);
return 'unavailable'; // Return 'unavailable' if the status cannot be retrieved
}
};
// Listener to monitor notification permission status changes while app is running
let currentPermissionStatus = 'unavailable';
const handleAppStateChange = async nextAppState => {
if (nextAppState === 'active') {
const newPermissionStatus = await checkNotificationPermissionStatus();
if (newPermissionStatus !== currentPermissionStatus) {
currentPermissionStatus = newPermissionStatus;
if (newPermissionStatus === 'granted') {
// Re-initialize notifications if permissions are granted
await initializeNotifications();
} else {
// Optionally, handle the case where permissions are revoked (e.g., disable in-app notifications)
console.warn('Notifications have been disabled at the system level.');
}
}
}
};
AppState.addEventListener('change', handleAppStateChange);
export const cleanUserOptOutFlag = async () => {
return AsyncStorage.removeItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG);
};
@ -265,7 +296,7 @@ export const configureNotifications = async onProcessNotifications => {
if (notification.data && notification.data.data) Object.assign(payload, notification.data.data);
delete payload.data;
// ^^^ weird, but sometimes payload data is not in `data` but in root level
console.debug('got push notification', payload);
console.debug('Received Push Notification Payload: ', payload);
await addNotification(payload);
@ -311,7 +342,7 @@ export const configureNotifications = async onProcessNotifications => {
* - if you are not using remote notification or do not have Firebase installed, use this:
* requestPermissions: Platform.OS === 'ios'
*/
requestPermissions: true,
requestPermissions: Platform.OS === 'ios',
});
}
});
@ -520,7 +551,6 @@ export const getStoredNotifications = async () => {
// on app launch (load module):
export const initializeNotifications = async onProcessNotifications => {
// Fetch custom GroundControl URI
try {
const baseUriStored = await AsyncStorage.getItem(GROUNDCONTROL_BASE_URI);
baseURI = baseUriStored || groundControlUri;
@ -530,13 +560,15 @@ export const initializeNotifications = async onProcessNotifications => {
await AsyncStorage.setItem(GROUNDCONTROL_BASE_URI, groundControlUri).catch(err => console.error('Failed to reset URI:', err));
}
// Set the application icon badge to 0
setApplicationIconBadgeNumber(0);
try {
if (await getPushToken()) {
currentPermissionStatus = await checkNotificationPermissionStatus();
if (currentPermissionStatus === 'granted' && (await getPushToken())) {
await configureNotifications(onProcessNotifications);
await postTokenConfig();
} else {
console.warn('Notifications are disabled at the system level.');
}
} catch (error) {
console.error('Failed to initialize notifications:', error);

View file

@ -57,7 +57,7 @@ const CompanionDelegates = () => {
const deliveredNotifications = await getDeliveredNotifications();
setTimeout(async () => {
try {
await removeAllDeliveredNotifications();
removeAllDeliveredNotifications();
} catch (error) {
console.error('Failed to remove delivered notifications:', error);
}

View file

@ -320,7 +320,8 @@
"would_you_like_to_receive_notifications": "Would you like to receive notifications when you get incoming payments?",
"notifications_subtitle": "Incoming payments and transaction confirmations",
"no_and_dont_ask": "No, and do not ask me again.",
"ask_me_later": "Ask me later."
"ask_me_later": "Ask me later.",
"permission_denied_message": "You have denied permission to send you notifications. If you would like to receive notifications, please enable them in your device settings."
},
"transactions": {
"cancel_explain": "We will replace this transaction with one that pays you and has higher fees. This effectively cancels the current transaction. This is called RBF—Replace by Fee.",

View file

@ -1,7 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, View, Pressable } from 'react-native';
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, View, Pressable, AppState } from 'react-native';
import { Button as ButtonRNElements } from '@rneui/themed';
// @ts-ignore: no declaration file
import {
getDefaultUri,
getPushToken,
@ -14,6 +13,7 @@ import {
cleanUserOptOutFlag,
isGroundControlUriValid,
checkPermissions,
checkNotificationPermissionStatus,
} from '../../blue_modules/notifications';
import { BlueCard, BlueSpacing20, BlueSpacing40, BlueText } from '../../BlueComponents';
import presentAlert from '../../components/Alert';
@ -53,21 +53,51 @@ const NotificationSettings: React.FC = () => {
setTapCount(prevCount => prevCount + 1);
};
const showNotificationPermissionAlert = () => {
presentAlert({
title: loc.settings.notifications,
message: loc.notifications.permission_denied_message,
buttons: [
{
text: loc._.ok,
style: 'cancel',
},
{
text: loc.settings.header,
onPress: onSystemSettings,
style: 'default',
},
],
});
};
const onNotificationsSwitch = async (value: boolean) => {
if (value) {
// User is trying to enable notifications
const currentStatus = await checkNotificationPermissionStatus();
if (currentStatus !== 'granted') {
// If notifications are not granted at the system level, show an alert and prevent toggle from enabling
showNotificationPermissionAlert();
setNotificationsEnabledState(false); // Keep the switch off
return;
}
}
try {
setNotificationsEnabledState(value);
if (value) {
// User is enabling notifications
await cleanUserOptOutFlag();
if (await getPushToken()) {
// we already have a token, so we just need to reenable ALL level on groundcontrol:
await setLevels(true);
const permissionsGranted = await tryToObtainPermissions();
if (permissionsGranted) {
if (await getPushToken()) {
await setLevels(true);
}
} else {
// ok, we dont have a token. we need to try to obtain permissions, configure callbacks and save token locally:
await tryToObtainPermissions();
// If permissions are denied, show alert and reset the toggle
showNotificationPermissionAlert();
setNotificationsEnabledState(false); // Reset the toggle to reflect the denied status
}
} else {
// User is disabling notifications
await setLevels(false);
}
@ -78,12 +108,21 @@ const NotificationSettings: React.FC = () => {
}
};
// Function to check and update notification permission status
const updateNotificationStatus = async () => {
const currentStatus = await checkNotificationPermissionStatus();
if (currentStatus !== 'granted') {
setNotificationsEnabledState(false); // Automatically toggle switch off if permissions are disabled
} else {
setNotificationsEnabledState(true);
}
};
useEffect(() => {
(async () => {
try {
setNotificationsEnabledState(await isNotificationsEnabled());
setURI((await getSavedUri()) ?? getDefaultUri());
// @ts-ignore: refactor later
setTokenInfo(
'token: ' +
JSON.stringify(await getPushToken()) +
@ -99,13 +138,23 @@ const NotificationSettings: React.FC = () => {
setIsLoading(false);
}
})();
// Add AppState listener to check permission status when app is active
const appStateListener = AppState.addEventListener('change', nextAppState => {
if (nextAppState === 'active') {
updateNotificationStatus();
}
});
return () => {
appStateListener.remove();
};
}, []);
const save = useCallback(async () => {
setIsLoading(true);
try {
if (URI) {
// validating only if its not empty. empty means use default
if (await isGroundControlUriValid(URI)) {
await saveUri(URI);
presentAlert({ message: loc.settings.saved });