Page MenuHomePhabricator

D8391.id28500.diff
No OneTemporary

D8391.id28500.diff

diff --git a/lib/types/thread-permission-types.js b/lib/types/thread-permission-types.js
--- a/lib/types/thread-permission-types.js
+++ b/lib/types/thread-permission-types.js
@@ -6,6 +6,10 @@
import { values } from '../utils/objects.js';
import { tBool, tShape } from '../utils/validation-utils.js';
+// When a new permission is added, if it should be configurable for a role, it
+// should be either added to an existing set or a new set alongside a
+// new user-facing permission. If it is a permission that should be ensured
+// across all roles, it should be added to `universalCommunityPermissions`.
export const threadPermissions = Object.freeze({
KNOW_OF: 'know_of',
MEMBERSHIP_DEPRECATED: 'membership',
@@ -84,6 +88,265 @@
export type ThreadPermissionFilterPrefix = $Values<
typeof threadPermissionFilterPrefixes,
>;
+
+// These are the set of user-facing permissions that we display as configurable
+// to the user when they are creating a custom role for their given community.
+// They are per-community rather than per-thread, so when configured they are
+// to be expected to be propagated across the community. Also notably,
+// `threadPermissions` is used on the keyserver for permission checks to
+// validate actions, but these `userSurfacedPermissions` are only used
+// on the client for the UI and propagated to the server. The
+// `configurableCommunityPermissions` mapping below is the association between
+// each userSurfacedPermission and a set of threadPermissions.
+export const userSurfacedPermissions = Object.freeze({
+ EDIT_CALENDAR: 'edit_calendar',
+ KNOW_OF_SECRET_CHANNELS: 'know_of_secret_channels',
+ VOICED_IN_ANNOUNCEMENT_CHANNELS: 'voiced_in_announcement_channels',
+ CREATE_AND_EDIT_CHANNELS: 'create_and_edit_channels',
+ DELETE_CHANNELS: 'delete_channels',
+ ADD_MEMBERS: 'add_members',
+ REMOVE_MEMBERS: 'remove_members',
+ CHANGE_ROLES: 'change_roles',
+ EDIT_VISIBILITY: 'edit_visibility',
+ MANAGE_PINS: 'manage_pins',
+ REACT_TO_MESSAGES: 'react_to_messages',
+ EDIT_MESSAGES: 'edit_messages',
+ MANAGE_INVITE_LINKS: 'manage_invite_links',
+});
+export type UserSurfacedPermission = $Values<typeof userSurfacedPermissions>;
+
+const editCalendarPermission = {
+ title: 'Edit calendar',
+ description: 'Allows members to edit the community calendar',
+ userSurfacedPermission: userSurfacedPermissions.EDIT_CALENDAR,
+};
+const editEntries = threadPermissions.EDIT_ENTRIES;
+const descendantEditEntries =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_ENTRIES;
+const editCalendarPermissions = new Set([editEntries, descendantEditEntries]);
+
+const knowOfSecretChannelsPermission = {
+ title: 'Know of secret channels',
+ description: 'Allows members to know of all secret channels',
+ userSurfacedPermission: userSurfacedPermissions.KNOW_OF_SECRET_CHANNELS,
+};
+const descendantKnowOf =
+ threadPermissionPropagationPrefixes.DESCENDANT + threadPermissions.KNOW_OF;
+const descendantVisible =
+ threadPermissionPropagationPrefixes.DESCENDANT + threadPermissions.VISIBLE;
+const knowOfSecretChannelsPermissions = new Set([
+ descendantKnowOf,
+ descendantVisible,
+]);
+
+const voicedPermission = {
+ title: 'Voiced in announcement channels',
+ description: 'Allows members to send messages in announcement channels',
+ userSurfacedPermission:
+ userSurfacedPermissions.VOICED_IN_ANNOUNCEMENT_CHANNELS,
+};
+const voiced = threadPermissions.VOICED;
+const voicedPermissions = new Set([voiced]);
+
+const createAndEditChannelsPermission = {
+ title: 'Create and edit channels',
+ description: 'Allows members to create new and edit existing channels',
+ userSurfacedPermission: userSurfacedPermissions.CREATE_AND_EDIT_CHANNELS,
+};
+const editThreadName = threadPermissions.EDIT_THREAD_NAME;
+const descendantEditThreadName =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_THREAD_NAME;
+const editThreadDescription = threadPermissions.EDIT_THREAD_DESCRIPTION;
+const descendantEditThreadDescription =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_THREAD_DESCRIPTION;
+const editThreadColor = threadPermissions.EDIT_THREAD_COLOR;
+const descendantEditThreadColor =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_THREAD_COLOR;
+const createSubchannels = threadPermissions.CREATE_SUBCHANNELS;
+const descendantCreateSubchannels =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissionFilterPrefixes.TOP_LEVEL +
+ threadPermissions.CREATE_SUBCHANNELS;
+const editThreadAvatar = threadPermissions.EDIT_THREAD_AVATAR;
+const descendantEditThreadAvatar =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_THREAD_AVATAR;
+const createAndEditChannelsPermissions = new Set([
+ editThreadName,
+ descendantEditThreadName,
+ editThreadDescription,
+ descendantEditThreadDescription,
+ editThreadColor,
+ descendantEditThreadColor,
+ createSubchannels,
+ descendantCreateSubchannels,
+ editThreadAvatar,
+ descendantEditThreadAvatar,
+]);
+
+const deleteChannelsPermission = {
+ title: 'Delete channels',
+ description: 'Allows members to delete channels',
+ userSurfacedPermission: userSurfacedPermissions.DELETE_CHANNELS,
+};
+const deleteThread = threadPermissions.DELETE_THREAD;
+const descendantDeleteThread =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.DELETE_THREAD;
+const deleteChannelsPermissions = new Set([
+ deleteThread,
+ descendantDeleteThread,
+]);
+
+const addMembersPermission = {
+ title: 'Add members',
+ description: 'Allows members to add other members to channels',
+ userSurfacedPermission: userSurfacedPermissions.ADD_MEMBERS,
+};
+const addMembers = threadPermissions.ADD_MEMBERS;
+const descendantAddMembers =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.ADD_MEMBERS;
+const addMembersPermissions = new Set([addMembers, descendantAddMembers]);
+
+const removeMembersPermission = {
+ title: 'Remove members',
+ description: 'Allows members to remove other members from channels',
+ userSurfacedPermission: userSurfacedPermissions.REMOVE_MEMBERS,
+};
+const removeMembers = threadPermissions.REMOVE_MEMBERS;
+const descendantRemoveMembers =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.REMOVE_MEMBERS;
+const removeMembersPermissions = new Set([
+ removeMembers,
+ descendantRemoveMembers,
+]);
+
+const changeRolePermission = {
+ title: 'Change roles',
+ description: 'Allows members to change the roles of other members',
+ userSurfacedPermission: userSurfacedPermissions.CHANGE_ROLES,
+};
+const changeRole = threadPermissions.CHANGE_ROLE;
+const descendantChangeRole =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.CHANGE_ROLE;
+const changeRolePermissions = new Set([changeRole, descendantChangeRole]);
+
+const editVisibilityPermission = {
+ title: 'Edit visibility',
+ description: 'Allows members to edit visibility permissions of channels',
+ userSurfacedPermission: userSurfacedPermissions.EDIT_VISIBILITY,
+};
+const editPermissions = threadPermissions.EDIT_PERMISSIONS;
+const descendantEditPermissions =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_PERMISSIONS;
+const editVisibilityPermissions = new Set([
+ editPermissions,
+ descendantEditPermissions,
+]);
+
+const managePinsPermission = {
+ title: 'Manage pins',
+ description: 'Allows members to pin or unpin messages in channels',
+ userSurfacedPermission: userSurfacedPermissions.MANAGE_PINS,
+};
+const managePins = threadPermissions.MANAGE_PINS;
+const descendantManagePins =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.MANAGE_PINS;
+const managePinsPermissions = new Set([managePins, descendantManagePins]);
+
+const reactToMessagePermission = {
+ title: 'React to messages',
+ description: 'Allows members to add reactions to messages',
+ userSurfacedPermission: userSurfacedPermissions.REACT_TO_MESSAGES,
+};
+const reactToMessage = threadPermissions.REACT_TO_MESSAGE;
+const descendantReactToMessage =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.REACT_TO_MESSAGE;
+const reactToMessagePermissions = new Set([
+ reactToMessage,
+ descendantReactToMessage,
+]);
+
+const editMessagePermission = {
+ title: 'Edit messages',
+ description: 'Allows members to edit their sent messages',
+ userSurfacedPermission: userSurfacedPermissions.EDIT_MESSAGES,
+};
+const editMessage = threadPermissions.EDIT_MESSAGE;
+const descendantEditMessage =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.EDIT_MESSAGE;
+const editMessagePermissions = new Set([editMessage, descendantEditMessage]);
+
+const manageInviteLinksPermission = {
+ title: 'Manage invite links',
+ description: 'Allows members to create and delete invite links',
+ userSurfacedPermission: userSurfacedPermissions.MANAGE_INVITE_LINKS,
+};
+const manageInviteLinks = threadPermissions.MANAGE_INVITE_LINKS;
+const descendantManageInviteLinks =
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.MANAGE_INVITE_LINKS;
+const manageInviteLinksPermissions = new Set([
+ manageInviteLinks,
+ descendantManageInviteLinks,
+]);
+
+export type UserSurfacedPermissionOption = {
+ +title: string,
+ +description: string,
+ +userSurfacedPermission: UserSurfacedPermission,
+};
+export const userSurfacedPermissionOptions: $ReadOnlySet<UserSurfacedPermissionOption> =
+ new Set([
+ editCalendarPermission,
+ knowOfSecretChannelsPermission,
+ voicedPermission,
+ createAndEditChannelsPermission,
+ deleteChannelsPermission,
+ addMembersPermission,
+ removeMembersPermission,
+ changeRolePermission,
+ editVisibilityPermission,
+ managePinsPermission,
+ reactToMessagePermission,
+ editMessagePermission,
+ manageInviteLinksPermission,
+ ]);
+
+type ConfigurableCommunityPermission = {
+ +[permission: UserSurfacedPermission]: $ReadOnlySet<string>,
+};
+export const configurableCommunityPermissions: ConfigurableCommunityPermission =
+ Object.freeze({
+ [userSurfacedPermissions.EDIT_CALENDAR]: editCalendarPermissions,
+ [userSurfacedPermissions.KNOW_OF_SECRET_CHANNELS]:
+ knowOfSecretChannelsPermissions,
+ [userSurfacedPermissions.VOICED_IN_ANNOUNCEMENT_CHANNELS]:
+ voicedPermissions,
+ [userSurfacedPermissions.CREATE_AND_EDIT_CHANNELS]:
+ createAndEditChannelsPermissions,
+ [userSurfacedPermissions.DELETE_CHANNELS]: deleteChannelsPermissions,
+ [userSurfacedPermissions.ADD_MEMBERS]: addMembersPermissions,
+ [userSurfacedPermissions.REMOVE_MEMBERS]: removeMembersPermissions,
+ [userSurfacedPermissions.CHANGE_ROLES]: changeRolePermissions,
+ [userSurfacedPermissions.EDIT_VISIBILITY]: editVisibilityPermissions,
+ [userSurfacedPermissions.MANAGE_PINS]: managePinsPermissions,
+ [userSurfacedPermissions.REACT_TO_MESSAGES]: reactToMessagePermissions,
+ [userSurfacedPermissions.EDIT_MESSAGES]: editMessagePermissions,
+ [userSurfacedPermissions.MANAGE_INVITE_LINKS]: manageInviteLinksPermissions,
+ });
+
export type ThreadPermissionInfo =
| { +value: true, +source: string }
| { +value: false, +source: null };
diff --git a/native/roles/create-roles-screen.react.js b/native/roles/create-roles-screen.react.js
--- a/native/roles/create-roles-screen.react.js
+++ b/native/roles/create-roles-screen.react.js
@@ -1,11 +1,22 @@
// @flow
import * as React from 'react';
+import { View, Text, TouchableOpacity } from 'react-native';
+import { ScrollView } from 'react-native-gesture-handler';
+import {
+ userSurfacedPermissionOptions,
+ type UserSurfacedPermissionOption,
+ type UserSurfacedPermission,
+} from 'lib/types/thread-permission-types.js';
import type { ThreadInfo } from 'lib/types/thread-types.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 { useStyles } from '../themes/colors.js';
export type CreateRolesScreenParams = {
+threadInfo: ThreadInfo,
@@ -17,9 +28,158 @@
+route: NavigationRoute<'CreateRolesScreen'>,
};
-// eslint-disable-next-line no-unused-vars
function CreateRolesScreen(props: CreateRolesScreenProps): React.Node {
- return <></>;
+ // eslint-disable-next-line no-unused-vars
+ const { threadInfo, action } = props.route.params;
+
+ const [customRoleName, setCustomRoleName] =
+ React.useState<string>('New role');
+ const [selectedPermissions, setSelectedPermissions] = React.useState<
+ $ReadOnlyArray<UserSurfacedPermission>,
+ >([]);
+
+ const styles = useStyles(unboundStyles);
+
+ const onClearPermissions = React.useCallback(() => {
+ setSelectedPermissions([]);
+ }, []);
+
+ const isSelectedPermissionsEmpty = selectedPermissions.length === 0;
+ const clearPermissionsText = React.useMemo(() => {
+ const textStyle = isSelectedPermissionsEmpty
+ ? styles.clearPermissionsTextDisabled
+ : styles.clearPermissionsText;
+
+ return (
+ <TouchableOpacity
+ onPress={onClearPermissions}
+ disabled={isSelectedPermissionsEmpty}
+ >
+ <Text style={textStyle}>Clear permissions</Text>
+ </TouchableOpacity>
+ );
+ }, [
+ isSelectedPermissionsEmpty,
+ onClearPermissions,
+ styles.clearPermissionsText,
+ styles.clearPermissionsTextDisabled,
+ ]);
+
+ const isUserSurfacedPermissionSelected = React.useCallback(
+ (option: UserSurfacedPermissionOption) =>
+ selectedPermissions.includes(option.userSurfacedPermission),
+ [selectedPermissions],
+ );
+
+ const onEnumValuePress = React.useCallback(
+ (option: UserSurfacedPermissionOption) =>
+ setSelectedPermissions(currentPermissions => {
+ if (currentPermissions.includes(option.userSurfacedPermission)) {
+ return currentPermissions.filter(
+ permission => permission !== option.userSurfacedPermission,
+ );
+ } else {
+ return [...currentPermissions, option.userSurfacedPermission];
+ }
+ }),
+ [],
+ );
+
+ const permissionsList = React.useMemo(
+ () =>
+ [...userSurfacedPermissionOptions].map(permission => (
+ <EnumSettingsOption
+ key={permission.title}
+ name={permission.title}
+ description={permission.description}
+ enumValue={isUserSurfacedPermissionSelected(permission)}
+ onEnumValuePress={() => onEnumValuePress(permission)}
+ />
+ )),
+ [isUserSurfacedPermissionSelected, onEnumValuePress],
+ );
+
+ const onChangeRoleNameInput = React.useCallback((roleName: string) => {
+ setCustomRoleName(roleName);
+ }, []);
+
+ return (
+ <View>
+ <View style={styles.roleNameContainer}>
+ <Text style={styles.roleNameText}>ROLE NAME</Text>
+ <View style={styles.roleInput}>
+ <TextInput
+ style={styles.roleInputComponent}
+ value={customRoleName}
+ onChangeText={onChangeRoleNameInput}
+ editable={true}
+ />
+ <SWMansionIcon name="edit-1" size={20} style={styles.pencilIcon} />
+ </View>
+ </View>
+ <View style={styles.permissionsContainer}>
+ <View style={styles.permissionsHeader}>
+ <Text style={styles.permissionsText}>PERMISSIONS</Text>
+ {clearPermissionsText}
+ </View>
+ <ScrollView style={styles.permissionsListContainer}>
+ {permissionsList}
+ </ScrollView>
+ </View>
+ </View>
+ );
}
+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',
+ },
+ 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,
+ },
+};
+
export default CreateRolesScreen;

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 30, 5:16 PM (21 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2601747
Default Alt Text
D8391.id28500.diff (16 KB)

Event Timeline