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 fec0eab8b7..eb6b6bcd93 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..aa6e1922ab 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -330,7 +330,7 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr return options } -private func forumRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isMuted: Bool?, isClosed: Bool, isPinned: Bool, isEditing: Bool, canPin: Bool, canManage: Bool) -> [ItemListRevealOption] { +private func forumRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isMuted: Bool?, isClosed: Bool, isPinned: Bool, isEditing: Bool, canPin: Bool, canOpenClose: Bool, canDelete: Bool) -> [ItemListRevealOption] { var options: [ItemListRevealOption] = [] if !isEditing { if let isMuted = isMuted { @@ -341,8 +341,10 @@ private func forumRevealOptions(strings: PresentationStrings, theme: Presentatio } } } - if canManage { + if canDelete { options.append(ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: strings.Common_Delete, icon: deleteIcon, color: theme.list.itemDisclosureActions.destructive.fillColor, textColor: theme.list.itemDisclosureActions.destructive.foregroundColor)) + } + if canOpenClose { if !isEditing { if !isClosed { options.append(ItemListRevealOption(key: RevealOptionKey.close.rawValue, title: strings.ChatList_CloseAction, icon: closeIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.inactive.foregroundColor)) @@ -2005,19 +2007,20 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if item.enableContextActions { if case .forum = item.chatListLocation { if case let .chat(itemPeer) = contentPeer, case let .channel(channel) = itemPeer.peer { - var canManage = false + var canOpenClose = false if channel.flags.contains(.isCreator) { - canManage = true - } else if channel.adminRights != nil { - canManage = true + canOpenClose = true + } else if channel.hasPermission(.pinMessages) { + canOpenClose = true } else if let threadInfo = threadInfo, threadInfo.isOwner { - canManage = true + canOpenClose = true } + let canDelete = channel.hasPermission(.deleteAllMessages) var isClosed = false if let threadInfo { isClosed = threadInfo.isClosed } - peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canPin: channel.flags.contains(.isCreator) || channel.adminRights != nil, canManage: canManage) + peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canPin: channel.flags.contains(.isCreator) || channel.adminRights != nil, canOpenClose: canOpenClose, canDelete: canDelete) peerLeftRevealOptions = [] } else { peerRevealOptions = [] 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/Images.xcassets/Settings/Menu/Topics.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/Menu/Topics.imageset/Contents.json new file mode 100644 index 0000000000..35c2cc2146 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/Topics.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "TopicsIcon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/Menu/Topics.imageset/TopicsIcon.svg b/submodules/TelegramUI/Images.xcassets/Settings/Menu/Topics.imageset/TopicsIcon.svg new file mode 100644 index 0000000000..65e891fbcd --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/Menu/Topics.imageset/TopicsIcon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/submodules/TelegramUI/Resources/Animations/anim_topics.json b/submodules/TelegramUI/Resources/Animations/anim_topics.json new file mode 100644 index 0000000000..d447d3c55f --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/anim_topics.json @@ -0,0 +1 @@ +{"v":"5.9.6","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Frame 169 Outlines 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":42,"s":[50]},{"t":55,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":35,"s":[256,256,0],"to":[0,34,0],"ti":[0,-34,0]},{"t":55,"s":[256,460,0]}],"ix":2,"l":2},"a":{"a":0,"k":[45,45,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":35,"s":[569,569,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":42,"s":[455.2,455.2,100]},{"t":55,"s":[569,569,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,1.657],[-1.657,0],[0,0],[0,-1.657],[1.657,0],[0,0]],"o":[[0,-1.657],[0,0],[1.657,0],[0,1.657],[0,0],[-1.657,0]],"v":[[-10.5,0],[-7.5,-3],[-7.329,-3],[-4.329,0],[-7.329,3],[-7.5,3]],"c":true}]},{"t":25,"s":[{"i":[[0,1.657],[-1.657,0],[0,0],[0,-1.657],[1.657,0],[0,0]],"o":[[0,-1.657],[0,0],[1.657,0],[0,1.657],[0,0],[-1.657,0]],"v":[[-10.5,0],[-7.5,-3],[7.5,-3],[10.5,0],[7.5,3],[-7.5,3]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[48.031,32.947],"ix":2},"a":{"a":0,"k":[-7.469,-0.053],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":14,"s":[0,0]},{"t":15,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,1.657],[-1.657,0],[0,0],[0,-1.657],[1.657,0],[0,0]],"o":[[0,-1.657],[0,0],[1.657,0],[0,1.657],[0,0],[-1.657,0]],"v":[[-15,0],[-12,-3],[-11.779,-3],[-8.779,0],[-11.779,3],[-12,3]],"c":true}]},{"t":20,"s":[{"i":[[0,1.657],[-1.657,0],[0,0],[0,-1.657],[1.657,0],[0,0]],"o":[[0,-1.657],[0,0],[1.657,0],[0,1.657],[0,0],[-1.657,0]],"v":[[-15,0],[-12,-3],[12,-3],[15,0],[12,3],[-12,3]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[48.147,21.009],"ix":2},"a":{"a":0,"k":[-11.853,0.009],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":9,"s":[0,0]},{"t":10,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.627,0],[0,6.627],[6.627,0],[0,-6.627]],"o":[[6.627,0],[0,-6.627],[-6.627,0],[0,6.627]],"v":[[0,12],[12,0],[0,-12],[-12,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27,27],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":5,"s":[0,0]},{"t":15,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":228,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Frame 169 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":35,"s":[256,256,0],"to":[0,-34,0],"ti":[0,34,0]},{"t":55,"s":[256,52,0]}],"ix":2,"l":2},"a":{"a":0,"k":[45,45,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":35,"s":[569,569,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":42,"s":[650,650,100]},{"t":55,"s":[569,569,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[0,-1.657],[-1.657,0],[0,0],[0,1.657],[1.657,0]],"o":[[-1.657,0],[0,1.657],[0,0],[1.657,0],[0,-1.657],[0,0]],"v":[[-7.5,-3],[-10.5,0],[-7.5,3],[-7.312,3],[-4.312,0],[-7.312,-3]],"c":true}]},{"t":40,"s":[{"i":[[0,0],[0,-1.657],[-1.657,0],[0,0],[0,1.657],[1.657,0]],"o":[[-1.657,0],[0,1.657],[0,0],[1.657,0],[0,-1.657],[0,0]],"v":[[-7.5,-3],[-10.5,0],[-7.5,3],[7.5,3],[10.5,0],[7.5,-3]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[48.002,68.935],"ix":2},"a":{"a":0,"k":[-7.498,-0.065],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":29,"s":[0,0]},{"t":30,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[0,-1.657],[-1.657,0],[0,0],[0,1.657],[1.657,0]],"o":[[-1.657,0],[0,1.657],[0,0],[1.657,0],[0,-1.657],[0,0]],"v":[[-12,-3],[-15,0],[-12,3],[-11.846,3],[-8.846,0],[-11.846,-3]],"c":true}]},{"t":35,"s":[{"i":[[0,0],[0,-1.657],[-1.657,0],[0,0],[0,1.657],[1.657,0]],"o":[[-1.657,0],[0,1.657],[0,0],[1.657,0],[0,-1.657],[0,0]],"v":[[-12,-3],[-15,0],[-12,3],[12,3],[15,0],[12,-3]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[47.979,56.922],"ix":2},"a":{"a":0,"k":[-12.021,-0.078],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":24,"s":[0,0]},{"t":25,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.627,0],[0,6.627],[6.627,0],[0,-6.627]],"o":[[6.627,0],[0,-6.627],[-6.627,0],[0,6.627]],"v":[[0,12],[12,0],[0,-12],[-12,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27,63],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":20,"s":[0,0]},{"t":30,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":225,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file 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..421f849dae 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -465,6 +465,11 @@ private enum PeerInfoReportType { case reaction(MessageId) } +private enum TopicsLimitedReason { + case participants(Int) + case discussion +} + private final class PeerInfoInteraction { let openChat: () -> Void let openUsername: (String) -> Void @@ -509,7 +514,7 @@ private final class PeerInfoInteraction { let editingOpenReactionsSetup: () -> Void let dismissInput: () -> Void let toggleForumTopics: (Bool) -> Void - let displayTopicsLimited: (Int) -> Void + let displayTopicsLimited: (TopicsLimitedReason) -> Void init( openUsername: @escaping (String) -> Void, @@ -555,7 +560,7 @@ private final class PeerInfoInteraction { editingOpenReactionsSetup: @escaping () -> Void, dismissInput: @escaping () -> Void, toggleForumTopics: @escaping (Bool) -> Void, - displayTopicsLimited: @escaping (Int) -> Void + displayTopicsLimited: @escaping (TopicsLimitedReason) -> Void ) { self.openUsername = openUsername self.openPhone = openPhone @@ -1615,26 +1620,30 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr } if isCreator, let appConfiguration = data.appConfiguration { - var minParticipants = 5 - if let data = appConfiguration.data, let value = data["forum_min_participants"] as? Int { + var minParticipants = 200 + if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Int { minParticipants = value } var canSetupTopics = false - var areTopicsLocked = true + var topicsLimitedReason: TopicsLimitedReason? if channel.flags.contains(.isForum) { canSetupTopics = true - areTopicsLocked = false + } else if case let .known(value) = cachedData.linkedDiscussionPeerId, value != nil { + canSetupTopics = true + topicsLimitedReason = .discussion } else if let memberCount = cachedData.participantsSummary.memberCount { canSetupTopics = true - areTopicsLocked = Int(memberCount) < minParticipants + if Int(memberCount) < minParticipants { + topicsLimitedReason = .participants(minParticipants) + } } if canSetupTopics { //TODO:localize - items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: "Topics", value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/ChatListFilters"), isLocked: areTopicsLocked, toggled: { value in - if areTopicsLocked { - interaction.displayTopicsLimited(minParticipants) + items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: "Topics", value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/Topics"), isLocked: topicsLimitedReason != nil, toggled: { value in + if let topicsLimitedReason = topicsLimitedReason { + interaction.displayTopicsLimited(topicsLimitedReason) } else { interaction.toggleForumTopics(value) } @@ -1654,7 +1663,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 +1675,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 +1780,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 +1788,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() })) @@ -2075,15 +2084,21 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } let _ = strongSelf.context.engine.peers.setChannelForumMode(id: strongSelf.peerId, isForum: value).start() }, - displayTopicsLimited: { [weak self] minCount in + displayTopicsLimited: { [weak self] reason in guard let self else { return } //TODO:localize let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let text = "Only groups with more than **\(minCount) members** can have topics enabled." - self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + let text: String + switch reason { + case let .participants(minCount): + text = "Only groups with more than **\(minCount) members** can have topics enabled." + case .discussion: + text = "Topics are currently unavailable in groups connected to channels." + } + self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_topics", scale: 0.066, colors: [:], title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) } )