diff --git a/lib/permissions/minimally-encoded-thread-permissions.js b/lib/permissions/minimally-encoded-thread-permissions.js --- a/lib/permissions/minimally-encoded-thread-permissions.js +++ b/lib/permissions/minimally-encoded-thread-permissions.js @@ -7,6 +7,7 @@ import { parseThreadPermissionString } from './prefixes.js'; import type { ThreadPermission, + ThreadPermissionInfo, ThreadPermissionsInfo, ThreadRolePermissionsBlob, } from '../types/thread-permission-types.js'; @@ -90,6 +91,30 @@ return bitmask.toString(16); }; +const threadPermissionsFromBitmaskHex = ( + permissionsBitmaskHex: string, +): ThreadPermissionsInfo => { + invariant( + tHexEncodedPermissionsBitmask.is(permissionsBitmaskHex), + 'permissionsBitmaskHex must be valid hex string.', + ); + + const permissionsBitmask = BigInt(`0x${permissionsBitmaskHex}`); + const permissions: { [permission: ThreadPermission]: ThreadPermissionInfo } = + {}; + + for (const [key, permissionBitmask] of entries( + minimallyEncodedThreadPermissions, + )) { + if ((permissionsBitmask & permissionBitmask) !== BigInt(0)) { + permissions[key] = { value: true, source: 'null' }; + } else { + permissions[key] = { value: false, source: null }; + } + } + return permissions; +}; + const hasPermission = ( permissionsBitmaskHex: string, permission: ThreadPermission, @@ -280,6 +305,7 @@ export { permissionsToBitmaskHex, + threadPermissionsFromBitmaskHex, hasPermission, rolePermissionToBitmaskHex, decodeRolePermissionBitmask, diff --git a/lib/permissions/minimally-encoded-thread-permissions.test.js b/lib/permissions/minimally-encoded-thread-permissions.test.js --- a/lib/permissions/minimally-encoded-thread-permissions.test.js +++ b/lib/permissions/minimally-encoded-thread-permissions.test.js @@ -15,32 +15,36 @@ minimallyEncodeRawThreadInfo, permissionsToBitmaskHex, rolePermissionToBitmaskHex, + threadPermissionsFromBitmaskHex, threadRolePermissionsBlobToBitmaskArray, } from './minimally-encoded-thread-permissions.js'; import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types.js'; -describe('minimallyEncodedThreadPermissions', () => { - const permissions = { - know_of: { value: true, source: '1' }, - visible: { value: true, source: '1' }, - voiced: { value: true, source: '1' }, - edit_entries: { value: true, source: '1' }, - edit_thread: { value: true, source: '1' }, - edit_thread_description: { value: true, source: '1' }, - edit_thread_color: { value: true, source: '1' }, - delete_thread: { value: true, source: '1' }, - create_subthreads: { value: true, source: '1' }, - create_sidebars: { value: true, source: '1' }, - join_thread: { value: false, source: null }, - edit_permissions: { value: false, source: null }, - add_members: { value: true, source: '1' }, - remove_members: { value: true, source: '1' }, - change_role: { value: true, source: '1' }, - leave_thread: { value: false, source: null }, - react_to_message: { value: true, source: '1' }, - edit_message: { value: true, source: '1' }, - }; +const permissions = { + know_of: { value: true, source: '1' }, + visible: { value: true, source: '1' }, + voiced: { value: true, source: '1' }, + edit_entries: { value: true, source: '1' }, + edit_thread: { value: true, source: '1' }, + edit_thread_description: { value: true, source: '1' }, + edit_thread_color: { value: true, source: '1' }, + delete_thread: { value: true, source: '1' }, + create_subthreads: { value: true, source: '1' }, + create_sidebars: { value: true, source: '1' }, + join_thread: { value: false, source: null }, + edit_permissions: { value: false, source: null }, + add_members: { value: true, source: '1' }, + remove_members: { value: true, source: '1' }, + change_role: { value: true, source: '1' }, + leave_thread: { value: false, source: null }, + react_to_message: { value: true, source: '1' }, + edit_message: { value: true, source: '1' }, + edit_thread_avatar: { value: false, source: null }, + manage_pins: { value: false, source: null }, + manage_invite_links: { value: false, source: null }, +}; +describe('minimallyEncodedThreadPermissions', () => { it('should encode ThreadPermissionsInfo as bitmask', () => { const permissionsBitmask = permissionsToBitmaskHex(permissions); expect(permissionsBitmask).toBe('373ff'); @@ -66,6 +70,42 @@ }); }); +describe('threadPermissionsFromBitmaskHex', () => { + const expectedDecodedThreadPermissions = { + know_of: { value: true, source: 'null' }, + visible: { value: true, source: 'null' }, + voiced: { value: true, source: 'null' }, + edit_entries: { value: true, source: 'null' }, + edit_thread: { value: true, source: 'null' }, + edit_thread_description: { value: true, source: 'null' }, + edit_thread_color: { value: true, source: 'null' }, + delete_thread: { value: true, source: 'null' }, + create_subthreads: { value: true, source: 'null' }, + create_sidebars: { value: true, source: 'null' }, + join_thread: { value: false, source: null }, + edit_permissions: { value: false, source: null }, + add_members: { value: true, source: 'null' }, + remove_members: { value: true, source: 'null' }, + change_role: { value: true, source: 'null' }, + leave_thread: { value: false, source: null }, + react_to_message: { value: true, source: 'null' }, + edit_message: { value: true, source: 'null' }, + edit_thread_avatar: { value: false, source: null }, + manage_pins: { value: false, source: null }, + manage_invite_links: { value: false, source: null }, + }; + + it('should decode ThreadPermissionsInfo from bitmask', () => { + const permissionsBitmask = permissionsToBitmaskHex(permissions); + const decodedThreadPermissions = + threadPermissionsFromBitmaskHex(permissionsBitmask); + + expect(decodedThreadPermissions).toStrictEqual( + expectedDecodedThreadPermissions, + ); + }); +}); + describe('rolePermissionToBitmaskHex', () => { it('should encode `child_opentoplevel_visible` successfully', () => { expect(rolePermissionToBitmaskHex('child_opentoplevel_visible')).toBe(