diff --git a/keyserver/src/database/migration-config.js b/keyserver/src/database/migration-config.js --- a/keyserver/src/database/migration-config.js +++ b/keyserver/src/database/migration-config.js @@ -823,6 +823,7 @@ ); }, ], + [65, updateRolesAndPermissionsForAllThreads], ]); const newDatabaseVersion: number = Math.max(...migrations.keys()); diff --git a/keyserver/src/fetchers/thread-fetchers.js b/keyserver/src/fetchers/thread-fetchers.js --- a/keyserver/src/fetchers/thread-fetchers.js +++ b/keyserver/src/fetchers/thread-fetchers.js @@ -11,7 +11,7 @@ } from 'lib/shared/thread-utils.js'; import { hasMinCodeVersion, - FUTURE_CODE_VERSION, + NEXT_CODE_VERSION, } from 'lib/shared/version-utils.js'; import type { AvatarDBContent, ClientAvatar } from 'lib/types/avatar-types.js'; import type { RawMessageInfo, MessageInfo } from 'lib/types/message-types.js'; @@ -295,8 +295,15 @@ const addingUsersToCommunityRootSupported = !hasMinCodeVersion( viewer.platformDetails, { - native: FUTURE_CODE_VERSION, - web: FUTURE_CODE_VERSION, + native: NEXT_CODE_VERSION, + web: NEXT_CODE_VERSION, + }, + ); + const manageFarcasterChannelTagsPermissionUnsupported = !hasMinCodeVersion( + viewer.platformDetails, + { + native: NEXT_CODE_VERSION, + web: NEXT_CODE_VERSION, }, ); @@ -316,6 +323,8 @@ minimallyEncodePermissions: minimallyEncodedPermissionsSupported, includeSpecialRoleFieldInRoles: specialRoleFieldSupported, allowAddingUsersToCommunityRoot: addingUsersToCommunityRootSupported, + filterManageFarcasterChannelTagsPermission: + manageFarcasterChannelTagsPermissionUnsupported, }, ); if (threadInfo) { diff --git a/lib/permissions/minimally-encoded-thread-permissions-test-data.js b/lib/permissions/minimally-encoded-thread-permissions-test-data.js --- a/lib/permissions/minimally-encoded-thread-permissions-test-data.js +++ b/lib/permissions/minimally-encoded-thread-permissions-test-data.js @@ -97,6 +97,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, isSender: false, }, @@ -184,6 +188,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, isSender: true, }, @@ -293,6 +301,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, subscription: { home: true, @@ -470,6 +482,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, isSender: false, }, @@ -565,6 +581,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, isSender: true, }, @@ -682,6 +702,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, subscription: { home: true, 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 @@ -43,6 +43,7 @@ manage_pins: BigInt(19), manage_invite_links: BigInt(20), voiced_in_announcement_channels: BigInt(21), + manage_farcaster_channel_tags: BigInt(22), }); // `minimallyEncodedThreadPermissions` is used to map each permission 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 @@ -53,6 +53,7 @@ manage_pins: { value: false, source: null }, manage_invite_links: { value: false, source: null }, voiced_in_announcement_channels: { value: false, source: null }, + manage_farcaster_channel_tags: { value: false, source: null }, }; describe('minimallyEncodedThreadPermissions', () => { @@ -129,6 +130,7 @@ manage_pins: { value: false, source: null }, manage_invite_links: { value: false, source: null }, voiced_in_announcement_channels: { value: false, source: null }, + manage_farcaster_channel_tags: { value: false, source: null }, }; it('should decode ThreadPermissionsInfo from bitmask', () => { @@ -170,6 +172,7 @@ manage_pins: { value: false, source: null }, manage_invite_links: { value: false, source: null }, voiced_in_announcement_channels: { value: false, source: null }, + manage_farcaster_channel_tags: { value: false, source: null }, }); }); }); @@ -626,6 +629,10 @@ value: false, source: null, }, + manage_farcaster_channel_tags: { + value: false, + source: null, + }, }, subscription: { home: true, diff --git a/lib/permissions/thread-permissions.js b/lib/permissions/thread-permissions.js --- a/lib/permissions/thread-permissions.js +++ b/lib/permissions/thread-permissions.js @@ -297,6 +297,7 @@ [threadPermissions.MANAGE_PINS]: true, [threadPermissions.MANAGE_INVITE_LINKS]: true, [threadPermissions.VOICED_IN_ANNOUNCEMENT_CHANNELS]: true, + [threadPermissions.MANAGE_FARCASTER_CHANNEL_TAGS]: true, [descendantKnowOf]: true, [descendantVisible]: true, [topLevelDescendantJoinThread]: true, diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -621,6 +621,7 @@ +minimallyEncodePermissions?: boolean, +includeSpecialRoleFieldInRoles?: boolean, +allowAddingUsersToCommunityRoot?: boolean, + +filterManageFarcasterChannelTagsPermission?: boolean, }; function rawThreadInfoFromServerThreadInfo( @@ -640,6 +641,8 @@ options?.includeSpecialRoleFieldInRoles; const allowAddingUsersToCommunityRoot = options?.allowAddingUsersToCommunityRoot; + const filterManageFarcasterChannelTagsPermission = + options?.filterManageFarcasterChannelTagsPermission; const filterThreadPermissions = ( innerThreadPermissions: ThreadPermissionsInfo, @@ -679,7 +682,9 @@ threadPermissionPropagationPrefixes.DESCENDANT + threadPermissionFilterPrefixes.TOP_LEVEL + threadPermissions.VOICED_IN_ANNOUNCEMENT_CHANNELS, - ].includes(k)), + ].includes(k)) || + (filterManageFarcasterChannelTagsPermission && + [threadPermissions.MANAGE_FARCASTER_CHANNEL_TAGS].includes(k)), )(innerThreadPermissions); }; 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 @@ -32,6 +32,7 @@ MANAGE_PINS: 'manage_pins', MANAGE_INVITE_LINKS: 'manage_invite_links', VOICED_IN_ANNOUNCEMENT_CHANNELS: 'voiced_in_announcement_channels', + MANAGE_FARCASTER_CHANNEL_TAGS: 'manage_farcaster_channel_tags', }); export type ThreadPermissionNotAffectedByBlock = $Values< @@ -73,7 +74,8 @@ ourThreadPermissions === 'edit_thread_avatar' || ourThreadPermissions === 'manage_pins' || ourThreadPermissions === 'manage_invite_links' || - ourThreadPermissions === 'voiced_in_announcement_channels', + ourThreadPermissions === 'voiced_in_announcement_channels' || + ourThreadPermissions === 'manage_farcaster_channel_tags', 'string is not threadPermissions enum', ); return ourThreadPermissions; @@ -124,6 +126,7 @@ REACT_TO_MESSAGES: 'react_to_messages', EDIT_MESSAGES: 'edit_messages', MANAGE_INVITE_LINKS: 'manage_invite_links', + MANAGE_FARCASTER_CHANNEL_TAGS: 'manage_farcaster_channel_tags', }); export type UserSurfacedPermission = $Values; export const userSurfacedPermissionsSet: $ReadOnlySet = @@ -276,6 +279,17 @@ const manageInviteLinks = threadPermissions.MANAGE_INVITE_LINKS; const manageInviteLinksPermissions = new Set([manageInviteLinks]); +const manageFarcasterChannelTagsPermission = { + title: 'Manage Farcaster channel tags', + description: 'Allows members to create and delete Farcaster channel tags', + userSurfacedPermission: userSurfacedPermissions.MANAGE_FARCASTER_CHANNEL_TAGS, +}; +const manageFarcasterChannelTags = + threadPermissions.MANAGE_FARCASTER_CHANNEL_TAGS; +const manageFarcasterChannelTagsPermissions = new Set([ + manageFarcasterChannelTags, +]); + export type UserSurfacedPermissionOption = { +title: string, +description: string, @@ -296,6 +310,7 @@ reactToMessagePermission, editMessagePermission, manageInviteLinksPermission, + manageFarcasterChannelTagsPermission, ]); type ConfigurableCommunityPermission = { @@ -319,6 +334,8 @@ [userSurfacedPermissions.REACT_TO_MESSAGES]: reactToMessagePermissions, [userSurfacedPermissions.EDIT_MESSAGES]: editMessagePermissions, [userSurfacedPermissions.MANAGE_INVITE_LINKS]: manageInviteLinksPermissions, + [userSurfacedPermissions.MANAGE_FARCASTER_CHANNEL_TAGS]: + manageFarcasterChannelTagsPermissions, }); export type ThreadPermissionInfo = diff --git a/native/redux/persist.js b/native/redux/persist.js --- a/native/redux/persist.js +++ b/native/redux/persist.js @@ -59,7 +59,9 @@ import { patchRawThreadInfosWithSpecialRole } from 'lib/permissions/special-roles.js'; import { filterThreadIDsInFilterList } from 'lib/reducers/calendar-filters-reducer.js'; import { highestLocalIDSelector } from 'lib/selectors/local-id-selectors.js'; +import { updateThreadStoreThreadInfos } from 'lib/shared/redux/client-db-utils.js'; import { legacyUpdateRolesAndPermissions } from 'lib/shared/redux/legacy-update-roles-and-permissions.js'; +import { updateRolesAndPermissions } from 'lib/shared/redux/update-roles-and-permissions.js'; import { inconsistencyResponsesToReports } from 'lib/shared/report-utils.js'; import { getContainingThreadID, @@ -1383,6 +1385,20 @@ ops: dbOperations, }; }, + [77]: (state: AppState) => { + const { newThreadStore, dbOperations } = updateThreadStoreThreadInfos( + state.threadStore, + updateRolesAndPermissions, + ); + + return { + state: { + ...state, + threadStore: newThreadStore, + }, + ops: dbOperations, + }; + }, }; // NOTE: renaming this object, and especially the `version` property @@ -1393,7 +1409,7 @@ storage: AsyncStorage, blacklist: persistBlacklist, debug: __DEV__, - version: 76, + version: 77, transforms: [ messageStoreMessagesBlocklistTransform, reportStoreTransform, diff --git a/web/redux/persist-constants.js b/web/redux/persist-constants.js --- a/web/redux/persist-constants.js +++ b/web/redux/persist-constants.js @@ -3,6 +3,6 @@ const rootKey = 'root'; const rootKeyPrefix = 'persist:'; const completeRootKey = `${rootKeyPrefix}${rootKey}`; -const storeVersion = 76; +const storeVersion = 77; export { rootKey, rootKeyPrefix, completeRootKey, storeVersion }; diff --git a/web/redux/persist.js b/web/redux/persist.js --- a/web/redux/persist.js +++ b/web/redux/persist.js @@ -18,6 +18,8 @@ } from 'lib/ops/message-store-ops.js'; import type { ClientDBThreadStoreOperation } from 'lib/ops/thread-store-ops.js'; import { patchRawThreadInfoWithSpecialRole } from 'lib/permissions/special-roles.js'; +import { updateThreadStoreThreadInfos } from 'lib/shared/redux/client-db-utils.js'; +import { updateRolesAndPermissions } from 'lib/shared/redux/update-roles-and-permissions.js'; import { keyserverStoreTransform } from 'lib/shared/transforms/keyserver-store-transform.js'; import { messageStoreMessagesBlocklistTransform } from 'lib/shared/transforms/message-store-transform.js'; import { defaultAlertInfos } from 'lib/types/alert-types.js'; @@ -552,6 +554,20 @@ ops: dbOperations, }; }, + [77]: (state: AppState) => { + const { newThreadStore, dbOperations } = updateThreadStoreThreadInfos( + state.threadStore, + updateRolesAndPermissions, + ); + + return { + state: { + ...state, + threadStore: newThreadStore, + }, + ops: dbOperations, + }; + }, }; const persistConfig: PersistConfig = {