diff --git a/TelegramCore/AccountStateManager.swift b/TelegramCore/AccountStateManager.swift index ab4f42e5ba..311ea2c675 100644 --- a/TelegramCore/AccountStateManager.swift +++ b/TelegramCore/AccountStateManager.swift @@ -700,6 +700,15 @@ public func messageForNotification(modifier: Modifier, id: MessageId, alwaysRetu let message = modifier.getMessage(id) if let message = message { + if let channel = message.peers[message.id.peerId] as? TelegramChannel { + switch channel.participationStatus { + case .kicked, .left: + return (nil, false) + case .member: + break + } + } + var foundReadState = false var isUnread = true if let readState = modifier.getCombinedPeerReadState(id.peerId) { diff --git a/TelegramCore/CachedChannelData.swift b/TelegramCore/CachedChannelData.swift index 57fef133fd..be3afa3d23 100644 --- a/TelegramCore/CachedChannelData.swift +++ b/TelegramCore/CachedChannelData.swift @@ -24,28 +24,35 @@ public struct CachedChannelParticipantsSummary: Coding, Equatable { public let memberCount: Int32? public let adminCount: Int32? public let bannedCount: Int32? + public let kickedCount: Int32? - init(memberCount: Int32?, adminCount: Int32?, bannedCount: Int32?) { + init(memberCount: Int32?, adminCount: Int32?, bannedCount: Int32?, kickedCount: Int32?) { self.memberCount = memberCount self.adminCount = adminCount self.bannedCount = bannedCount + self.kickedCount = kickedCount } public init(decoder: Decoder) { if let memberCount = decoder.decodeOptionalInt32ForKey("p.m") { self.memberCount = memberCount } else { - self.memberCount = 0 + self.memberCount = nil } if let adminCount = decoder.decodeOptionalInt32ForKey("p.a") { self.adminCount = adminCount } else { - self.adminCount = 0 + self.adminCount = nil } if let bannedCount = decoder.decodeOptionalInt32ForKey("p.b") { self.bannedCount = bannedCount } else { - self.bannedCount = 0 + self.bannedCount = nil + } + if let kickedCount = decoder.decodeOptionalInt32ForKey("p.k") { + self.kickedCount = kickedCount + } else { + self.kickedCount = nil } } @@ -65,22 +72,31 @@ public struct CachedChannelParticipantsSummary: Coding, Equatable { } else { encoder.encodeNil(forKey: "p.b") } + if let kickedCount = self.kickedCount { + encoder.encodeInt32(kickedCount, forKey: "p.k") + } else { + encoder.encodeNil(forKey: "p.k") + } } public static func ==(lhs: CachedChannelParticipantsSummary, rhs: CachedChannelParticipantsSummary) -> Bool { - return lhs.memberCount == rhs.memberCount && lhs.adminCount == rhs.adminCount && lhs.bannedCount == rhs.bannedCount + return lhs.memberCount == rhs.memberCount && lhs.adminCount == rhs.adminCount && lhs.bannedCount == rhs.bannedCount && lhs.kickedCount == rhs.kickedCount } public func withUpdatedMemberCount(_ memberCount: Int32?) -> CachedChannelParticipantsSummary { - return CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: self.adminCount, bannedCount: self.bannedCount) + return CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: self.adminCount, bannedCount: self.bannedCount, kickedCount: self.kickedCount) } public func withUpdatedAdminCount(_ adminCount: Int32?) -> CachedChannelParticipantsSummary { - return CachedChannelParticipantsSummary(memberCount: self.memberCount, adminCount: adminCount, bannedCount: self.bannedCount) + return CachedChannelParticipantsSummary(memberCount: self.memberCount, adminCount: adminCount, bannedCount: self.bannedCount, kickedCount: self.kickedCount) } public func withUpdatedBannedCount(_ bannedCount: Int32?) -> CachedChannelParticipantsSummary { - return CachedChannelParticipantsSummary(memberCount: self.memberCount, adminCount: self.adminCount, bannedCount: bannedCount) + return CachedChannelParticipantsSummary(memberCount: self.memberCount, adminCount: self.adminCount, bannedCount: bannedCount, kickedCount: self.kickedCount) + } + + public func withUpdatedKickedCount(_ kickedCount: Int32?) -> CachedChannelParticipantsSummary { + return CachedChannelParticipantsSummary(memberCount: self.memberCount, adminCount: self.adminCount, bannedCount: self.bannedCount, kickedCount: kickedCount) } } @@ -99,7 +115,7 @@ public final class CachedChannelData: CachedPeerData { init() { self.flags = [] self.about = nil - self.participantsSummary = CachedChannelParticipantsSummary(memberCount: nil, adminCount: nil, bannedCount: nil) + self.participantsSummary = CachedChannelParticipantsSummary(memberCount: nil, adminCount: nil, bannedCount: nil, kickedCount: nil) self.exportedInvitation = nil self.botInfos = [] self.topParticipants = nil diff --git a/TelegramCore/ChannelBlacklist.swift b/TelegramCore/ChannelBlacklist.swift index 85af787ecc..7dc4d64c93 100644 --- a/TelegramCore/ChannelBlacklist.swift +++ b/TelegramCore/ChannelBlacklist.swift @@ -9,10 +9,22 @@ import Foundation import MtProtoKitDynamic #endif -public func channelBlacklist(account: Account, peerId: PeerId) -> Signal<[RenderedChannelParticipant], NoError> { +private enum ChannelBlacklistFilter { + case restricted + case banned +} + +private func fetchChannelBlacklist(account: Account, peerId: PeerId, filter: ChannelBlacklistFilter) -> Signal<[RenderedChannelParticipant], NoError> { return account.postbox.modify { modifier -> Signal<[RenderedChannelParticipant], NoError> in if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer) { - return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsKicked(q: ""), offset: 0, limit: 100)) + let apiFilter: Api.ChannelParticipantsFilter + switch filter { + case .restricted: + apiFilter = .channelParticipantsBanned(q: "") + case .banned: + apiFilter = .channelParticipantsKicked(q: "") + } + return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: apiFilter, offset: 0, limit: 100)) |> retryRequest |> map { result -> [RenderedChannelParticipant] in var items: [RenderedChannelParticipant] = [] @@ -38,25 +50,105 @@ public func channelBlacklist(account: Account, peerId: PeerId) -> Signal<[Render } |> switchToLatest } -public func removeChannelBlacklistedPeer(account: Account, peerId: PeerId, memberId: PeerId) -> Signal { - return account.postbox.modify { modifier -> Signal in - if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer), let memberPeer = modifier.getPeer(memberId), let inputUser = apiInputUser(memberPeer) { - return account.network.request(Api.functions.channels.kickFromChannel(channel: inputChannel, userId: inputUser, kicked: .boolFalse)) - |> retryRequest - |> mapToSignal { result -> Signal in - account.stateManager.addUpdates(result) - return account.postbox.modify { modifier -> Void in - modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in - if let cachedData = cachedData as? CachedChannelData, let bannedCount = cachedData.participantsSummary.bannedCount { - return cachedData.withUpdatedParticipantsSummary(cachedData.participantsSummary.withUpdatedBannedCount(max(bannedCount - 1, 0))) - } else { - return cachedData - } - }) - } +public func channelBlacklistParticipants(account: Account, peerId: PeerId) -> Signal<[RenderedChannelParticipant], NoError> { + return combineLatest(fetchChannelBlacklist(account: account, peerId: peerId, filter: .restricted), fetchChannelBlacklist(account: account, peerId: peerId, filter: .banned)) + |> map { restricted, banned -> [RenderedChannelParticipant] in + var result: [RenderedChannelParticipant] = [] + var peerIds = Set() + for participant in restricted { + if !peerIds.contains(participant.peer.id) { + peerIds.insert(participant.peer.id) + result.append(participant) } - } else { - return .complete() + } + for participant in banned { + if !peerIds.contains(participant.peer.id) { + peerIds.insert(participant.peer.id) + result.append(participant) + } + } + return result } - } |> switchToLatest +} + +public func updateChannelMemberBannedRights(account: Account, peerId: PeerId, memberId: PeerId, rights: TelegramChannelBannedRights) -> Signal { + return fetchChannelParticipant(account: account, peerId: peerId, participantId: memberId) + |> mapToSignal { currentParticipant -> Signal in + return account.postbox.modify { modifier -> Signal in + if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer), let memberPeer = modifier.getPeer(memberId), let inputUser = apiInputUser(memberPeer) { + return account.network.request(Api.functions.channels.editBanned(channel: inputChannel, userId: inputUser, bannedRights: rights.apiBannedRights)) + |> retryRequest + |> mapToSignal { result -> Signal in + return account.postbox.modify { modifier -> Void in + modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in + if let cachedData = cachedData as? CachedChannelData { + var updatedData = cachedData + var wasKicked = false + var wasBanned = false + var wasMember = false + if let currentParticipant = currentParticipant { + switch currentParticipant { + case .creator: + break + case let .member(_, _, _, banInfo): + if let banInfo = banInfo { + if banInfo.flags.contains(.banReadMessages) { + wasKicked = true + } else if !banInfo.flags.isEmpty { + wasBanned = true + } + } + wasMember = true + } + } + + var isKicked = false + var isBanned = false + if rights.flags.contains(.banReadMessages) { + isKicked = true + } else if !rights.flags.isEmpty { + isBanned = true + } + + let isMember = !wasKicked && !rights.flags.contains(.banReadMessages) + + if isKicked != wasKicked { + if let kickedCount = updatedData.participantsSummary.kickedCount { + updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedKickedCount(max(0, kickedCount + (isKicked ? 1 : -1)))) + } + } + + if isBanned != wasBanned { + if let bannedCount = updatedData.participantsSummary.bannedCount { + updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedBannedCount(max(0, bannedCount + (isBanned ? 1 : -1)))) + } + } + + if isMember != wasMember { + if let memberCount = updatedData.participantsSummary.memberCount { + updatedData = updatedData.withUpdatedParticipantsSummary(updatedData.participantsSummary.withUpdatedMemberCount(max(0, memberCount + (isMember ? 1 : -1)))) + } + + if !isMember, let topParticipants = updatedData.topParticipants { + var updatedParticipants = topParticipants.participants + if let index = updatedParticipants.index(where: { $0.peerId == memberId }) { + updatedParticipants.remove(at: index) + + updatedData = updatedData.withUpdatedTopParticipants(CachedChannelParticipants(participants: updatedParticipants)) + } + } + } + + return updatedData + } else { + return cachedData + } + }) + } + } + } else { + return .complete() + } + } |> switchToLatest + } } diff --git a/TelegramCore/ChannelParticipants.swift b/TelegramCore/ChannelParticipants.swift index ec3d20edec..b7c69481e1 100644 --- a/TelegramCore/ChannelParticipants.swift +++ b/TelegramCore/ChannelParticipants.swift @@ -28,9 +28,10 @@ func updateChannelParticipantsSummary(account: Account, peerId: PeerId) -> Signa if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer) { let admins = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 0)) let members = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsRecent, offset: 0, limit: 0)) - let banned = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsKicked(q: ""), offset: 0, limit: 0)) - return combineLatest(admins, members, banned) - |> mapToSignal { admins, members, banned -> Signal in + let banned = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsBanned(q: ""), offset: 0, limit: 0)) + let kicked = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsKicked(q: ""), offset: 0, limit: 0)) + return combineLatest(admins, members, banned, kicked) + |> mapToSignal { admins, members, banned, kicked -> Signal in return account.postbox.modify { modifier -> Void in modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if let current = current as? CachedChannelData { @@ -49,7 +50,12 @@ func updateChannelParticipantsSummary(account: Account, peerId: PeerId) -> Signa case let .channelParticipants(count, _, _): bannedCount = count } - return current.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: adminCount, bannedCount: bannedCount)) + let kickedCount: Int32 + switch kicked { + case let .channelParticipants(count, _, _): + kickedCount = count + } + return current.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: adminCount, bannedCount: bannedCount, kickedCount: kickedCount)) } return current }) diff --git a/TelegramCore/PeerAdmins.swift b/TelegramCore/PeerAdmins.swift index 3092aeb3fa..6616655e03 100644 --- a/TelegramCore/PeerAdmins.swift +++ b/TelegramCore/PeerAdmins.swift @@ -199,7 +199,7 @@ public enum UpdatePeerAdminRightsError { case addMemberError(AddPeerMemberError) } -private func fetchChannelParticipant(account: Account, peerId: PeerId, participantId: PeerId) -> Signal { +func fetchChannelParticipant(account: Account, peerId: PeerId, participantId: PeerId) -> Signal { return account.postbox.modify { modifier -> Signal in if let peer = modifier.getPeer(peerId), let adminPeer = modifier.getPeer(participantId), let inputUser = apiInputUser(adminPeer) { if let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) { diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 20974027fe..0bc733ba9c 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -64,6 +64,14 @@ func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoin } else if let contact = media as? TelegramMediaContact { let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName) return .ready(.media(input)) + } else if let map = media as? TelegramMediaMap { + let input: Api.InputMedia + if let venue = map.venue { + input = .inputMediaVenue(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude), title: venue.title, address: venue.address ?? "", provider: venue.provider ?? "", venueId: venue.id ?? "") + } else { + input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude)) + } + return .ready(.media(input)) } else { return .ready(.text(text)) } diff --git a/TelegramCore/TelegramChannelAdminRights.swift b/TelegramCore/TelegramChannelAdminRights.swift index 103137c795..fb5c38b21b 100644 --- a/TelegramCore/TelegramChannelAdminRights.swift +++ b/TelegramCore/TelegramChannelAdminRights.swift @@ -24,7 +24,7 @@ public struct TelegramChannelAdminRightsFlags: OptionSet { public static let canInviteUsers = TelegramChannelAdminRightsFlags(rawValue: 1 << 5) public static let canChangeInviteLink = TelegramChannelAdminRightsFlags(rawValue: 1 << 6) public static let canPinMessages = TelegramChannelAdminRightsFlags(rawValue: 1 << 7) - public static let canAddAdmins = TelegramChannelAdminRightsFlags(rawValue: 1 << 8) + public static let canAddAdmins = TelegramChannelAdminRightsFlags(rawValue: 1 << 9) public static var groupSpecific: TelegramChannelAdminRightsFlags = [ .canChangeInfo, diff --git a/TelegramCore/UpdateCachedPeerData.swift b/TelegramCore/UpdateCachedPeerData.swift index 03dd1d9908..3dd5f0ea27 100644 --- a/TelegramCore/UpdateCachedPeerData.swift +++ b/TelegramCore/UpdateCachedPeerData.swift @@ -246,7 +246,7 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos return previous.withUpdatedFlags(channelFlags) .withUpdatedAbout(about) - .withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: kickedCount)) + .withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: bannedCount, kickedCount: kickedCount)) .withUpdatedExportedInvitation(ExportedInvitation(apiExportedInvite: apiExportedInvite)) .withUpdatedBotInfos(botInfos) .withUpdatedPinnedMessageId(pinnedMessageId)