1
1
/* @flow strict-local */
2
2
3
- import React , { useCallback } from 'react' ;
3
+ import React , { useCallback , useContext } from 'react' ;
4
4
import type { Node } from 'react' ;
5
- import { Platform , Linking , NativeModules } from 'react-native' ;
5
+ import { Alert , Platform , Linking , NativeModules } from 'react-native' ;
6
6
import OpenNotification from 'react-native-open-notification' ;
7
7
8
8
import type { RouteProp } from '../react-navigation' ;
@@ -14,14 +14,71 @@ import Screen from '../common/Screen';
14
14
import * as api from '../api' ;
15
15
import ServerPushSetupBanner from '../common/ServerPushSetupBanner' ;
16
16
import NestedNavRow from '../common/NestedNavRow' ;
17
+ import { useAppState } from '../reactNativeUtils' ;
18
+ import { IconAlertTriangle } from '../common/Icons' ;
19
+ import type { LocalizableText } from '../types' ;
20
+ import { TranslationContext } from '../boot/TranslationProvider' ;
17
21
18
- const { ZLPConstants } = NativeModules ;
22
+ const {
23
+ ZLPConstants,
24
+ Notifications, // android
25
+ ZLPNotifications, // ios
26
+ } = NativeModules ;
19
27
20
28
type Props = $ReadOnly < { |
21
29
navigation : AppNavigationProp < 'notifications' > ,
22
30
route : RouteProp < 'notifications' , void> ,
23
31
| } > ;
24
32
33
+ /**
34
+ * A problem in the system notification settings that we should warn about.
35
+ */
36
+ enum SystemSettingsWarning {
37
+ Disabled = 0 ,
38
+ // TODO: …more, e.g.:
39
+ // TODO(#5484): Android notification sound file missing
40
+ // TODO(#438): Badge count disabled (once iOS supports it)
41
+ }
42
+
43
+ function systemSettingsWarningMsg ( warning : SystemSettingsWarning ) : LocalizableText {
44
+ switch ( warning ) {
45
+ case SystemSettingsWarning . Disabled :
46
+ return 'Notifications are disabled.' ;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * An array of the `SystemSettingsWarning`s that currently apply.
52
+ */
53
+ const useSystemSettingsWarnings = ( ) : $ReadOnlyArray < SystemSettingsWarning > => {
54
+ const [ disabled , setDisabled ] = React . useState ( false ) ;
55
+
56
+ // Subject to races if the native-method calls can resolve out of order
57
+ // (unknown).
58
+ const getAndSetDisabled = React . useCallback ( async ( ) => {
59
+ setDisabled (
60
+ Platform . OS === 'android'
61
+ ? ! ( await Notifications . areNotificationsEnabled ( ) )
62
+ : ! ( await ZLPNotifications . areNotificationsAuthorized ( ) ) ,
63
+ ) ;
64
+ } , [ ] ) ;
65
+
66
+ // Greg points out that neither iOS or Android seems to have an API for
67
+ // subscribing to changes, so one has to poll, and this seems like a fine
68
+ // way to do so:
69
+ // https://github.com/zulip/zulip-mobile/pull/5627#discussion_r1058055540
70
+ const appState = useAppState ( ) ;
71
+ React . useEffect ( ( ) => {
72
+ getAndSetDisabled ( ) ;
73
+ } , [ getAndSetDisabled , appState ] ) ;
74
+
75
+ const result = [ ] ;
76
+ if ( disabled ) {
77
+ result . push ( SystemSettingsWarning . Disabled ) ;
78
+ }
79
+ return result ;
80
+ } ;
81
+
25
82
function openSystemNotificationSettings ( ) {
26
83
if ( Platform . OS === 'ios' ) {
27
84
Linking . openURL (
@@ -48,14 +105,38 @@ function openSystemNotificationSettings() {
48
105
49
106
/** (NB this is a per-account screen -- these are per-account settings.) */
50
107
export default function NotificationsScreen ( props : Props ) : Node {
108
+ const _ = useContext ( TranslationContext ) ;
109
+
51
110
const auth = useSelector ( getAuth ) ;
52
111
const offlineNotification = useSelector ( state => getSettings ( state ) . offlineNotification ) ;
53
112
const onlineNotification = useSelector ( state => getSettings ( state ) . onlineNotification ) ;
54
113
const streamNotification = useSelector ( state => getSettings ( state ) . streamNotification ) ;
55
114
115
+ const systemSettingsWarnings = useSystemSettingsWarnings ( ) ;
116
+
56
117
const handleSystemSettingsPress = useCallback ( ( ) => {
118
+ if ( systemSettingsWarnings . length > 1 ) {
119
+ Alert . alert (
120
+ _ ( 'System settings for Zulip' ) ,
121
+ // List all warnings that apply.
122
+ systemSettingsWarnings . map ( w => _ ( systemSettingsWarningMsg ( w ) ) ) . join ( '\n\n' ) ,
123
+ [
124
+ { text : _ ( 'Cancel' ) , style : 'cancel' } ,
125
+ {
126
+ text : _ ( 'Open settings' ) ,
127
+ onPress : ( ) => {
128
+ openSystemNotificationSettings ( ) ;
129
+ } ,
130
+ style : 'default' ,
131
+ } ,
132
+ ] ,
133
+ { cancelable : true } ,
134
+ ) ;
135
+ return ;
136
+ }
137
+
57
138
openSystemNotificationSettings ( ) ;
58
- } , [ ] ) ;
139
+ } , [ systemSettingsWarnings , _ ] ) ;
59
140
60
141
// TODO(#3999): It'd be good to show "working on it" UI feedback while a
61
142
// request is pending, after the user touches a switch.
@@ -87,7 +168,28 @@ export default function NotificationsScreen(props: Props): Node {
87
168
return (
88
169
< Screen title = "Notifications" >
89
170
< ServerPushSetupBanner isDismissable = { false } />
90
- < NestedNavRow title = "System settings for Zulip" onPress = { handleSystemSettingsPress } />
171
+ < NestedNavRow
172
+ icon = {
173
+ systemSettingsWarnings . length > 0
174
+ ? {
175
+ Component : IconAlertTriangle ,
176
+ color : 'hsl(40, 100%, 60%)' , // Material warning-color
177
+ }
178
+ : undefined
179
+ }
180
+ title = "System settings for Zulip"
181
+ subtitle = { ( ( ) => {
182
+ switch ( systemSettingsWarnings . length ) {
183
+ case 0 :
184
+ return undefined ;
185
+ case 1 :
186
+ return systemSettingsWarningMsg ( systemSettingsWarnings [ 0 ] ) ;
187
+ default :
188
+ return 'Multiple issues. Tap to learn more.' ;
189
+ }
190
+ } ) ( ) }
191
+ onPress = { handleSystemSettingsPress }
192
+ />
91
193
< SwitchRow
92
194
label = "Notifications when offline"
93
195
value = { offlineNotification }
0 commit comments