Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
thalia
ThaliApp
Commits
a468f25d
Verified
Commit
a468f25d
authored
Jul 12, 2018
by
Gijs Hendriksen
Committed by
Sébastiaan Versteeg
Aug 09, 2018
Browse files
Added settings screen for push notification categories
parent
fe2d8c7d
Changes
23
Hide whitespace changes
Inline
Side-by-side
__tests__/actions/__snapshots__/pushNotifications.spec.js.snap
View file @
a468f25d
...
...
@@ -8,6 +8,17 @@ Object {
exports[`push notifications actions should create an action to register the push token 1`] = `
Object {
"categories": null,
"type": "PUSH_NOTIFICATIONS_REGISTER",
}
`;
exports[`push notifications actions should create an action to register the push tokens and update push categories 1`] = `
Object {
"categories": Array [
"category1",
"category2",
],
"type": "PUSH_NOTIFICATIONS_REGISTER",
}
`;
__tests__/actions/pushNotifications.spec.js
View file @
a468f25d
...
...
@@ -10,6 +10,10 @@ describe('push notifications actions', () => {
expect
(
actions
.
register
()).
toMatchSnapshot
();
});
it
(
'
should create an action to register the push tokens and update push categories
'
,
()
=>
{
expect
(
actions
.
register
([
'
category1
'
,
'
category2
'
])).
toMatchSnapshot
();
});
it
(
'
should create an action to invalidate the push token
'
,
()
=>
{
expect
(
actions
.
invalidate
()).
toMatchSnapshot
();
});
...
...
__tests__/ui/components/navigator/__snapshots__/Sidebar.spec.js.snap
View file @
a468f25d
...
...
@@ -403,6 +403,109 @@ exports[`Sidebar component renders correctly 1`] = `
},
]
}
>
<View
style={
Array [
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "flex-start",
"padding": 8,
},
Object {
"backgroundColor": "#FFFFFF",
"borderRadius": 0,
},
Array [
Object {
"padding": 16,
},
Object {},
],
]
}
>
<Text
accessible={true}
allowFontScaling={false}
ellipsizeMode="tail"
onPress={[Function]}
style={
Array [
Object {
"color": "#313131",
"fontSize": 24,
},
Array [
Object {
"marginRight": 10,
},
Object {
"marginRight": 30,
"textAlign": "center",
"width": 28,
},
],
Object {
"fontFamily": "Material Icons",
"fontStyle": "normal",
"fontWeight": "normal",
},
]
}
>
</Text>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
style={
Array [
Object {
"backgroundColor": "transparent",
"fontWeight": "600",
},
Object {
"color": "#313131",
},
]
}
>
Settings
</Text>
</View>
</View>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
hasTVPreferredFocus={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Array [
Object {
"overflow": "hidden",
},
Object {
"backgroundColor": "#FFFFFF",
"borderRadius": 0,
},
]
}
testID={undefined}
tvParallaxProperties={undefined}
>
<View
style={
...
...
app/actions/pushNotifications.js
View file @
a468f25d
export
const
REGISTER
=
'
PUSH_NOTIFICATIONS_REGISTER
'
;
export
const
INVALIDATE
=
'
PUSH_NOTIFICATIONS_INVALIDATE
'
;
export
function
register
()
{
return
{
type
:
REGISTER
};
export
function
register
(
categories
=
null
)
{
return
{
type
:
REGISTER
,
categories
};
}
export
function
invalidate
()
{
...
...
app/actions/settings.js
0 → 100644
View file @
a468f25d
// Group setting sections together to prepare for more submenus to be added.
export
const
pushNotificationsSettingsActions
=
{
RETRIEVE
:
'
SETTINGS_PUSH_NOTIFICATIONS
'
,
LOADING
:
'
SETTINGS_PUSH_NOTIFICATIONS_LOADING
'
,
SUCCESS
:
'
SETTINGS_PUSH_NOTIFICATIONS_SUCCESS
'
,
FAILURE
:
'
SETTINGS_PUSH_NOTIFICATIONS_FAILURE
'
,
SAVE_CATEGORIES
:
'
SETTINGS_PUSH_NOTIFICATIONS_SAVE_CATEGORIES
'
,
retrieve
:
()
=>
({
type
:
pushNotificationsSettingsActions
.
RETRIEVE
,
}),
loading
:
()
=>
({
type
:
pushNotificationsSettingsActions
.
LOADING
,
}),
success
:
categoryList
=>
({
type
:
pushNotificationsSettingsActions
.
SUCCESS
,
categoryList
,
}),
failure
:
()
=>
({
type
:
pushNotificationsSettingsActions
.
FAILURE
,
}),
saveCategories
:
categories
=>
({
type
:
pushNotificationsSettingsActions
.
SAVE_CATEGORIES
,
categories
,
}),
};
app/app.js
View file @
a468f25d
...
...
@@ -26,6 +26,7 @@ const USERNAMEKEY = '@MyStore:username';
const
TOKENKEY
=
'
@MyStore:token
'
;
const
DISPLAYNAMEKEY
=
'
@MyStore:displayName
'
;
const
PHOTOKEY
=
'
@MyStore:photo
'
;
const
PUSHCATEGORYKEY
=
'
@MyStore:pushCategories
'
;
const
pairsToObject
=
(
obj
,
pair
)
=>
{
const
obj2
=
{
...
obj
};
...
...
@@ -62,7 +63,7 @@ class Main extends Component {
}
componentDidMount
()
{
AsyncStorage
.
multiGet
([
USERNAMEKEY
,
TOKENKEY
,
DISPLAYNAMEKEY
,
PHOTOKEY
])
AsyncStorage
.
multiGet
([
USERNAMEKEY
,
TOKENKEY
,
DISPLAYNAMEKEY
,
PHOTOKEY
,
PUSHCATEGORYKEY
])
.
then
(
(
result
)
=>
{
const
values
=
result
.
reduce
(
pairsToObject
,
{});
...
...
@@ -70,12 +71,13 @@ class Main extends Component {
const
token
=
values
[
TOKENKEY
];
const
displayName
=
values
[
DISPLAYNAMEKEY
];
const
photo
=
values
[
PHOTOKEY
];
const
pushCategories
=
JSON
.
parse
(
values
[
PUSHCATEGORYKEY
]);
if
(
username
!==
null
&&
token
!==
null
)
{
store
.
dispatch
(
loginActions
.
success
(
username
,
token
));
store
.
dispatch
(
loginActions
.
profileSuccess
(
displayName
,
photo
));
store
.
dispatch
(
loginActions
.
profile
(
token
));
store
.
dispatch
(
register
());
store
.
dispatch
(
register
(
pushCategories
));
}
},
);
...
...
app/assets/locales/index.js
View file @
a468f25d
const
files
=
{};
files
[
'
app/ui/screens/user/ProfileNL
'
]
=
require
(
'
./nl/app/ui/screens/user/Profile.json
'
);
files
[
'
app/ui/screens/user/LoginNL
'
]
=
require
(
'
./nl/app/ui/screens/user/Login.json
'
);
files
[
'
app/ui/screens/welcome/WelcomeNL
'
]
=
require
(
'
./nl/app/ui/screens/welcome/Welcome.json
'
);
files
[
'
app/ui/screens/welcome/EventDetailCardNL
'
]
=
require
(
'
./nl/app/ui/screens/welcome/EventDetailCard.json
'
);
files
[
'
app/ui/screens/memberList/MemberListNL
'
]
=
require
(
'
./nl/app/ui/screens/memberList/MemberList.json
'
);
files
[
'
app/ui/screens/welcome/EventDetailCardNL
'
]
=
require
(
'
./nl/app/ui/screens/welcome/EventDetailCard.json
'
);
files
[
'
app/ui/screens/welcome/WelcomeNL
'
]
=
require
(
'
./nl/app/ui/screens/welcome/Welcome.json
'
);
files
[
'
app/ui/screens/pizza/PizzaNL
'
]
=
require
(
'
./nl/app/ui/screens/pizza/Pizza.json
'
);
files
[
'
app/ui/screens/settings/SettingsNL
'
]
=
require
(
'
./nl/app/ui/screens/settings/Settings.json
'
);
files
[
'
app/ui/screens/settings/PushNotificationsNL
'
]
=
require
(
'
./nl/app/ui/screens/settings/PushNotifications.json
'
);
files
[
'
app/ui/screens/events/CalendarNL
'
]
=
require
(
'
./nl/app/ui/screens/events/Calendar.json
'
);
files
[
'
app/ui/screens/events/RegistrationNL
'
]
=
require
(
'
./nl/app/ui/screens/events/Registration.json
'
);
files
[
'
app/ui/screens/events/CalendarItemNL
'
]
=
require
(
'
./nl/app/ui/screens/events/CalendarItem.json
'
);
files
[
'
app/ui/screens/events/EventNL
'
]
=
require
(
'
./nl/app/ui/screens/events/Event.json
'
);
files
[
'
app/ui/screens/events/RegistrationNL
'
]
=
require
(
'
./nl/app/ui/screens/events/Registration.json
'
);
files
[
'
app/ui/screens/events/CalendarNL
'
]
=
require
(
'
./nl/app/ui/screens/events/Calendar.json
'
);
files
[
'
app/ui/screens/pizza/PizzaNL
'
]
=
require
(
'
./nl/app/ui/screens/pizza/Pizza.json
'
);
files
[
'
app/ui/components/standardHeader/StandardHeaderNL
'
]
=
require
(
'
./nl/app/ui/components/standardHeader/StandardHeader.json
'
);
files
[
'
app/ui/components/navigator/SidebarNL
'
]
=
require
(
'
./nl/app/ui/components/navigator/Sidebar.json
'
);
files
[
'
app/ui/components/errorScreen/ErrorScreenNL
'
]
=
require
(
'
./nl/app/ui/components/errorScreen/ErrorScreen.json
'
);
files
[
'
app/ui/components/navigator/SidebarNL
'
]
=
require
(
'
./nl/app/ui/components/navigator/Sidebar.json
'
);
export
default
{
nl
:
{
'
screens/user/Profile
'
:
files
[
'
app/ui/screens/user/ProfileNL
'
],
'
screens/user/Login
'
:
files
[
'
app/ui/screens/user/LoginNL
'
],
'
screens/welcome/Welcome
'
:
files
[
'
app/ui/screens/welcome/WelcomeNL
'
],
'
screens/welcome/EventDetailCard
'
:
files
[
'
app/ui/screens/welcome/EventDetailCardNL
'
],
'
screens/memberList/MemberList
'
:
files
[
'
app/ui/screens/memberList/MemberListNL
'
],
'
screens/welcome/EventDetailCard
'
:
files
[
'
app/ui/screens/welcome/EventDetailCardNL
'
],
'
screens/welcome/Welcome
'
:
files
[
'
app/ui/screens/welcome/WelcomeNL
'
],
'
screens/pizza/Pizza
'
:
files
[
'
app/ui/screens/pizza/PizzaNL
'
],
'
screens/settings/Settings
'
:
files
[
'
app/ui/screens/settings/SettingsNL
'
],
'
screens/settings/PushNotifications
'
:
files
[
'
app/ui/screens/settings/PushNotificationsNL
'
],
'
screens/events/Calendar
'
:
files
[
'
app/ui/screens/events/CalendarNL
'
],
'
screens/events/Registration
'
:
files
[
'
app/ui/screens/events/RegistrationNL
'
],
'
screens/events/CalendarItem
'
:
files
[
'
app/ui/screens/events/CalendarItemNL
'
],
'
screens/events/Event
'
:
files
[
'
app/ui/screens/events/EventNL
'
],
'
screens/events/Registration
'
:
files
[
'
app/ui/screens/events/RegistrationNL
'
],
'
screens/events/Calendar
'
:
files
[
'
app/ui/screens/events/CalendarNL
'
],
'
screens/pizza/Pizza
'
:
files
[
'
app/ui/screens/pizza/PizzaNL
'
],
'
components/standardHeader/StandardHeader
'
:
files
[
'
app/ui/components/standardHeader/StandardHeaderNL
'
],
'
components/navigator/Sidebar
'
:
files
[
'
app/ui/components/navigator/SidebarNL
'
],
'
components/errorScreen/ErrorScreen
'
:
files
[
'
app/ui/components/errorScreen/ErrorScreenNL
'
],
'
components/navigator/Sidebar
'
:
files
[
'
app/ui/components/navigator/SidebarNL
'
],
},
};
app/assets/locales/nl/app/ui/components/navigator/Sidebar.json
View file @
a468f25d
...
...
@@ -6,5 +6,6 @@
"Welcome"
:
"Welkom"
,
"Calendar"
:
"Agenda"
,
"Logout"
:
"Uitloggen"
,
"Member List"
:
"Ledenlijst"
"Member List"
:
"Ledenlijst"
,
"Settings"
:
"Instellingen"
}
app/assets/locales/nl/app/ui/components/standardHeader/StandardHeader.json
View file @
a468f25d
...
...
@@ -4,5 +4,7 @@
"Calendar"
:
"Agenda"
,
"Pizza"
:
"Pizza"
,
"Profile"
:
"Profiel"
,
"Registration"
:
"Registratie"
"Registration"
:
"Registratie"
,
"Settings"
:
"Instellingen"
,
"Notifications"
:
"Meldingen"
}
app/assets/locales/nl/app/ui/screens/settings/PushNotifications.json
0 → 100644
View file @
a468f25d
{
"Sorry, we couldn't load any data."
:
"Sorry, we konden geen gegevens laden."
,
"(required)"
:
"(verplicht)"
}
app/assets/locales/nl/app/ui/screens/settings/Settings.json
0 → 100644
View file @
a468f25d
{
"Notifications"
:
"Meldingen"
}
app/reducers/index.js
View file @
a468f25d
...
...
@@ -8,6 +8,7 @@ import profile from './profile';
import
pizza
from
'
./pizza
'
;
import
registration
from
'
./registration
'
;
import
members
from
'
./members
'
;
import
settings
from
'
./settings
'
;
export
default
combineReducers
({
session
,
...
...
@@ -19,4 +20,5 @@ export default combineReducers({
pizza
,
registration
,
members
,
settings
,
});
app/reducers/settings.js
0 → 100644
View file @
a468f25d
import
{
pushNotificationsSettingsActions
}
from
'
../actions/settings
'
;
const
initialState
=
{
pushNotifications
:
{
categoryList
:
[],
status
:
'
loading
'
,
},
};
export
default
function
settings
(
state
=
initialState
,
action
=
{})
{
switch
(
action
.
type
)
{
case
pushNotificationsSettingsActions
.
LOADING
:
{
return
{
...
state
,
pushNotifications
:
{
...
state
.
pushNotifications
,
status
:
'
loading
'
,
},
};
}
case
pushNotificationsSettingsActions
.
SUCCESS
:
{
return
{
...
state
,
pushNotifications
:
{
...
state
.
pushNotifications
,
categoryList
:
action
.
categoryList
,
status
:
'
success
'
,
},
};
}
case
pushNotificationsSettingsActions
.
FAILURE
:
{
return
{
...
state
,
pushNotifications
:
{
...
state
.
pushNotifications
,
status
:
'
failure
'
,
},
};
}
default
:
{
return
state
;
}
}
}
app/sagas/index.js
View file @
a468f25d
...
...
@@ -10,6 +10,7 @@ import pizzaSaga from './pizza';
import
registrationSaga
from
'
./registration
'
;
import
deepLinkingSaga
from
'
./deepLinking
'
;
import
membersSaga
from
'
./members
'
;
import
settingsSaga
from
'
./settings
'
;
const
sagas
=
function
*
sagas
()
{
yield
all
([
...
...
@@ -23,6 +24,7 @@ const sagas = function* sagas() {
fork
(
registrationSaga
),
fork
(
deepLinkingSaga
),
fork
(
membersSaga
),
fork
(
settingsSaga
),
]);
};
...
...
app/sagas/pushNotifications.js
View file @
a468f25d
...
...
@@ -6,8 +6,9 @@ import { Sentry } from 'react-native-sentry';
import
{
apiRequest
,
tokenSelector
}
from
'
../utils/url
'
;
import
*
as
pushNotificationsActions
from
'
../actions/pushNotifications
'
;
const
register
=
function
*
register
()
{
const
register
=
function
*
register
(
action
)
{
const
token
=
yield
select
(
tokenSelector
);
const
{
categories
}
=
action
;
if
(
token
===
undefined
)
{
// There is no token, thus do nothing
...
...
@@ -28,6 +29,15 @@ const register = function* register() {
pushToken
=
yield
call
(
FCM
.
getFCMToken
);
}
const
body
=
{
registration_id
:
pushToken
,
type
:
Platform
.
OS
,
};
if
(
categories
!==
null
)
{
body
.
receive_category
=
categories
;
}
const
data
=
{
method
:
'
POST
'
,
headers
:
{
...
...
@@ -35,10 +45,7 @@ const register = function* register() {
'
Content-Type
'
:
'
application/json
'
,
Authorization
:
`Token
${
token
}
`
,
},
body
:
JSON
.
stringify
({
registration_id
:
pushToken
,
type
:
Platform
.
OS
,
}),
body
:
JSON
.
stringify
(
body
),
};
try
{
...
...
app/sagas/settings.js
0 → 100644
View file @
a468f25d
import
{
AsyncStorage
}
from
'
react-native
'
;
import
{
takeEvery
,
select
,
call
,
put
}
from
'
redux-saga/effects
'
;
import
{
pushNotificationsSettingsActions
}
from
'
../actions/settings
'
;
import
*
as
navigationActions
from
'
../actions/navigation
'
;
import
{
apiRequest
,
tokenSelector
}
from
'
../utils/url
'
;
const
PUSHCATEGORYKEY
=
'
@MyStore:pushCategories
'
;
function
*
pushNotifications
()
{
const
token
=
yield
select
(
tokenSelector
);
const
data
=
{
method
:
'
GET
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
Authorization
:
`Token
${
token
}
`
,
},
};
yield
put
(
pushNotificationsSettingsActions
.
loading
());
yield
put
(
navigationActions
.
navigate
(
'
pushNotificationsSettings
'
));
try
{
const
categoryList
=
yield
call
(
apiRequest
,
'
devices/categories
'
,
data
);
const
preferencesJson
=
yield
call
(
AsyncStorage
.
getItem
,
PUSHCATEGORYKEY
);
if
(
preferencesJson
===
null
)
{
for
(
let
i
=
0
;
i
<
categoryList
.
length
;
i
+=
1
)
{
categoryList
[
i
].
enabled
=
true
;
}
}
else
{
const
categoryPreferences
=
JSON
.
parse
(
preferencesJson
);
for
(
let
i
=
0
;
i
<
categoryList
.
length
;
i
+=
1
)
{
categoryList
[
i
].
enabled
=
categoryPreferences
.
includes
(
categoryList
[
i
].
key
);
}
}
yield
put
(
pushNotificationsSettingsActions
.
success
(
categoryList
));
}
catch
(
error
)
{
yield
put
(
pushNotificationsSettingsActions
.
failure
());
}
}
function
*
saveCategories
(
action
)
{
const
{
categories
}
=
action
;
try
{
yield
call
(
AsyncStorage
.
setItem
,
PUSHCATEGORYKEY
,
JSON
.
stringify
(
categories
));
}
catch
(
error
)
{
// Swallow error
}
}
function
*
settingsSaga
()
{
yield
takeEvery
(
pushNotificationsSettingsActions
.
RETRIEVE
,
pushNotifications
);
yield
takeEvery
(
pushNotificationsSettingsActions
.
SAVE_CATEGORIES
,
saveCategories
);
}
export
default
settingsSaga
;
app/ui/components/navigator/ReduxNavigator.js
View file @
a468f25d
...
...
@@ -15,6 +15,8 @@ import Pizza from '../../screens/pizza/Pizza';
import
StandardHeader
from
'
../standardHeader/StandardHeader
'
;
import
Registration
from
'
../../screens/events/Registration
'
;
import
MemberList
from
'
../../screens/memberList/MemberList
'
;
import
Settings
from
'
../../screens/settings/Settings
'
;
import
PushNotifications
from
'
../../screens/settings/PushNotifications
'
;
import
*
as
actions
from
'
../../../actions/navigation
'
;
import
styles
from
'
./style/ReduxNavigator
'
;
...
...
@@ -36,6 +38,10 @@ const sceneToComponent = (scene) => {
return
<
Registration
/>
;
case
'
members
'
:
return
<
MemberList
/>
;
case
'
settings
'
:
return
<
Settings
/>
;
case
'
pushNotificationsSettings
'
:
return
<
PushNotifications
/>
;
default
:
return
<
Welcome
/>
;
}
...
...
app/ui/components/navigator/Sidebar.js
View file @
a468f25d
...
...
@@ -47,6 +47,13 @@ const Sidebar = (props) => {
style
:
{},
scene
:
'
members
'
,
},
{
onPress
:
()
=>
props
.
navigate
(
'
settings
'
,
true
),
iconName
:
'
settings
'
,
text
:
props
.
t
(
'
Settings
'
),
style
:
{},
scene
:
'
settings
'
,
},
{
onPress
:
logoutPrompt
(
props
),
iconName
:
'
lock
'
,
...
...
app/ui/components/standardHeader/StandardHeader.js
View file @
a468f25d
...
...
@@ -26,6 +26,10 @@ const sceneToTitle = (scene, t) => {
return
t
(
'
Profile
'
);
case
'
registration
'
:
return
t
(
'
Registration
'
);
case
'
settings
'
:
return
t
(
'
Settings
'
);
case
'
pushNotificationsSettings
'
:
return
t
(
'
Notifications
'
);
default
:
return
'
ThaliApp
'
;
}
...
...
app/ui/screens/settings/PushNotifications.js
0 → 100644
View file @
a468f25d
import
React
,
{
Component
}
from
'
react
'
;
import
{
View
,
Text
,
Switch
}
from
'
react-native
'
;
import
{
connect
}
from
'
react-redux
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
translate
}
from
'
react-i18next
'
;
import
LoadingScreen
from
'
../../components/loadingScreen/LoadingScreen
'
;
import
ErrorScreen
from
'
../../components/errorScreen/ErrorScreen
'
;
import
styles
from
'
./style/PushNotifications
'
;
import
Colors
from
'
../../style/Colors
'
;
import
{
pushNotificationsSettingsActions
}
from
'
../../../actions/settings
'
;
import
*
as
pushNotificationsActions
from
'
../../../actions/pushNotifications
'
;
class
PushNotifications
extends
Component
{
static
getDerivedStateFromProps
=
(
props
)
=>
{
if
(
props
.
status
!==
'
success
'
)
{
return
null
;
}
const
newState
=
{};
for
(
let
i
=
0
;
i
<
props
.
categoryList
.
length
;
i
+=
1
)
{
newState
[
props
.
categoryList
[
i
].
key
]
=
props
.
categoryList
[
i
].
enabled
;
}
return
newState
;
};
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{};
}
updateField
=
(
key
,
value
)
=>
{
const
update
=
{};
update
[
key
]
=
value
;
this
.
setState
(
update
,
()
=>
{
const
categories
=
Object
.
keys
(
this
.
state
).
filter
(
k
=>
this
.
state
[
k
]);
this
.
props
.
register
(
categories
);
this
.
props
.
saveCategories
(
categories
);
});
};
render
()
{
if
(
this
.
props
.
status
===
'
loading
'
)
{
return
<
LoadingScreen
/>
;
}
else
if
(
this
.
props
.
status
===
'
failure
'
)
{
return
<
ErrorScreen
message
=
{
this
.
props
.
t
(
'
Sorry, we couldn
\'
t load any data.
'
)}
/>
;
}
return
(
<
View
style
=
{
styles
.
container
}
>
{
this
.
props
.
categoryList
.
map
(
category
=>
(
<
View
style
=
{
styles
.
setting
}
key
=
{
category
.
key
}
>
<
Text
style
=
{
styles
.
label
}
key
=
{
category
.
key
}
>
{
category
.
name
}
{
category
.
key
===
'
general
'
&&
this
.
props
.
t
(
'
(required)
'
)}
<
/Text
>
<
Switch
value
=
{
this
.
state
[
category
.
key
]}
onValueChange
=
{
value
=>
this
.
updateField
(
category
.
key
,
value
)}
thumbTintColor
=
{
this
.
state
[
category
.
key
]
?
Colors
.
darkMagenta
:
Colors
.
lightGray
}
onTintColor
=
{
Colors
.
magenta
}
disabled
=
{
category
.
key
===
'
general
'
}
/
>
<
/View
>
))}
<
/View
>
);
}
}
PushNotifications
.
propTypes
=
{
categoryList
:
PropTypes
.
arrayOf
(
PropTypes
.
shape
({
key
:
PropTypes
.
string
.
isRequired
,
name
:
PropTypes
.
string
.
isRequired
,
enabled
:
PropTypes
.
bool
.
isRequired
,
})).
isRequired
,
status
:
PropTypes
.
string
.
isRequired
,
register
:
PropTypes
.
func
.
isRequired
,
saveCategories
:
PropTypes
.
func
.
isRequired
,
t
:
PropTypes
.
func
.
isRequired
,
};