mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 13:26:33 +01:00
306 lines
9.4 KiB
TypeScript
306 lines
9.4 KiB
TypeScript
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, View, Pressable, AppState } from 'react-native';
|
|
import { Button as ButtonRNElements } from '@rneui/themed';
|
|
import {
|
|
getDefaultUri,
|
|
getPushToken,
|
|
getSavedUri,
|
|
getStoredNotifications,
|
|
saveUri,
|
|
isNotificationsEnabled,
|
|
setLevels,
|
|
tryToObtainPermissions,
|
|
cleanUserOptOutFlag,
|
|
isGroundControlUriValid,
|
|
checkPermissions,
|
|
checkNotificationPermissionStatus,
|
|
NOTIFICATIONS_NO_AND_DONT_ASK_FLAG,
|
|
} from '../../blue_modules/notifications';
|
|
import { BlueCard, BlueSpacing20, BlueSpacing40, BlueText } from '../../BlueComponents';
|
|
import presentAlert from '../../components/Alert';
|
|
import { Button } from '../../components/Button';
|
|
import CopyToClipboardButton from '../../components/CopyToClipboardButton';
|
|
import ListItem, { PressableWrapper } from '../../components/ListItem';
|
|
import { useTheme } from '../../components/themes';
|
|
import loc from '../../loc';
|
|
import { Divider } from '@rneui/base';
|
|
import { openSettings } from 'react-native-permissions';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
|
|
const NotificationSettings: React.FC = () => {
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isNotificationsEnabledState, setNotificationsEnabledState] = useState<boolean | undefined>(undefined);
|
|
const [tokenInfo, setTokenInfo] = useState('<empty>');
|
|
const [URI, setURI] = useState<string | undefined>();
|
|
const [tapCount, setTapCount] = useState(0);
|
|
const { colors } = useTheme();
|
|
const stylesWithThemeHook = {
|
|
root: {
|
|
backgroundColor: colors.background,
|
|
},
|
|
scroll: {
|
|
backgroundColor: colors.background,
|
|
},
|
|
scrollBody: {
|
|
backgroundColor: colors.background,
|
|
},
|
|
uri: {
|
|
borderColor: colors.formBorder,
|
|
borderBottomColor: colors.formBorder,
|
|
backgroundColor: colors.inputBackgroundColor,
|
|
},
|
|
};
|
|
|
|
const handleTap = () => {
|
|
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) {
|
|
const currentStatus = await checkNotificationPermissionStatus();
|
|
if (currentStatus === 'blocked') {
|
|
// If permissions are denied/blocked, show alert and reset the toggle
|
|
showNotificationPermissionAlert();
|
|
setNotificationsEnabledState(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
setNotificationsEnabledState(value);
|
|
if (value) {
|
|
await cleanUserOptOutFlag();
|
|
const permissionsGranted = await tryToObtainPermissions();
|
|
if (permissionsGranted) {
|
|
await setLevels(true);
|
|
await AsyncStorage.removeItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG);
|
|
} else {
|
|
showNotificationPermissionAlert();
|
|
setNotificationsEnabledState(false);
|
|
}
|
|
} else {
|
|
await setLevels(false);
|
|
await AsyncStorage.setItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG, 'true');
|
|
setNotificationsEnabledState(false);
|
|
}
|
|
|
|
setNotificationsEnabledState(await isNotificationsEnabled());
|
|
} catch (error) {
|
|
console.error(error);
|
|
presentAlert({ message: (error as Error).message });
|
|
setNotificationsEnabledState(false);
|
|
}
|
|
};
|
|
|
|
const updateNotificationStatus = async () => {
|
|
try {
|
|
const currentStatus = await checkNotificationPermissionStatus();
|
|
const isEnabled = await isNotificationsEnabled();
|
|
const isDisabledByUser = (await AsyncStorage.getItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG)) === 'true';
|
|
|
|
if (!isDisabledByUser) {
|
|
setNotificationsEnabledState(currentStatus === 'granted' && isEnabled);
|
|
} else {
|
|
setNotificationsEnabledState(false);
|
|
}
|
|
} catch (error) {
|
|
console.log('Error updating notification status:', error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
const isDisabledByUser = (await AsyncStorage.getItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG)) === 'true';
|
|
|
|
if (isDisabledByUser) {
|
|
console.debug('Notifications were disabled by the user. Skipping auto-activation.');
|
|
setNotificationsEnabledState(false);
|
|
} else {
|
|
await updateNotificationStatus();
|
|
}
|
|
|
|
setURI((await getSavedUri()) ?? getDefaultUri());
|
|
setTokenInfo(
|
|
'token: ' +
|
|
JSON.stringify(await getPushToken()) +
|
|
' permissions: ' +
|
|
JSON.stringify(await checkPermissions()) +
|
|
' stored notifications: ' +
|
|
JSON.stringify(await getStoredNotifications()),
|
|
);
|
|
} catch (e) {
|
|
console.error(e);
|
|
presentAlert({ message: (e as Error).message });
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
})();
|
|
|
|
const appStateListener = AppState.addEventListener('change', nextAppState => {
|
|
if (nextAppState === 'active') {
|
|
setTimeout(async () => {
|
|
const isDisabledByUser = (await AsyncStorage.getItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG)) === 'true';
|
|
if (!isDisabledByUser) {
|
|
updateNotificationStatus();
|
|
}
|
|
}, 300);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
appStateListener.remove();
|
|
};
|
|
}, []);
|
|
|
|
const save = useCallback(async () => {
|
|
setIsLoading(true);
|
|
try {
|
|
if (URI) {
|
|
if (await isGroundControlUriValid(URI)) {
|
|
await saveUri(URI);
|
|
presentAlert({ message: loc.settings.saved });
|
|
} else {
|
|
presentAlert({ message: loc.settings.not_a_valid_uri });
|
|
}
|
|
} else {
|
|
await saveUri('');
|
|
presentAlert({ message: loc.settings.saved });
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving URI:', error);
|
|
}
|
|
setIsLoading(false);
|
|
}, [URI]);
|
|
|
|
const onSystemSettings = () => {
|
|
openSettings('notifications');
|
|
};
|
|
|
|
return (
|
|
<ScrollView style={stylesWithThemeHook.scroll} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
|
|
<ListItem
|
|
Component={PressableWrapper}
|
|
title={loc.settings.notifications}
|
|
subtitle={loc.notifications.notifications_subtitle}
|
|
disabled={isLoading}
|
|
isLoading={isNotificationsEnabledState === undefined}
|
|
switch={{ onValueChange: onNotificationsSwitch, value: isNotificationsEnabledState, testID: 'NotificationsSwitch' }}
|
|
/>
|
|
|
|
<Pressable onPress={handleTap}>
|
|
<BlueCard>
|
|
<BlueText style={styles.multilineText}>{loc.settings.push_notifications_explanation}</BlueText>
|
|
</BlueCard>
|
|
</Pressable>
|
|
|
|
{tapCount >= 10 && (
|
|
<>
|
|
<Divider />
|
|
<BlueCard>
|
|
<BlueText>{loc.settings.groundcontrol_explanation}</BlueText>
|
|
</BlueCard>
|
|
|
|
<ButtonRNElements
|
|
icon={{
|
|
name: 'github',
|
|
type: 'font-awesome',
|
|
color: colors.foregroundColor,
|
|
}}
|
|
onPress={() => Linking.openURL('https://github.com/BlueWallet/GroundControl')}
|
|
titleStyle={{ color: colors.buttonAlternativeTextColor }}
|
|
title="github.com/BlueWallet/GroundControl"
|
|
color={colors.buttonTextColor}
|
|
buttonStyle={styles.buttonStyle}
|
|
/>
|
|
|
|
<BlueCard>
|
|
<View style={[styles.uri, stylesWithThemeHook.uri]}>
|
|
<TextInput
|
|
placeholder={getDefaultUri()}
|
|
value={URI}
|
|
onChangeText={setURI}
|
|
numberOfLines={1}
|
|
style={styles.uriText}
|
|
placeholderTextColor="#81868e"
|
|
editable={!isLoading}
|
|
textContentType="URL"
|
|
autoCapitalize="none"
|
|
underlineColorAndroid="transparent"
|
|
/>
|
|
</View>
|
|
|
|
<BlueSpacing20 />
|
|
<BlueText style={styles.centered} onPress={() => setTapCount(tapCount + 1)}>
|
|
♪ Ground Control to Major Tom ♪
|
|
</BlueText>
|
|
<BlueText style={styles.centered} onPress={() => setTapCount(tapCount + 1)}>
|
|
♪ Commencing countdown, engines on ♪
|
|
</BlueText>
|
|
|
|
<View>
|
|
<CopyToClipboardButton stringToCopy={tokenInfo} displayText={tokenInfo} />
|
|
</View>
|
|
|
|
<BlueSpacing20 />
|
|
<Button onPress={save} title={loc.settings.save} />
|
|
</BlueCard>
|
|
</>
|
|
)}
|
|
<BlueSpacing40 />
|
|
<ListItem title={loc.settings.privacy_system_settings} onPress={onSystemSettings} chevron />
|
|
</ScrollView>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
uri: {
|
|
flexDirection: 'row',
|
|
borderWidth: 1,
|
|
borderBottomWidth: 0.5,
|
|
minHeight: 44,
|
|
height: 44,
|
|
alignItems: 'center',
|
|
borderRadius: 4,
|
|
},
|
|
centered: {
|
|
textAlign: 'center',
|
|
},
|
|
uriText: {
|
|
flex: 1,
|
|
color: '#81868e',
|
|
marginHorizontal: 8,
|
|
minHeight: 36,
|
|
height: 36,
|
|
},
|
|
buttonStyle: {
|
|
backgroundColor: 'transparent',
|
|
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
|
},
|
|
multilineText: {
|
|
textAlign: 'left',
|
|
lineHeight: 20,
|
|
paddingBottom: 10,
|
|
},
|
|
});
|
|
|
|
export default NotificationSettings;
|