This commit is contained in:
114
react/features/settings/components/native/AdvancedSection.tsx
Normal file
114
react/features/settings/components/native/AdvancedSection.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Alert, NativeModules, Platform, Text } from 'react-native';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
import styles from './styles';
|
||||
|
||||
const { AppInfo } = NativeModules;
|
||||
|
||||
const AdvancedSection = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
disableCrashReporting,
|
||||
disableCallIntegration,
|
||||
disableP2P
|
||||
} = useSelector((state: IReduxState) => state['features/base/settings']);
|
||||
|
||||
const onSwitchToggled = useCallback((name: string) => (enabled?: boolean) => {
|
||||
|
||||
if (name === 'disableCrashReporting' && enabled === true) {
|
||||
Alert.alert(
|
||||
t('settingsView.alertTitle'),
|
||||
t('settingsView.disableCrashReportingWarning'),
|
||||
[
|
||||
{
|
||||
onPress: () => dispatch(updateSettings({ disableCrashReporting: true })),
|
||||
text: t('settingsView.alertOk')
|
||||
},
|
||||
{
|
||||
text: t('settingsView.alertCancel')
|
||||
}
|
||||
]
|
||||
);
|
||||
} else {
|
||||
dispatch(updateSettings({ [name]: enabled }));
|
||||
}
|
||||
}, [ dispatch, updateSettings ]);
|
||||
|
||||
const switches = useMemo(() => {
|
||||
const partialSwitches = [
|
||||
{
|
||||
label: 'settingsView.disableCallIntegration',
|
||||
state: disableCallIntegration,
|
||||
name: 'disableCallIntegration'
|
||||
},
|
||||
{
|
||||
label: 'settingsView.disableP2P',
|
||||
state: disableP2P,
|
||||
name: 'disableP2P'
|
||||
},
|
||||
{
|
||||
label: 'settingsView.disableCrashReporting',
|
||||
state: disableCrashReporting,
|
||||
name: 'disableCrashReporting'
|
||||
}
|
||||
];
|
||||
|
||||
if (Platform.OS !== 'android') {
|
||||
partialSwitches.shift();
|
||||
}
|
||||
|
||||
if (!AppInfo.GOOGLE_SERVICES_ENABLED) {
|
||||
partialSwitches.pop();
|
||||
}
|
||||
|
||||
return partialSwitches;
|
||||
}, [ disableCallIntegration, disableP2P, disableCrashReporting ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormSection
|
||||
label = 'settingsView.advanced'>
|
||||
{
|
||||
switches.map(({ label, state, name }) => (
|
||||
<FormRow
|
||||
key = { label }
|
||||
label = { label }>
|
||||
<Switch
|
||||
checked = { Boolean(state) }
|
||||
onChange = { onSwitchToggled(name) } />
|
||||
</FormRow>
|
||||
))
|
||||
}
|
||||
</FormSection>
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.fieldSeparator } />
|
||||
<FormSection
|
||||
label = 'settingsView.buildInfoSection'>
|
||||
<FormRow
|
||||
label = 'settingsView.version'>
|
||||
<Text style = { styles.text }>
|
||||
{`${AppInfo.version} build ${AppInfo.buildNumber}`}
|
||||
</Text>
|
||||
</FormRow>
|
||||
<FormRow
|
||||
label = 'settingsView.sdkVersion'>
|
||||
<Text style = { styles.text }>
|
||||
{AppInfo.sdkVersion}
|
||||
</Text>
|
||||
</FormRow>
|
||||
</FormSection>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdvancedSection;
|
||||
101
react/features/settings/components/native/ConferenceSection.tsx
Normal file
101
react/features/settings/components/native/ConferenceSection.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { getDefaultURL } from '../../../app/functions.native';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions.native';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
const ConferenceSection = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
serverURL,
|
||||
startCarMode,
|
||||
startWithAudioMuted,
|
||||
startWithVideoMuted
|
||||
} = useSelector((state: IReduxState) => state['features/base/settings']);
|
||||
|
||||
const defaultServerURL = useSelector((state: IReduxState) => getDefaultURL(state));
|
||||
const [ newServerURL, setNewServerURL ] = useState(serverURL ?? '');
|
||||
|
||||
const serverURLChangeEnabled = useSelector((state: IReduxState) => isServerURLChangeEnabled(state));
|
||||
|
||||
const switches = useMemo(() => [
|
||||
{
|
||||
label: 'settingsView.startCarModeInLowBandwidthMode',
|
||||
state: startCarMode,
|
||||
name: 'startCarMode'
|
||||
},
|
||||
{
|
||||
label: 'settingsView.startWithAudioMuted',
|
||||
state: startWithAudioMuted,
|
||||
name: 'startWithAudioMuted'
|
||||
},
|
||||
{
|
||||
label: 'settingsView.startWithVideoMuted',
|
||||
state: startWithVideoMuted,
|
||||
name: 'startWithVideoMuted'
|
||||
}
|
||||
], [ startCarMode, startWithAudioMuted, startWithVideoMuted ]);
|
||||
|
||||
const onChangeServerURL = useCallback(value => {
|
||||
|
||||
setNewServerURL(value);
|
||||
dispatch(updateSettings({
|
||||
serverURL: value
|
||||
}));
|
||||
}, [ dispatch, newServerURL ]);
|
||||
|
||||
const processServerURL = useCallback(() => {
|
||||
const normalizedURL = normalizeUserInputURL(newServerURL);
|
||||
|
||||
onChangeServerURL(normalizedURL);
|
||||
}, [ newServerURL ]);
|
||||
|
||||
useEffect(() => () => processServerURL(), []);
|
||||
|
||||
const onSwitchToggled = useCallback((name: string) => (enabled?: boolean) => {
|
||||
|
||||
// @ts-ignore
|
||||
dispatch(updateSettings({ [name]: enabled }));
|
||||
}, [ dispatch ]);
|
||||
|
||||
return (
|
||||
<FormSection
|
||||
label = 'settingsView.conferenceSection'>
|
||||
<Input
|
||||
autoCapitalize = 'none'
|
||||
customStyles = {{ container: styles.customContainer }}
|
||||
editable = { serverURLChangeEnabled }
|
||||
keyboardType = { 'url' }
|
||||
label = { t('settingsView.serverURL') }
|
||||
onBlur = { processServerURL }
|
||||
onChange = { onChangeServerURL }
|
||||
placeholder = { defaultServerURL }
|
||||
textContentType = { 'URL' } // iOS only
|
||||
value = { newServerURL } />
|
||||
{
|
||||
switches.map(({ label, state, name }) => (
|
||||
<FormRow
|
||||
key = { label }
|
||||
label = { label }>
|
||||
<Switch
|
||||
checked = { Boolean(state) }
|
||||
onChange = { onSwitchToggled(name) } />
|
||||
</FormRow>
|
||||
))
|
||||
}
|
||||
</FormSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConferenceSection;
|
||||
146
react/features/settings/components/native/FormRow.tsx
Normal file
146
react/features/settings/components/native/FormRow.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
|
||||
import styles, { ANDROID_UNDERLINE_COLOR, PLACEHOLDER_COLOR } from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link FormRow}.
|
||||
*/
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Component's children.
|
||||
*/
|
||||
children: React.ReactElement;
|
||||
|
||||
/**
|
||||
* Prop to decide if a row separator is to be rendered.
|
||||
*/
|
||||
fieldSeparator?: boolean;
|
||||
|
||||
/**
|
||||
* The i18n key of the text label of the form field.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* One of 'row' (default) or 'column'.
|
||||
*/
|
||||
layout?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} which renders a standardized row on a
|
||||
* form. The component should have exactly one child component.
|
||||
*/
|
||||
class FormRow extends Component<IProps> {
|
||||
/**
|
||||
* Initializes a new {@code FormRow} instance.
|
||||
*
|
||||
* @param {Object} props - Component properties.
|
||||
*/
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
React.Children.only(this.props.children);
|
||||
|
||||
this._getDefaultFieldProps = this._getDefaultFieldProps.bind(this);
|
||||
this._getRowStyle = this._getRowStyle.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @override
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
override render() {
|
||||
const { t } = this.props;
|
||||
|
||||
// Some field types need additional props to look good and standardized
|
||||
// on a form.
|
||||
const newChild
|
||||
= React.cloneElement(
|
||||
this.props.children,
|
||||
this._getDefaultFieldProps(this.props.children));
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { this._getRowStyle() } >
|
||||
<View style = { styles.fieldLabelContainer as ViewStyle } >
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
styles.fieldLabelText
|
||||
] } >
|
||||
{ t(this.props.label) }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.fieldValueContainer as ViewStyle } >
|
||||
{ newChild }
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the default props to the field child component of this form
|
||||
* row.
|
||||
*
|
||||
* Currently tested/supported field types:
|
||||
* - TextInput
|
||||
* - Switch (needs no addition props ATM).
|
||||
*
|
||||
* @param {Object} field - The field (child) component.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getDefaultFieldProps(field?: React.ReactElement) {
|
||||
if (field?.type) { // @ts-ignore
|
||||
switch (field.type.displayName) {
|
||||
case 'TextInput':
|
||||
return {
|
||||
placeholderTextColor: PLACEHOLDER_COLOR,
|
||||
style: [
|
||||
styles.textInputField,
|
||||
this.props.layout === 'column' ? styles.textInputFieldColumn : undefined
|
||||
],
|
||||
underlineColorAndroid: ANDROID_UNDERLINE_COLOR
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the row style array based on the row's props.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
_getRowStyle() {
|
||||
const { fieldSeparator, layout } = this.props;
|
||||
const rowStyle: ViewStyle[] = [
|
||||
styles.fieldContainer as ViewStyle
|
||||
];
|
||||
|
||||
if (fieldSeparator) {
|
||||
rowStyle.push(styles.fieldSeparator);
|
||||
}
|
||||
|
||||
if (layout === 'column') {
|
||||
rowStyle.push(
|
||||
styles.fieldContainerColumn as ViewStyle
|
||||
);
|
||||
}
|
||||
|
||||
return rowStyle;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(FormRow);
|
||||
42
react/features/settings/components/native/FormSection.tsx
Normal file
42
react/features/settings/components/native/FormSection.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link FormSection}.
|
||||
*/
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* The children to be displayed within this Link.
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
|
||||
/**
|
||||
* The i18n key of the text label of the section.
|
||||
*/
|
||||
label?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Section accordion on settings form.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function FormSection({ children, label, t }: IProps) {
|
||||
return (
|
||||
<View>
|
||||
{label && <Text style = { styles.formSectionTitleText }>
|
||||
{ t(label) }
|
||||
</Text>}
|
||||
{ children }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(FormSection);
|
||||
61
react/features/settings/components/native/GeneralSection.tsx
Normal file
61
react/features/settings/components/native/GeneralSection.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import i18next, { DEFAULT_LANGUAGE } from '../../../base/i18n/i18next';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconArrowRight } from '../../../base/icons/svg';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
import { navigate } from '../../../mobile/navigation/components/settings/SettingsNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
const GeneralSection = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
disableSelfView,
|
||||
} = useSelector((state: IReduxState) => state['features/base/settings']);
|
||||
|
||||
const { language = DEFAULT_LANGUAGE } = i18next;
|
||||
|
||||
const onSelfViewToggled = useCallback((enabled?: boolean) =>
|
||||
dispatch(updateSettings({ disableSelfView: enabled }))
|
||||
, [ dispatch, updateSettings ]);
|
||||
|
||||
const navigateToLanguageSelect = useCallback(() => {
|
||||
navigate(screen.settings.language);
|
||||
}, [ navigate, screen ]);
|
||||
|
||||
return (
|
||||
<FormSection>
|
||||
<FormRow label = 'videothumbnail.hideSelfView'>
|
||||
<Switch
|
||||
checked = { Boolean(disableSelfView) }
|
||||
onChange = { onSelfViewToggled } />
|
||||
</FormRow>
|
||||
<FormRow label = 'settings.language'>
|
||||
<View style = { styles.languageButtonContainer as ViewStyle }>
|
||||
<TouchableHighlight onPress = { navigateToLanguageSelect }>
|
||||
<View style = { styles.languageButton as ViewStyle }>
|
||||
<Text
|
||||
style = { styles.languageText }>{t(`languages:${language}`)}</Text>
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconArrowRight } />
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</FormRow>
|
||||
</FormSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralSection;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback, useLayoutEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ScrollView, Text, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import i18next, { DEFAULT_LANGUAGE, LANGUAGES } from '../../../base/i18n/i18next';
|
||||
import { IconArrowLeft } from '../../../base/icons/svg';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import BaseThemeNative from '../../../base/ui/components/BaseTheme.native';
|
||||
import HeaderNavigationButton from '../../../mobile/navigation/components/HeaderNavigationButton';
|
||||
import { goBack, navigate } from '../../../mobile/navigation/components/settings/SettingsNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
const LanguageSelectView = ({ isInWelcomePage }: { isInWelcomePage?: boolean; }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigation = useNavigation();
|
||||
const { conference } = useSelector((state: IReduxState) => state['features/base/conference']);
|
||||
const { language: currentLanguage = DEFAULT_LANGUAGE } = i18next;
|
||||
|
||||
const setLanguage = useCallback(language => () => {
|
||||
i18next.changeLanguage(language);
|
||||
conference?.setTranscriptionLanguage(language);
|
||||
navigate(screen.settings.main);
|
||||
}, [ conference, i18next ]);
|
||||
|
||||
const headerLeft = () => (
|
||||
<HeaderNavigationButton
|
||||
color = { BaseThemeNative.palette.link01 }
|
||||
onPress = { goBack }
|
||||
src = { IconArrowLeft }
|
||||
style = { styles.backBtn }
|
||||
twoActions = { true } />
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft
|
||||
});
|
||||
}, [ navigation ]);
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
|
||||
// @ts-ignore
|
||||
safeAreaInsets = { [ !isInWelcomePage && 'bottom', 'left', 'right' ].filter(Boolean) }
|
||||
style = { styles.settingsViewContainer }>
|
||||
<ScrollView
|
||||
bounces = { isInWelcomePage }
|
||||
contentContainerStyle = { styles.profileView as ViewStyle }>
|
||||
{
|
||||
LANGUAGES.map(language => (
|
||||
<TouchableHighlight
|
||||
disabled = { currentLanguage === language }
|
||||
key = { language }
|
||||
onPress = { setLanguage(language) }>
|
||||
<View
|
||||
style = { styles.languageOption as ViewStyle }>
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
styles.fieldLabelText,
|
||||
currentLanguage === language && styles.selectedLanguage ] }>
|
||||
{ t(`languages:${language}`) }
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
))
|
||||
}
|
||||
</ScrollView>
|
||||
</JitsiScreen>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageSelectView;
|
||||
57
react/features/settings/components/native/LinksSection.tsx
Normal file
57
react/features/settings/components/native/LinksSection.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Linking, View, ViewStyle } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { getLegalUrls } from '../../../base/config/functions.native';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
|
||||
import FormSection from './FormSection';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
const LinksSection = () => {
|
||||
const {
|
||||
privacy,
|
||||
helpCentre,
|
||||
terms
|
||||
} = useSelector((state: IReduxState) => getLegalUrls(state));
|
||||
|
||||
const links = useMemo(() => [
|
||||
{
|
||||
label: 'settingsView.help',
|
||||
link: helpCentre
|
||||
},
|
||||
{
|
||||
label: 'settingsView.terms',
|
||||
link: terms
|
||||
},
|
||||
{
|
||||
label: 'settingsView.privacy',
|
||||
link: privacy
|
||||
}
|
||||
], [ privacy, helpCentre, terms ]);
|
||||
|
||||
const onLinkPress = useCallback(link => () => Linking.openURL(link), [ Linking ]);
|
||||
|
||||
return (
|
||||
<FormSection>
|
||||
<View style = { styles.linksSection as ViewStyle }>
|
||||
{
|
||||
links.map(({ label, link }) => (
|
||||
<Button
|
||||
accessibilityLabel = { label }
|
||||
key = { label }
|
||||
labelKey = { label }
|
||||
onClick = { onLinkPress(link) }
|
||||
style = { styles.linksButton }
|
||||
type = { BUTTON_TYPES.TERTIARY } />
|
||||
))
|
||||
}
|
||||
</View>
|
||||
</FormSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinksSection;
|
||||
21
react/features/settings/components/native/LogoutDialog.tsx
Normal file
21
react/features/settings/components/native/LogoutDialog.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import ConfirmDialog
|
||||
from '../../../base/dialog/components/native/ConfirmDialog';
|
||||
|
||||
interface ILogoutDialogProps extends WithTranslation {
|
||||
onLogout: Function;
|
||||
}
|
||||
|
||||
|
||||
const LogoutDialog: React.FC<ILogoutDialogProps> = ({ onLogout }: ILogoutDialogProps) => (
|
||||
<ConfirmDialog
|
||||
cancelLabel = 'dialog.Cancel'
|
||||
confirmLabel = 'dialog.Yes'
|
||||
descriptionKey = 'dialog.logoutQuestion'
|
||||
onSubmit = { onLogout } />
|
||||
);
|
||||
|
||||
export default LogoutDialog;
|
||||
|
||||
145
react/features/settings/components/native/ModeratorSection.tsx
Normal file
145
react/features/settings/components/native/ModeratorSection.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import {
|
||||
setFollowMe,
|
||||
setFollowMeRecorder,
|
||||
setStartMutedPolicy,
|
||||
setStartReactionsMuted
|
||||
} from '../../../base/conference/actions';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
import { getModeratorTabProps } from '../../functions.native';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
|
||||
const ModeratorSection = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
audioModerationEnabled,
|
||||
chatWithPermissionsEnabled,
|
||||
followMeActive,
|
||||
followMeEnabled,
|
||||
followMeRecorderActive,
|
||||
followMeRecorderEnabled,
|
||||
startAudioMuted,
|
||||
startVideoMuted,
|
||||
startReactionsMuted,
|
||||
videoModerationEnabled
|
||||
} = useSelector((state: IReduxState) => getModeratorTabProps(state));
|
||||
|
||||
const { disableReactionsModeration } = useSelector((state: IReduxState) => state['features/base/config']);
|
||||
|
||||
const onStartAudioMutedToggled = useCallback((enabled?: boolean) => {
|
||||
dispatch(setStartMutedPolicy(
|
||||
Boolean(enabled), Boolean(startVideoMuted)));
|
||||
}, [ startVideoMuted, dispatch, setStartMutedPolicy ]);
|
||||
|
||||
const onStartVideoMutedToggled = useCallback((enabled?: boolean) => {
|
||||
dispatch(setStartMutedPolicy(
|
||||
Boolean(startAudioMuted), Boolean(enabled)));
|
||||
}, [ startAudioMuted, dispatch, setStartMutedPolicy ]);
|
||||
|
||||
const onFollowMeToggled = useCallback((enabled?: boolean) => {
|
||||
dispatch(setFollowMe(Boolean(enabled)));
|
||||
}, [ dispatch, setFollowMe ]);
|
||||
|
||||
const onFollowMeRecorderToggled = useCallback((enabled?: boolean) => {
|
||||
dispatch(setFollowMeRecorder(Boolean(enabled)));
|
||||
}, [ dispatch, setFollowMeRecorder ]);
|
||||
|
||||
const onStartReactionsMutedToggled = useCallback((enabled?: boolean) => {
|
||||
dispatch(setStartReactionsMuted(Boolean(enabled), true));
|
||||
dispatch(updateSettings({ soundsReactions: enabled }));
|
||||
}, [ dispatch, updateSettings, setStartReactionsMuted ]);
|
||||
|
||||
const { conference } = useSelector((state: IReduxState) => state['features/base/conference']);
|
||||
const onChatWithPermissionsToggled = useCallback((enabled?: boolean) => {
|
||||
const currentPermissions = conference?.getMetadataHandler().getMetadata().permissions || {};
|
||||
|
||||
conference?.getMetadataHandler().setMetadata('permissions', {
|
||||
...currentPermissions,
|
||||
groupChatRestricted: enabled
|
||||
});
|
||||
}, [ dispatch, conference ]);
|
||||
|
||||
const followMeRecorderChecked = followMeRecorderEnabled && !followMeRecorderActive;
|
||||
|
||||
const moderationSettings = useMemo(() => {
|
||||
const moderation = [
|
||||
{
|
||||
disabled: audioModerationEnabled,
|
||||
label: 'settings.startAudioMuted',
|
||||
state: startAudioMuted,
|
||||
onChange: onStartAudioMutedToggled
|
||||
},
|
||||
{
|
||||
disabled: videoModerationEnabled,
|
||||
label: 'settings.startVideoMuted',
|
||||
state: startVideoMuted,
|
||||
onChange: onStartVideoMutedToggled
|
||||
},
|
||||
{
|
||||
disabled: followMeActive || followMeRecorderActive,
|
||||
label: 'settings.followMe',
|
||||
state: followMeEnabled && !followMeActive && !followMeRecorderChecked,
|
||||
onChange: onFollowMeToggled
|
||||
},
|
||||
{
|
||||
disabled: followMeRecorderActive || followMeActive,
|
||||
label: 'settings.followMeRecorder',
|
||||
state: followMeRecorderChecked,
|
||||
onChange: onFollowMeRecorderToggled
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
label: 'settings.startReactionsMuted',
|
||||
state: startReactionsMuted,
|
||||
onChange: onStartReactionsMutedToggled
|
||||
},
|
||||
{
|
||||
label: 'settings.chatWithPermissions',
|
||||
state: chatWithPermissionsEnabled,
|
||||
onChange: onChatWithPermissionsToggled
|
||||
},
|
||||
];
|
||||
|
||||
if (disableReactionsModeration) {
|
||||
moderation.pop();
|
||||
}
|
||||
|
||||
return moderation;
|
||||
}, [ startAudioMuted,
|
||||
startVideoMuted,
|
||||
followMeEnabled,
|
||||
followMeRecorderEnabled,
|
||||
disableReactionsModeration,
|
||||
onStartAudioMutedToggled,
|
||||
onStartVideoMutedToggled,
|
||||
onFollowMeToggled,
|
||||
onFollowMeRecorderToggled,
|
||||
onStartReactionsMutedToggled,
|
||||
startReactionsMuted ]);
|
||||
|
||||
return (
|
||||
<FormSection
|
||||
label = 'settings.playSounds'>
|
||||
{
|
||||
moderationSettings.map(({ label, state, onChange, disabled }) => (
|
||||
<FormRow
|
||||
key = { label }
|
||||
label = { label }>
|
||||
<Switch
|
||||
checked = { Boolean(state) }
|
||||
disabled = { disabled }
|
||||
onChange = { onChange } />
|
||||
</FormRow>
|
||||
))
|
||||
}
|
||||
</FormSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModeratorSection;
|
||||
@@ -0,0 +1,139 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
import { getNotificationsTabProps } from '../../functions.any';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSection from './FormSection';
|
||||
import styles from './styles';
|
||||
|
||||
const NotificationsSection = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
soundsIncomingMessage,
|
||||
soundsParticipantJoined,
|
||||
soundsParticipantKnocking,
|
||||
soundsParticipantLeft,
|
||||
soundsReactions,
|
||||
soundsTalkWhileMuted,
|
||||
enableReactions,
|
||||
enabledNotifications,
|
||||
disabledSounds,
|
||||
moderatorMutedSoundsReactions
|
||||
} = useSelector((state: IReduxState) => getNotificationsTabProps(state));
|
||||
|
||||
const sounds = useMemo(() => {
|
||||
const partialSounds = [
|
||||
{
|
||||
label: 'settings.reactions',
|
||||
state: soundsReactions,
|
||||
name: 'soundsReactions',
|
||||
disabled: Boolean(moderatorMutedSoundsReactions
|
||||
|| disabledSounds.includes('REACTION_SOUND'))
|
||||
},
|
||||
{
|
||||
label: 'settings.incomingMessage',
|
||||
state: soundsIncomingMessage,
|
||||
name: 'soundsIncomingMessage'
|
||||
},
|
||||
{
|
||||
label: 'settings.participantJoined',
|
||||
state: soundsParticipantJoined,
|
||||
name: 'soundsParticipantJoined'
|
||||
},
|
||||
{
|
||||
label: 'settings.participantLeft',
|
||||
state: soundsParticipantLeft,
|
||||
name: 'soundsParticipantLeft'
|
||||
},
|
||||
{
|
||||
label: 'settings.talkWhileMuted',
|
||||
state: soundsTalkWhileMuted,
|
||||
name: 'soundsTalkWhileMuted'
|
||||
},
|
||||
{
|
||||
label: 'settings.participantKnocking',
|
||||
state: soundsParticipantKnocking,
|
||||
name: 'soundsParticipantKnocking'
|
||||
}
|
||||
];
|
||||
|
||||
if (!enableReactions) {
|
||||
partialSounds.shift();
|
||||
}
|
||||
|
||||
return partialSounds;
|
||||
|
||||
}, [ soundsReactions,
|
||||
soundsIncomingMessage,
|
||||
soundsParticipantJoined,
|
||||
soundsParticipantLeft,
|
||||
soundsTalkWhileMuted,
|
||||
soundsParticipantKnocking,
|
||||
enableReactions ]);
|
||||
|
||||
const onSoundToggled = useCallback((name: string) => (enabled?: boolean) => {
|
||||
dispatch(updateSettings({ [name]: enabled }));
|
||||
}, [ dispatch, updateSettings ]);
|
||||
|
||||
|
||||
const onNotificationToggled = useCallback((name: string) => (enabled?: boolean) => {
|
||||
dispatch(updateSettings({
|
||||
userSelectedNotifications: {
|
||||
...enabledNotifications,
|
||||
[name]: Boolean(enabled)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}, [ dispatch, updateSettings, enabledNotifications ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormSection
|
||||
label = 'settings.playSounds'>
|
||||
{
|
||||
sounds.map(({ label, state, name, disabled }) => (
|
||||
<FormRow
|
||||
key = { label }
|
||||
label = { label }>
|
||||
<Switch
|
||||
checked = { Boolean(state) }
|
||||
disabled = { disabled }
|
||||
onChange = { onSoundToggled(name) } />
|
||||
</FormRow>
|
||||
))
|
||||
}
|
||||
</FormSection>
|
||||
{
|
||||
Object.keys(enabledNotifications).length > 0 && (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
<Divider style = { styles.fieldSeparator } />
|
||||
<FormSection
|
||||
label = 'notify.displayNotifications'>
|
||||
{
|
||||
Object.keys(enabledNotifications).map(name => (
|
||||
<FormRow
|
||||
key = { name }
|
||||
label = { name }>
|
||||
<Switch
|
||||
checked = { Boolean(enabledNotifications[name]) }
|
||||
onChange = { onNotificationToggled(name) } />
|
||||
</FormRow>)
|
||||
)
|
||||
}
|
||||
</FormSection>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationsSection;
|
||||
165
react/features/settings/components/native/ProfileView.tsx
Normal file
165
react/features/settings/components/native/ProfileView.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ScrollView, Text, View, ViewStyle } from 'react-native';
|
||||
import { Edge } from 'react-native-safe-area-context';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { login, logout } from '../../../authentication/actions.native';
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import { IconArrowLeft } from '../../../base/icons/svg';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import BaseThemeNative from '../../../base/ui/components/BaseTheme.native';
|
||||
import Button from '../../../base/ui/components/native/Button';
|
||||
import Input from '../../../base/ui/components/native/Input';
|
||||
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
|
||||
import HeaderNavigationButton
|
||||
from '../../../mobile/navigation/components/HeaderNavigationButton';
|
||||
import {
|
||||
goBack,
|
||||
navigate
|
||||
} from '../../../mobile/navigation/components/settings/SettingsNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
|
||||
import FormSection from './FormSection';
|
||||
import { AVATAR_SIZE } from './constants';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
const ProfileView = ({ isInWelcomePage }: {
|
||||
isInWelcomePage?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const { displayName: reduxDisplayName, email: reduxEmail } = useSelector(
|
||||
(state: IReduxState) => state['features/base/settings']
|
||||
);
|
||||
const participant = useSelector((state: IReduxState) => getLocalParticipant(state));
|
||||
const { locationURL } = useSelector((state: IReduxState) => state['features/base/connection']);
|
||||
|
||||
const [ displayName, setDisplayName ] = useState(reduxDisplayName);
|
||||
const [ email, setEmail ] = useState(reduxEmail);
|
||||
|
||||
const { authLogin: isAutenticated } = useSelector((state: IReduxState) => state['features/base/conference']);
|
||||
|
||||
const onDisplayNameChanged = useCallback(newDisplayName => {
|
||||
setDisplayName(newDisplayName);
|
||||
}, [ setDisplayName ]);
|
||||
|
||||
const onEmailChanged = useCallback(newEmail => {
|
||||
setEmail(newEmail);
|
||||
}, [ setEmail ]);
|
||||
|
||||
const onApplySettings = useCallback(() => {
|
||||
dispatch(updateSettings({
|
||||
displayName,
|
||||
email
|
||||
}));
|
||||
|
||||
navigate(screen.settings.main);
|
||||
},
|
||||
[ dispatch, updateSettings, email, displayName ]);
|
||||
|
||||
const onLogin = useCallback(() => {
|
||||
dispatch(login());
|
||||
}, [ dispatch ]);
|
||||
|
||||
const onLogout = useCallback(() => {
|
||||
dispatch(logout());
|
||||
}, [ dispatch ]);
|
||||
|
||||
const headerLeft = () => (
|
||||
<HeaderNavigationButton
|
||||
color = { BaseThemeNative.palette.link01 }
|
||||
onPress = { goBack }
|
||||
src = { IconArrowLeft }
|
||||
style = { styles.backBtn }
|
||||
twoActions = { true } />
|
||||
);
|
||||
|
||||
const headerRight = () => {
|
||||
if (isAutenticated) {
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
label = { t('toolbar.logout') }
|
||||
onPress = { onLogout }
|
||||
style = { styles.logBtn }
|
||||
twoActions = { true } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
label = { t('toolbar.login') }
|
||||
onPress = { onLogin }
|
||||
style = { styles.logBtn }
|
||||
twoActions = { true } />
|
||||
);
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft,
|
||||
headerRight: !isInWelcomePage
|
||||
&& !locationURL?.hostname?.includes('8x8.vc')
|
||||
&& headerRight
|
||||
});
|
||||
}, [ navigation ]);
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
hasBottomTextInput = { true }
|
||||
|
||||
// @ts-ignore
|
||||
safeAreaInsets = { [ !isInWelcomePage && 'bottom', 'left', 'right' ].filter(Boolean) as Edge[] }
|
||||
style = { styles.settingsViewContainer }>
|
||||
<ScrollView
|
||||
bounces = { isInWelcomePage }
|
||||
contentContainerStyle = { styles.profileView as ViewStyle }>
|
||||
<View>
|
||||
<View style = { styles.avatarContainer as ViewStyle }>
|
||||
<Avatar
|
||||
participantId = { participant?.id }
|
||||
size = { AVATAR_SIZE } />
|
||||
</View>
|
||||
<FormSection>
|
||||
<Input
|
||||
customStyles = {{ container: styles.customContainer }}
|
||||
label = { t('settingsView.displayName') }
|
||||
onChange = { onDisplayNameChanged }
|
||||
placeholder = { t('settingsView.displayNamePlaceholderText') }
|
||||
textContentType = { 'name' } // iOS only
|
||||
value = { displayName ?? '' } />
|
||||
<Input
|
||||
autoCapitalize = 'none'
|
||||
customStyles = {{ container: styles.customContainer }}
|
||||
keyboardType = { 'email-address' }
|
||||
label = { t('settingsView.email') }
|
||||
onChange = { onEmailChanged }
|
||||
placeholder = { t('settingsView.emailPlaceholderText') }
|
||||
textContentType = { 'emailAddress' } // iOS only
|
||||
value = { email ?? '' } />
|
||||
<Text style = { styles.gavatarMessageContainer }>
|
||||
{ t('settingsView.gavatarMessage') }
|
||||
</Text>
|
||||
</FormSection>
|
||||
</View>
|
||||
<Button
|
||||
accessibilityLabel = { t('settingsView.apply') }
|
||||
labelKey = { 'settingsView.apply' }
|
||||
onClick = { onApplySettings }
|
||||
style = { styles.applyProfileSettingsButton }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</ScrollView>
|
||||
</JitsiScreen>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileView;
|
||||
101
react/features/settings/components/native/SettingsView.tsx
Normal file
101
react/features/settings/components/native/SettingsView.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
ScrollView,
|
||||
Text,
|
||||
TextStyle,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
ViewStyle
|
||||
} from 'react-native';
|
||||
import { Divider } from 'react-native-paper';
|
||||
import { Edge } from 'react-native-safe-area-context';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Avatar from '../../../base/avatar/components/Avatar';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconArrowRight } from '../../../base/icons/svg';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
import { navigate } from '../../../mobile/navigation/components/settings/SettingsNavigationContainerRef';
|
||||
import { screen } from '../../../mobile/navigation/routes';
|
||||
import { shouldShowModeratorSettings } from '../../functions.native';
|
||||
|
||||
import AdvancedSection from './AdvancedSection';
|
||||
import ConferenceSection from './ConferenceSection';
|
||||
import GeneralSection from './GeneralSection';
|
||||
import LinksSection from './LinksSection';
|
||||
import ModeratorSection from './ModeratorSection';
|
||||
import NotificationsSection from './NotificationsSection';
|
||||
import { AVATAR_SIZE } from './constants';
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
interface IProps {
|
||||
|
||||
isInWelcomePage?: boolean | undefined;
|
||||
}
|
||||
|
||||
const SettingsView = ({ isInWelcomePage }: IProps) => {
|
||||
const { displayName } = useSelector((state: IReduxState) => state['features/base/settings']);
|
||||
const localParticipant = useSelector((state: IReduxState) => getLocalParticipant(state));
|
||||
const showModeratorSettings = useSelector((state: IReduxState) => shouldShowModeratorSettings(state));
|
||||
const { visible } = useSelector((state: IReduxState) => state['features/settings']);
|
||||
|
||||
const addBottomInset = !isInWelcomePage;
|
||||
const localParticipantId = localParticipant?.id;
|
||||
const scrollBounces = Boolean(isInWelcomePage);
|
||||
|
||||
if (visible !== undefined && !visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
disableForcedKeyboardDismiss = { true }
|
||||
safeAreaInsets = { [ addBottomInset && 'bottom', 'left', 'right' ].filter(Boolean) as Edge[] }
|
||||
style = { styles.settingsViewContainer }>
|
||||
<ScrollView bounces = { scrollBounces }>
|
||||
<View style = { styles.profileContainerWrapper as ViewStyle }>
|
||||
<TouchableHighlight
|
||||
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
onPress = { () => navigate(screen.settings.profile) }>
|
||||
<View
|
||||
style = { styles.profileContainer as ViewStyle }>
|
||||
<Avatar
|
||||
participantId = { localParticipantId }
|
||||
size = { AVATAR_SIZE } />
|
||||
<Text style = { styles.displayName as TextStyle }>
|
||||
{ displayName }
|
||||
</Text>
|
||||
<Icon
|
||||
size = { 24 }
|
||||
src = { IconArrowRight }
|
||||
style = { styles.profileViewArrow } />
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
<GeneralSection />
|
||||
{ isInWelcomePage && <>
|
||||
<Divider style = { styles.fieldSeparator as ViewStyle } />
|
||||
<ConferenceSection />
|
||||
</> }
|
||||
<Divider style = { styles.fieldSeparator as ViewStyle } />
|
||||
<NotificationsSection />
|
||||
|
||||
{ showModeratorSettings
|
||||
&& <>
|
||||
<Divider style = { styles.fieldSeparator as ViewStyle } />
|
||||
<ModeratorSection />
|
||||
</> }
|
||||
<Divider style = { styles.fieldSeparator as ViewStyle } />
|
||||
<AdvancedSection />
|
||||
<Divider style = { styles.fieldSeparator as ViewStyle } />
|
||||
<LinksSection />
|
||||
</ScrollView>
|
||||
</JitsiScreen>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsView;
|
||||
1
react/features/settings/components/native/constants.ts
Normal file
1
react/features/settings/components/native/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const AVATAR_SIZE = 64;
|
||||
244
react/features/settings/components/native/styles.ts
Normal file
244
react/features/settings/components/native/styles.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
|
||||
export const ANDROID_UNDERLINE_COLOR = 'transparent';
|
||||
export const PLACEHOLDER_COLOR = BaseTheme.palette.focus01;
|
||||
|
||||
/**
|
||||
* The styles of the native components of the feature {@code settings}.
|
||||
*/
|
||||
export default {
|
||||
|
||||
profileContainerWrapper: {
|
||||
margin: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
profileContainer: {
|
||||
backgroundColor: BaseTheme.palette.ui02,
|
||||
borderRadius: BaseTheme.shape.borderRadius,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
padding: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
profileView: {
|
||||
flexGrow: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
applyProfileSettingsButton: {
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
marginVertical: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
avatarContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
padding: BaseTheme.spacing[3],
|
||||
margin: BaseTheme.spacing[4]
|
||||
},
|
||||
|
||||
gavatarMessageContainer: {
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
color: BaseTheme.palette.text02,
|
||||
marginTop: -BaseTheme.spacing[2],
|
||||
...BaseTheme.typography.bodyShortRegular
|
||||
},
|
||||
|
||||
displayName: {
|
||||
...BaseTheme.typography.bodyLongRegularLarge,
|
||||
color: BaseTheme.palette.text01,
|
||||
marginLeft: BaseTheme.spacing[3],
|
||||
position: 'relative'
|
||||
},
|
||||
|
||||
profileViewArrow: {
|
||||
position: 'absolute',
|
||||
right: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for screen container.
|
||||
*/
|
||||
settingsViewContainer: {
|
||||
backgroundColor: BaseTheme.palette.ui01,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* Standardized style for a field container {@code View}.
|
||||
*/
|
||||
fieldContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
minHeight: BaseTheme.spacing[8],
|
||||
paddingHorizontal: BaseTheme.spacing[2],
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
/**
|
||||
* * Appended style for column layout fields.
|
||||
*/
|
||||
fieldContainerColumn: {
|
||||
alignItems: 'flex-start',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
/**
|
||||
* Standard container for a {@code View} containing a field label.
|
||||
*/
|
||||
fieldLabelContainer: {
|
||||
alignItems: 'center',
|
||||
flexShrink: 1,
|
||||
flexDirection: 'row',
|
||||
paddingLeft: BaseTheme.spacing[3],
|
||||
paddingRight: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
/**
|
||||
* Text of the field labels on the form.
|
||||
*/
|
||||
fieldLabelText: {
|
||||
...BaseTheme.typography.bodyShortRegularLarge
|
||||
},
|
||||
|
||||
/**
|
||||
* Field container style for all but last row {@code View}.
|
||||
*/
|
||||
fieldSeparator: {
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
borderBottomWidth: 1,
|
||||
borderColor: BaseTheme.palette.ui05,
|
||||
marginVertical: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for the {@code View} containing each
|
||||
* field values (the actual field).
|
||||
*/
|
||||
fieldValueContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
flexShrink: 1,
|
||||
justifyContent: 'flex-end',
|
||||
paddingRight: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for the form section separator titles.
|
||||
*/
|
||||
|
||||
formSectionTitleContent: {
|
||||
backgroundColor: BaseTheme.palette.ui02,
|
||||
paddingVertical: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
formSectionTitleText: {
|
||||
...BaseTheme.typography.bodyShortBold,
|
||||
color: BaseTheme.palette.text02,
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
marginVertical: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
/**
|
||||
* Global {@code Text} color for the components.
|
||||
*/
|
||||
text: {
|
||||
color: BaseTheme.palette.text01
|
||||
},
|
||||
|
||||
/**
|
||||
* Text input container style.
|
||||
*/
|
||||
customContainer: {
|
||||
marginBottom: BaseTheme.spacing[3],
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
marginTop: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
languageButtonContainer: {
|
||||
borderRadius: BaseTheme.shape.borderRadius,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
|
||||
languageButton: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: BaseTheme.spacing[7],
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
languageOption: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
height: BaseTheme.spacing[6],
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
borderBottomWidth: 1,
|
||||
borderColor: BaseTheme.palette.ui05
|
||||
},
|
||||
|
||||
selectedLanguage: {
|
||||
color: BaseTheme.palette.text03
|
||||
},
|
||||
|
||||
languageText: {
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
color: BaseTheme.palette.text01,
|
||||
marginHorizontal: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
/**
|
||||
* Standard text input field style.
|
||||
*/
|
||||
textInputField: {
|
||||
color: BaseTheme.palette.field01,
|
||||
flex: 1,
|
||||
...BaseTheme.typography.bodyShortRegularLarge,
|
||||
textAlign: 'right'
|
||||
},
|
||||
|
||||
/**
|
||||
* Appended style for column layout fields.
|
||||
*/
|
||||
textInputFieldColumn: {
|
||||
backgroundColor: 'rgb(245, 245, 245)',
|
||||
borderRadius: 8,
|
||||
marginVertical: 5,
|
||||
paddingVertical: 3,
|
||||
textAlign: 'left'
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for screen container.
|
||||
*/
|
||||
screenContainer: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
linksSection: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
marginHorizontal: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
linksButton: {
|
||||
width: '33%',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
...BaseTheme.typography.bodyShortBoldLarge
|
||||
},
|
||||
|
||||
logBtn: {
|
||||
marginRight: BaseTheme.spacing[3]
|
||||
},
|
||||
|
||||
backBtn: {
|
||||
marginLeft: BaseTheme.spacing[3]
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user