diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index ca29b2b094..e205a9dd59 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -386,8 +386,6 @@ D08CAA881ED81DD40000FDA8 /* LocalizationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CAA861ED81DD40000FDA8 /* LocalizationInfo.swift */; }; D08CAA8C1ED81EDF0000FDA8 /* Localizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */; }; D08CAA8D1ED81EDF0000FDA8 /* Localizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */; }; - D08D7E8120A0F3E10005D80C /* GroupPeerMembers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08D7E8020A0F3E10005D80C /* GroupPeerMembers.swift */; }; - D08D7E8220A0F3E10005D80C /* GroupPeerMembers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08D7E8020A0F3E10005D80C /* GroupPeerMembers.swift */; }; D08F4A661E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */; }; D08F4A671E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */; }; D08F4A691E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */; }; @@ -922,7 +920,6 @@ D08CAA831ED8164B0000FDA8 /* Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localization.swift; sourceTree = ""; }; D08CAA861ED81DD40000FDA8 /* LocalizationInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationInfo.swift; sourceTree = ""; }; D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localizations.swift; sourceTree = ""; }; - D08D7E8020A0F3E10005D80C /* GroupPeerMembers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupPeerMembers.swift; sourceTree = ""; }; D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeInstalledStickerPacksOperations.swift; sourceTree = ""; }; D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeInstalledStickerPacksOperations.swift; sourceTree = ""; }; D093D7ED206413F600BC3599 /* SecureIdDataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDataTypes.swift; sourceTree = ""; }; @@ -1790,7 +1787,6 @@ D0C44B601FC616E200227BE0 /* SearchGroupMembers.swift */, D0F8C39C20178B9B00236FC5 /* GroupFeedPeers.swift */, D018EE042045E95000CBB130 /* CheckPeerChatServiceActions.swift */, - D08D7E8020A0F3E10005D80C /* GroupPeerMembers.swift */, ); name = Peers; sourceTree = ""; @@ -2092,7 +2088,6 @@ D0F3A8A21E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */, D01C7ED61EF5E468008305F1 /* ProxySettings.swift in Sources */, D049EAE81E44B67100A2CD3A /* RecentPeerItem.swift in Sources */, - D08D7E8120A0F3E10005D80C /* GroupPeerMembers.swift in Sources */, D02ABC7B1E30058F00CAE539 /* DeleteMessagesInteractively.swift in Sources */, C2E064681ECEEF0A00387BB8 /* TelegramMediaInvoice.swift in Sources */, D0448C9F1E27F5EB005A61A7 /* Random.swift in Sources */, @@ -2448,7 +2443,6 @@ D01A21A71F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */, D0E68776207534CA0064BDB2 /* Api0.swift in Sources */, D00C7CEC1E37A8540080C3D5 /* SetSecretChatMessageAutoremoveTimeoutInteractively.swift in Sources */, - D08D7E8220A0F3E10005D80C /* GroupPeerMembers.swift in Sources */, D048B4AD20A5DA4300C79D31 /* ManagedProxyInfoUpdates.swift in Sources */, D0B844481DAB91FD005F29E1 /* ManagedMessageHistoryHoles.swift in Sources */, D0F3CC7B1DDE2859008148FA /* RequestEditMessage.swift in Sources */, diff --git a/TelegramCore/Api3.swift b/TelegramCore/Api3.swift index 483a8ce643..3a7b5cbd5b 100644 --- a/TelegramCore/Api3.swift +++ b/TelegramCore/Api3.swift @@ -1031,30 +1031,6 @@ public extension Api { }) } - public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, geoPoint: Api.InputGeoPoint?) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Updates?) { - let buffer = Buffer() - buffer.appendInt32(97630429) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 13) != 0 {geoPoint!.serialize(buffer, true)} - return (FunctionDescription({return "(messages.editMessage flags: \(flags), peer: \(peer), id: \(id), message: \(String(describing: message)), replyMarkup: \(String(describing: replyMarkup)), entities: \(String(describing: entities)), geoPoint: \(String(describing: geoPoint)))"}), buffer, { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - public static func editInlineBotMessage(flags: Int32, id: Api.InputBotInlineMessageID, message: String?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Bool?) { let buffer = Buffer() buffer.appendInt32(319564933) @@ -1800,6 +1776,31 @@ public extension Api { }) } + public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, geoPoint: Api.InputGeoPoint?) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Updates?) { + let buffer = Buffer() + buffer.appendInt32(-1073683256) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 13) != 0 {geoPoint!.serialize(buffer, true)} + return (FunctionDescription({return "(messages.editMessage flags: \(flags), peer: \(peer), id: \(id), message: \(String(describing: message)), media: \(String(describing: media)), replyMarkup: \(String(describing: replyMarkup)), entities: \(String(describing: entities)), geoPoint: \(String(describing: geoPoint)))"}), buffer, { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func getStickers(emoticon: String, hash: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.messages.Stickers?) { let buffer = Buffer() buffer.appendInt32(71126828) diff --git a/TelegramCore/ChannelBlacklist.swift b/TelegramCore/ChannelBlacklist.swift index 11b945c9b6..532f4020c2 100644 --- a/TelegramCore/ChannelBlacklist.swift +++ b/TelegramCore/ChannelBlacklist.swift @@ -132,15 +132,36 @@ public func channelBlacklistParticipants(account: Account, peerId: PeerId) -> Si } } -public func updateChannelMemberBannedRights(account: Account, peerId: PeerId, memberId: PeerId, rights: TelegramChannelBannedRights) -> Signal { +public func updateChannelMemberBannedRights(account: Account, peerId: PeerId, memberId: PeerId, rights: TelegramChannelBannedRights?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> { 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)) + |> mapToSignal { currentParticipant -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in + return account.postbox.modify { modifier -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in + if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer), let _ = modifier.getPeer(account.peerId), let memberPeer = modifier.getPeer(memberId), let inputUser = apiInputUser(memberPeer) { + let updatedParticipant: ChannelParticipant + if let currentParticipant = currentParticipant, case let .member(_, invitedAt, _, currentBanInfo) = currentParticipant { + let banInfo: ChannelParticipantBannedInfo? + if let rights = rights, !rights.flags.isEmpty { + banInfo = ChannelParticipantBannedInfo(rights: rights, restrictedBy: currentBanInfo?.restrictedBy ?? account.peerId, isMember: currentBanInfo?.isMember ?? true) + } else { + banInfo = nil + } + updatedParticipant = ChannelParticipant.member(id: memberId, invitedAt: invitedAt, adminInfo: nil, banInfo: banInfo) + } else { + let banInfo: ChannelParticipantBannedInfo? + if let rights = rights, !rights.flags.isEmpty { + banInfo = ChannelParticipantBannedInfo(rights: rights, restrictedBy: account.peerId, isMember: false) + } else { + banInfo = nil + } + updatedParticipant = ChannelParticipant.member(id: memberId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: nil, banInfo: banInfo) + } + + let effectiveRights: TelegramChannelBannedRights = rights ?? TelegramChannelBannedRights(flags: [], untilDate: 0) + + return account.network.request(Api.functions.channels.editBanned(channel: inputChannel, userId: inputUser, bannedRights: effectiveRights.apiBannedRights)) |> retryRequest - |> mapToSignal { result -> Signal in - return account.postbox.modify { modifier -> Void in + |> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), NoError> in + return account.postbox.modify { modifier -> (ChannelParticipant?, RenderedChannelParticipant) in modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in if let cachedData = cachedData as? CachedChannelData { var updatedData = cachedData @@ -166,13 +187,13 @@ public func updateChannelMemberBannedRights(account: Account, peerId: PeerId, me var isKicked = false var isBanned = false - if rights.flags.contains(.banReadMessages) { + if effectiveRights.flags.contains(.banReadMessages) { isKicked = true - } else if !rights.flags.isEmpty { + } else if !effectiveRights.flags.isEmpty { isBanned = true } - let isMember = !wasKicked && !rights.flags.contains(.banReadMessages) + let isMember = !wasKicked && !effectiveRights.flags.contains(.banReadMessages) if isKicked != wasKicked { if let kickedCount = updatedData.participantsSummary.kickedCount { @@ -206,6 +227,18 @@ public func updateChannelMemberBannedRights(account: Account, peerId: PeerId, me return cachedData } }) + var peers: [PeerId: Peer] = [:] + var presences: [PeerId: PeerPresence] = [:] + peers[memberPeer.id] = memberPeer + if let presence = modifier.getPeerPresence(peerId: memberPeer.id) { + presences[memberPeer.id] = presence + } + if case let .member(_, _, _, maybeBanInfo) = updatedParticipant, let banInfo = maybeBanInfo { + if let peer = modifier.getPeer(banInfo.restrictedBy) { + peers[peer.id] = peer + } + } + return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: memberPeer, peers: peers, presences: presences)) } } } else { diff --git a/TelegramCore/ChannelMembers.swift b/TelegramCore/ChannelMembers.swift index c996bef63f..a307529b15 100644 --- a/TelegramCore/ChannelMembers.swift +++ b/TelegramCore/ChannelMembers.swift @@ -9,50 +9,82 @@ import Foundation import MtProtoKitDynamic #endif -public enum ChannelMembersFilter { - case none +public enum ChannelMembersCategoryFilter { + case all case search(String) } -public func channelMembers(postbox: Postbox, network: Network, peerId: PeerId, filter: ChannelMembersFilter = .none) -> Signal<[RenderedChannelParticipant], NoError> { +public enum ChannelMembersCategory { + case recent(ChannelMembersCategoryFilter) + case admins + case restricted(ChannelMembersCategoryFilter) + case banned(ChannelMembersCategoryFilter) +} + +public func channelMembers(postbox: Postbox, network: Network, peerId: PeerId, category: ChannelMembersCategory = .recent(.all), offset: Int32 = 0, limit: Int32 = 64) -> Signal<[RenderedChannelParticipant], NoError> { return postbox.modify { modifier -> Signal<[RenderedChannelParticipant], NoError> in if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer) { let apiFilter: Api.ChannelParticipantsFilter - switch filter { - case .none: - apiFilter = .channelParticipantsRecent - case let .search(query): - apiFilter = .channelParticipantsSearch(q: query) - } - return network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: apiFilter, offset: 0, limit: 100, hash: 0)) - |> retryRequest - |> map { result -> [RenderedChannelParticipant] in - var items: [RenderedChannelParticipant] = [] - switch result { - case let .channelParticipants(_, participants, users): - var peers: [PeerId: Peer] = [:] - var presences:[PeerId: PeerPresence] = [:] - for user in users { - let peer = TelegramUser(user: user) - peers[peer.id] = peer - if let presence = TelegramUserPresence(apiUser: user) { - presences[peer.id] = presence - } - } - - for participant in CachedChannelParticipants(apiParticipants: participants).participants { - if let peer = peers[participant.peerId] { - items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: presences)) - } - - } - case .channelParticipantsNotModified: - break + switch category { + case let .recent(filter): + switch filter { + case .all: + apiFilter = .channelParticipantsRecent + case let .search(query): + apiFilter = .channelParticipantsSearch(q: query) + } + case .admins: + apiFilter = .channelParticipantsAdmins + case let .restricted(filter): + switch filter { + case .all: + apiFilter = .channelParticipantsBanned(q: "") + case let .search(query): + apiFilter = .channelParticipantsBanned(q: query) + } + case let .banned(filter): + switch filter { + case .all: + apiFilter = .channelParticipantsKicked(q: "") + case let .search(query): + apiFilter = .channelParticipantsKicked(q: query) + } + } + return network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: apiFilter, offset: offset, limit: limit, hash: 0)) + |> retryRequest + |> mapToSignal { result -> Signal<[RenderedChannelParticipant], NoError> in + return postbox.modify { modifier -> [RenderedChannelParticipant] in + var items: [RenderedChannelParticipant] = [] + switch result { + case let .channelParticipants(_, participants, users): + var peers: [PeerId: Peer] = [:] + var presences: [PeerId: PeerPresence] = [:] + for user in users { + let peer = TelegramUser(user: user) + peers[peer.id] = peer + if let presence = TelegramUserPresence(apiUser: user) { + presences[peer.id] = presence + } + } + updatePeers(modifier: modifier, peers: Array(peers.values), update: { _, updated in + return updated + }) + modifier.updatePeerPresences(presences) + + for participant in CachedChannelParticipants(apiParticipants: participants).participants { + if let peer = peers[participant.peerId] { + items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: presences)) + } + + } + case .channelParticipantsNotModified: + break + } + return items } - return items } } else { return .single([]) } - } |> switchToLatest + } |> switchToLatest } diff --git a/TelegramCore/ChannelParticipants.swift b/TelegramCore/ChannelParticipants.swift index 4ce2b2ed9a..df953a15c4 100644 --- a/TelegramCore/ChannelParticipants.swift +++ b/TelegramCore/ChannelParticipants.swift @@ -13,8 +13,8 @@ public struct RenderedChannelParticipant: Equatable { public let participant: ChannelParticipant public let peer: Peer public let peers: [PeerId: Peer] - public let presences:[PeerId: PeerPresence] - public init(participant: ChannelParticipant, peer: Peer, peers: [PeerId: Peer] = [:], presences:[PeerId : PeerPresence] = [:]) { + public let presences: [PeerId: PeerPresence] + public init(participant: ChannelParticipant, peer: Peer, peers: [PeerId: Peer] = [:], presences: [PeerId: PeerPresence] = [:]) { self.participant = participant self.peer = peer self.peers = peers @@ -34,47 +34,47 @@ func updateChannelParticipantsSummary(account: Account, peerId: PeerId) -> Signa let banned = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsBanned(q: ""), offset: 0, limit: 0, hash: 0)) let kicked = account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsKicked(q: ""), offset: 0, limit: 0, hash: 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 { - let adminCount: Int32 - switch admins { - case let .channelParticipants(count, _, _): - adminCount = count - case .channelParticipantsNotModified: - assertionFailure() - adminCount = 0 - } - let memberCount: Int32 - switch members { - case let .channelParticipants(count, _, _): - memberCount = count - case .channelParticipantsNotModified: - assertionFailure() - memberCount = 0 - } - let bannedCount: Int32 - switch banned { - case let .channelParticipants(count, _, _): - bannedCount = count - case .channelParticipantsNotModified: - assertionFailure() - bannedCount = 0 - } - let kickedCount: Int32 - switch kicked { - case let .channelParticipants(count, _, _): - kickedCount = count - case .channelParticipantsNotModified: - assertionFailure() - kickedCount = 0 - } - return current.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: adminCount, bannedCount: bannedCount, kickedCount: kickedCount)) + |> 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 { + let adminCount: Int32 + switch admins { + case let .channelParticipants(count, _, _): + adminCount = count + case .channelParticipantsNotModified: + assertionFailure() + adminCount = 0 } - return current - }) - } |> mapError { _ -> MTRpcError in return MTRpcError(errorCode: 0, errorDescription: "") } + let memberCount: Int32 + switch members { + case let .channelParticipants(count, _, _): + memberCount = count + case .channelParticipantsNotModified: + assertionFailure() + memberCount = 0 + } + let bannedCount: Int32 + switch banned { + case let .channelParticipants(count, _, _): + bannedCount = count + case .channelParticipantsNotModified: + assertionFailure() + bannedCount = 0 + } + let kickedCount: Int32 + switch kicked { + case let .channelParticipants(count, _, _): + kickedCount = count + case .channelParticipantsNotModified: + assertionFailure() + kickedCount = 0 + } + return current.withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: memberCount, adminCount: adminCount, bannedCount: bannedCount, kickedCount: kickedCount)) + } + return current + }) + } |> mapError { _ -> MTRpcError in return MTRpcError(errorCode: 0, errorDescription: "") } } |> `catch` { _ -> Signal in return .complete() diff --git a/TelegramCore/GroupPeerMembers.swift b/TelegramCore/GroupPeerMembers.swift deleted file mode 100644 index 3739f2d5ed..0000000000 --- a/TelegramCore/GroupPeerMembers.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation -#if os(macOS) -import PostboxMac -import SwiftSignalKitMac -#else -import Postbox -import SwiftSignalKit -#endif - -public final class GroupPeerMembersContext { - private let postbox: Postbox - private let network: Network - private let peerId: PeerId - - public init(postbox: Postbox, network: Network, peerId: PeerId) { - self.postbox = postbox - self.network = network - self.peerId = peerId - } -} diff --git a/TelegramCore/PeerAdmins.swift b/TelegramCore/PeerAdmins.swift index 8294c3045d..b6dcf4e286 100644 --- a/TelegramCore/PeerAdmins.swift +++ b/TelegramCore/PeerAdmins.swift @@ -225,15 +225,34 @@ public func fetchChannelParticipant(account: Account, peerId: PeerId, participan } |> switchToLatest } -public func updatePeerAdminRights(account: Account, peerId: PeerId, adminId: PeerId, rights: TelegramChannelAdminRights) -> Signal { +public func updatePeerAdminRights(account: Account, peerId: PeerId, adminId: PeerId, rights: TelegramChannelAdminRights) -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdatePeerAdminRightsError> { return fetchChannelParticipant(account: account, peerId: peerId, participantId: adminId) |> mapError { error -> UpdatePeerAdminRightsError in return .generic } - |> mapToSignal { currentParticipant -> Signal in - return account.postbox.modify { modifier -> Signal in + |> mapToSignal { currentParticipant -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdatePeerAdminRightsError> in + return account.postbox.modify { modifier -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdatePeerAdminRightsError> in + if let peer = modifier.getPeer(peerId), let adminPeer = modifier.getPeer(adminId), let inputUser = apiInputUser(adminPeer) { if let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) { + let updatedParticipant: ChannelParticipant + if let currentParticipant = currentParticipant, case let .member(_, invitedAt, currentAdminInfo, _) = currentParticipant { + let adminInfo: ChannelParticipantAdminInfo? + if !rights.flags.isEmpty { + adminInfo = ChannelParticipantAdminInfo(rights: rights, promotedBy: currentAdminInfo?.promotedBy ?? account.peerId, canBeEditedByAccountPeer: true) + } else { + adminInfo = nil + } + updatedParticipant = ChannelParticipant.member(id: adminId, invitedAt: invitedAt, adminInfo: adminInfo, banInfo: nil) + } else { + let adminInfo: ChannelParticipantAdminInfo? + if !rights.flags.isEmpty { + adminInfo = ChannelParticipantAdminInfo(rights: rights, promotedBy: account.peerId, canBeEditedByAccountPeer: true) + } else { + adminInfo = nil + } + updatedParticipant = ChannelParticipant.member(id: adminId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: adminInfo, banInfo: nil) + } return account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights.apiAdminRights)) |> map { [$0] } |> `catch` { error -> Signal<[Api.Updates], UpdatePeerAdminRightsError> in @@ -253,11 +272,11 @@ public func updatePeerAdminRights(account: Account, peerId: PeerId, adminId: Pee } return .fail(.generic) } - |> mapToSignal { result -> Signal in + |> mapToSignal { result -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdatePeerAdminRightsError> in for updates in result { account.stateManager.addUpdates(updates) } - return account.postbox.modify { modifier -> Void in + return account.postbox.modify { modifier -> (ChannelParticipant?, RenderedChannelParticipant) in modifier.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in if let cachedData = cachedData as? CachedChannelData, let adminCount = cachedData.participantsSummary.adminCount { var updatedAdminCount = adminCount @@ -283,6 +302,18 @@ public func updatePeerAdminRights(account: Account, peerId: PeerId, adminId: Pee return cachedData } }) + var peers: [PeerId: Peer] = [:] + var presences: [PeerId: PeerPresence] = [:] + peers[adminPeer.id] = adminPeer + if let presence = modifier.getPeerPresence(peerId: adminPeer.id) { + presences[adminPeer.id] = presence + } + if case let .member(_, _, maybeAdminInfo, _) = updatedParticipant, let adminInfo = maybeAdminInfo { + if let peer = modifier.getPeer(adminInfo.promotedBy) { + peers[peer.id] = peer + } + } + return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: adminPeer, peers: peers, presences: presences)) } |> mapError { _ -> UpdatePeerAdminRightsError in return .generic } } } else { diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 4d5aa4fe0c..0f39c6c5e2 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -65,44 +65,48 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods return .ready(.forward(forwardInfo)) } else if let contextResult = contextResult { return .ready(.chatContextResult(contextResult)) - } else if let media = media.first { - if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { - if let reference = image.reference, case let .cloud(id, accessHash) = reference { - return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), ttlSeconds: nil), text)) - } else { - return .upload(uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute)) - } - } else if let file = media as? TelegramMediaFile { - if let resource = file.resource as? CloudDocumentMediaResource { - if peerId.namespace == Namespaces.Peer.SecretChat { - return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)) - } else { - return .ready(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)) - } - } else { - return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)) - } - } 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, text)) - } else if let map = media as? TelegramMediaMap { - let input: Api.InputMedia - if let liveBroadcastingTimeout = map.liveBroadcastingTimeout { - input = .inputMediaGeoLive(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude), period: liveBroadcastingTimeout) - } else 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 ?? "", venueType: venue.type ?? "") - } else { - input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude)) - } - return .ready(.media(input, text)) - } else { - return .ready(.text(text)) - } + } else if let media = media.first, let mediaResult = mediaContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, media: media, text: text, autoremoveAttribute: autoremoveAttribute, messageId: messageId, attributes: attributes) { + return mediaResult } else { return .ready(.text(text)) } } +func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, peerId: PeerId, media: Media, text: String, autoremoveAttribute: AutoremoveTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> PendingMessageUploadContent? { + if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { + if let reference = image.reference, case let .cloud(id, accessHash) = reference { + return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), ttlSeconds: nil), text)) + } else { + return .upload(uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute)) + } + } else if let file = media as? TelegramMediaFile { + if let resource = file.resource as? CloudDocumentMediaResource { + if peerId.namespace == Namespaces.Peer.SecretChat { + return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)) + } else { + return .ready(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)) + } + } else { + return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)) + } + } 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, text)) + } else if let map = media as? TelegramMediaMap { + let input: Api.InputMedia + if let liveBroadcastingTimeout = map.liveBroadcastingTimeout { + input = .inputMediaGeoLive(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude), period: liveBroadcastingTimeout) + } else 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 ?? "", venueType: venue.type ?? "") + } else { + input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude)) + } + return .ready(.media(input, text)) + } else { + return nil + } +} + private enum PredownloadedResource { case localReference(CachedSentMediaReferenceKey?) case media(Media) diff --git a/TelegramCore/RemovePeerMember.swift b/TelegramCore/RemovePeerMember.swift index 02d5a3cdc8..45ca21171e 100644 --- a/TelegramCore/RemovePeerMember.swift +++ b/TelegramCore/RemovePeerMember.swift @@ -13,6 +13,9 @@ public func removePeerMember(account: Account, peerId: PeerId, memberId: PeerId) if peerId.namespace == Namespaces.Peer.CloudChannel { return updateChannelMemberBannedRights(account: account, peerId: peerId, memberId: memberId, rights: TelegramChannelBannedRights(flags: [.banReadMessages], untilDate: 0)) + |> mapToSignal { _ -> Signal in + return .complete() + } } return account.postbox.modify { modifier -> Signal in diff --git a/TelegramCore/RequestEditMessage.swift b/TelegramCore/RequestEditMessage.swift index 888b534a0c..2f689c29af 100644 --- a/TelegramCore/RequestEditMessage.swift +++ b/TelegramCore/RequestEditMessage.swift @@ -9,72 +9,146 @@ import Foundation import MtProtoKitDynamic #endif -public func requestEditMessage(account: Account, messageId: MessageId, text: String, entities: TextEntitiesMessageAttribute? = nil, disableUrlPreview: Bool = false) -> Signal { - return account.postbox.modify { modifier -> (Peer?, SimpleDictionary) in - guard let message = modifier.getMessage(messageId) else { - return (nil, SimpleDictionary()) - } - - if text.isEmpty { - for media in message.media { - switch media { - case _ as TelegramMediaImage, _ as TelegramMediaFile: - break - default: - return (nil, SimpleDictionary()) - } - } - } - - var peers = SimpleDictionary() +public enum RequestEditMessageMedia { + case keep + case update(Media) +} - if let entities = entities { - for peerId in entities.associatedPeerIds { - if let peer = modifier.getPeer(peerId) { - peers[peer.id] = peer +public enum RequestEditMessageResult { + case progress(Float) + case done(Bool) +} + +public func requestEditMessage(account: Account, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute? = nil, disableUrlPreview: Bool = false) -> Signal { + let uploadedMedia: Signal + switch media { + case .keep: + uploadedMedia = .single(.progress(0.0)) |> then(.single(nil)) + case let .update(media): + if let uploadData = mediaContentToUpload(network: account.network, postbox: account.postbox, auxiliaryMethods: account.auxiliaryMethods, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, peerId: messageId.peerId, media: media, text: "", autoremoveAttribute: nil, messageId: nil, attributes: []) { + switch uploadData { + case let .ready(content): + uploadedMedia = .single(.content(content)) + case let .upload(upload): + uploadedMedia = .single(.progress(0.027)) |> then(upload) + |> map { result -> PendingMessageUploadedContentResult? in + switch result { + case let .progress(value): + return .progress(max(value, 0.027)) + case let .content(content): + return .content(content) + } + } + |> `catch` { _ -> Signal in + return .single(nil) + } } + } else { + uploadedMedia = .single(nil) + } + } + return uploadedMedia + |> mapToSignal { uploadedMediaResult -> Signal in + var pendingMediaContent: PendingMessageUploadedContent? + if let uploadedMediaResult = uploadedMediaResult { + switch uploadedMediaResult { + case let .progress(value): + return .single(.progress(value)) + case let .content(content): + pendingMediaContent = content } } - return (modifier.getPeer(messageId.peerId), peers) - } - |> mapToSignal { peer, associatedPeers in - if let peer = peer, let inputPeer = apiInputPeer(peer) { - var flags: Int32 = 1 << 11 - - var apiEntities: [Api.MessageEntity]? + return account.postbox.modify { modifier -> (Peer?, SimpleDictionary) in + guard let message = modifier.getMessage(messageId) else { + return (nil, SimpleDictionary()) + } + + if text.isEmpty { + for media in message.media { + switch media { + case _ as TelegramMediaImage, _ as TelegramMediaFile: + break + default: + return (nil, SimpleDictionary()) + } + } + } + + var peers = SimpleDictionary() + if let entities = entities { - apiEntities = apiTextAttributeEntities(entities, associatedPeers: associatedPeers) - flags |= Int32(1 << 3) - } - - if disableUrlPreview { - flags |= Int32(1 << 1) - } - - return account.network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: text, replyMarkup: nil, entities: apiEntities, geoPoint: nil)) - |> map { result -> Api.Updates? in - return result - } - |> `catch` { error -> Signal in - if error.errorDescription == "MESSAGE_NOT_MODIFIED" { - return .single(nil) - } else { - return .fail(error) + for peerId in entities.associatedPeerIds { + if let peer = modifier.getPeer(peerId) { + peers[peer.id] = peer } } - |> mapError { _ -> NoError in - return NoError() + } + return (modifier.getPeer(messageId.peerId), peers) + } + |> mapToSignal { peer, associatedPeers -> Signal in + if let peer = peer, let inputPeer = apiInputPeer(peer) { + var flags: Int32 = 1 << 11 + + var apiEntities: [Api.MessageEntity]? + if let entities = entities { + apiEntities = apiTextAttributeEntities(entities, associatedPeers: associatedPeers) + flags |= Int32(1 << 3) } - |> mapToSignal { result -> Signal in - if let result = result { - account.stateManager.addUpdates(result) - return .single(true) - } else { - return .single(false) + + if disableUrlPreview { + flags |= Int32(1 << 1) + } + + var inputMedia: Api.InputMedia? = nil + if let pendingMediaContent = pendingMediaContent { + switch pendingMediaContent { + case let .media(media, _): + inputMedia = media + default: + break } } - } else { - return .single(false) + if let _ = inputMedia { + flags |= Int32(1 << 14) + } + + return account.network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: text, media: inputMedia, replyMarkup: nil, entities: apiEntities, geoPoint: nil)) + |> map { result -> Api.Updates? in + return result + } + |> `catch` { error -> Signal in + if error.errorDescription == "MESSAGE_NOT_MODIFIED" { + return .single(nil) + } else { + return .fail(error) + } + } + |> mapError { _ -> NoError in + return NoError() + } + |> mapToSignal { result -> Signal in + if let result = result { + return account.postbox.modify { modifier -> RequestEditMessageResult in + var toMedia: Media? + if let message = result.messages.first.flatMap(StoreMessage.init(apiMessage:)) { + toMedia = message.media.first + } + + if case let .update(fromMedia) = media, let toMedia = toMedia { + applyMediaResourceChanges(from: fromMedia, to: toMedia, postbox: account.postbox) + } + account.stateManager.addUpdates(result) + + return .done(true) + } + + } else { + return .single(.done(false)) + } + } + } else { + return .single(.done(false)) + } } } } @@ -91,7 +165,7 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan } else { flags |= 1 << 12 } - return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, replyMarkup: nil, entities: nil, geoPoint: coordinate.flatMap { Api.InputGeoPoint.inputGeoPoint(lat: $0.latitude, long: $0.longitude) })) + return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: nil, replyMarkup: nil, entities: nil, geoPoint: coordinate.flatMap { Api.InputGeoPoint.inputGeoPoint(lat: $0.latitude, long: $0.longitude) })) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/TelegramCore/SearchGroupMembers.swift b/TelegramCore/SearchGroupMembers.swift index 32a75bdd00..2707988458 100644 --- a/TelegramCore/SearchGroupMembers.swift +++ b/TelegramCore/SearchGroupMembers.swift @@ -33,7 +33,7 @@ public func searchGroupMembers(postbox: Postbox, network: Network, peerId: PeerI return searchLocalGroupMembers(postbox: postbox, peerId: peerId, query: query) |> mapToSignal { local -> Signal<[Peer], NoError> in return .single(local) - |> then(channelMembers(postbox: postbox, network: network, peerId: peerId, filter: .search(query)) + |> then(channelMembers(postbox: postbox, network: network, peerId: peerId, category: .recent(.search(query))) |> map { participants -> [Peer] in var result: [Peer] = local let existingIds = Set(local.map { $0.id })