diff --git a/native/community-creation/community-configuration.react.js b/native/community-creation/community-configuration.react.js
index ca177ca02..910c2904c 100644
--- a/native/community-creation/community-configuration.react.js
+++ b/native/community-creation/community-configuration.react.js
@@ -1,215 +1,216 @@
// @flow
import * as React from 'react';
import { Text, View } from 'react-native';
import {
useNewThinThread,
newThreadActionTypes,
} from 'lib/actions/thread-actions.js';
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
import { threadInfoSelector } from 'lib/selectors/thread-selectors.js';
import type { LoadingStatus } from 'lib/types/loading-types.js';
import { threadTypes } from 'lib/types/thread-types-enum.js';
import type { NewThreadResult } from 'lib/types/thread-types.js';
import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
import CommunityCreationKeyserverLabel from './community-creation-keyserver-label.react.js';
import type { CommunityCreationNavigationProp } from './community-creation-navigator.react.js';
import RegistrationButtonContainer from '../account/registration/registration-button-container.react.js';
import RegistrationButton from '../account/registration/registration-button.react.js';
import RegistrationContainer from '../account/registration/registration-container.react.js';
import RegistrationContentContainer from '../account/registration/registration-content-container.react.js';
import { useNavigateToThread } from '../chat/message-list-types.js';
import {
ThreadSettingsCategoryFooter,
ThreadSettingsCategoryHeader,
} from '../chat/settings/thread-settings-category.react.js';
import EnumSettingsOption from '../components/enum-settings-option.react.js';
import TextInput from '../components/text-input.react.js';
import { useCalendarQuery } from '../navigation/nav-selectors.js';
import { type NavigationRoute } from '../navigation/route-names.js';
import { useSelector } from '../redux/redux-utils.js';
import { useColors, useStyles } from '../themes/colors.js';
type Props = {
+navigation: CommunityCreationNavigationProp<'CommunityConfiguration'>,
+route: NavigationRoute<'CommunityConfiguration'>,
};
const createNewCommunityLoadingStatusSelector =
createLoadingStatusSelector(newThreadActionTypes);
// eslint-disable-next-line no-unused-vars
function CommunityConfiguration(props: Props): React.Node {
const styles = useStyles(unboundStyles);
const colors = useColors();
const dispatchActionPromise = useDispatchActionPromise();
const callNewThinThread = useNewThinThread();
const calendarQueryFunc = useCalendarQuery();
const createNewCommunityLoadingStatus: LoadingStatus = useSelector(
createNewCommunityLoadingStatusSelector,
);
const [pendingCommunityName, setPendingCommunityName] = React.useState('');
const [announcementSetting, setAnnouncementSetting] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState();
const onChangePendingCommunityName = React.useCallback((newValue: string) => {
setErrorMessage();
setPendingCommunityName(newValue);
}, []);
const callCreateNewCommunity = React.useCallback(async () => {
const calendarQuery = calendarQueryFunc();
try {
const newThreadResult: NewThreadResult = await callNewThinThread({
name: pendingCommunityName,
type: announcementSetting
? threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT
: threadTypes.COMMUNITY_ROOT,
calendarQuery,
});
return newThreadResult;
} catch (e) {
setErrorMessage('Community creation failed. Please try again.');
throw e;
}
}, [
announcementSetting,
calendarQueryFunc,
callNewThinThread,
pendingCommunityName,
]);
const [newCommunityID, setNewCommunityID] = React.useState(null);
const createNewCommunity = React.useCallback(async () => {
setErrorMessage();
const newThreadResultPromise = callCreateNewCommunity();
void dispatchActionPromise(newThreadActionTypes, newThreadResultPromise);
const newThreadResult = await newThreadResultPromise;
setNewCommunityID(newThreadResult.newThreadID);
}, [callCreateNewCommunity, dispatchActionPromise]);
const navigateToThread = useNavigateToThread();
const threadInfos = useSelector(threadInfoSelector);
React.useEffect(() => {
if (!newCommunityID) {
return;
}
const communityThreadInfo = threadInfos[newCommunityID];
if (communityThreadInfo) {
navigateToThread({ threadInfo: communityThreadInfo });
}
}, [navigateToThread, newCommunityID, threadInfos]);
const onCheckBoxPress = React.useCallback(() => {
setErrorMessage();
setAnnouncementSetting(!announcementSetting);
}, [announcementSetting]);
const enumSettingsOptionDescription =
'Make it so only admins can post to ' +
'the root channel of the community.';
return (
Name
You may edit your community’s image and name later.
{errorMessage}
);
}
const unboundStyles = {
containerPaddingOverride: {
padding: 0,
},
communityNameRow: {
backgroundColor: 'panelForeground',
flexDirection: 'row',
paddingHorizontal: 24,
paddingVertical: 8,
},
communityNameLabel: {
color: 'panelForegroundTertiaryLabel',
fontSize: 16,
width: 96,
},
communityNamePendingValue: {
color: 'panelForegroundSecondaryLabel',
flex: 1,
fontFamily: 'Arial',
fontSize: 16,
margin: 0,
paddingLeft: 4,
paddingRight: 0,
paddingVertical: 0,
borderBottomColor: 'transparent',
},
communityNameNoticeContainer: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
},
communityNameNoticeText: {
color: 'panelForegroundTertiaryLabel',
},
errorMessageContainer: {
alignItems: 'center',
},
errorMessageText: {
color: 'redText',
},
};
export default CommunityConfiguration;
diff --git a/native/components/enum-settings-option.react.js b/native/components/enum-settings-option.react.js
index 2eac60c2a..869387eca 100644
--- a/native/components/enum-settings-option.react.js
+++ b/native/components/enum-settings-option.react.js
@@ -1,110 +1,155 @@
// @flow
import * as React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import CommIcon from '../components/comm-icon.react.js';
import { useColors, useStyles } from '../themes/colors.js';
+type InputType = 'radio' | 'checkbox';
+
type EnumSettingsOptionProps = {
+icon?: string,
+name: string,
+description: string,
+enumValue: boolean,
+onEnumValuePress: () => mixed,
+ +type?: InputType,
};
function EnumSettingsOption(props: EnumSettingsOptionProps): React.Node {
const styles = useStyles(unboundStyles);
const colors = useColors();
- const { icon, name, description, enumValue, onEnumValuePress } = props;
+ const {
+ icon,
+ name,
+ description,
+ enumValue,
+ onEnumValuePress,
+ type = 'radio',
+ } = props;
const enumIcon = React.useMemo(() => {
if (!icon) {
return null;
}
return (
);
}, [icon, styles.enumIcon, colors.purpleButton]);
- const checkBoxFill = enumValue ? (
-
- ) : null;
-
const infoContainerStyle = React.useMemo(
() =>
props.icon
? styles.enumInfoContainer
: { ...styles.enumInfoContainer, marginLeft: 10 },
[props.icon, styles.enumInfoContainer],
);
+ const enumInputStyles = React.useMemo(() => {
+ const style = [styles.enumInput];
+
+ if (type === 'radio') {
+ style.push(styles.radio);
+ } else {
+ style.push(styles.checkBox);
+ }
+
+ return style;
+ }, [styles.checkBox, styles.enumInput, styles.radio, type]);
+
+ const enumInputFilledStyles = React.useMemo(() => {
+ const style = [styles.enumInputFill];
+
+ if (type === 'radio') {
+ style.push(styles.radioFill);
+ } else {
+ style.push(styles.checkBoxFill);
+ }
+
+ return style;
+ }, [styles.checkBoxFill, styles.enumInputFill, styles.radioFill, type]);
+
+ const enumInputFill = React.useMemo(
+ () => (enumValue ? : null),
+ [enumValue, enumInputFilledStyles],
+ );
+
return (
{enumIcon}
{name}
{description}
- {checkBoxFill}
+ {enumInputFill}
);
}
const unboundStyles = {
enumCell: {
flexDirection: 'row',
height: 96,
backgroundColor: 'panelForeground',
},
enumIcon: {
padding: 16,
},
enumInfoContainer: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-evenly',
padding: 8,
},
enumInfoName: {
color: 'panelForegroundLabel',
fontSize: 18,
lineHeight: 24,
},
enumInfoDescription: {
color: 'panelForegroundSecondaryLabel',
lineHeight: 18,
},
- enumCheckBoxContainer: {
+ enumInputContainer: {
padding: 22,
justifyContent: 'center',
alignItems: 'center',
},
- enumCheckBox: {
+ enumInput: {
height: 32,
width: 32,
- borderRadius: 3.5,
borderWidth: 1,
borderColor: 'panelSecondaryForegroundBorder',
justifyContent: 'center',
alignItems: 'center',
},
- enumCheckBoxFill: {
+ checkBox: {
+ borderRadius: 3.5,
+ },
+ radio: {
+ borderRadius: 16,
+ },
+ enumInputFill: {
height: 20,
width: 20,
- borderRadius: 2.1875,
backgroundColor: 'panelForegroundSecondaryLabel',
},
+ checkBoxFill: {
+ borderRadius: 2.1875,
+ },
+ radioFill: {
+ borderRadius: 10,
+ },
};
export default EnumSettingsOption;
diff --git a/native/roles/create-roles-screen.react.js b/native/roles/create-roles-screen.react.js
index f85dd140c..85f489ae2 100644
--- a/native/roles/create-roles-screen.react.js
+++ b/native/roles/create-roles-screen.react.js
@@ -1,318 +1,319 @@
// @flow
import * as React from 'react';
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { modifyCommunityRoleActionTypes } from 'lib/actions/thread-actions.js';
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
import type { LoadingStatus } from 'lib/types/loading-types.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
import {
type UserSurfacedPermission,
type UserSurfacedPermissionOption,
userSurfacedPermissionOptions,
} from 'lib/types/thread-permission-types.js';
import CreateRolesHeaderRightButton from './create-roles-header-right-button.react.js';
import type { RolesNavigationProp } from './roles-navigator.react.js';
import EnumSettingsOption from '../components/enum-settings-option.react.js';
import SWMansionIcon from '../components/swmansion-icon.react.js';
import TextInput from '../components/text-input.react.js';
import type { NavigationRoute } from '../navigation/route-names.js';
import { useSelector } from '../redux/redux-utils.js';
import { useStyles } from '../themes/colors.js';
export type CreateRolesScreenParams = {
+threadInfo: ThreadInfo,
+action: 'create_role' | 'edit_role',
+existingRoleID?: string,
+roleName: string,
+rolePermissions: $ReadOnlySet,
};
type CreateRolesScreenProps = {
+navigation: RolesNavigationProp<'CreateRolesScreen'>,
+route: NavigationRoute<'CreateRolesScreen'>,
};
const createRolesLoadingStatusSelector = createLoadingStatusSelector(
modifyCommunityRoleActionTypes,
);
function CreateRolesScreen(props: CreateRolesScreenProps): React.Node {
const {
threadInfo,
action,
existingRoleID,
roleName: defaultRoleName,
rolePermissions: defaultRolePermissions,
} = props.route.params;
const createRolesLoadingStatus: LoadingStatus = useSelector(
createRolesLoadingStatusSelector,
);
const [customRoleName, setCustomRoleName] =
React.useState(defaultRoleName);
const [selectedPermissions, setSelectedPermissions] = React.useState<
$ReadOnlySet,
>(defaultRolePermissions);
const [roleCreationFailed, setRoleCreationFailed] =
React.useState(false);
const styles = useStyles(unboundStyles);
const errorStyles = React.useMemo(
() =>
roleCreationFailed
? [styles.errorContainer, styles.errorContainerVisible]
: styles.errorContainer,
[roleCreationFailed, styles.errorContainer, styles.errorContainerVisible],
);
const onClearPermissions = React.useCallback(() => {
setSelectedPermissions(new Set());
}, []);
const isSelectedPermissionsEmpty = selectedPermissions.size === 0;
const clearPermissionsText = React.useMemo(() => {
const textStyle = isSelectedPermissionsEmpty
? styles.clearPermissionsTextDisabled
: styles.clearPermissionsText;
return (
Clear permissions
);
}, [
isSelectedPermissionsEmpty,
onClearPermissions,
styles.clearPermissionsText,
styles.clearPermissionsTextDisabled,
]);
const isUserSurfacedPermissionSelected = React.useCallback(
(option: UserSurfacedPermissionOption) =>
selectedPermissions.has(option.userSurfacedPermission),
[selectedPermissions],
);
const onEnumValuePress = React.useCallback(
(option: UserSurfacedPermissionOption) =>
setSelectedPermissions(currentPermissions => {
if (currentPermissions.has(option.userSurfacedPermission)) {
const newPermissions = new Set(currentPermissions);
newPermissions.delete(option.userSurfacedPermission);
return newPermissions;
} else {
return new Set([
...currentPermissions,
option.userSurfacedPermission,
]);
}
}),
[],
);
React.useEffect(
() =>
props.navigation.setParams({
threadInfo,
action,
existingRoleID,
roleName: customRoleName,
rolePermissions: selectedPermissions,
}),
[
props.navigation,
threadInfo,
action,
existingRoleID,
customRoleName,
selectedPermissions,
],
);
const permissionsList = React.useMemo(
() =>
[...userSurfacedPermissionOptions].map(permission => (
onEnumValuePress(permission)}
+ type="checkbox"
/>
)),
[isUserSurfacedPermissionSelected, onEnumValuePress],
);
const onChangeRoleNameInput = React.useCallback((roleName: string) => {
setRoleCreationFailed(false);
setCustomRoleName(roleName);
}, []);
React.useEffect(
() =>
props.navigation.setOptions({
headerRight: () => {
if (createRolesLoadingStatus === 'loading') {
return (
);
}
return (
);
},
}),
[
createRolesLoadingStatus,
props.navigation,
styles.activityIndicator,
props.route,
],
);
const createRolesScreen = React.useMemo(
() => (
ROLE NAME
There is already a role with this name in the community
PERMISSIONS
{clearPermissionsText}
{permissionsList}
),
[
clearPermissionsText,
customRoleName,
errorStyles,
onChangeRoleNameInput,
permissionsList,
styles.errorText,
styles.pencilIcon,
styles.permissionsContainer,
styles.permissionsHeader,
styles.permissionsListContainer,
styles.permissionsListContentContainer,
styles.permissionsText,
styles.roleInput,
styles.roleInputComponent,
styles.roleNameContainer,
styles.roleNameText,
],
);
return createRolesScreen;
}
const unboundStyles = {
roleNameContainer: {
marginTop: 30,
},
roleNameText: {
color: 'panelBackgroundLabel',
fontSize: 12,
marginBottom: 5,
marginLeft: 10,
},
roleInput: {
backgroundColor: 'panelForeground',
padding: 12,
flexDirection: 'row',
justifyContent: 'space-between',
},
roleInputComponent: {
color: 'panelForegroundLabel',
fontSize: 16,
},
pencilIcon: {
color: 'panelInputSecondaryForeground',
},
errorContainer: {
marginTop: 10,
alignItems: 'center',
opacity: 0,
},
errorContainerVisible: {
opacity: 1,
},
errorText: {
color: 'redText',
fontSize: 14,
},
permissionsContainer: {
marginTop: 20,
paddingBottom: 220,
},
permissionsHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
},
permissionsText: {
color: 'panelBackgroundLabel',
fontSize: 12,
marginLeft: 10,
},
clearPermissionsText: {
color: 'purpleLink',
fontSize: 12,
marginRight: 15,
},
clearPermissionsTextDisabled: {
color: 'disabledButton',
fontSize: 12,
marginRight: 15,
},
permissionsListContainer: {
backgroundColor: 'panelForeground',
marginTop: 10,
},
permissionsListContentContainer: {
paddingBottom: 48,
},
activityIndicator: {
paddingRight: 15,
},
};
export default CreateRolesScreen;