Changeset View
Changeset View
Standalone View
Standalone View
lib/types/thread-types.js
// @flow | // @flow | ||||
import invariant from 'invariant'; | |||||
import t, { type TInterface } from 'tcomb'; | import t, { type TInterface } from 'tcomb'; | ||||
import { | import { | ||||
type AvatarDBContent, | type AvatarDBContent, | ||||
type ClientAvatar, | type ClientAvatar, | ||||
type UpdateUserAvatarRequest, | |||||
clientAvatarValidator, | clientAvatarValidator, | ||||
type UpdateUserAvatarRequest, | |||||
} from './avatar-types.js'; | } from './avatar-types.js'; | ||||
import type { Shape } from './core.js'; | import type { Shape } from './core.js'; | ||||
import type { CalendarQuery, RawEntryInfo } from './entry-types.js'; | import type { CalendarQuery, RawEntryInfo } from './entry-types.js'; | ||||
import type { Media } from './media-types.js'; | import type { Media } from './media-types.js'; | ||||
import type { | import type { | ||||
RawMessageInfo, | |||||
MessageTruncationStatuses, | MessageTruncationStatuses, | ||||
RawMessageInfo, | |||||
} from './message-types.js'; | } from './message-types.js'; | ||||
import { | import { | ||||
type ThreadSubscription, | type ThreadSubscription, | ||||
threadSubscriptionValidator, | threadSubscriptionValidator, | ||||
} from './subscription-types.js'; | } from './subscription-types.js'; | ||||
import type { ServerUpdateInfo, ClientUpdateInfo } from './update-types.js'; | import { | ||||
type ThreadPermissionsInfo, | |||||
type ThreadRolePermissionsBlob, | |||||
threadPermissionsInfoValidator, | |||||
threadRolePermissionsBlobValidator, | |||||
} from './thread-permission-types.js'; | |||||
import { type ThreadType, threadTypeValidator } from './thread-types-enum.js'; | |||||
import type { ClientUpdateInfo, ServerUpdateInfo } from './update-types.js'; | |||||
import type { UserInfo, UserInfos } from './user-types.js'; | import type { UserInfo, UserInfos } from './user-types.js'; | ||||
import type { ThreadEntity } from '../utils/entity-text.js'; | import { type ThreadEntity } from '../utils/entity-text.js'; | ||||
import { values } from '../utils/objects.js'; | import { tID, tShape } from '../utils/validation-utils.js'; | ||||
import { tNumEnum, tBool, tID, tShape } from '../utils/validation-utils.js'; | |||||
export const threadTypes = Object.freeze({ | |||||
//OPEN: 0, (DEPRECATED) | |||||
//CLOSED: 1, (DEPRECATED) | |||||
//SECRET: 2, (DEPRECATED) | |||||
// has parent, not top-level (appears under parent in inbox), and visible to | |||||
// all members of parent | |||||
SIDEBAR: 5, | |||||
// canonical thread for each pair of users. represents the friendship | |||||
PERSONAL: 6, | |||||
// canonical thread for each single user | |||||
PRIVATE: 7, | |||||
// local "thick" thread (outside of community). no parent, can only have | |||||
// sidebar children. currently a proxy for COMMUNITY_SECRET_SUBTHREAD until we | |||||
// launch actual E2E | |||||
LOCAL: 4, | |||||
// aka "org". no parent, top-level, has admin | |||||
COMMUNITY_ROOT: 8, | |||||
// like COMMUNITY_ROOT, but members aren't voiced | |||||
COMMUNITY_ANNOUNCEMENT_ROOT: 9, | |||||
// an open subthread. has parent, top-level (not sidebar), and visible to all | |||||
// members of parent. root ancestor is a COMMUNITY_ROOT | |||||
COMMUNITY_OPEN_SUBTHREAD: 3, | |||||
// like COMMUNITY_SECRET_SUBTHREAD, but members aren't voiced | |||||
COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD: 10, | |||||
// a secret subthread. optional parent, top-level (not sidebar), visible only | |||||
// to its members. root ancestor is a COMMUNITY_ROOT | |||||
COMMUNITY_SECRET_SUBTHREAD: 4, | |||||
// like COMMUNITY_SECRET_SUBTHREAD, but members aren't voiced | |||||
COMMUNITY_SECRET_ANNOUNCEMENT_SUBTHREAD: 11, | |||||
// like COMMUNITY_SECRET_ANNOUNCEMENT_SUBTHREAD, but you can't leave | |||||
GENESIS: 12, | |||||
}); | |||||
export type ThreadType = $Values<typeof threadTypes>; | |||||
export function assertThreadType(threadType: number): ThreadType { | |||||
invariant( | |||||
threadType === 3 || | |||||
threadType === 4 || | |||||
threadType === 5 || | |||||
threadType === 6 || | |||||
threadType === 7 || | |||||
threadType === 8 || | |||||
threadType === 9 || | |||||
threadType === 10 || | |||||
threadType === 11 || | |||||
threadType === 12, | |||||
'number is not ThreadType enum', | |||||
); | |||||
return threadType; | |||||
} | |||||
const threadTypeValidator = tNumEnum(values(threadTypes)); | |||||
export const communityThreadTypes: $ReadOnlyArray<number> = Object.freeze([ | |||||
threadTypes.COMMUNITY_ROOT, | |||||
threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT, | |||||
threadTypes.GENESIS, | |||||
]); | |||||
export const communitySubthreads: $ReadOnlyArray<number> = Object.freeze([ | |||||
threadTypes.COMMUNITY_OPEN_SUBTHREAD, | |||||
threadTypes.COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD, | |||||
threadTypes.COMMUNITY_SECRET_SUBTHREAD, | |||||
threadTypes.COMMUNITY_SECRET_ANNOUNCEMENT_SUBTHREAD, | |||||
]); | |||||
export function threadTypeIsCommunityRoot(threadType: ThreadType): boolean { | |||||
return communityThreadTypes.includes(threadType); | |||||
} | |||||
export const threadPermissions = Object.freeze({ | |||||
KNOW_OF: 'know_of', | |||||
MEMBERSHIP_DEPRECATED: 'membership', | |||||
VISIBLE: 'visible', | |||||
VOICED: 'voiced', | |||||
EDIT_ENTRIES: 'edit_entries', | |||||
EDIT_THREAD_NAME: 'edit_thread', | |||||
EDIT_THREAD_DESCRIPTION: 'edit_thread_description', | |||||
EDIT_THREAD_COLOR: 'edit_thread_color', | |||||
DELETE_THREAD: 'delete_thread', | |||||
CREATE_SUBCHANNELS: 'create_subthreads', | |||||
CREATE_SIDEBARS: 'create_sidebars', | |||||
JOIN_THREAD: 'join_thread', | |||||
EDIT_PERMISSIONS: 'edit_permissions', | |||||
ADD_MEMBERS: 'add_members', | |||||
REMOVE_MEMBERS: 'remove_members', | |||||
CHANGE_ROLE: 'change_role', | |||||
LEAVE_THREAD: 'leave_thread', | |||||
REACT_TO_MESSAGE: 'react_to_message', | |||||
EDIT_MESSAGE: 'edit_message', | |||||
EDIT_THREAD_AVATAR: 'edit_thread_avatar', | |||||
MANAGE_PINS: 'manage_pins', | |||||
}); | |||||
export type ThreadPermission = $Values<typeof threadPermissions>; | |||||
export function assertThreadPermissions( | |||||
ourThreadPermissions: string, | |||||
): ThreadPermission { | |||||
invariant( | |||||
ourThreadPermissions === 'know_of' || | |||||
ourThreadPermissions === 'membership' || | |||||
ourThreadPermissions === 'visible' || | |||||
ourThreadPermissions === 'voiced' || | |||||
ourThreadPermissions === 'edit_entries' || | |||||
ourThreadPermissions === 'edit_thread' || | |||||
ourThreadPermissions === 'edit_thread_description' || | |||||
ourThreadPermissions === 'edit_thread_color' || | |||||
ourThreadPermissions === 'delete_thread' || | |||||
ourThreadPermissions === 'create_subthreads' || | |||||
ourThreadPermissions === 'create_sidebars' || | |||||
ourThreadPermissions === 'join_thread' || | |||||
ourThreadPermissions === 'edit_permissions' || | |||||
ourThreadPermissions === 'add_members' || | |||||
ourThreadPermissions === 'remove_members' || | |||||
ourThreadPermissions === 'change_role' || | |||||
ourThreadPermissions === 'leave_thread' || | |||||
ourThreadPermissions === 'react_to_message' || | |||||
ourThreadPermissions === 'edit_message' || | |||||
ourThreadPermissions === 'edit_thread_avatar' || | |||||
ourThreadPermissions === 'manage_pins', | |||||
'string is not threadPermissions enum', | |||||
); | |||||
return ourThreadPermissions; | |||||
} | |||||
const threadPermissionValidator = t.enums.of(values(threadPermissions)); | |||||
export const threadPermissionPropagationPrefixes = Object.freeze({ | |||||
DESCENDANT: 'descendant_', | |||||
CHILD: 'child_', | |||||
}); | |||||
export type ThreadPermissionPropagationPrefix = $Values< | |||||
typeof threadPermissionPropagationPrefixes, | |||||
>; | |||||
export const threadPermissionFilterPrefixes = Object.freeze({ | |||||
// includes only SIDEBAR, COMMUNITY_OPEN_SUBTHREAD, | |||||
// COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD | |||||
OPEN: 'open_', | |||||
// excludes only SIDEBAR | |||||
TOP_LEVEL: 'toplevel_', | |||||
// includes only COMMUNITY_OPEN_SUBTHREAD, | |||||
// COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD | |||||
OPEN_TOP_LEVEL: 'opentoplevel_', | |||||
}); | |||||
export type ThreadPermissionFilterPrefix = $Values< | |||||
typeof threadPermissionFilterPrefixes, | |||||
>; | |||||
export type ThreadPermissionInfo = | |||||
| { +value: true, +source: string } | |||||
| { +value: false, +source: null }; | |||||
const threadPermissionInfoValidator = t.union([ | |||||
tShape({ value: tBool(true), source: t.String }), | |||||
tShape({ value: tBool(false), source: t.Nil }), | |||||
]); | |||||
export type ThreadPermissionsBlob = { | |||||
+[permission: string]: ThreadPermissionInfo, | |||||
}; | |||||
export type ThreadRolePermissionsBlob = { +[permission: string]: boolean }; | |||||
const threadRolePermissionsBlobValidator = t.dict(t.String, t.Boolean); | |||||
export type ThreadPermissionsInfo = { | |||||
+[permission: ThreadPermission]: ThreadPermissionInfo, | |||||
}; | |||||
const threadPermissionsInfoValidator = t.dict( | |||||
threadPermissionValidator, | |||||
threadPermissionInfoValidator, | |||||
); | |||||
export type MemberInfo = { | export type MemberInfo = { | ||||
+id: string, | +id: string, | ||||
+role: ?string, | +role: ?string, | ||||
+permissions: ThreadPermissionsInfo, | +permissions: ThreadPermissionsInfo, | ||||
+isSender: boolean, | +isSender: boolean, | ||||
}; | }; | ||||
const memberInfoValidator = tShape<MemberInfo>({ | const memberInfoValidator = tShape<MemberInfo>({ | ||||
▲ Show 20 Lines • Show All 371 Lines • Show Last 20 Lines |