Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3389042
D8391.id28500.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
D8391.id28500.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8391: [native] Populate the create role screen
Attached
Detach File
Event Timeline
Log In to Comment