From 3ec3cce9c10e96fe9d60d966a5a96474feba4114 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 23 Oct 2022 00:22:27 +0400 Subject: [PATCH] [WIP] Topics --- .../Telegram-iOS/en.lproj/Localizable.strings | 5 +- .../ChatListUI/Sources/ChatContextMenus.swift | 2 +- .../Sources/ChatListController.swift | 2 +- .../Sources/Node/ChatListItem.swift | 2 +- .../Sources/ChannelAdminController.swift | 79 +++++++++---------- .../Sources/ChannelAdminsController.swift | 2 +- .../ChannelBannedMemberController.swift | 6 +- .../ChannelMembersSearchContainerNode.swift | 8 +- .../ChannelMembersSearchControllerNode.swift | 27 ++++--- .../ChannelPermissionsController.swift | 54 ++++++++++--- .../Sources/ApiUtils/TelegramChannel.swift | 15 ++++ .../TelegramCore/Sources/ForumChannels.swift | 12 ++- .../SyncCore_TelegramChatAdminRights.swift | 24 ++++-- .../SyncCore_TelegramChatBannedRights.swift | 1 + .../Peers/ChannelOwnershipTransfer.swift | 7 +- .../ChatRecentActionsHistoryTransition.swift | 11 ++- .../Sources/PeerInfo/PeerInfoData.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 8 +- 18 files changed, 165 insertions(+), 102 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 02667eb150..afb1e005c7 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -2686,6 +2686,7 @@ Unused sets are archived when you add more."; "Channel.AdminLog.PinMessages" = "Pin Messages"; "Channel.AdminLog.AddMembers" = "Add Members"; "Channel.AdminLog.SendPolls" = "Send Polls"; +"Channel.AdminLog.ManageTopics" = "Manage Topics"; "Channel.AdminLog.CanChangeInfo" = "Change Info"; "Channel.AdminLog.CanSendMessages" = "Post Messages"; @@ -2693,6 +2694,7 @@ Unused sets are archived when you add more."; "Channel.AdminLog.CanBanUsers" = "Ban Users"; "Channel.AdminLog.CanInviteUsers" = "Add Users"; "Channel.AdminLog.CanPinMessages" = "Pin Messages"; +"Channel.AdminLog.CanManageTopics" = "Manage Topics"; "Channel.AdminLog.CanAddAdmins" = "Add New Admins"; "Channel.AdminLog.CanBeAnonymous" = "Remain Anonymous"; "Channel.AdminLog.CanEditMessages" = "Edit Messages"; @@ -3982,6 +3984,7 @@ Unused sets are archived when you add more."; "GroupPermission.NoChangeInfo" = "no info"; "GroupPermission.NoAddMembers" = "no add"; "GroupPermission.NoPinMessages" = "no pin"; +"GroupPermission.NoManageTopics" = "no topics"; "GroupPermission.Title" = "Exception"; "GroupPermission.NewTitle" = "New Exception"; @@ -8137,7 +8140,7 @@ Sorry for the inconvenience."; "ChatList.StartAction" = "Start"; "ChatList.CloseAction" = "Close"; -"Channel.EditAdmin.PermissionCreateTopics" = "Create Topics"; +"Channel.EditAdmin.PermissionManageTopics" = "Manage Topics"; "ChatList.Search.FilterTopics" = "Topics"; "DialogList.SearchSectionTopics" = "Topics"; diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 5415ebf1aa..3a1685039b 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -509,7 +509,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: var items: [ContextMenuItem] = [] - if channel.hasPermission(.pinMessages) { + if channel.hasPermission(.manageTopics) { //TODO:localize items.append(.action(ContextMenuActionItem(text: isPinned ? "Unpin" : "Pin", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 54de766542..a0473729da 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2652,7 +2652,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController sourceController?.beginMessageSearch("") }))) - } else if channel.hasPermission(.pinMessages) { + } else if channel.hasPermission(.manageTopics) { items.append(.separator) //TODO:localize diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 6153584090..25b737487d 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2008,7 +2008,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var canManage = false if channel.flags.contains(.isCreator) { canManage = true - } else if channel.adminRights != nil { + } else if channel.hasPermission(.pinMessages) { canManage = true } else if let threadInfo = threadInfo, threadInfo.isOwner { canManage = true diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index 296d15a299..16f44376c2 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -438,11 +438,9 @@ private func stringForRight(strings: PresentationStrings, right: TelegramChatAdm return strings.Channel_EditAdmin_PermissionInviteSubscribers } } else if right.contains(.canPinMessages) { - if isForum { - return strings.Channel_EditAdmin_PermissionCreateTopics - } else { - return strings.Channel_EditAdmin_PermissionPinMessages - } + return strings.Channel_EditAdmin_PermissionPinMessages + } else if right.contains(.canManageTopics) { + return strings.Channel_EditAdmin_PermissionManageTopics } else if right.contains(.canAddAdmins) { return strings.Channel_EditAdmin_PermissionAddAdmins } else if right.contains(.canBeAnonymous) { @@ -530,12 +528,8 @@ private func rightEnabledByDefault(channelPeer: Peer, right: TelegramChatAdminRi return false } -private func areAllAdminRightsEnabled(_ flags: TelegramChatAdminRightsFlags, group: Bool, except: TelegramChatAdminRightsFlags) -> Bool { - if group { - return TelegramChatAdminRightsFlags.groupSpecific.subtracting(except).intersection(flags) == TelegramChatAdminRightsFlags.groupSpecific.subtracting(except) - } else { - return TelegramChatAdminRightsFlags.broadcastSpecific.subtracting(except).intersection(flags) == TelegramChatAdminRightsFlags.broadcastSpecific.subtracting(except) - } +private func areAllAdminRightsEnabled(_ flags: TelegramChatAdminRightsFlags, peer: EnginePeer, except: TelegramChatAdminRightsFlags) -> Bool { + return TelegramChatAdminRightsFlags.peerSpecific(peer: peer).subtracting(except).intersection(flags) == TelegramChatAdminRightsFlags.peerSpecific(peer: peer).subtracting(except) } private func channelAdminControllerEntries(presentationData: PresentationData, state: ChannelAdminControllerState, accountPeerId: PeerId, channelPeer: EnginePeer?, adminPeer: EnginePeer?, adminPresence: EnginePeer.Presence?, initialParticipant: ChannelParticipant?, invite: Bool, canEdit: Bool) -> [ChannelAdminEntry] { @@ -561,10 +555,10 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s var maskRightsFlags: TelegramChatAdminRightsFlags let rightsOrder: [TelegramChatAdminRightsFlags] + maskRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .channel(channel)) switch channel.info { case .broadcast: isGroup = false - maskRightsFlags = .broadcastSpecific rightsOrder = [ .canChangeInfo, .canPostMessages, @@ -576,17 +570,30 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s ] case .group: isGroup = true - maskRightsFlags = .groupSpecific - rightsOrder = [ - .canChangeInfo, - .canDeleteMessages, - .canBanUsers, - .canInviteUsers, - .canPinMessages, - .canManageCalls, - .canBeAnonymous, - .canAddAdmins - ] + if channel.flags.contains(.isForum) { + rightsOrder = [ + .canChangeInfo, + .canDeleteMessages, + .canBanUsers, + .canInviteUsers, + .canPinMessages, + .canManageTopics, + .canManageCalls, + .canBeAnonymous, + .canAddAdmins + ] + } else { + rightsOrder = [ + .canChangeInfo, + .canDeleteMessages, + .canBanUsers, + .canInviteUsers, + .canPinMessages, + .canManageCalls, + .canBeAnonymous, + .canAddAdmins + ] + } } if isCreator { @@ -664,7 +671,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s entries.append(.addAdminsInfo(presentationData.theme, currentRightsFlags.contains(.canAddAdmins) ? presentationData.strings.Channel_EditAdmin_PermissinAddAdminOn : presentationData.strings.Channel_EditAdmin_PermissinAddAdminOff)) } - if case let .user(admin) = admin, admin.botInfo == nil && !admin.isDeleted && channel.flags.contains(.isCreator) && areAllAdminRightsEnabled(currentRightsFlags, group: isGroup, except: .canBeAnonymous) { + if case let .user(admin) = admin, admin.botInfo == nil && !admin.isDeleted && channel.flags.contains(.isCreator) && areAllAdminRightsEnabled(currentRightsFlags, peer: .channel(channel), except: .canBeAnonymous) { canTransfer = true } @@ -755,7 +762,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s let isGroup = true let isChannel = false - let maskRightsFlags: TelegramChatAdminRightsFlags = .groupSpecific + let maskRightsFlags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group)) let rightsOrder: [TelegramChatAdminRightsFlags] = [ .canChangeInfo, .canDeleteMessages, @@ -795,7 +802,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s entries.append(.addAdminsInfo(presentationData.theme, currentRightsFlags.contains(.canAddAdmins) ? presentationData.strings.Channel_EditAdmin_PermissinAddAdminOn : presentationData.strings.Channel_EditAdmin_PermissinAddAdminOff)) } - if case let .user(admin) = admin, case .creator = group.role, admin.botInfo == nil && !admin.isDeleted && areAllAdminRightsEnabled(currentRightsFlags, group: true, except: .canBeAnonymous) { + if case let .user(admin) = admin, case .creator = group.role, admin.botInfo == nil && !admin.isDeleted && areAllAdminRightsEnabled(currentRightsFlags, peer: .legacyGroup(group), except: .canBeAnonymous) { entries.append(.transfer(presentationData.theme, presentationData.strings.Group_EditAdmin_TransferOwnership)) } @@ -1061,13 +1068,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD return } - let maskRightsFlags: TelegramChatAdminRightsFlags - switch channel.info { - case .broadcast: - maskRightsFlags = .broadcastSpecific - case .group: - maskRightsFlags = .groupSpecific - } + let maskRightsFlags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .channel(channel)) var currentRank: String? var currentFlags: TelegramChatAdminRightsFlags? @@ -1175,13 +1176,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD } if updateFlags == nil { - let maskRightsFlags: TelegramChatAdminRightsFlags - switch channel.info { - case .broadcast: - maskRightsFlags = .broadcastSpecific - case .group: - maskRightsFlags = .groupSpecific - } + let maskRightsFlags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .channel(channel)) if channel.flags.contains(.isCreator) { updateFlags = maskRightsFlags.subtracting([.canAddAdmins, .canBeAnonymous]) @@ -1235,7 +1230,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD })) } } - } else if case .legacyGroup = channelPeer { + } else if case let .legacyGroup(group) = channelPeer { var updateFlags: TelegramChatAdminRightsFlags? var updateRank: String? updateState { current in @@ -1251,7 +1246,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD return } - let maskRightsFlags: TelegramChatAdminRightsFlags = .groupSpecific + let maskRightsFlags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group)) let defaultFlags = maskRightsFlags.subtracting([.canBeAnonymous, .canAddAdmins]) if updateFlags == nil { diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index 7fade11a32..969973b787 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -681,7 +681,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation var peers: [PeerId: Peer] = [:] peers[creator.id] = creator peers[peer.id] = peer - result.append(RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers)) + result.append(RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .internal_groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers)) case .member: break } diff --git a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift index 42fadf1903..9d7fc65b1b 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift @@ -291,7 +291,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation entries.append(.rightsHeader(presentationData.theme, presentationData.strings.GroupPermission_SectionTitle)) var index = 0 - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: .channel(channel)) { let defaultEnabled = !defaultBannedRights.flags.contains(right) && channel.hasPermission(.banMembers) entries.append(.rightItem(presentationData.theme, index, stringForGroupPermission(strings: presentationData.strings, right: right, isForum: channel.flags.contains(.isForum)), right, defaultEnabled && !currentRightsFlags.contains(right), defaultEnabled && !state.updating)) index += 1 @@ -337,7 +337,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation entries.append(.rightsHeader(presentationData.theme, presentationData.strings.GroupPermission_SectionTitle)) var index = 0 - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: .legacyGroup(group)) { let defaultEnabled = !defaultBannedRightsFlags.contains(right) entries.append(.rightItem(presentationData.theme, index, stringForGroupPermission(strings: presentationData.strings, right: right, isForum: false), right, defaultEnabled && !currentRightsFlags.contains(right), defaultEnabled && !state.updating)) index += 1 @@ -406,7 +406,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) } else { effectiveRightsFlags.insert(rights) - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: EnginePeer(peer)) { if groupPermissionDependencies(right).contains(rights) { effectiveRightsFlags.insert(right) } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 9a2beaae58..703b0ea734 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -826,7 +826,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon case let .member(_, _, _, banInfo, _): if let banInfo = banInfo { var exceptionsString = "" - for (rights, _) in allGroupPermissionList { + for (rights, _) in allGroupPermissionList(peer: .channel(channel)) { if banInfo.rights.flags.contains(rights) { if !exceptionsString.isEmpty { exceptionsString.append(", ") @@ -930,7 +930,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon return entries } - } else if let _ = peerView.peers[peerId] as? TelegramGroup, let cachedData = peerView.cachedData as? CachedGroupData { + } else if let group = peerView.peers[peerId] as? TelegramGroup, let cachedData = peerView.cachedData as? CachedGroupData { updateActivity(true) let foundGroupMembers: Signal<[RenderedChannelParticipant], NoError> let foundMembers: Signal<[RenderedChannelParticipant], NoError> @@ -969,7 +969,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon peers[creator.id] = creator } peers[peer.id] = peer - renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .groupSpecific), promotedBy: creatorPeer?.id ?? context.account.peerId, canBeEditedByAccountPeer: creatorPeer?.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers) + renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group))), promotedBy: creatorPeer?.id ?? context.account.peerId, canBeEditedByAccountPeer: creatorPeer?.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers) case .member: var peers: [PeerId: Peer] = [:] peers[peer.id] = peer @@ -1086,7 +1086,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon case let .member(_, _, _, banInfo, _): if let banInfo = banInfo { var exceptionsString = "" - for (rights, _) in allGroupPermissionList { + for (rights, _) in allGroupPermissionList(peer: .legacyGroup(group)) { if banInfo.rights.flags.contains(rights) { if !exceptionsString.isEmpty { exceptionsString.append(", ") diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift index dbaa9f35dd..0e401dd704 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift @@ -259,6 +259,9 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { guard let strongSelf = self else { return } + guard let mainPeer = peerViewMainPeer(peerView) else { + return + } guard let cachedData = peerView.cachedData as? CachedGroupData, let participants = cachedData.participants else { return } @@ -279,19 +282,17 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { var entries: [ChannelMembersSearchEntry] = [] var canInviteByLink = false - if let peer = peerViewMainPeer(peerView) { - if !(peer.addressName?.isEmpty ?? true) { + if !(mainPeer.addressName?.isEmpty ?? true) { + canInviteByLink = true + } else if let peer = mainPeer as? TelegramChannel { + if peer.flags.contains(.isCreator) || (peer.adminRights?.rights.contains(.canInviteUsers) == true) { + canInviteByLink = true + } + } else if let peer = mainPeer as? TelegramGroup { + if case .creator = peer.role { + canInviteByLink = true + } else if case let .admin(rights, _) = peer.role, rights.rights.contains(.canInviteUsers) { canInviteByLink = true - } else if let peer = peer as? TelegramChannel { - if peer.flags.contains(.isCreator) || (peer.adminRights?.rights.contains(.canInviteUsers) == true) { - canInviteByLink = true - } - } else if let peer = peer as? TelegramGroup { - if case .creator = peer.role { - canInviteByLink = true - } else if case let .admin(rights, _) = peer.role, rights.rights.contains(.canInviteUsers) { - canInviteByLink = true - } } } @@ -399,7 +400,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { var peers: [PeerId: Peer] = [:] peers[creator.id] = creator peers[peer.id] = peer - renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers, presences: peerView.peerPresences) + renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(mainPeer))), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers, presences: peerView.peerPresences) case .member: var peers: [PeerId: Peer] = [:] peers[peer.id] = peer diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index a1c5463aa7..264eee9fe5 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -300,7 +300,7 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry { case let .member(_, _, _, banInfo, _): var exceptionsString = "" if let banInfo = banInfo { - for (rights, _) in allGroupPermissionList { + for (rights, _) in internal_allPossibleGroupPermissionList { if !defaultBannedRights.contains(rights) && banInfo.rights.flags.contains(rights) { if !exceptionsString.isEmpty { exceptionsString.append(", ") @@ -352,11 +352,9 @@ func stringForGroupPermission(strings: PresentationStrings, right: TelegramChatB } else if right.contains(.banAddMembers) { return strings.Channel_BanUser_PermissionAddMembers } else if right.contains(.banPinMessages) { - if isForum { - return strings.Channel_EditAdmin_PermissionCreateTopics - } else { - return strings.Channel_EditAdmin_PermissionPinMessages - } + return strings.Channel_EditAdmin_PermissionPinMessages + } else if right.contains(.banManageTopics) { + return strings.Channel_EditAdmin_PermissionManageTopics } else { return "" } @@ -379,12 +377,14 @@ func compactStringForGroupPermission(strings: PresentationStrings, right: Telegr return strings.GroupPermission_NoAddMembers } else if right.contains(.banPinMessages) { return strings.GroupPermission_NoPinMessages + } else if right.contains(.banManageTopics) { + return strings.GroupPermission_NoManageTopics } else { return "" } } -public let allGroupPermissionList: [(TelegramChatBannedRightsFlags, TelegramChannelPermission)] = [ +private let internal_allPossibleGroupPermissionList: [(TelegramChatBannedRightsFlags, TelegramChannelPermission)] = [ (.banSendMessages, .banMembers), (.banSendMedia, .banMembers), (.banSendGifs, .banMembers), @@ -392,9 +392,37 @@ public let allGroupPermissionList: [(TelegramChatBannedRightsFlags, TelegramChan (.banSendPolls, .banMembers), (.banAddMembers, .banMembers), (.banPinMessages, .pinMessages), + (.banManageTopics, .manageTopics), (.banChangeInfo, .changeInfo) ] +public func allGroupPermissionList(peer: EnginePeer) -> [(TelegramChatBannedRightsFlags, TelegramChannelPermission)] { + if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + return [ + (.banSendMessages, .banMembers), + (.banSendMedia, .banMembers), + (.banSendGifs, .banMembers), + (.banEmbedLinks, .banMembers), + (.banSendPolls, .banMembers), + (.banAddMembers, .banMembers), + (.banPinMessages, .pinMessages), + (.banManageTopics, .manageTopics), + (.banChangeInfo, .changeInfo) + ] + } else { + return [ + (.banSendMessages, .banMembers), + (.banSendMedia, .banMembers), + (.banSendGifs, .banMembers), + (.banEmbedLinks, .banMembers), + (.banSendPolls, .banMembers), + (.banAddMembers, .banMembers), + (.banPinMessages, .pinMessages), + (.banChangeInfo, .changeInfo) + ] + } +} + let publicGroupRestrictedPermissions: TelegramChatBannedRightsFlags = [ .banPinMessages, .banChangeInfo @@ -415,6 +443,8 @@ func groupPermissionDependencies(_ right: TelegramChatBannedRightsFlags) -> Tele return [] } else if right.contains(.banPinMessages) { return [] + } else if right.contains(.banManageTopics) { + return [] } else { return [] } @@ -433,7 +463,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen entries.append(.permissionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_SectionTitle)) var rightIndex: Int = 0 - for (rights, correspondingAdminRight) in allGroupPermissionList { + for (rights, correspondingAdminRight) in allGroupPermissionList(peer: .channel(channel)) { var enabled: Bool? = true if channel.addressName != nil && publicGroupRestrictedPermissions.contains(rights) { enabled = false @@ -482,7 +512,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen entries.append(.permissionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_SectionTitle)) var rightIndex: Int = 0 - for (rights, _) in allGroupPermissionList { + for (rights, _) in allGroupPermissionList(peer: .legacyGroup(group)) { entries.append(.permission(presentationData.theme, rightIndex, stringForGroupPermission(strings: presentationData.strings, right: rights, isForum: false), !effectiveRightsFlags.contains(rights), rights, true)) rightIndex += 1 } @@ -586,7 +616,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) } else { effectiveRightsFlags.insert(rights) - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: .channel(channel)) { if groupPermissionDependencies(right).contains(rights) { effectiveRightsFlags.insert(right) } @@ -616,7 +646,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) } else { effectiveRightsFlags.insert(rights) - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: .legacyGroup(group)) { if groupPermissionDependencies(right).contains(rights) { effectiveRightsFlags.insert(right) } @@ -718,7 +748,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent guard let channel = view.peers[view.peerId] as? TelegramChannel else { return } - for (listRight, permission) in allGroupPermissionList { + for (listRight, permission) in allGroupPermissionList(peer: .channel(channel)) { if listRight == right { let text: String let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift index c48caafdb4..9895010b23 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramChannel.swift @@ -5,6 +5,7 @@ import Postbox public enum TelegramChannelPermission { case sendMessages case pinMessages + case manageTopics case inviteMembers case editAllMessages case deleteAllMessages @@ -66,6 +67,20 @@ public extension TelegramChannel { } return true } + case .manageTopics: + if self.flags.contains(.isCreator) { + return true + } + if let adminRights = self.adminRights, adminRights.rights.contains(.canManageTopics) { + return true + } + if let bannedRights = self.bannedRights, bannedRights.flags.contains(.banManageTopics) { + return false + } + if let defaultBannedRights = self.defaultBannedRights, defaultBannedRights.flags.contains(.banManageTopics) { + return false + } + return true case .inviteMembers: if case .broadcast = self.info { if let adminRights = self.adminRights { diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 3e9f5e2ae3..b23502cd9f 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -226,10 +226,16 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title: } if let topicId = topicId { - return resolveForumThreads(postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId))]) + return account.postbox.transaction { transaction -> Void in + transaction.removeHole(peerId: peerId, threadId: topicId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1)) + } |> castError(CreateForumChannelTopicError.self) - |> map { _ -> Int64 in - return topicId + |> mapToSignal { _ -> Signal in + return resolveForumThreads(postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId))]) + |> castError(CreateForumChannelTopicError.self) + |> map { _ -> Int64 in + return topicId + } } } else { return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift index 871997a723..7bcddceb66 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift @@ -21,17 +21,17 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { public static let canAddAdmins = TelegramChatAdminRightsFlags(rawValue: 1 << 9) public static let canBeAnonymous = TelegramChatAdminRightsFlags(rawValue: 1 << 10) public static let canManageCalls = TelegramChatAdminRightsFlags(rawValue: 1 << 11) + public static let canManageTopics = TelegramChatAdminRightsFlags(rawValue: 1 << 13) public static var all: TelegramChatAdminRightsFlags { - return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canBeAnonymous, .canManageCalls] + return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canBeAnonymous, .canManageCalls, .canManageTopics] } public static var allChannel: TelegramChatAdminRightsFlags { - return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canManageCalls] + return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canManageCalls, .canManageTopics] } - - public static var groupSpecific: TelegramChatAdminRightsFlags = [ + public static let internal_groupSpecific: TelegramChatAdminRightsFlags = [ .canChangeInfo, .canDeleteMessages, .canBanUsers, @@ -42,7 +42,7 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { .canAddAdmins ] - public static var broadcastSpecific: TelegramChatAdminRightsFlags = [ + public static let internal_broadcastSpecific: TelegramChatAdminRightsFlags = [ .canChangeInfo, .canPostMessages, .canEditMessages, @@ -52,6 +52,20 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { .canAddAdmins ] + public static func peerSpecific(peer: EnginePeer) -> TelegramChatAdminRightsFlags { + if case let .channel(channel) = peer { + if channel.flags.contains(.isForum) { + return internal_groupSpecific.union(.canManageTopics) + } else if case .broadcast = channel.info { + return internal_broadcastSpecific + } else { + return internal_groupSpecific + } + } else { + return internal_groupSpecific + } + } + public var count: Int { var result = 0 var index = 0 diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift index 1bb9f99b39..211bc1eb8c 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift @@ -23,6 +23,7 @@ public struct TelegramChatBannedRightsFlags: OptionSet, Hashable { public static let banChangeInfo = TelegramChatBannedRightsFlags(rawValue: 1 << 10) public static let banAddMembers = TelegramChatBannedRightsFlags(rawValue: 1 << 15) public static let banPinMessages = TelegramChatBannedRightsFlags(rawValue: 1 << 17) + public static let banManageTopics = TelegramChatBannedRightsFlags(rawValue: 1 << 18) } public struct TelegramChatBannedRights: PostboxCoding, Equatable { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift index 1e5cdb1a5e..847f856cca 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelOwnershipTransfer.swift @@ -83,12 +83,7 @@ func _internal_updateChannelOwnership(account: Account, accountStateManager: Acc return account.postbox.transaction { transaction -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> in if let channel = transaction.getPeer(channelId) as? TelegramChannel, let inputChannel = apiInputChannel(channel), let accountUser = transaction.getPeer(account.peerId), let user = transaction.getPeer(memberId), let inputUser = apiInputUser(user) { - var flags: TelegramChatAdminRightsFlags - if case .broadcast = channel.info { - flags = TelegramChatAdminRightsFlags.broadcastSpecific - } else { - flags = TelegramChatAdminRightsFlags.groupSpecific - } + let flags: TelegramChatAdminRightsFlags = TelegramChatAdminRightsFlags.peerSpecific(peer: .channel(channel)) let updatedParticipant = ChannelParticipant.creator(id: user.id, adminInfo: nil, rank: currentParticipant?.rank) let updatedPreviousCreator = ChannelParticipant.member(id: accountUser.id, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: flags), promotedBy: accountUser.id, canBeEditedByAccountPeer: false), banInfo: nil, rank: currentCreator?.rank) diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift index 513dc4c6af..cbf871fd33 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsHistoryTransition.swift @@ -663,6 +663,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), + (.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics), (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) ] @@ -792,8 +793,8 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins), (.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageLiveStreams) ] - prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.broadcastSpecific) - newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.broadcastSpecific) + prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) + newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) } else { order = [ (.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo), @@ -801,12 +802,13 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.canBanUsers, self.presentationData.strings.Channel_AdminLog_CanBanUsers), (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink), (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), + (.canManageTopics, self.presentationData.strings.Channel_AdminLog_CanManageTopics), (.canBeAnonymous, self.presentationData.strings.Channel_AdminLog_CanBeAnonymous), (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins), (.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageCalls) ] - prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.groupSpecific) - newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.groupSpecific) + prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) + newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) } if prevFlags.isEmpty && newFlags.isEmpty && (prevAdminRights != nil) != (newAdminRights != nil) { @@ -1016,6 +1018,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), + (.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics), (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) ] diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index 1a8157d1bf..7db45f607f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -932,7 +932,7 @@ func canEditPeerInfo(context: AccountContext, peer: Peer?, threadData: MessageHi } if let channel = peer as? TelegramChannel { if let threadData = threadData { - if channel.hasPermission(.pinMessages) { + if channel.hasPermission(.manageTopics) { return true } if threadData.author == context.account.peerId { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 0c8b705341..2353c9b61b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1654,7 +1654,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr var activePermissionCount: Int? if let defaultBannedRights = channel.defaultBannedRights { var count = 0 - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: .channel(channel)) { if !defaultBannedRights.flags.contains(right) { count += 1 } @@ -1666,7 +1666,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr interaction.openParticipantsSection(.members) })) if !channel.flags.contains(.isGigagroup) { - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList.count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList(peer: .channel(channel)).count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { interaction.openPermissions() })) } @@ -1771,7 +1771,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr var activePermissionCount: Int? if let defaultBannedRights = group.defaultBannedRights { var count = 0 - for (right, _) in allGroupPermissionList { + for (right, _) in allGroupPermissionList(peer: .legacyGroup(group)) { if !defaultBannedRights.flags.contains(right) { count += 1 } @@ -1779,7 +1779,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr activePermissionCount = count } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList.count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList(peer: .legacyGroup(group)).count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { interaction.openPermissions() }))