mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 13:26:33 +01:00
FIX: Deleting wallets would throw reject unhandled
This commit is contained in:
parent
5c93a81429
commit
0eb6b4f192
@ -17,21 +17,9 @@ let alreadyConfigured = false;
|
||||
let baseURI = groundControlUri;
|
||||
|
||||
function Notifications(props) {
|
||||
async function _setPushToken(token) {
|
||||
const _setPushToken = async token => {
|
||||
token = JSON.stringify(token);
|
||||
return AsyncStorage.setItem(PUSH_TOKEN, token);
|
||||
}
|
||||
|
||||
Notifications.getPushToken = async () => {
|
||||
try {
|
||||
let token = await AsyncStorage.getItem(PUSH_TOKEN);
|
||||
token = JSON.parse(token);
|
||||
return token;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
AsyncStorage.removeItem(PUSH_TOKEN);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -40,13 +28,13 @@ function Notifications(props) {
|
||||
*
|
||||
* @returns {Promise<boolean>} TRUE if acquired token, FALSE if not
|
||||
*/
|
||||
const configureNotifications = async function () {
|
||||
const configureNotifications = async () => {
|
||||
return new Promise(function (resolve) {
|
||||
requestNotifications(['alert', 'sound', 'badge']).then(({ status, _ }) => {
|
||||
if (status === 'granted') {
|
||||
PushNotification.configure({
|
||||
// (optional) Called when Token is generated (iOS and Android)
|
||||
onRegister: async function (token) {
|
||||
onRegister: async token => {
|
||||
console.debug('TOKEN:', token);
|
||||
alreadyConfigured = true;
|
||||
await _setPushToken(token);
|
||||
@ -54,7 +42,7 @@ function Notifications(props) {
|
||||
},
|
||||
|
||||
// (required) Called when a remote is received or opened, or local notification is opened
|
||||
onNotification: async function (notification) {
|
||||
onNotification: async notification => {
|
||||
// since we do not know whether we:
|
||||
// 1) received notification while app is in background (and storage is not decrypted so wallets are not loaded)
|
||||
// 2) opening this notification right now but storage is still unencrypted
|
||||
@ -79,7 +67,7 @@ function Notifications(props) {
|
||||
},
|
||||
|
||||
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
|
||||
onAction: function (notification) {
|
||||
onAction: notification => {
|
||||
console.debug('ACTION:', notification.action);
|
||||
console.debug('NOTIFICATION:', notification);
|
||||
|
||||
@ -118,7 +106,7 @@ function Notifications(props) {
|
||||
// …
|
||||
};
|
||||
|
||||
Notifications.cleanUserOptOutFlag = async function () {
|
||||
Notifications.cleanUserOptOutFlag = async () => {
|
||||
return AsyncStorage.removeItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG);
|
||||
};
|
||||
|
||||
@ -131,9 +119,9 @@ function Notifications(props) {
|
||||
*
|
||||
* @returns {Promise<boolean>} TRUE if permissions were obtained, FALSE otherwise
|
||||
*/
|
||||
Notifications.tryToObtainPermissions = async function (anchor) {
|
||||
Notifications.tryToObtainPermissions = async anchor => {
|
||||
if (!isNotificationsCapable) return false;
|
||||
if (await Notifications.getPushToken()) {
|
||||
if (await getPushToken()) {
|
||||
// we already have a token, no sense asking again, just configure pushes to register callbacks and we are done
|
||||
if (!alreadyConfigured) configureNotifications(); // no await so it executes in background while we return TRUE and use token
|
||||
return true;
|
||||
@ -145,36 +133,36 @@ function Notifications(props) {
|
||||
}
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
const options = [loc.notifications.no_and_dont_ask, loc.notifications.ask_me_later, loc._.ok];
|
||||
const buttons = [loc.notifications.no_and_dont_ask, loc.notifications.ask_me_later, loc._.ok];
|
||||
const options = {
|
||||
title: loc.settings.notifications,
|
||||
message: `${loc.notifications.would_you_like_to_receive_notifications}\n${loc.settings.push_notifications_explanation}`,
|
||||
options: buttons,
|
||||
cancelButtonIndex: 0, // Assuming 'no and don't ask' is still treated as the cancel action
|
||||
};
|
||||
|
||||
ActionSheet.showActionSheetWithOptions(
|
||||
{
|
||||
title: loc.settings.notifications,
|
||||
message: `${loc.notifications.would_you_like_to_receive_notifications}\n${loc.settings.push_notifications_explanation}`,
|
||||
options,
|
||||
cancelButtonIndex: 0, // Assuming 'no and don't ask' is still treated as the cancel action
|
||||
anchor: anchor ? findNodeHandle(anchor.current) : undefined,
|
||||
},
|
||||
buttonIndex => {
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
AsyncStorage.setItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG, '1').then(() => resolve(false));
|
||||
break;
|
||||
case 1:
|
||||
resolve(false);
|
||||
break;
|
||||
case 2:
|
||||
configureNotifications().then(resolve);
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
if (anchor) {
|
||||
options.anchor = findNodeHandle(anchor.current);
|
||||
}
|
||||
ActionSheet.showActionSheetWithOptions(options, buttonIndex => {
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
AsyncStorage.setItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG, '1').then(() => resolve(false));
|
||||
break;
|
||||
case 1:
|
||||
resolve(false);
|
||||
break;
|
||||
case 2:
|
||||
configureNotifications().then(resolve);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async function _sleep(ms) {
|
||||
const _sleep = async ms => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Submits onchain bitcoin addresses and ln invoice preimage hashes to GroundControl server, so later we could
|
||||
@ -185,13 +173,13 @@ function Notifications(props) {
|
||||
* @param txids {string[]}
|
||||
* @returns {Promise<object>} Response object from API rest call
|
||||
*/
|
||||
Notifications.majorTomToGroundControl = async function (addresses, hashes, txids) {
|
||||
Notifications.majorTomToGroundControl = async (addresses, hashes, txids) => {
|
||||
try {
|
||||
if (!Array.isArray(addresses) || !Array.isArray(hashes) || !Array.isArray(txids)) {
|
||||
throw new Error('No addresses, hashes, or txids provided');
|
||||
}
|
||||
|
||||
const pushToken = await Notifications.getPushToken();
|
||||
const pushToken = await getPushToken();
|
||||
if (!pushToken || !pushToken.token || !pushToken.os) {
|
||||
return;
|
||||
}
|
||||
@ -236,36 +224,9 @@ function Notifications(props) {
|
||||
}
|
||||
};
|
||||
|
||||
Notifications.isNotificationsEnabled = async function () {
|
||||
Notifications.isNotificationsEnabled = async () => {
|
||||
const levels = await getLevels();
|
||||
return !!(await Notifications.getPushToken()) && !!levels.level_all;
|
||||
};
|
||||
|
||||
Notifications.getDefaultUri = function () {
|
||||
return groundControlUri;
|
||||
};
|
||||
|
||||
Notifications.saveUri = async function (uri) {
|
||||
baseURI = uri || groundControlUri; // setting the url to use currently. if not set - use default
|
||||
return AsyncStorage.setItem(GROUNDCONTROL_BASE_URI, uri);
|
||||
};
|
||||
|
||||
Notifications.getSavedUri = async function () {
|
||||
try {
|
||||
const baseUriStored = await AsyncStorage.getItem(GROUNDCONTROL_BASE_URI);
|
||||
if (baseUriStored) {
|
||||
baseURI = baseUriStored;
|
||||
}
|
||||
return baseUriStored;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
try {
|
||||
await AsyncStorage.setItem(GROUNDCONTROL_BASE_URI, groundControlUri);
|
||||
} catch (storageError) {
|
||||
console.error('Failed to reset URI:', storageError);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return !!(await getPushToken()) && !!levels.level_all;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -294,7 +255,7 @@ function Notifications(props) {
|
||||
*
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
Notifications.checkPermissions = async function () {
|
||||
Notifications.checkPermissions = async () => {
|
||||
return new Promise(function (resolve) {
|
||||
PushNotification.checkPermissions(result => {
|
||||
resolve(result);
|
||||
@ -308,8 +269,8 @@ function Notifications(props) {
|
||||
* @param levelAll {Boolean}
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
Notifications.setLevels = async function (levelAll) {
|
||||
const pushToken = await Notifications.getPushToken();
|
||||
Notifications.setLevels = async levelAll => {
|
||||
const pushToken = await getPushToken();
|
||||
if (!pushToken || !pushToken.token || !pushToken.os) return;
|
||||
|
||||
try {
|
||||
@ -333,8 +294,8 @@ function Notifications(props) {
|
||||
*
|
||||
* @returns {Promise<{}|*>}
|
||||
*/
|
||||
const getLevels = async function () {
|
||||
const pushToken = await Notifications.getPushToken();
|
||||
const getLevels = async () => {
|
||||
const pushToken = await getPushToken();
|
||||
if (!pushToken || !pushToken.token || !pushToken.os) return;
|
||||
|
||||
let response;
|
||||
@ -357,7 +318,7 @@ function Notifications(props) {
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
Notifications.getStoredNotifications = async function () {
|
||||
Notifications.getStoredNotifications = async () => {
|
||||
let notifications = [];
|
||||
try {
|
||||
const stringified = await AsyncStorage.getItem(NOTIFICATIONS_STORAGE);
|
||||
@ -377,7 +338,7 @@ function Notifications(props) {
|
||||
return notifications;
|
||||
};
|
||||
|
||||
Notifications.addNotification = async function (notification) {
|
||||
Notifications.addNotification = async notification => {
|
||||
let notifications = [];
|
||||
try {
|
||||
const stringified = await AsyncStorage.getItem(NOTIFICATIONS_STORAGE);
|
||||
@ -393,8 +354,8 @@ function Notifications(props) {
|
||||
await AsyncStorage.setItem(NOTIFICATIONS_STORAGE, JSON.stringify(notifications));
|
||||
};
|
||||
|
||||
const postTokenConfig = async function () {
|
||||
const pushToken = await Notifications.getPushToken();
|
||||
const postTokenConfig = async () => {
|
||||
const pushToken = await getPushToken();
|
||||
if (!pushToken || !pushToken.token || !pushToken.os) return;
|
||||
|
||||
try {
|
||||
@ -418,7 +379,7 @@ function Notifications(props) {
|
||||
}
|
||||
};
|
||||
|
||||
Notifications.clearStoredNotifications = async function () {
|
||||
Notifications.clearStoredNotifications = async () => {
|
||||
try {
|
||||
await AsyncStorage.setItem(NOTIFICATIONS_STORAGE, JSON.stringify([]));
|
||||
} catch (_) {}
|
||||
@ -461,7 +422,7 @@ function Notifications(props) {
|
||||
// every launch should clear badges:
|
||||
Notifications.setApplicationIconBadgeNumber(0);
|
||||
|
||||
if (!(await Notifications.getPushToken())) return;
|
||||
if (!(await getPushToken())) return;
|
||||
// if we previously had token that means we already acquired permission from the user and it is safe to call
|
||||
// `configure` to register callbacks etc
|
||||
await configureNotifications();
|
||||
@ -472,6 +433,18 @@ function Notifications(props) {
|
||||
|
||||
export const isNotificationsCapable = hasGmsSync() || hasHmsSync() || Platform.OS !== 'android';
|
||||
|
||||
export const getPushToken = async () => {
|
||||
try {
|
||||
let token = await AsyncStorage.getItem(PUSH_TOKEN);
|
||||
token = JSON.parse(token);
|
||||
return token;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
AsyncStorage.removeItem(PUSH_TOKEN);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The opposite of `majorTomToGroundControl` call.
|
||||
*
|
||||
@ -481,36 +454,39 @@ export const isNotificationsCapable = hasGmsSync() || hasHmsSync() || Platform.O
|
||||
* @returns {Promise<object>} Response object from API rest call
|
||||
*/
|
||||
export const unsubscribe = async (addresses, hashes, txids) => {
|
||||
if (!Array.isArray(addresses) || !Array.isArray(hashes) || !Array.isArray(txids))
|
||||
if (!Array.isArray(addresses) || !Array.isArray(hashes) || !Array.isArray(txids)) {
|
||||
throw new Error('No addresses, hashes, or txids provided');
|
||||
const pushToken = await Notifications.getPushToken();
|
||||
if (!pushToken || !pushToken.token || !pushToken.os) return;
|
||||
}
|
||||
|
||||
const token = await getPushToken();
|
||||
if (!token?.token || !token?.os) {
|
||||
console.error('No push token or OS found');
|
||||
return;
|
||||
}
|
||||
|
||||
const body = JSON.stringify({
|
||||
addresses,
|
||||
hashes,
|
||||
txids,
|
||||
token: token.token,
|
||||
os: token.os,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(`${baseURI}/unsubscribe`, {
|
||||
method: 'POST',
|
||||
headers: _getHeaders(),
|
||||
body: JSON.stringify({
|
||||
addresses,
|
||||
hashes,
|
||||
txids,
|
||||
token: pushToken.token,
|
||||
os: pushToken.os,
|
||||
}),
|
||||
body,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Unsubscribe request failed:', response.status);
|
||||
console.error('Failed to unsubscribe:', response.statusText);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.debug('Abandoning notifications Permissions...');
|
||||
await PushNotification.abandonPermissions();
|
||||
console.debug('Abandoned notifications Permissions...');
|
||||
return result;
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error in unsubscribe:', error);
|
||||
console.error('Error during unsubscribe:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -521,4 +497,36 @@ function _getHeaders() {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
|
||||
export const getDefaultUri = () => {
|
||||
return groundControlUri;
|
||||
};
|
||||
|
||||
export const saveUri = async uri => {
|
||||
baseURI = uri || groundControlUri; // setting the url to use currently. if not set - use default
|
||||
try {
|
||||
await AsyncStorage.setItem(GROUNDCONTROL_BASE_URI, baseURI);
|
||||
} catch (storageError) {
|
||||
console.error('Failed to reset URI:', storageError);
|
||||
throw storageError;
|
||||
}
|
||||
};
|
||||
|
||||
export const getSavedUri = async () => {
|
||||
try {
|
||||
const baseUriStored = await AsyncStorage.getItem(GROUNDCONTROL_BASE_URI);
|
||||
if (baseUriStored) {
|
||||
baseURI = baseUriStored;
|
||||
}
|
||||
return baseUriStored;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
try {
|
||||
await AsyncStorage.setItem(GROUNDCONTROL_BASE_URI, groundControlUri);
|
||||
} catch (storageError) {
|
||||
console.error('Failed to reset URI:', storageError);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
export default Notifications;
|
||||
|
@ -150,7 +150,8 @@ const ReceiveDetails = () => {
|
||||
console.error('Error obtaining notifications permissions:', error);
|
||||
}
|
||||
}
|
||||
}, [wallet, saveToDisk, address, setAddressBIP21Encoded, isElectrumDisabled, sleep]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [walletID, saveToDisk, address, setAddressBIP21Encoded, isElectrumDisabled, sleep]);
|
||||
|
||||
const onEnablePaymentsCodeSwitchValue = useCallback(() => {
|
||||
if (wallet.allowBIP47()) {
|
||||
|
@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, View, Pressable } from 'react-native';
|
||||
import { Button as ButtonRNElements } from '@rneui/themed';
|
||||
// @ts-ignore: no declaration file
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import Notifications, { getDefaultUri, getPushToken, getSavedUri, saveUri } from '../../blue_modules/notifications';
|
||||
import { BlueCard, BlueSpacing20, BlueSpacing40, BlueText } from '../../BlueComponents';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { Button } from '../../components/Button';
|
||||
@ -48,8 +48,7 @@ const NotificationSettings: React.FC = () => {
|
||||
// User is enabling notifications
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.cleanUserOptOutFlag();
|
||||
// @ts-ignore: refactor later
|
||||
if (await Notifications.getPushToken()) {
|
||||
if (await getPushToken()) {
|
||||
// we already have a token, so we just need to reenable ALL level on groundcontrol:
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.setLevels(true);
|
||||
@ -77,13 +76,12 @@ const NotificationSettings: React.FC = () => {
|
||||
try {
|
||||
// @ts-ignore: refactor later
|
||||
setNotificationsEnabled(await Notifications.isNotificationsEnabled());
|
||||
// @ts-ignore: refactor later
|
||||
setURI(await Notifications.getSavedUri());
|
||||
setURI((await getSavedUri()) ?? getDefaultUri());
|
||||
// @ts-ignore: refactor later
|
||||
setTokenInfo(
|
||||
'token: ' +
|
||||
// @ts-ignore: refactor later
|
||||
JSON.stringify(await Notifications.getPushToken()) +
|
||||
JSON.stringify(await getPushToken()) +
|
||||
' permissions: ' +
|
||||
// @ts-ignore: refactor later
|
||||
JSON.stringify(await Notifications.checkPermissions()) +
|
||||
@ -107,15 +105,13 @@ const NotificationSettings: React.FC = () => {
|
||||
// validating only if its not empty. empty means use default
|
||||
// @ts-ignore: refactor later
|
||||
if (await Notifications.isGroundControlUriValid(URI)) {
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.saveUri(URI);
|
||||
await saveUri(URI);
|
||||
presentAlert({ message: loc.settings.saved });
|
||||
} else {
|
||||
presentAlert({ message: loc.settings.not_a_valid_uri });
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore: refactor later
|
||||
await Notifications.saveUri('');
|
||||
await saveUri('');
|
||||
presentAlert({ message: loc.settings.saved });
|
||||
}
|
||||
} catch (error) {
|
||||
@ -167,8 +163,7 @@ const NotificationSettings: React.FC = () => {
|
||||
<BlueCard>
|
||||
<View style={[styles.uri, stylesWithThemeHook.uri]}>
|
||||
<TextInput
|
||||
// @ts-ignore: refactor later
|
||||
placeholder={Notifications.getDefaultUri()}
|
||||
placeholder={getDefaultUri()}
|
||||
value={URI}
|
||||
onChangeText={setURI}
|
||||
numberOfLines={1}
|
||||
|
Loading…
Reference in New Issue
Block a user