BlueWallet/screen/settings/NotificationSettings.tsx

306 lines
9.4 KiB
TypeScript
Raw Normal View History

import React, { useCallback, useEffect, useState } from 'react';
2024-11-11 21:42:38 -04:00
import { I18nManager, Linking, ScrollView, StyleSheet, TextInput, View, Pressable, AppState } from 'react-native';
import { Button as ButtonRNElements } from '@rneui/themed';
2024-11-11 18:29:45 -04:00
import {
2024-11-11 17:45:21 -04:00
getDefaultUri,
getPushToken,
getSavedUri,
getStoredNotifications,
saveUri,
isNotificationsEnabled,
2024-11-11 18:29:45 -04:00
setLevels,
tryToObtainPermissions,
cleanUserOptOutFlag,
isGroundControlUriValid,
checkPermissions,
2024-11-11 21:42:38 -04:00
checkNotificationPermissionStatus,
2024-11-13 01:36:39 -04:00
NOTIFICATIONS_NO_AND_DONT_ASK_FLAG,
2024-11-11 17:45:21 -04:00
} from '../../blue_modules/notifications';
2024-10-26 02:11:21 -04:00
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';
2024-10-31 08:26:32 -04:00
import { openSettings } from 'react-native-permissions';
2024-11-13 01:36:39 -04:00
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);
};
2024-11-11 21:42:38 -04:00
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) => {
2024-11-11 21:42:38 -04:00
if (value) {
const currentStatus = await checkNotificationPermissionStatus();
2024-11-13 01:36:39 -04:00
if (currentStatus === 'blocked') {
// If permissions are denied/blocked, show alert and reset the toggle
2024-11-11 21:42:38 -04:00
showNotificationPermissionAlert();
2024-11-13 01:36:39 -04:00
setNotificationsEnabledState(false);
2024-11-11 21:42:38 -04:00
return;
}
}
try {
2024-11-11 17:45:21 -04:00
setNotificationsEnabledState(value);
if (value) {
2024-11-11 18:29:45 -04:00
await cleanUserOptOutFlag();
2024-11-11 21:42:38 -04:00
const permissionsGranted = await tryToObtainPermissions();
if (permissionsGranted) {
2024-11-13 18:18:06 -04:00
await setLevels(true);
2024-11-13 01:36:39 -04:00
await AsyncStorage.removeItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG);
} else {
2024-11-11 21:42:38 -04:00
showNotificationPermissionAlert();
2024-11-13 01:36:39 -04:00
setNotificationsEnabledState(false);
}
} else {
2024-11-11 18:29:45 -04:00
await setLevels(false);
2024-11-13 01:36:39 -04:00
await AsyncStorage.setItem(NOTIFICATIONS_NO_AND_DONT_ASK_FLAG, 'true');
setNotificationsEnabledState(false);
}
2024-11-11 17:45:21 -04:00
setNotificationsEnabledState(await isNotificationsEnabled());
} catch (error) {
console.error(error);
2024-10-04 00:57:16 -04:00
presentAlert({ message: (error as Error).message });
setNotificationsEnabledState(false);
}
};
2024-11-11 21:42:38 -04:00
const updateNotificationStatus = async () => {
try {
const currentStatus = await checkNotificationPermissionStatus();
2024-11-13 01:36:39 -04:00
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);
2024-11-11 21:42:38 -04:00
}
};
useEffect(() => {
(async () => {
try {
2024-11-13 01:36:39 -04:00
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: ' +
2024-11-11 18:29:45 -04:00
JSON.stringify(await checkPermissions()) +
' stored notifications: ' +
2024-11-11 17:45:21 -04:00
JSON.stringify(await getStoredNotifications()),
);
2024-10-04 00:53:34 -04:00
} catch (e) {
console.error(e);
presentAlert({ message: (e as Error).message });
} finally {
setIsLoading(false);
}
})();
2024-11-11 21:42:38 -04:00
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);
2024-11-11 21:42:38 -04:00
}
});
return () => {
appStateListener.remove();
};
}, []);
const save = useCallback(async () => {
setIsLoading(true);
try {
if (URI) {
2024-11-11 18:29:45 -04:00
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]);
2024-10-26 02:11:21 -04:00
const onSystemSettings = () => {
2024-10-31 08:26:32 -04:00
openSettings('notifications');
2024-10-26 02:11:21 -04:00
};
return (
<ScrollView style={stylesWithThemeHook.scroll} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
<ListItem
Component={PressableWrapper}
title={loc.settings.notifications}
2024-10-04 07:54:31 -04:00
subtitle={loc.notifications.notifications_subtitle}
disabled={isLoading}
isLoading={isNotificationsEnabledState === undefined}
2024-11-11 17:45:21 -04:00
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>
</>
)}
2024-10-26 02:11:21 -04:00
<BlueSpacing40 />
<ListItem title={loc.settings.privacy_system_settings} onPress={onSystemSettings} chevron />
</ScrollView>
);
};
2024-10-04 00:12:09 -04:00
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;