diff --git a/Telegram/BUILD b/Telegram/BUILD index 4aac94e40b..9f901a5282 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -1191,7 +1191,8 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//submodules/TelegramUI:TelegramUI" + "//submodules/TelegramUI:TelegramUI", + "//submodules/TelegramUI/Components/ShareExtensionContext" ], ) diff --git a/Telegram/Share/ShareRootController.swift b/Telegram/Share/ShareRootController.swift index c403e04b91..fe182a9759 100644 --- a/Telegram/Share/ShareRootController.swift +++ b/Telegram/Share/ShareRootController.swift @@ -1,6 +1,7 @@ import UIKit import TelegramUI import BuildConfig +import ShareExtensionContext @objc(ShareRootController) class ShareRootController: UIViewController { diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 40293ad6d5..d4306581f2 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9856,3 +9856,6 @@ Sorry for the inconvenience."; "Location.TypeCity" = "City"; "Location.TypeStreet" = "Street"; "Location.TypeLocation" = "Location"; + +"Story.ViewList.ViewerCount_1" = "1 Viewer"; +"Story.ViewList.ViewerCount_any" = "%d Viewers"; diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index d7f0ccc244..26832ba114 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -659,6 +659,9 @@ public protocol ChatController: ViewController { var purposefulAction: (() -> Void)? { get set } + var selectedMessageIds: Set? { get } + var presentationInterfaceStateSignal: Signal { get } + func updatePresentationMode(_ mode: ChatControllerPresentationMode) func beginMessageSearch(_ query: String) func displayPromoAnnouncement(text: String) diff --git a/submodules/AccountContext/Sources/FetchMediaUtils.swift b/submodules/AccountContext/Sources/FetchMediaUtils.swift index 9ea59da48d..08c3069e65 100644 --- a/submodules/AccountContext/Sources/FetchMediaUtils.swift +++ b/submodules/AccountContext/Sources/FetchMediaUtils.swift @@ -20,6 +20,10 @@ public func freeMediaFileResourceInteractiveFetched(account: Account, userLocati return fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: userLocation, userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(resource)) } +public func freeMediaFileResourceInteractiveFetched(postbox: Postbox, userLocation: MediaResourceUserLocation, fileReference: FileMediaReference, resource: MediaResource) -> Signal { + return fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(resource)) +} + public func cancelFreeMediaFileInteractiveFetch(account: Account, file: TelegramMediaFile) { account.postbox.mediaBox.cancelInteractiveResourceFetch(file.resource) } diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index 976372a6a4..947dbe23b3 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -2,7 +2,10 @@ import Foundation import Display import SwiftSignalKit import TelegramCore +import Postbox import TelegramPresentationData +import AnimationCache +import MultiAnimationRenderer public struct ChatListNodePeersFilter: OptionSet { public var rawValue: Int32 @@ -56,7 +59,68 @@ public final class PeerSelectionControllerParams { public let selectForumThreads: Bool public let hasCreation: Bool - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, filter: ChatListNodePeersFilter = [.onlyWriteable], requestPeerType: [ReplyMarkupButtonRequestPeerType]? = nil, forumPeerId: EnginePeer.Id? = nil, hasFilters: Bool = false, hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((EnginePeer, Int64?) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessageIds: [EngineMessage.Id] = [], hasTypeHeaders: Bool = false, selectForumThreads: Bool = false, hasCreation: Bool = false) { + /*public convenience init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + filter: ChatListNodePeersFilter = [.onlyWriteable], + requestPeerType: [ReplyMarkupButtonRequestPeerType]? = nil, + forumPeerId: EnginePeer.Id? = nil, + hasFilters: Bool = false, + hasChatListSelector: Bool = true, + hasContactSelector: Bool = true, + hasGlobalSearch: Bool = true, + title: String? = nil, + attemptSelection: ((EnginePeer, Int64?) -> Void)? = nil, + createNewGroup: (() -> Void)? = nil, + pretendPresentedInModal: Bool = false, + multipleSelection: Bool = false, + forwardedMessageIds: [EngineMessage.Id] = [], + hasTypeHeaders: Bool = false, + selectForumThreads: Bool = false, + hasCreation: Bool = false + ) { + self.init( + context: .account(context), + updatedPresentationData: updatedPresentationData, + filter: filter, + requestPeerType: requestPeerType, + forumPeerId: forumPeerId, + hasFilters: hasFilters, + hasChatListSelector: hasChatListSelector, + hasContactSelector: hasContactSelector, + hasGlobalSearch: hasGlobalSearch, + title: title, + attemptSelection: attemptSelection, + createNewGroup: createNewGroup, + pretendPresentedInModal: pretendPresentedInModal, + multipleSelection: multipleSelection, + forwardedMessageIds: forwardedMessageIds, + hasTypeHeaders: hasTypeHeaders, + selectForumThreads: selectForumThreads, + hasCreation: hasCreation + ) + }*/ + + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + filter: ChatListNodePeersFilter = [.onlyWriteable], + requestPeerType: [ReplyMarkupButtonRequestPeerType]? = nil, + forumPeerId: EnginePeer.Id? = nil, + hasFilters: Bool = false, + hasChatListSelector: Bool = true, + hasContactSelector: Bool = true, + hasGlobalSearch: Bool = true, + title: String? = nil, + attemptSelection: ((EnginePeer, Int64?) -> Void)? = nil, + createNewGroup: (() -> Void)? = nil, + pretendPresentedInModal: Bool = false, + multipleSelection: Bool = false, + forwardedMessageIds: [EngineMessage.Id] = [], + hasTypeHeaders: Bool = false, + selectForumThreads: Bool = false, + hasCreation: Bool = false + ) { self.context = context self.updatedPresentationData = updatedPresentationData self.filter = filter @@ -85,6 +149,39 @@ public enum AttachmentTextInputPanelSendMode { case whenOnline } +public enum PeerSelectionControllerContext { + public final class Custom { + public let accountPeerId: EnginePeer.Id + public let postbox: Postbox + public let network: Network + public let animationCache: AnimationCache + public let animationRenderer: MultiAnimationRenderer + public let presentationData: PresentationData + public let updatedPresentationData: Signal + + public init( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + presentationData: PresentationData, + updatedPresentationData: Signal + ) { + self.accountPeerId = accountPeerId + self.postbox = postbox + self.network = network + self.animationCache = animationCache + self.animationRenderer = animationRenderer + self.presentationData = presentationData + self.updatedPresentationData = updatedPresentationData + } + } + + case account(AccountContext) + case custom(Custom) +} + public protocol PeerSelectionController: ViewController { var peerSelected: ((EnginePeer, Int64?) -> Void)? { get set } var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?) -> Void)? { get set } diff --git a/submodules/ActionSheetPeerItem/BUILD b/submodules/ActionSheetPeerItem/BUILD index cacfba4843..d3aa0cc103 100644 --- a/submodules/ActionSheetPeerItem/BUILD +++ b/submodules/ActionSheetPeerItem/BUILD @@ -11,6 +11,7 @@ swift_library( ], deps = [ "//submodules/TelegramCore:TelegramCore", + "//submodules/Postbox", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/AvatarNode:AvatarNode", diff --git a/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift b/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift index ca81c18456..c918a8712b 100644 --- a/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift +++ b/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift @@ -3,12 +3,16 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore +import Postbox import TelegramPresentationData import AvatarNode import AccountContext public class ActionSheetPeerItem: ActionSheetItem { - public let context: AccountContext + public let accountPeerId: EnginePeer.Id + public let postbox: Postbox + public let network: Network + public let contentSettings: ContentSettings public let peer: EnginePeer public let theme: PresentationTheme public let title: String @@ -16,8 +20,37 @@ public class ActionSheetPeerItem: ActionSheetItem { public let strings: PresentationStrings public let action: () -> Void - public init(context: AccountContext, peer: EnginePeer, title: String, isSelected: Bool, strings: PresentationStrings, theme: PresentationTheme, action: @escaping () -> Void) { - self.context = context + public convenience init(context: AccountContext, peer: EnginePeer, title: String, isSelected: Bool, strings: PresentationStrings, theme: PresentationTheme, action: @escaping () -> Void) { + self.init( + accountPeerId: context.account.peerId, + postbox: context.account.postbox, + network: context.account.network, + contentSettings: context.currentContentSettings.with { $0 }, + peer: peer, + title: title, + isSelected: isSelected, + strings: strings, + theme: theme, + action: action + ) + } + + public init( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + contentSettings: ContentSettings, + peer: EnginePeer, + title: String, + isSelected: Bool, + strings: PresentationStrings, + theme: PresentationTheme, + action: @escaping () -> Void + ) { + self.accountPeerId = accountPeerId + self.postbox = postbox + self.network = network + self.contentSettings = contentSettings self.peer = peer self.title = title self.isSelected = isSelected @@ -121,7 +154,7 @@ public class ActionSheetPeerItemNode: ActionSheetItemNode { let textColor: UIColor = self.theme.primaryTextColor self.label.attributedText = NSAttributedString(string: item.title, font: defaultFont, textColor: textColor) - self.avatarNode.setPeer(context: item.context, theme: item.theme, peer: item.peer) + self.avatarNode.setPeer(accountPeerId: item.accountPeerId, postbox: item.postbox, network: item.network, contentSettings: item.contentSettings, theme: item.theme, peer: item.peer) self.checkNode.isHidden = !item.isSelected diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index 56a04d915d..d6ad8d1dc9 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -1061,7 +1061,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail avatarVideo = Signal { subscriber in let entityRenderer: LegacyPaintEntityRenderer? = avatarAdjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: nil, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: nil, adjustments: adjustments) } else { return nil } diff --git a/submodules/AvatarNode/BUILD b/submodules/AvatarNode/BUILD index 1b43b7e3ff..38dc9f4680 100644 --- a/submodules/AvatarNode/BUILD +++ b/submodules/AvatarNode/BUILD @@ -13,6 +13,7 @@ swift_library( "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/TelegramCore:TelegramCore", + "//submodules/Postbox", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/AnimationUI:AnimationUI", "//submodules/AppBundle:AppBundle", diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 7a39ef44d1..88130623c6 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore +import Postbox import SwiftSignalKit import TelegramPresentationData import AnimationUI @@ -381,6 +382,120 @@ public final class AvatarNode: ASDisplayNode { self.imageNode.isHidden = true } + public func setPeer( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + contentSettings: ContentSettings, + theme: PresentationTheme, + peer: EnginePeer?, + authorOfMessage: MessageReference? = nil, + overrideImage: AvatarNodeImageOverride? = nil, + emptyColor: UIColor? = nil, + clipStyle: AvatarNodeClipStyle = .round, + synchronousLoad: Bool = false, + displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), + storeUnrounded: Bool = false + ) { + var synchronousLoad = synchronousLoad + var representation: TelegramMediaImageRepresentation? + var icon = AvatarNodeIcon.none + if let overrideImage = overrideImage { + switch overrideImage { + case .none: + representation = nil + case let .image(image): + representation = image + synchronousLoad = false + case .savedMessagesIcon: + representation = nil + icon = .savedMessagesIcon + case .repliesIcon: + representation = nil + icon = .repliesIcon + case let .archivedChatsIcon(hiddenByDefault): + representation = nil + icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault) + case let .editAvatarIcon(forceNone): + representation = forceNone ? nil : peer?.smallProfileImage + icon = .editAvatarIcon + case .deletedIcon: + representation = nil + icon = .deletedIcon + case .phoneIcon: + representation = nil + icon = .phoneIcon + } + } else if peer?.restrictionText(platform: "ios", contentSettings: contentSettings) == nil { + representation = peer?.smallProfileImage + } + let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.displayLetters ?? [], representation, clipStyle) + if updatedState != self.state || overrideImage != self.overrideImage || theme !== self.theme { + self.state = updatedState + self.overrideImage = overrideImage + self.theme = theme + + let parameters: AvatarNodeParameters + + if let peer = peer, let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) { + self.contents = nil + self.displaySuspended = true + self.imageReady.set(self.imageNode.contentReady) + self.imageNode.setSignal(signal |> beforeNext { [weak self] next in + Queue.mainQueue().async { + self?.unroundedImage = next?.1 + } + } + |> map { next -> UIImage? in + return next?.0 + }) + + if case .editAvatarIcon = icon { + if self.editOverlayNode == nil { + let editOverlayNode = AvatarEditOverlayNode() + editOverlayNode.frame = self.imageNode.frame + editOverlayNode.isUserInteractionEnabled = false + self.addSubnode(editOverlayNode) + + self.editOverlayNode = editOverlayNode + } + self.editOverlayNode?.isHidden = false + } else { + self.editOverlayNode?.isHidden = true + } + + parameters = AvatarNodeParameters(theme: theme, accountPeerId: accountPeerId, peerId: peer.id, colors: calculateColors(explicitColorIndex: nil, peerId: peer.id, icon: icon, theme: theme), letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle) + } else { + self.imageReady.set(.single(true)) + self.displaySuspended = false + if self.isNodeLoaded { + self.imageNode.contents = nil + } + + self.editOverlayNode?.isHidden = true + let colors = calculateColors(explicitColorIndex: nil, peerId: peer?.id ?? EnginePeer.Id(0), icon: icon, theme: theme) + parameters = AvatarNodeParameters(theme: theme, accountPeerId: accountPeerId, peerId: peer?.id ?? EnginePeer.Id(0), colors: colors, letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle) + + if let badgeView = self.badgeView { + let badgeColor: UIColor + if colors.isEmpty { + badgeColor = .white + } else { + badgeColor = colors[colors.count - 1] + } + badgeView.update(content: .color(badgeColor)) + } + } + if self.parameters == nil || self.parameters != parameters { + self.parameters = parameters + self.setNeedsDisplay() + if synchronousLoad { + self.recursivelyEnsureDisplaySynchronously(true) + } + } + } + } + public func setPeer( context genericContext: AccountContext, account: Account? = nil, @@ -778,6 +893,38 @@ public final class AvatarNode: ASDisplayNode { self.contentNode.playArchiveAnimation() } + public func setPeer( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + contentSettings: ContentSettings, + theme: PresentationTheme, + peer: EnginePeer?, + authorOfMessage: MessageReference? = nil, + overrideImage: AvatarNodeImageOverride? = nil, + emptyColor: UIColor? = nil, + clipStyle: AvatarNodeClipStyle = .round, + synchronousLoad: Bool = false, + displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), + storeUnrounded: Bool = false + ) { + self.contentNode.setPeer( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + contentSettings: contentSettings, + theme: theme, + peer: peer, + authorOfMessage: authorOfMessage, + overrideImage: overrideImage, + emptyColor: emptyColor, + clipStyle: clipStyle, + synchronousLoad: synchronousLoad, + displayDimensions: displayDimensions, + storeUnrounded: storeUnrounded + ) + } + public func setPeer( context: AccountContext, account: Account? = nil, diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 4a034a8f53..618a8a20a3 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -6,6 +6,7 @@ import ImageIO import TelegramCore import TinyThumbnail import FastBlur +import Postbox private let roundCorners = { () -> UIImage in let diameter: CGFloat = 60.0 @@ -27,8 +28,12 @@ public enum PeerAvatarImageType { } public func peerAvatarImageData(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, synchronousLoad: Bool) -> Signal<(Data, PeerAvatarImageType)?, NoError>? { + return peerAvatarImageData(postbox: account.postbox, network: account.network, peerReference: peerReference, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) +} + +public func peerAvatarImageData(postbox: Postbox, network: Network, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, synchronousLoad: Bool) -> Signal<(Data, PeerAvatarImageType)?, NoError>? { if let smallProfileImage = representation { - let resourceData = account.postbox.mediaBox.resourceData(smallProfileImage.resource, attemptSynchronously: synchronousLoad) + let resourceData = postbox.mediaBox.resourceData(smallProfileImage.resource, attemptSynchronously: synchronousLoad) let imageData = resourceData |> take(1) |> mapToSignal { maybeData -> Signal<(Data, PeerAvatarImageType)?, NoError> in @@ -65,11 +70,11 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?, }) var fetchedDataDisposable: Disposable? if let peerReference = peerReference { - fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .avatar(peer: peerReference, resource: smallProfileImage.resource), statsCategory: .generic).start() + fetchedDataDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .avatar(peer: peerReference, resource: smallProfileImage.resource), statsCategory: .generic).start() } else if let authorOfMessage = authorOfMessage { - fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .messageAuthorAvatar(message: authorOfMessage, resource: smallProfileImage.resource), statsCategory: .generic).start() + fetchedDataDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .messageAuthorAvatar(message: authorOfMessage, resource: smallProfileImage.resource), statsCategory: .generic).start() } else { - fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .standalone(resource: smallProfileImage.resource), statsCategory: .generic).start() + fetchedDataDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: .other, userContentType: .avatar, reference: .standalone(resource: smallProfileImage.resource), statsCategory: .generic).start() } return ActionDisposable { resourceDataDisposable.dispose() @@ -85,6 +90,22 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?, } public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, forceProvidedRepresentation: Bool = false, representation: TelegramMediaImageRepresentation? = nil, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false, blurred: Bool = false) -> Signal { + return peerAvatarCompleteImage( + postbox: account.postbox, + network: account.network, + peer: peer, + forceProvidedRepresentation: forceProvidedRepresentation, + representation: representation, + size: size, + round: round, + font: font, + drawLetters: drawLetters, + fullSize: fullSize, + blurred: blurred + ) +} + +public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: EnginePeer, forceProvidedRepresentation: Bool = false, representation: TelegramMediaImageRepresentation? = nil, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false, blurred: Bool = false) -> Signal { let iconSignal: Signal let clipStyle: AvatarNodeClipStyle @@ -105,8 +126,8 @@ public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, forcePro thumbnailRepresentation = peer.profileImageRepresentations.first } - if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: thumbnailRepresentation, displayDimensions: size, clipStyle: clipStyle, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { - if fullSize, let fullSizeSignal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) { + if let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: thumbnailRepresentation, displayDimensions: size, clipStyle: clipStyle, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { + if fullSize, let fullSizeSignal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) { iconSignal = combineLatest(.single(nil) |> then(signal), .single(nil) |> then(fullSizeSignal)) |> mapToSignal { thumbnailImage, fullSizeImage -> Signal in if let fullSizeImage = fullSizeImage { @@ -151,7 +172,24 @@ public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, forcePro } public func peerAvatarImage(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false) -> Signal<(UIImage, UIImage)?, NoError>? { - if let imageData = peerAvatarImageData(account: account, peerReference: peerReference, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) { + return peerAvatarImage( + postbox: account.postbox, + network: account.network, + peerReference: peerReference, + authorOfMessage: authorOfMessage, + representation: representation, + displayDimensions: displayDimensions, + clipStyle: clipStyle, + blurred: blurred, + inset: inset, + emptyColor: emptyColor, + synchronousLoad: synchronousLoad, + provideUnrounded: synchronousLoad + ) +} + +public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false) -> Signal<(UIImage, UIImage)?, NoError>? { + if let imageData = peerAvatarImageData(postbox: postbox, network: network, peerReference: peerReference, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) { return imageData |> mapToSignal { data -> Signal<(UIImage, UIImage)?, NoError> in let generate = deferred { () -> Signal<(UIImage, UIImage)?, NoError> in diff --git a/submodules/BrowserUI/Sources/BrowserScreen.swift b/submodules/BrowserUI/Sources/BrowserScreen.swift index e151b7dc29..b59a23ab54 100644 --- a/submodules/BrowserUI/Sources/BrowserScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserScreen.swift @@ -537,7 +537,7 @@ public class BrowserScreen: ViewController { action(.default) }))] - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items)))) + let contextController = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items)))) self.controller?.present(contextController, in: .window(.root)) }) } diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 13f12d8715..029beeaa00 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -377,7 +377,7 @@ public final class CallListController: TelegramBaseController { } } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: self, sourceNode: buttonNode.contentNode, keepInPlace: false, blurBackground: false)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(ExtractedContentSourceImpl(controller: self, sourceNode: buttonNode.contentNode, keepInPlace: false, blurBackground: false)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) self.presentInGlobalOverlay(contextController) } @@ -500,7 +500,7 @@ public final class CallListController: TelegramBaseController { }) }))) - let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(CallListTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(CallListTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } } diff --git a/submodules/ChatListSearchRecentPeersNode/BUILD b/submodules/ChatListSearchRecentPeersNode/BUILD index 6b9a159477..d0c9096304 100644 --- a/submodules/ChatListSearchRecentPeersNode/BUILD +++ b/submodules/ChatListSearchRecentPeersNode/BUILD @@ -14,12 +14,16 @@ swift_library( "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/TelegramCore:TelegramCore", + "//submodules/Postbox", "//submodules/ListSectionHeaderNode:ListSectionHeaderNode", "//submodules/HorizontalPeerItem:HorizontalPeerItem", "//submodules/MergeLists:MergeLists", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/ContextUI:ContextUI", "//submodules/AccountContext:AccountContext", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift index 59f4f139e0..77bca338cd 100644 --- a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift +++ b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift @@ -4,12 +4,16 @@ import AsyncDisplayKit import Display import SwiftSignalKit import TelegramCore +import Postbox import TelegramPresentationData import MergeLists import HorizontalPeerItem import ListSectionHeaderNode import ContextUI import AccountContext +import TelegramUIPreferences +import AnimationCache +import MultiAnimationRenderer private func calculateItemCustomWidth(width: CGFloat) -> CGFloat { let itemInsets = UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0) @@ -73,10 +77,42 @@ private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool) -> ListViewItem { - return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: mode, context: context, peer: self.peer, presence: self.presence, unreadBadge: self.unreadBadge, action: peerSelected, contextAction: { peer, node, gesture, location in - peerContextAction(peer, node, gesture, location) - }, isPeerSelected: isPeerSelected, customWidth: self.itemCustomWidth) + func item( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + energyUsageSettings: EnergyUsageSettings, + contentSettings: ContentSettings, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, + mode: HorizontalPeerItemMode, + peerSelected: @escaping (EnginePeer) -> Void, + peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, + isPeerSelected: @escaping (EnginePeer.Id) -> Bool + ) -> ListViewItem { + return HorizontalPeerItem( + theme: self.theme, + strings: self.strings, + mode: mode, + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + energyUsageSettings: energyUsageSettings, + contentSettings: contentSettings, + animationCache: animationCache, + animationRenderer: animationRenderer, + resolveInlineStickers: resolveInlineStickers, + peer: self.peer, + presence: self.presence, + unreadBadge: self.unreadBadge, + action: peerSelected, + contextAction: { peer, node, gesture, location in + peerContextAction(peer, node, gesture, location) + }, + isPeerSelected: isPeerSelected, + customWidth: self.itemCustomWidth + ) } } @@ -88,12 +124,56 @@ private struct ChatListSearchRecentNodeTransition { let animated: Bool } -private func preparedRecentPeersTransition(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition { +private func preparedRecentPeersTransition( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + energyUsageSettings: EnergyUsageSettings, + contentSettings: ContentSettings, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, + mode: HorizontalPeerItemMode, + peerSelected: @escaping (EnginePeer) -> Void, + peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, + isPeerSelected: @escaping (EnginePeer.Id) -> Bool, + share: Bool = false, + from fromEntries: [ChatListSearchRecentPeersEntry], + to toEntries: [ChatListSearchRecentPeersEntry], + firstTime: Bool, + animated: Bool +) -> ChatListSearchRecentNodeTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, mode: mode, peerSelected: peerSelected, peerContextAction: peerContextAction, isPeerSelected: isPeerSelected), directionHint: .Down) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, mode: mode, peerSelected: peerSelected, peerContextAction: peerContextAction, isPeerSelected: isPeerSelected), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + energyUsageSettings: energyUsageSettings, + contentSettings: contentSettings, + animationCache: animationCache, + animationRenderer: animationRenderer, + resolveInlineStickers: resolveInlineStickers, + mode: mode, + peerSelected: peerSelected, + peerContextAction: peerContextAction, + isPeerSelected: isPeerSelected + ), directionHint: .Down) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + energyUsageSettings: energyUsageSettings, + contentSettings: contentSettings, + animationCache: animationCache, + animationRenderer: animationRenderer, + resolveInlineStickers: resolveInlineStickers, + mode: mode, + peerSelected: peerSelected, + peerContextAction: peerContextAction, + isPeerSelected: isPeerSelected + ), directionHint: nil) } return ChatListSearchRecentNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, animated: animated) } @@ -122,7 +202,19 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { return self.ready.get() } - public init(context: AccountContext, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false) { + public init( + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + energyUsageSettings: EnergyUsageSettings, + contentSettings: ContentSettings, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, + theme: PresentationTheme, + mode: HorizontalPeerItemMode, + strings: PresentationStrings, + peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false) { self.theme = theme self.strings = strings self.themeAndStringsPromise = Promise((self.theme, self.strings)) @@ -144,7 +236,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { let peersDisposable = DisposableSet() - let recent: Signal<([EnginePeer], [EnginePeer.Id: (Int32, Bool)], [EnginePeer.Id : EnginePeer.Presence]), NoError> = context.engine.peers.recentPeers() + let recent: Signal<([EnginePeer], [EnginePeer.Id: (Int32, Bool)], [EnginePeer.Id : EnginePeer.Presence]), NoError> = _internal_recentPeers(accountPeerId: accountPeerId, postbox: postbox) |> filter { value -> Bool in switch value { case .disabled: @@ -162,15 +254,27 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { peers.filter { !$0.isDeleted }.map { - context.account.postbox.peerView(id: $0.id) + postbox.peerView(id: $0.id) } ) |> mapToSignal { peerViews -> Signal<([EnginePeer], [EnginePeer.Id: (Int32, Bool)], [EnginePeer.Id: EnginePeer.Presence]), NoError> in - return context.engine.data.subscribe( - EngineDataMap(peerViews.map { item in - return TelegramEngine.EngineData.Item.Messages.PeerUnreadCount(id: item.peerId) - }) - ) + return postbox.combinedView(keys: peerViews.map { item -> PostboxViewKey in + let key = PostboxViewKey.unreadCounts(items: [UnreadMessageCountsItem.peer(id: item.peerId, handleThreads: true)]) + return key + }) + |> map { views -> [EnginePeer.Id: Int] in + var result: [EnginePeer.Id: Int] = [:] + for item in peerViews { + let key = PostboxViewKey.unreadCounts(items: [UnreadMessageCountsItem.peer(id: item.peerId, handleThreads: true)]) + + if let view = views.views[key] as? UnreadMessageCountsView { + result[item.peerId] = Int(view.count(for: .peer(id: item.peerId, handleThreads: true)) ?? 0) + } else { + result[item.peerId] = 0 + } + } + return result + } |> map { unreadCounts in var peers: [EnginePeer] = [] var unread: [EnginePeer.Id: (Int32, Bool)] = [:] @@ -216,7 +320,24 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { let animated = !firstTime.swap(false) - let transition = preparedRecentPeersTransition(context: context, mode: mode, peerSelected: peerSelected, peerContextAction: peerContextAction, isPeerSelected: isPeerSelected, from: previous.swap(entries), to: entries, firstTime: !animated, animated: animated) + let transition = preparedRecentPeersTransition( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + energyUsageSettings: energyUsageSettings, + contentSettings: contentSettings, + animationCache: animationCache, + animationRenderer: animationRenderer, + resolveInlineStickers: resolveInlineStickers, + mode: mode, + peerSelected: peerSelected, + peerContextAction: peerContextAction, + isPeerSelected: isPeerSelected, + from: previous.swap(entries), + to: entries, + firstTime: !animated, + animated: animated + ) strongSelf.enqueueTransition(transition) @@ -227,7 +348,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { } })) if case .actionSheet = mode { - peersDisposable.add(context.engine.peers.managedUpdatedRecentPeers().start()) + peersDisposable.add(_internal_managedUpdatedRecentPeers(accountPeerId: accountPeerId, postbox: postbox, network: network).start()) } self.disposable.set(peersDisposable) } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 508a43dfe2..9fdf39feef 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1293,7 +1293,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController case let .groupReference(groupReference): let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupReference.groupId), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) case let .peer(peerData): let peer = peerData.peer @@ -1311,12 +1311,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: nil, isClosed: nil, chatListController: strongSelf, joined: joined, canSelect: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } else { let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } } else { @@ -1329,7 +1329,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } case let .forum(pinnedIndex, _, threadId, _, _): @@ -1347,7 +1347,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: isPinned, isClosed: threadInfo?.isClosed, chatListController: strongSelf, joined: joined, canSelect: true) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: source, items: chatForumTopicMenuItems(context: strongSelf.context, peerId: peer.peerId, threadId: threadId, isPinned: isPinned, isClosed: threadInfo?.isClosed, chatListController: strongSelf, joined: joined, canSelect: true) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } } @@ -1378,7 +1378,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if case let .channel(channel) = peer, channel.flags.contains(.isForum) { let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .forum(peerId: channel.id), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } else { let contextContentSource: ContextContentSource @@ -1394,7 +1394,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } } @@ -1769,7 +1769,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) } - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) } @@ -2938,7 +2938,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) } - let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) } @@ -3340,7 +3340,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) sourceController.presentInGlobalOverlay(contextController) }) } @@ -3400,7 +3400,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) } - let contextController = ContextController(account: self.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) self.presentInGlobalOverlay(contextController) }) } @@ -5549,7 +5549,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatListTabBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatListTabBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) } diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index 11f471f9a0..8ea328e359 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -1548,7 +1548,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi }) }))) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) }) }, @@ -1589,7 +1589,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi }) }))) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) } ) diff --git a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift index 400be7eb22..f65374eda0 100644 --- a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift @@ -120,13 +120,28 @@ class ChatListRecentPeersListItemNode: ListViewItemNode { peersNode = currentPeersNode peersNode.updateThemeAndStrings(theme: item.theme, strings: item.strings) } else { - peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list(compact: false), strings: item.strings, peerSelected: { peer in - self?.item?.peerSelected(peer) - }, peerContextAction: { peer, node, gesture, location in - self?.item?.peerContextAction(peer, node, gesture, location) - }, isPeerSelected: { _ in - return false - }) + peersNode = ChatListSearchRecentPeersNode( + accountPeerId: item.context.account.peerId, + postbox: item.context.account.postbox, + network: item.context.account.network, + energyUsageSettings: item.context.sharedContext.energyUsageSettings, + contentSettings: item.context.currentContentSettings.with { $0 }, + animationCache: item.context.animationCache, + animationRenderer: item.context.animationRenderer, + resolveInlineStickers: item.context.engine.stickers.resolveInlineStickers, + theme: item.theme, + mode: .list(compact: false), + strings: item.strings, + peerSelected: { peer in + self?.item?.peerSelected(peer) + }, + peerContextAction: { peer, node, gesture, location in + self?.item?.peerContextAction(peer, node, gesture, location) + }, + isPeerSelected: { _ in + return false + } + ) strongSelf.ready.set(peersNode.isReady) strongSelf.peersNode = peersNode strongSelf.addSubnode(peersNode) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index cf3d90336a..8cd0bfa31e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1046,7 +1046,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return items } - let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node, shouldBeDismissed: shouldBeDismissed)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node, shouldBeDismissed: shouldBeDismissed)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture) self.presentInGlobalOverlay?(controller, nil) return @@ -1120,7 +1120,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return items } - let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, recognizer: nil, gesture: gesture) self.presentInGlobalOverlay?(controller, nil) } @@ -1184,7 +1184,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo switch previewData { case let .gallery(gallery): gallery.setHintWillBePresentedInPreviewingContext(true) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay?(contextController, nil) case .instantPage: break diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index f8faa34edc..e40623fe5a 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -572,7 +572,7 @@ public class ContactsController: ViewController { }))) return items } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) self.presentInGlobalOverlay(contextController) } @@ -644,7 +644,7 @@ public class ContactsController: ViewController { }) }))) - let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ContactsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(ContactsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } } diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 74f59f8521..23bb97a6b8 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -436,12 +436,12 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { let items = contactContextMenuItems(context: self.context, peerId: peer.id, contactsController: contactsController, isStories: isStories) |> map { ContextController.Items(content: .list($0)) } if isStories, let node = node?.subnodes?.first(where: { $0 is ContextExtractedContentContainingNode }) as? ContextExtractedContentContainingNode { - let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ContactContextExtractedContentSource(sourceNode: node, shouldBeDismissed: .single(false))), items: items, recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(ContactContextExtractedContentSource(sourceNode: node, shouldBeDismissed: .single(false))), items: items, recognizer: nil, gesture: gesture) contactsController.presentInGlobalOverlay(controller) } else { let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: items, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: items, gesture: gesture) contactsController.presentInGlobalOverlay(contextController) } } diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index f1e00f7752..f3d8aeca84 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -296,7 +296,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } init( - account: Account, controller: ContextController, presentationData: PresentationData, source: ContextContentSource, @@ -2465,7 +2464,6 @@ public final class ContextController: ViewController, StandalonePresentableContr } } - private let account: Account private var presentationData: PresentationData private let source: ContextContentSource private var items: Signal @@ -2520,8 +2518,7 @@ public final class ContextController: ViewController, StandalonePresentableContr public var getOverlayViews: (() -> [UIView])? - public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, workaroundUseLegacyImplementation: Bool = false) { - self.account = account + public init(presentationData: PresentationData, source: ContextContentSource, items: Signal, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, workaroundUseLegacyImplementation: Bool = false) { self.presentationData = presentationData self.source = source self.items = items @@ -2588,7 +2585,7 @@ public final class ContextController: ViewController, StandalonePresentableContr } override public func loadDisplayNode() { - self.displayNode = ContextControllerNode(account: self.account, controller: self, presentationData: self.presentationData, source: self.source, items: self.items, beginDismiss: { [weak self] result in + self.displayNode = ContextControllerNode(controller: self, presentationData: self.presentationData, source: self.source, items: self.items, beginDismiss: { [weak self] result in self?.dismiss(result: result, completion: nil) }, recognizer: self.recognizer, gesture: self.gesture, beganAnimatingOut: { [weak self] in guard let strongSelf = self else { diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 5eb05177bd..9f4b901d4d 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -984,7 +984,7 @@ private final class DrawingScreenComponent: CombinedComponent { ) ] let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) - let contextController = ContextController(account: self.context.account, presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 7.0, y: 3.0))), items: .single(ContextController.Items(content: .list(items)))) + let contextController = ContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: UIScreen.main.bounds, customPosition: CGPoint(x: 7.0, y: 3.0))), items: .single(ContextController.Items(content: .list(items)))) self.present(contextController) } @@ -3384,7 +3384,7 @@ public final class DrawingToolsInteraction { } let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) - let contextController = ContextController(account: self.context.account, presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: CGRect(origin: .zero, size: CGSize(width: validLayout.size.width, height: validLayout.size.height - (validLayout.inputHeight ?? 0.0))), customPosition: CGPoint(x: 0.0, y: 1.0))), items: .single(ContextController.Items(content: .list(items)))) + let contextController = ContextController(presentationData: presentationData, source: .reference(ReferenceContentSource(sourceView: sourceView, contentArea: CGRect(origin: .zero, size: CGSize(width: validLayout.size.width, height: validLayout.size.height - (validLayout.inputHeight ?? 0.0))), customPosition: CGPoint(x: 0.0, y: 1.0))), items: .single(ContextController.Items(content: .list(items)))) self.present(contextController, .window(.root), nil) self.currentFontPicker = contextController contextController.view.disablesInteractiveKeyboardGestureRecognizer = true diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index a8f2936c65..220b46c6f4 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -784,7 +784,7 @@ public class StickerPickerScreen: ViewController { }))) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) controller.presentInGlobalOverlay(contextController) }) } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 0aaf8b7d58..f6ca4b29c5 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -559,7 +559,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { return } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) controller.presentInGlobalOverlay(contextController) } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index b6a7ea0300..76ac072cb1 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -2432,7 +2432,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { let items: Signal<[ContextMenuItem], NoError> = self.contextMenuMainItems(dismiss: { dismissImpl?() }) - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.moreBarButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) self.isShowingContextMenuPromise.set(true) controller.presentInGlobalOverlay(contextController) dismissImpl = { [weak contextController] in diff --git a/submodules/GameUI/Sources/GameControllerNode.swift b/submodules/GameUI/Sources/GameControllerNode.swift index 5ee44cd3fb..650fc78e7d 100644 --- a/submodules/GameUI/Sources/GameControllerNode.swift +++ b/submodules/GameUI/Sources/GameControllerNode.swift @@ -145,8 +145,8 @@ final class GameControllerNode: ViewControllerTracingNode { if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty { if eventName == "share_score" { self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, threadIds, text, account, _ in - if let strongSelf = self, let message = strongSelf.message { - let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: message.id, to: $0, threadId: threadIds[$0], as: nil) } + if let strongSelf = self, let message = strongSelf.message, let account = account as? ShareControllerAppAccountContext { + let signals = peerIds.map { TelegramEngine(account: account.context.account).messages.forwardGameWithScore(messageId: message.id, to: $0, threadId: threadIds[$0], as: nil) } return .single(.preparing(false)) |> castError(ShareControllerError.self) |> then( diff --git a/submodules/HorizontalPeerItem/BUILD b/submodules/HorizontalPeerItem/BUILD index 1bce1aa70f..36c2ddebc8 100644 --- a/submodules/HorizontalPeerItem/BUILD +++ b/submodules/HorizontalPeerItem/BUILD @@ -12,6 +12,7 @@ swift_library( deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/TelegramCore:TelegramCore", + "//submodules/Postbox:Postbox", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/TelegramPresentationData:TelegramPresentationData", @@ -20,6 +21,9 @@ swift_library( "//submodules/TelegramStringFormatting:TelegramStringFormatting", "//submodules/ContextUI:ContextUI", "//submodules/AccountContext:AccountContext", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", ], visibility = [ "//visibility:public", diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index db39d7e49c..0e4e98ab49 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -3,6 +3,7 @@ import UIKit import Display import AsyncDisplayKit import TelegramCore +import Postbox import SwiftSignalKit import TelegramPresentationData import TelegramStringFormatting @@ -10,6 +11,9 @@ import PeerOnlineMarkerNode import SelectablePeerNode import ContextUI import AccountContext +import TelegramUIPreferences +import AnimationCache +import MultiAnimationRenderer public enum HorizontalPeerItemMode { case list(compact: Bool) @@ -22,7 +26,15 @@ public final class HorizontalPeerItem: ListViewItem { let theme: PresentationTheme let strings: PresentationStrings let mode: HorizontalPeerItemMode - let context: AccountContext + let accountPeerId: EnginePeer.Id + let postbox: Postbox + let network: Network + let energyUsageSettings: EnergyUsageSettings + let contentSettings: ContentSettings + let animationCache: AnimationCache + let animationRenderer: MultiAnimationRenderer + let resolveInlineStickers: ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> + public let peer: EnginePeer let action: (EnginePeer) -> Void let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? @@ -31,11 +43,37 @@ public final class HorizontalPeerItem: ListViewItem { let presence: EnginePeer.Presence? let unreadBadge: (Int32, Bool)? - public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) { + public init( + theme: PresentationTheme, + strings: PresentationStrings, + mode: HorizontalPeerItemMode, + accountPeerId: EnginePeer.Id, + postbox: Postbox, + network: Network, + energyUsageSettings: EnergyUsageSettings, + contentSettings: ContentSettings, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, + peer: EnginePeer, + presence: EnginePeer.Presence?, + unreadBadge: (Int32, Bool)?, + action: @escaping (EnginePeer) -> Void, + contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, + isPeerSelected: @escaping (EnginePeer.Id) -> Bool, + customWidth: CGFloat? + ) { self.theme = theme self.strings = strings self.mode = mode - self.context = context + self.accountPeerId = accountPeerId + self.postbox = postbox + self.network = network + self.energyUsageSettings = energyUsageSettings + self.contentSettings = contentSettings + self.animationCache = animationCache + self.animationRenderer = animationRenderer + self.resolveInlineStickers = resolveInlineStickers self.peer = peer self.action = action self.contextAction = contextAction @@ -186,7 +224,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode { } else { strongSelf.peerNode.compact = false } - strongSelf.peerNode.setup(context: item.context, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads) + strongSelf.peerNode.setup(accountPeerId: item.accountPeerId, postbox: item.postbox, network: item.network, energyUsageSettings: item.energyUsageSettings, contentSettings: item.contentSettings, animationCache: item.animationCache, animationRenderer: item.animationRenderer, resolveInlineStickers: item.resolveInlineStickers, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads) strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size) strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false) diff --git a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift index cbf4ba18d2..3b0e437cf6 100644 --- a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift @@ -465,7 +465,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese }) }))) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) }, peerAction: { peer, isEnabled in let presentationData = context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift index 9a3c30c161..59a906df9f 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift @@ -418,7 +418,7 @@ public final class InviteLinkInviteController: ViewController { }) }))) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) self?.controller?.presentInGlobalOverlay(contextController) }, copyLink: { [weak self] invite in UIPasteboard.general.string = invite.link diff --git a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift index 16957138cf..62f57045e6 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift @@ -586,7 +586,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio }))) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) }, createLink: { let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, completion: { invite in @@ -790,7 +790,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio }))) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) }, openAdmin: { admin in let controller = inviteLinkListController(context: context, peerId: peerId, admin: admin) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index c971fa218c..e6c2b8af97 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -686,7 +686,7 @@ public final class InviteLinkViewController: ViewController { } } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) self?.controller?.presentInGlobalOverlay(contextController) }) }) diff --git a/submodules/InviteLinksUI/Sources/InviteRequestsController.swift b/submodules/InviteLinksUI/Sources/InviteRequestsController.swift index b7ee300160..2c5f76a94b 100644 --- a/submodules/InviteLinksUI/Sources/InviteRequestsController.swift +++ b/submodules/InviteLinksUI/Sources/InviteRequestsController.swift @@ -268,7 +268,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio // dismissPromise.set(true) // } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) }) }) diff --git a/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift b/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift index e63c38ef2c..9d24d564c0 100644 --- a/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift @@ -455,7 +455,7 @@ public final class InviteRequestsSearchContainerNode: SearchDisplayControllerCon // dismissPromise.set(true) // } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlay(contextController) }) }) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index 1cef0c71f2..94c96be4b1 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -78,7 +78,7 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity { return self.entity.mirrored } - let account: Account + let postbox: Postbox let file: TelegramMediaFile? let entity: DrawingStickerEntity let animated: Bool @@ -96,8 +96,8 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity { let imagePromise = Promise() - init(account: Account, entity: DrawingStickerEntity) { - self.account = account + init(postbox: Postbox, entity: DrawingStickerEntity) { + self.postbox = postbox self.entity = entity self.animated = entity.isAnimated @@ -105,7 +105,7 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity { case let .file(file): self.file = file if file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" { - self.source = AnimatedStickerResourceSource(account: account, resource: file.resource, isVideo: file.isVideoSticker || file.mimeType == "video/webm") + self.source = AnimatedStickerResourceSource(postbox: postbox, resource: file.resource, isVideo: file.isVideoSticker || file.mimeType == "video/webm") if let source = self.source { let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512) let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384, height: 384)) @@ -132,7 +132,7 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity { })) } } else { - self.disposables.add((chatMessageSticker(account: self.account, userLocation: .other, file: file, small: false, fetched: true, onlyFullSize: true, thumbnail: false, synchronousLoad: false) + self.disposables.add((chatMessageSticker(postbox: self.postbox, userLocation: .other, file: file, small: false, fetched: true, onlyFullSize: true, thumbnail: false, synchronousLoad: false) |> deliverOn(self.queue)).start(next: { [weak self] generator in if let strongSelf = self { let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: entity.baseSize, boundingSize: entity.baseSize, intrinsicInsets: UIEdgeInsets())) @@ -412,7 +412,7 @@ private class LegacyPaintVectorEntity: LegacyPaintEntity { } public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRenderer { - private let account: Account? + private let postbox: Postbox? private let queue = Queue() private let entities: [LegacyPaintEntity] @@ -421,8 +421,8 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender private let isAvatar: Bool - public init(account: Account?, adjustments: TGMediaEditAdjustments) { - self.account = account + public init(postbox: Postbox?, adjustments: TGMediaEditAdjustments) { + self.postbox = postbox self.originalSize = adjustments.originalSize self.cropRect = adjustments.cropRect.isEmpty ? nil : adjustments.cropRect self.isAvatar = ((adjustments as? TGVideoEditAdjustments)?.documentId ?? 0) != 0 @@ -431,14 +431,14 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender if let paintingData = adjustments.paintingData, let entitiesData = paintingData.entitiesData { let entities = decodeDrawingEntities(data: entitiesData) for entity in entities { - if let sticker = entity as? DrawingStickerEntity, let account { - renderEntities.append(LegacyPaintStickerEntity(account: account, entity: sticker)) + if let sticker = entity as? DrawingStickerEntity, let postbox { + renderEntities.append(LegacyPaintStickerEntity(postbox: postbox, entity: sticker)) } else if let text = entity as? DrawingTextEntity { renderEntities.append(LegacyPaintTextEntity(entity: text)) - if let renderSubEntities = text.renderSubEntities, let account { + if let renderSubEntities = text.renderSubEntities, let postbox { for entity in renderSubEntities { if let entity = entity as? DrawingStickerEntity { - renderEntities.append(LegacyPaintStickerEntity(account: account, entity: entity)) + renderEntities.append(LegacyPaintStickerEntity(postbox: postbox, entity: entity)) } } } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 0b09329aa4..43ad12832f 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -1850,7 +1850,6 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.titleView.isHighlighted = true let contextController = ContextController( - account: self.context.account, presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceNode: self.titleView.contextSourceNode)), items: .single(ContextController.Items(content: .custom(content))), @@ -2226,7 +2225,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { return ContextController.Items(content: .list(items)) } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceNode: node)), items: items, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(MediaPickerContextReferenceContentSource(controller: self, sourceNode: node)), items: items, gesture: gesture) self.presentInGlobalOverlay(contextController) } } diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index 731e974567..1fd43edf48 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -1655,7 +1655,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta }) }))) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController) }, manageInviteLinks: { let controller = inviteLinkListController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, admin: nil) diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index 4e11b6eadb..990fb86e35 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -488,7 +488,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: peerNearbyContextMenuItems(context: context, peerId: peer.id, present: { c in presentControllerImpl?(c, nil) }) |> map { ContextController.Items(content: .list($0), animationCache: nil) }, gesture: gesture) presentInGlobalOverlayImpl?(contextController) diff --git a/submodules/SelectablePeerNode/BUILD b/submodules/SelectablePeerNode/BUILD index 39b96b1280..b13c4278f4 100644 --- a/submodules/SelectablePeerNode/BUILD +++ b/submodules/SelectablePeerNode/BUILD @@ -12,6 +12,7 @@ swift_library( deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/TelegramCore:TelegramCore", + "//submodules/Postbox:Postbox", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/TelegramPresentationData:TelegramPresentationData", @@ -24,6 +25,9 @@ swift_library( "//submodules/CheckNode:CheckNode", "//submodules/ComponentFlow:ComponentFlow", "//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", ], visibility = [ "//visibility:public", diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 4fb309b77b..89ef9f128f 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore +import Postbox import SwiftSignalKit import TelegramPresentationData import AvatarNode @@ -14,6 +15,9 @@ import AccountContext import CheckNode import ComponentFlow import EmojiStatusComponent +import AnimationCache +import MultiAnimationRenderer +import TelegramUIPreferences private let avatarFont = avatarPlaceholderFont(size: 24.0) private let textFont = Font.regular(11.0) @@ -144,6 +148,30 @@ public final class SelectablePeerNode: ASDisplayNode { } public func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { + self.setup( + accountPeerId: context.account.peerId, + postbox: context.account.postbox, + network: context.account.network, + energyUsageSettings: context.sharedContext.energyUsageSettings, + contentSettings: context.currentContentSettings.with { $0 }, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + resolveInlineStickers: { fileIds in + return context.engine.stickers.resolveInlineStickers(fileIds: fileIds) + }, + theme: theme, + strings: strings, + peer: peer, + customTitle: customTitle, + iconId: iconId, + iconColor: iconColor, + online: online, + numberOfLines: numberOfLines, + synchronousLoad: synchronousLoad + ) + } + + public func setup(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network, energyUsageSettings: EnergyUsageSettings, contentSettings: ContentSettings, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, customTitle: String? = nil, iconId: Int64? = nil, iconColor: Int32? = nil, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { let isFirstTime = self.peer == nil self.peer = peer guard let mainPeer = peer.chatMainPeer else { @@ -159,7 +187,7 @@ public final class SelectablePeerNode: ASDisplayNode { let text: String var overrideImage: AvatarNodeImageOverride? - if peer.peerId == context.account.peerId { + if peer.peerId == accountPeerId { text = self.compact ? strings.DeleteAccount_SavedMessages : strings.DialogList_SavedMessages overrideImage = .savedMessagesIcon } else if peer.peerId.isReplies { @@ -173,7 +201,7 @@ public final class SelectablePeerNode: ASDisplayNode { } self.textNode.maximumNumberOfLines = numberOfLines self.textNode.attributedText = NSAttributedString(string: customTitle ?? text, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : defaultColor, paragraphAlignment: .center) - self.avatarNode.setPeer(context: context, theme: theme, peer: mainPeer, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: isForum ? .roundedRect : .round, synchronousLoad: synchronousLoad) + self.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: contentSettings, theme: theme, peer: mainPeer, overrideImage: overrideImage, emptyColor: self.theme.avatarPlaceholderColor, clipStyle: isForum ? .roundedRect : .round, synchronousLoad: synchronousLoad) let onlineLayout = self.onlineNode.asyncLayout() let (onlineSize, onlineApply) = onlineLayout(online, false) @@ -195,9 +223,11 @@ public final class SelectablePeerNode: ASDisplayNode { let iconSize = self.iconView.update( transition: .easeInOut(duration: 0.2), component: AnyComponent(EmojiStatusComponent( - context: context, - animationCache: context.animationCache, - animationRenderer: context.animationRenderer, + postbox: postbox, + energyUsageSettings: energyUsageSettings, + resolveInlineStickers: resolveInlineStickers, + animationCache: animationCache, + animationRenderer: animationRenderer, content: iconContent, isVisibleForAnimations: true, action: nil diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift index 659760bbe1..cfd6ac1a60 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageExceptionsScreen.swift @@ -423,7 +423,6 @@ public func storageUsageExceptionsScreen( let source: ContextContentSource = .reference(StorageUsageExceptionsContextReferenceContentSource(sourceView: sourceNode.labelNode.view)) let contextController = ContextController( - account: context.account, presentationData: presentationData, source: source, items: items, diff --git a/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift b/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift index 48df82976f..6d314dafe5 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift @@ -43,7 +43,26 @@ private struct PeersEntry: Comparable, Identifiable { } func item(context: AccountContext) -> ListViewItem { - return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: .list(compact: true), context: context, peer: self.peer, presence: nil, unreadBadge: nil, action: { _ in }, contextAction: nil, isPeerSelected: { _ in return false }, customWidth: nil) + return HorizontalPeerItem( + theme: self.theme, + strings: self.strings, + mode: .list(compact: true), + accountPeerId: context.account.peerId, + postbox: context.account.postbox, + network: context.account.network, + energyUsageSettings: context.sharedContext.energyUsageSettings, + contentSettings: context.currentContentSettings.with { $0 }, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + resolveInlineStickers: context.engine.stickers.resolveInlineStickers, + peer: self.peer, + presence: nil, + unreadBadge: nil, + action: { _ in }, + contextAction: nil, + isPeerSelected: { _ in return false }, + customWidth: nil + ) } } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift index 5d34e5e2f6..784a9b396d 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift @@ -438,12 +438,41 @@ public func passcodeOptionsAccessController(context: AccountContext, animateIn: } } -public func passcodeEntryController(context: AccountContext, animateIn: Bool = true, modalPresentation: Bool = false, completion: @escaping (Bool) -> Void) -> Signal { - return context.sharedContext.accountManager.transaction { transaction -> PostboxAccessChallengeData in +public func passcodeEntryController( + context: AccountContext, + animateIn: Bool = true, + modalPresentation: Bool = false, + completion: @escaping (Bool) -> Void +) -> Signal { + return passcodeEntryController( + accountManager: context.sharedContext.accountManager, + applicationBindings: context.sharedContext.applicationBindings, + presentationData: context.sharedContext.currentPresentationData.with { $0 }, + updatedPresentationData: context.sharedContext.presentationData, + statusBarHost: context.sharedContext.mainWindow?.statusBarHost, + appLockContext: context.sharedContext.appLockContext, + animateIn: animateIn, + modalPresentation: modalPresentation, + completion: completion + ) +} + +public func passcodeEntryController( + accountManager: AccountManager, + applicationBindings: TelegramApplicationBindings, + presentationData: PresentationData, + updatedPresentationData: Signal, + statusBarHost: StatusBarHost?, + appLockContext: AppLockContext, + animateIn: Bool = true, + modalPresentation: Bool = false, + completion: @escaping (Bool) -> Void +) -> Signal { + return accountManager.transaction { transaction -> PostboxAccessChallengeData in return transaction.getAccessChallengeData() } |> mapToSignal { accessChallengeData -> Signal<(PostboxAccessChallengeData, PresentationPasscodeSettings?), NoError> in - return context.sharedContext.accountManager.transaction { transaction -> (PostboxAccessChallengeData, PresentationPasscodeSettings?) in + return accountManager.transaction { transaction -> (PostboxAccessChallengeData, PresentationPasscodeSettings?) in let passcodeSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationPasscodeSettings)?.get(PresentationPasscodeSettings.self) return (accessChallengeData, passcodeSettings) } @@ -459,12 +488,12 @@ public func passcodeEntryController(context: AccountContext, animateIn: Bool = t biometrics = .enabled(nil) #else if let passcodeSettings = passcodeSettings, passcodeSettings.enableBiometrics { - biometrics = .enabled(context.sharedContext.applicationBindings.isMainApp ? passcodeSettings.biometricsDomainState : passcodeSettings.shareBiometricsDomainState) + biometrics = .enabled(applicationBindings.isMainApp ? passcodeSettings.biometricsDomainState : passcodeSettings.shareBiometricsDomainState) } else { biometrics = .none } #endif - let controller = PasscodeEntryController(applicationBindings: context.sharedContext.applicationBindings, accountManager: context.sharedContext.accountManager, appLockContext: context.sharedContext.appLockContext, presentationData: context.sharedContext.currentPresentationData.with { $0 }, presentationDataSignal: context.sharedContext.presentationData, statusBarHost: context.sharedContext.mainWindow?.statusBarHost, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: { + let controller = PasscodeEntryController(applicationBindings: applicationBindings, accountManager: accountManager, appLockContext: appLockContext, presentationData: presentationData, presentationDataSignal: updatedPresentationData, statusBarHost: statusBarHost, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: { completion(false) }, modalPresentation: modalPresentation)) controller.presentationCompleted = { [weak controller] in diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index 9aee46a7ae..5ee5b81628 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -687,7 +687,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme }))) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController, nil) }) }, colorContextAction: { isCurrent, reference, accentColor, node, gesture in @@ -936,7 +936,7 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme } } } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController, nil) }) }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 7fa3424a02..d0cdb5c54f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -746,7 +746,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }))) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController, nil) }) }, colorContextAction: { isCurrent, reference, accentColor, node, gesture in @@ -995,7 +995,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The } } } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) presentInGlobalOverlayImpl?(contextController, nil) }) }) diff --git a/submodules/ShareController/BUILD b/submodules/ShareController/BUILD index 2a7672158e..a1b763d9a3 100644 --- a/submodules/ShareController/BUILD +++ b/submodules/ShareController/BUILD @@ -37,6 +37,8 @@ swift_library( "//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent", "//submodules/ComponentFlow:ComponentFlow", "//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", ], visibility = [ "//visibility:public", diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index f3b203ad23..1eae4e2398 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -16,6 +16,9 @@ import StickerResources import SaveToCameraRoll import TelegramStringFormatting import WallpaperBackgroundNode +import TelegramIntents +import AnimationCache +import MultiAnimationRenderer public struct ShareControllerAction { let title: String @@ -66,7 +69,7 @@ public enum ShareControllerSubject { case image([ImageRepresentationWithReference]) case media(AnyMediaReference) case mapMedia(TelegramMediaMap) - case fromExternal(([PeerId], [PeerId: Int64], String, Account, Bool) -> Signal) + case fromExternal(([PeerId], [PeerId: Int64], String, ShareControllerAccountContext, Bool) -> Signal) } private enum ExternalShareItem { @@ -297,6 +300,121 @@ private func collectExternalShareItems(strings: PresentationStrings, dateTimeFor }) } +public protocol ShareControllerEnvironment: AnyObject { + var presentationData: PresentationData { get } + var updatedPresentationData: Signal { get } + var isMainApp: Bool { get } + var energyUsageSettings: EnergyUsageSettings { get } + + var mediaManager: MediaManager? { get } + + func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable + func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id]) +} + +public final class ShareControllerAppEnvironment: ShareControllerEnvironment { + let sharedContext: SharedAccountContext + + public private(set) var presentationData: PresentationData + public var updatedPresentationData: Signal { + return self.sharedContext.presentationData + } + public var isMainApp: Bool { + return self.sharedContext.applicationBindings.isMainApp + } + public var energyUsageSettings: EnergyUsageSettings { + return self.sharedContext.energyUsageSettings + } + + public var mediaManager: MediaManager? { + return self.sharedContext.mediaManager + } + + public init(sharedContext: SharedAccountContext) { + self.sharedContext = sharedContext + + self.presentationData = sharedContext.currentPresentationData.with { $0 } + } + + public func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable { + return self.sharedContext.setAccountUserInterfaceInUse(id) + } + + public func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id]) { + if let account = account as? ShareControllerAppAccountContext { + TelegramIntents.donateSendMessageIntent(account: account.context.account, sharedContext: self.sharedContext, intentContext: .share, peerIds: peerIds) + } else { + assertionFailure() + } + } +} + +public protocol ShareControllerAccountContext: AnyObject { + var accountId: AccountRecordId { get } + var accountPeerId: EnginePeer.Id { get } + var stateManager: AccountStateManager { get } + var animationCache: AnimationCache { get } + var animationRenderer: MultiAnimationRenderer { get } + var contentSettings: ContentSettings { get } + var appConfiguration: AppConfiguration { get } + + func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> +} + +public final class ShareControllerAppAccountContext: ShareControllerAccountContext { + public let context: AccountContext + + public var accountId: AccountRecordId { + return self.context.account.id + } + public var accountPeerId: EnginePeer.Id { + return self.context.account.stateManager.accountPeerId + } + public var stateManager: AccountStateManager { + return self.context.account.stateManager + } + public var animationCache: AnimationCache { + return self.context.animationCache + } + public var animationRenderer: MultiAnimationRenderer { + return self.context.animationRenderer + } + public var contentSettings: ContentSettings { + return self.context.currentContentSettings.with { $0 } + } + public var appConfiguration: AppConfiguration { + return self.context.currentAppConfiguration.with { $0 } + } + + public init(context: AccountContext) { + self.context = context + } + + public func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> { + return self.context.engine.stickers.resolveInlineStickers(fileIds: fileIds) + } +} + +public final class ShareControllerSwitchableAccount: Equatable { + public let account: ShareControllerAccountContext + public let peer: Peer + + public init(account: ShareControllerAccountContext, peer: Peer) { + self.account = account + self.peer = peer + } + + public static func ==(lhs: ShareControllerSwitchableAccount, rhs: ShareControllerSwitchableAccount) -> Bool { + if lhs.account !== rhs.account { + return false + } + if !arePeersEqual(lhs.peer, rhs.peer) { + return false + } + return true + } +} + public final class ShareController: ViewController { private var controllerNode: ShareControllerNode { return self.displayNode as! ShareControllerNode @@ -309,9 +427,8 @@ public final class ShareController: ViewController { private var animatedIn = false - private let sharedContext: SharedAccountContext - private let currentContext: AccountContext - private var currentAccount: Account + private let environment: ShareControllerEnvironment + private var currentContext: ShareControllerAccountContext private var presentationData: PresentationData private var presentationDataDisposable: Disposable? private let forceTheme: PresentationTheme? @@ -321,7 +438,7 @@ public final class ShareController: ViewController { private let immediateExternalShare: Bool private let subject: ShareControllerSubject private let presetText: String? - private let switchableAccounts: [AccountWithInfo] + private let switchableAccounts: [ShareControllerSwitchableAccount] private let immediatePeerId: PeerId? private let segmentedValues: [ShareControllerSegmentedValue]? private let fromForeignApp: Bool @@ -349,13 +466,31 @@ public final class ShareController: ViewController { public var debugAction: (() -> Void)? public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) { - self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, fromForeignApp: fromForeignApp, segmentedValues: segmentedValues, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, forcedActionTitle: forcedActionTitle, shareAsLink: shareAsLink) + self.init( + environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), + currentContext: ShareControllerAppAccountContext(context: context), + subject: subject, + presetText: presetText, + preferredAction: preferredAction, + showInChat: showInChat, + fromForeignApp: fromForeignApp, + segmentedValues: segmentedValues, + externalShare: externalShare, + immediateExternalShare: immediateExternalShare, + switchableAccounts: switchableAccounts.map { info in + return ShareControllerSwitchableAccount(account: ShareControllerAppAccountContext(context: context.sharedContext.makeTempAccountContext(account: info.account)), peer: info.peer) + }, + immediatePeerId: immediatePeerId, + updatedPresentationData: updatedPresentationData, + forceTheme: forceTheme, + forcedActionTitle: forcedActionTitle, + shareAsLink: shareAsLink + ) } - public init(sharedContext: SharedAccountContext, currentContext: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) { - self.sharedContext = sharedContext + public init(environment: ShareControllerEnvironment, currentContext: ShareControllerAccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, fromForeignApp: Bool = false, segmentedValues: [ShareControllerSegmentedValue]? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [ShareControllerSwitchableAccount] = [], immediatePeerId: PeerId? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, forcedActionTitle: String? = nil, shareAsLink: Bool = false) { + self.environment = environment self.currentContext = currentContext - self.currentAccount = currentContext.account self.subject = subject self.presetText = presetText self.externalShare = externalShare @@ -367,7 +502,7 @@ public final class ShareController: ViewController { self.forceTheme = forceTheme self.shareAsLink = shareAsLink - self.presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 } + self.presentationData = updatedPresentationData?.initial ?? environment.presentationData if let forceTheme = self.forceTheme { self.presentationData = self.presentationData.withUpdated(theme: forceTheme) } @@ -422,12 +557,12 @@ public final class ShareController: ViewController { canSave = true isVideo = file.isVideo } - if case .saveToCameraRoll = preferredAction, canSave { + if let currentContext = currentContext as? ShareControllerAppAccountContext, case .saveToCameraRoll = preferredAction, canSave { self.actionIsMediaSaving = true self.defaultAction = ShareControllerAction(title: isVideo ? self.presentationData.strings.Gallery_SaveVideo : self.presentationData.strings.Gallery_SaveImage, action: { [weak self] in if let strongSelf = self { if case let .message(message, media) = mediaReference, let messageId = message.id, let file = media as? TelegramMediaFile { - let _ = (messageMediaFileStatus(context: currentContext, messageId: messageId, file: file) + let _ = (messageMediaFileStatus(context: currentContext.context, messageId: messageId, file: file) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] fetchStatus in if let strongSelf = self { @@ -490,7 +625,7 @@ public final class ShareController: ViewController { guard let strongSelf = self else { return } - let _ = (TelegramEngine(account: strongSelf.currentAccount).messages.exportMessageLink(peerId: chatPeer.id, messageId: message.id) + let _ = (_internal_exportMessageLink(postbox: strongSelf.currentContext.stateManager.postbox, network: strongSelf.currentContext.stateManager.network, peerId: chatPeer.id, messageId: message.id) |> map { result -> String? in return result } @@ -517,14 +652,14 @@ public final class ShareController: ViewController { }) } - self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.sharedContext.presentationData) + self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.environment.updatedPresentationData) |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self, strongSelf.isNodeLoaded { strongSelf.controllerNode.updatePresentationData(presentationData) } }) - self.switchToAccount(account: currentAccount, animateIn: false) + self.switchToAccount(account: self.currentContext, animateIn: false) if self.fromForeignApp { if let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as? UIApplication { @@ -555,7 +690,7 @@ public final class ShareController: ViewController { fromPublicChannel = true } - self.displayNode = ShareControllerNode(sharedContext: self.sharedContext, presentationData: self.presentationData, presetText: self.presetText, defaultAction: self.defaultAction, requestLayout: { [weak self] transition in + self.displayNode = ShareControllerNode(environment: self.environment, presentationData: self.presentationData, presetText: self.presetText, defaultAction: self.defaultAction, requestLayout: { [weak self] transition in self?.requestLayout(transition: transition) }, presentError: { [weak self] title, text in guard let strongSelf = self else { @@ -833,13 +968,13 @@ public final class ShareController: ViewController { } var useLegacy = false - if self.sharedContext.applicationBindings.isMainApp { + if self.environment.isMainApp { useLegacy = true } if peerIds.contains(where: { $0.namespace == Namespaces.Peer.SecretChat }) { useLegacy = true } - if let data = self.currentContext.currentAppConfiguration.with({ $0 }).data { + if let currentContext = self.currentContext as? ShareControllerAppAccountContext, let data = currentContext.context.currentAppConfiguration.with({ $0 }).data { if let _ = data["ios_disable_modern_sharing"] { useLegacy = true } @@ -852,7 +987,7 @@ public final class ShareController: ViewController { } } self.controllerNode.shareExternal = { [weak self] _ in - if let strongSelf = self { + if let strongSelf = self, let currentContext = strongSelf.currentContext as? ShareControllerAppAccountContext { var collectableItems: [CollectableExternalShareItem] = [] var subject = strongSelf.subject if let segmentedValues = strongSelf.segmentedValues { @@ -909,7 +1044,7 @@ public final class ShareController: ViewController { } } } - let accountPeerId = strongSelf.currentAccount.peerId + let accountPeerId = strongSelf.currentContext.accountPeerId let authorPeerId: PeerId? if let author = message.effectiveAuthor { authorPeerId = author.id @@ -922,7 +1057,7 @@ public final class ShareController: ViewController { var restrictedText: String? for attribute in message.attributes { if let attribute = attribute as? RestrictedContentMessageAttribute { - restrictedText = attribute.platformText(platform: "ios", contentSettings: strongSelf.currentContext.currentContentSettings.with { $0 }) ?? "" + restrictedText = attribute.platformText(platform: "ios", contentSettings: strongSelf.currentContext.contentSettings) ?? "" } } @@ -935,7 +1070,7 @@ public final class ShareController: ViewController { case .fromExternal: break } - return (collectExternalShareItems(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, nameOrder: strongSelf.presentationData.nameDisplayOrder, engine: TelegramEngine(account: strongSelf.currentAccount), postbox: strongSelf.currentAccount.postbox, collectableItems: collectableItems, takeOne: !strongSelf.immediateExternalShare) + return (collectExternalShareItems(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, nameOrder: strongSelf.presentationData.nameDisplayOrder, engine: currentContext.context.engine, postbox: strongSelf.currentContext.stateManager.postbox, collectableItems: collectableItems, takeOne: !strongSelf.immediateExternalShare) |> deliverOnMainQueue) |> map { state in switch state { @@ -992,7 +1127,7 @@ public final class ShareController: ViewController { } strongSelf.controllerNode.animateOut(shared: false, completion: {}) - let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 } + let presentationData = strongSelf.environment.presentationData let controller = ActionSheetController(presentationData: presentationData) controller.dismissed = { [weak self] cancelled in if cancelled { @@ -1004,10 +1139,21 @@ public final class ShareController: ViewController { } var items: [ActionSheetItem] = [] for info in strongSelf.switchableAccounts { - items.append(ActionSheetPeerItem(context: strongSelf.sharedContext.makeTempAccountContext(account: info.account), peer: EnginePeer(info.peer), title: EnginePeer(info.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), isSelected: info.account.id == strongSelf.currentAccount.id, strings: presentationData.strings, theme: presentationData.theme, action: { [weak self] in - dismissAction() - self?.switchToAccount(account: info.account, animateIn: true) - })) + items.append(ActionSheetPeerItem( + accountPeerId: info.account.accountPeerId, + postbox: info.account.stateManager.postbox, + network: info.account.stateManager.network, + contentSettings: info.account.contentSettings, + peer: EnginePeer(info.peer), + title: EnginePeer(info.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), + isSelected: info.account.accountId == strongSelf.currentContext.accountId, + strings: presentationData.strings, + theme: presentationData.theme, + action: { [weak self] in + dismissAction() + self?.switchToAccount(account: info.account, animateIn: true) + } + )) } controller.setItemGroups([ ActionSheetItemGroup(items: items) @@ -1023,7 +1169,7 @@ public final class ShareController: ViewController { self.peersDisposable.set((self.peers.get() |> deliverOnMainQueue).start(next: { [weak self] next in if let strongSelf = self { - strongSelf.controllerNode.updatePeers(context: strongSelf.sharedContext.makeTempAccountContext(account: strongSelf.currentAccount), switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction) + strongSelf.controllerNode.updatePeers(context: strongSelf.currentContext, switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction) } })) self._ready.set(self.controllerNode.ready.get()) @@ -1034,9 +1180,20 @@ public final class ShareController: ViewController { } private func shareModern(text: String, peerIds: [EnginePeer.Id], topicIds: [EnginePeer.Id: Int64], showNames: Bool, silently: Bool) -> Signal { - return self.currentContext.engine.data.get(EngineDataMap( - peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) - )) + return self.currentContext.stateManager.postbox.combinedView( + keys: peerIds.map { peerId in + return PostboxViewKey.basicPeer(peerId) + } + ) + |> map { views -> [EnginePeer.Id: EnginePeer?] in + var result: [EnginePeer.Id: EnginePeer?] = [:] + for peerId in peerIds { + if let view = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView, let peer = view.peer { + result[peerId] = EnginePeer(peer) + } + } + return result + } |> deliverOnMainQueue |> castError(ShareControllerError.self) |> mapToSignal { [weak self] peers -> Signal in @@ -1111,7 +1268,24 @@ public final class ShareController: ViewController { )) } messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messages + )) } case let .text(string): for peerId in peerIds { @@ -1155,7 +1329,24 @@ public final class ShareController: ViewController { replyToMessageId: replyToMessageId )) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messages + )) } case let .quote(string, url): for peerId in peerIds { @@ -1203,7 +1394,24 @@ public final class ShareController: ViewController { replyToMessageId: replyToMessageId )) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messages + )) } case let .image(representations): for peerId in peerIds { @@ -1246,7 +1454,24 @@ public final class ShareController: ViewController { )) } messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messages + )) } case let .media(mediaReference): var sendTextAsCaption = false @@ -1339,7 +1564,24 @@ public final class ShareController: ViewController { replyToMessageId: replyToMessageId )) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messages + )) } case let .mapMedia(media): for peerId in peerIds { @@ -1381,7 +1623,24 @@ public final class ShareController: ViewController { replyToMessageId: replyToMessageId )) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messages + )) } case let .messages(messages): for peerId in peerIds { @@ -1484,10 +1743,27 @@ public final class ShareController: ViewController { )) } messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) - shareSignals.append(standaloneSendEnqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) + shareSignals.append(standaloneSendEnqueueMessages( + accountPeerId: strongSelf.currentContext.accountPeerId, + postbox: strongSelf.currentContext.stateManager.postbox, + network: strongSelf.currentContext.stateManager.network, + stateManager: strongSelf.currentContext.stateManager, + auxiliaryMethods: AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in + return nil + }, fetchResourceMediaReferenceHash: { resource in + return .single(nil) + }, prepareSecretThumbnailData: { data in + return nil + }, backgroundUpload: { postbox, _, resource in + return .single(nil) + }), + peerId: peerId, + threadId: topicIds[peerId], + messages: messagesToEnqueue + )) } case let .fromExternal(f): - return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) + return f(peerIds, topicIds, text, strongSelf.currentContext, silently) |> map { state -> ShareState in switch state { case let .preparing(long): @@ -1499,14 +1775,21 @@ public final class ShareController: ViewController { } } } - let account = strongSelf.currentAccount + let account = strongSelf.currentContext let queue = Queue.mainQueue() //var displayedError = false return combineLatest(queue: queue, shareSignals) |> `catch` { error -> Signal<[StandaloneSendMessageStatus], ShareControllerError> in Queue.mainQueue().async { - let _ = (TelegramEngine(account: account).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: error.peerId)) - |> deliverOnMainQueue).start(next: { peer in + let _ = (account.stateManager.postbox.combinedView(keys: [PostboxViewKey.basicPeer(error.peerId)]) + |> map { views -> EnginePeer? in + if let view = views.views[PostboxViewKey.basicPeer(error.peerId)] as? BasicPeerView { + return view.peer.flatMap(EnginePeer.init) + } else { + return nil + } + } + |> deliverOnMainQueue).start(next: { peer in guard let strongSelf = self, let peer = peer else { return } @@ -1535,13 +1818,16 @@ public final class ShareController: ViewController { } private func shareLegacy(text: String, peerIds: [EnginePeer.Id], topicIds: [EnginePeer.Id: Int64], showNames: Bool, silently: Bool) -> Signal { - return self.currentContext.engine.data.get(EngineDataMap( + guard let currentContext = self.currentContext as? ShareControllerAppAccountContext else { + return .single(.done) + } + return currentContext.context.engine.data.get(EngineDataMap( peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) )) |> deliverOnMainQueue |> castError(ShareControllerError.self) |> mapToSignal { [weak self] peers -> Signal in - guard let strongSelf = self else { + guard let strongSelf = self, let currentContext = strongSelf.currentContext as? ShareControllerAppAccountContext else { return .complete() } @@ -1599,7 +1885,7 @@ public final class ShareController: ViewController { messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } case let .text(string): for peerId in peerIds { @@ -1631,7 +1917,7 @@ public final class ShareController: ViewController { } messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } case let .quote(string, url): for peerId in peerIds { @@ -1666,7 +1952,7 @@ public final class ShareController: ViewController { let entities = generateChatInputTextEntities(attributedText) messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } case let .image(representations): for peerId in peerIds { @@ -1695,7 +1981,7 @@ public final class ShareController: ViewController { var messages: [EnqueueMessage] = [] messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } case let .media(mediaReference): var sendTextAsCaption = false @@ -1772,7 +2058,7 @@ public final class ShareController: ViewController { } messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } case let .mapMedia(media): for peerId in peerIds { @@ -1804,7 +2090,7 @@ public final class ShareController: ViewController { } messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } case let .messages(messages): for peerId in peerIds { @@ -1895,10 +2181,10 @@ public final class ShareController: ViewController { messagesToEnqueue.append(.forward(source: message.id, threadId: threadId, grouping: .auto, attributes: [], correlationId: nil)) } messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently) - shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) + shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messagesToEnqueue)) } case let .fromExternal(f): - return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) + return f(peerIds, topicIds, text, currentContext, silently) |> map { state -> ShareState in switch state { case let .preparing(long): @@ -1910,7 +2196,7 @@ public final class ShareController: ViewController { } } } - let account = strongSelf.currentAccount + let account = currentContext.context.account let queue = Queue.mainQueue() var displayedError = false return combineLatest(queue: queue, shareSignals) @@ -1992,15 +2278,14 @@ public final class ShareController: ViewController { } private func saveToCameraRoll(messages: [Message], completion: @escaping () -> Void) { - let postbox = self.currentAccount.postbox + guard let accountContext = self.currentContext as? ShareControllerAppAccountContext else { + return + } + let context = accountContext.context + + let postbox = self.currentContext.stateManager.postbox let signals: [Signal] = messages.compactMap { message -> Signal? in if let media = message.media.first { - let context: AccountContext - if self.currentContext.account.id == self.currentAccount.id { - context = self.currentContext - } else { - context = self.sharedContext.makeTempAccountContext(account: self.currentAccount) - } return SaveToCameraRoll.saveToCameraRoll(context: context, postbox: postbox, userLocation: .peer(message.id.peerId), mediaReference: .message(message: MessageReference(message), media: media)) } else { return nil @@ -2021,34 +2306,63 @@ public final class ShareController: ViewController { } private func saveToCameraRoll(representations: [ImageRepresentationWithReference]) { - let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) - let context: AccountContext - if self.currentContext.account.id == self.currentAccount.id { - context = self.currentContext - } else { - context = self.sharedContext.makeTempAccountContext(account: self.currentAccount) + guard let accountContext = self.currentContext as? ShareControllerAppAccountContext else { + return } + let context = accountContext.context + + let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .standalone(media: media)) |> map(Optional.init), dismissImmediately: true, completion: {}) } private func saveToCameraRoll(mediaReference: AnyMediaReference, completion: (() -> Void)?) { - let context: AccountContext - if self.currentContext.account.id == self.currentAccount.id { - context = self.currentContext - } else { - context = self.sharedContext.makeTempAccountContext(account: self.currentAccount) + guard let accountContext = self.currentContext as? ShareControllerAppAccountContext else { + return } + let context = accountContext.context + self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: mediaReference) |> map(Optional.init), dismissImmediately: completion == nil, completion: completion ?? {}) } - private func switchToAccount(account: Account, animateIn: Bool) { - self.currentAccount = account - self.accountActiveDisposable.set(self.sharedContext.setAccountUserInterfaceInUse(account.id)) + private func switchToAccount(account: ShareControllerAccountContext, animateIn: Bool) { + self.currentContext = account + self.accountActiveDisposable.set(self.environment.setAccountUserInterfaceInUse(id: account.accountId)) + + let tailChatList = account.stateManager.postbox.tailChatListView( + groupId: .root, + filterPredicate: nil, + count: 150, + summaryComponents: ChatListEntrySummaryComponents( + components: [ + ChatListEntryMessageTagSummaryKey( + tag: .unseenPersonalMessage, + actionType: PendingMessageActionType.consumeUnseenPersonalMessage + ): ChatListEntrySummaryComponents.Component( + tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud), + actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud) + ), + ChatListEntryMessageTagSummaryKey( + tag: .unseenReaction, + actionType: PendingMessageActionType.readReaction + ): ChatListEntrySummaryComponents.Component( + tagSummary: ChatListEntryMessageTagSummaryComponent(namespace: Namespaces.Message.Cloud), + actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(namespace: Namespaces.Message.Cloud) + ) + ] + ) + ) + let peer = self.currentContext.stateManager.postbox.combinedView(keys: [PostboxViewKey.basicPeer(self.currentContext.accountPeerId)]) + |> take(1) + |> map { views -> EnginePeer? in + guard let view = views.views[PostboxViewKey.basicPeer(self.currentContext.accountPeerId)] as? BasicPeerView else { + return nil + } + return view.peer.flatMap(EnginePeer.init) + } self.peers.set(combineLatest( - TelegramEngine(account: self.currentAccount).data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.currentAccount.peerId)), - self.currentAccount.viewTracker.tailChatListView(groupId: .root, count: 150) - |> take(1) + peer, + tailChatList |> take(1) ) |> mapToSignal { maybeAccountPeer, view -> Signal<([(EngineRenderedPeer, EnginePeer.Presence?)], EnginePeer), NoError> in let accountPeer = maybeAccountPeer! @@ -2065,9 +2379,17 @@ public final class ShareController: ViewController { } } - return TelegramEngine(account: account).data.subscribe(EngineDataMap( - peers.map { TelegramEngine.EngineData.Item.Peer.Presence(id: $0.peerId) } - )) + let key = PostboxViewKey.peerPresences(peerIds: Set(peers.map(\.peerId))) + return account.stateManager.postbox.combinedView(keys: [key]) + |> map { views -> [EnginePeer.Id: EnginePeer.Presence?] in + var result: [EnginePeer.Id: EnginePeer.Presence?] = [:] + if let view = views.views[key] as? PeerPresencesView { + result = view.presences.mapValues { value -> EnginePeer.Presence? in + return EnginePeer.Presence(value) + } + } + return result + } |> map { presenceMap -> ([(EngineRenderedPeer, EnginePeer.Presence?)], EnginePeer) in var resultPeers: [(EngineRenderedPeer, EnginePeer.Presence?)] = [] for peer in peers { @@ -2080,7 +2402,7 @@ public final class ShareController: ViewController { self.peersDisposable.set((self.peers.get() |> deliverOnMainQueue).start(next: { [weak self] next in if let strongSelf = self { - strongSelf.controllerNode.updatePeers(context: strongSelf.sharedContext.makeTempAccountContext(account: strongSelf.currentAccount), switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction) + strongSelf.controllerNode.updatePeers(context: strongSelf.currentContext, switchableAccounts: strongSelf.switchableAccounts, peers: next.0, accountPeer: next.1, defaultAction: strongSelf.defaultAction) if animateIn && !animatedIn { animatedIn = true diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index 78faf245c5..cac653c540 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -22,8 +22,8 @@ enum ShareExternalState { } final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate { - private let sharedContext: SharedAccountContext - private var context: AccountContext? + private let environment: ShareControllerEnvironment + private var context: ShareControllerAccountContext? private var presentationData: PresentationData private let forceTheme: PresentationTheme? private let externalShare: Bool @@ -87,8 +87,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate private let showNames = ValuePromise(true) - init(sharedContext: SharedAccountContext, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, fromPublicChannel: Bool, segmentedValues: [ShareControllerSegmentedValue]?) { - self.sharedContext = sharedContext + init(environment: ShareControllerEnvironment, presentationData: PresentationData, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, fromForeignApp: Bool, forceTheme: PresentationTheme?, fromPublicChannel: Bool, segmentedValues: [ShareControllerSegmentedValue]?) { + self.environment = environment self.presentationData = presentationData self.forceTheme = forceTheme self.externalShare = externalShare @@ -191,7 +191,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } } self.actionButtonNode.contextAction = { [weak self] node, gesture in - if let strongSelf = self, let context = strongSelf.context, let node = node as? ContextReferenceContentNode { + if let strongSelf = self, let node = node as? ContextReferenceContentNode { let presentationData = strongSelf.presentationData let fromForeignApp = strongSelf.fromForeignApp let items: Signal = @@ -237,7 +237,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate ]) return ContextController.Items(content: .list(items), animationCache: nil) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: fromForeignApp ? -116.0 : 0.0))), items: items, gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: fromForeignApp ? -116.0 : 0.0))), items: items, gesture: gesture) contextController.immediateItemsTransitionAnimation = true strongSelf.present?(contextController) } @@ -378,7 +378,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate var didPresent = false var presentImpl: (() -> Void)? - let threads = threadList(context: context, peerId: mainPeer.id) + let threads = threadList(accountPeerId: context.accountPeerId, postbox: context.stateManager.postbox, peerId: mainPeer.id) |> deliverOnMainQueue |> beforeNext { _ in if !didPresent { @@ -388,7 +388,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } let topicsContentNode = ShareTopicsContainerNode( - sharedContext: self.sharedContext, + environment: self.environment, context: context, theme: self.presentationData.theme, strings: self.presentationData.strings, @@ -781,9 +781,20 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } if let context = self.context, let tryShare = self.tryShare { - let _ = (context.engine.data.get(EngineDataMap( - peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) - )) + let _ = (context.stateManager.postbox.combinedView( + keys: peerIds.map { peerId in + return PostboxViewKey.basicPeer(peerId) + } + ) + |> map { views -> [EnginePeer.Id: EnginePeer?] in + var result: [EnginePeer.Id: EnginePeer?] = [:] + for peerId in peerIds { + if let view = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView, let peer = view.peer { + result[peerId] = EnginePeer(peer) + } + } + return result + } |> deliverOnMainQueue).start(next: { [weak self] peers in guard let self else { return @@ -839,7 +850,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } if let context = self.context { - donateSendMessageIntent(account: context.account, sharedContext: self.sharedContext, intentContext: .share, peerIds: peerIds) + self.environment.donateSendMessageIntent(account: context, peerIds: peerIds) } if let signal = self.share?(self.inputFieldNode.text, peerIds, topicIds, showNames, silently) { @@ -879,7 +890,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate self.completed?(peerIds) let delay: Double - if let peerId = peerIds.first, peerIds.count == 1 && peerId == self.context?.account.peerId { + if let peerId = peerIds.first, peerIds.count == 1 && peerId == self.context?.accountPeerId { delay = 0.88 } else { delay = 0.44 @@ -903,7 +914,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate if fromForeignApp, case let .preparing(long) = status, !transitioned { transitioned = true if long { - strongSelf.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, forceNativeAppearance: true, account: strongSelf.context?.account, sharedContext: strongSelf.sharedContext), fastOut: true) + strongSelf.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, forceNativeAppearance: true, postbox: strongSelf.context?.stateManager.postbox, environment: strongSelf.environment), fastOut: true) } else { strongSelf.transitionToContentNode(ShareLoadingContainerNode(theme: strongSelf.presentationData.theme, forceNativeAppearance: true), fastOut: true) } @@ -1012,7 +1023,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } } - func updatePeers(context: AccountContext, switchableAccounts: [AccountWithInfo], peers: [(EngineRenderedPeer, EnginePeer.Presence?)], accountPeer: EnginePeer, defaultAction: ShareControllerAction?) { + func updatePeers(context: ShareControllerAccountContext, switchableAccounts: [ShareControllerSwitchableAccount], peers: [(EngineRenderedPeer, EnginePeer.Presence?)], accountPeer: EnginePeer, defaultAction: ShareControllerAction?) { self.context = context if let peersContentNode = self.peersContentNode, peersContentNode.accountPeer.id == accountPeer.id { @@ -1022,27 +1033,47 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate if let peerId = self.immediatePeerId { self.immediatePeerId = nil - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.RenderedPeer(id: peerId)) + let _ = (context.stateManager.postbox.combinedView(keys: [PostboxViewKey.peer(peerId: peerId, components: [])]) + |> take(1) + |> map { views -> EngineRenderedPeer? in + guard let view = views.views[PostboxViewKey.peer(peerId: peerId, components: [])] as? PeerView else { + return nil + } + var peers: [EnginePeer.Id: EnginePeer] = [:] + guard let peer = view.peers[peerId] else { + return nil + } + peers[peer.id] = EnginePeer(peer) + + if let secretChat = peer as? TelegramSecretChat { + guard let mainPeer = view.peers[secretChat.regularPeerId] else { + return nil + } + peers[mainPeer.id] = EnginePeer(mainPeer) + } + + return EngineRenderedPeer(peerId: peerId, peers: peers, associatedMedia: view.media) + } |> deliverOnMainQueue).start(next: { [weak self] peer in if let strongSelf = self, let peer = peer { - strongSelf.controllerInteraction?.togglePeer(peer, peer.peerId != context.account.peerId) + strongSelf.controllerInteraction?.togglePeer(peer, peer.peerId != context.accountPeerId) } }) } let animated = self.peersContentNode == nil - let peersContentNode = SharePeersContainerNode(sharedContext: self.sharedContext, context: context, switchableAccounts: switchableAccounts, theme: self.presentationData.theme, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, peers: peers, accountPeer: accountPeer, controllerInteraction: self.controllerInteraction!, externalShare: self.externalShare, switchToAnotherAccount: { [weak self] in + let peersContentNode = SharePeersContainerNode(environment: self.environment, context: context, switchableAccounts: switchableAccounts, theme: self.presentationData.theme, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, peers: peers, accountPeer: accountPeer, controllerInteraction: self.controllerInteraction!, externalShare: self.externalShare, switchToAnotherAccount: { [weak self] in self?.switchToAnotherAccount?() }, debugAction: { [weak self] in self?.debugAction?() }, extendedInitialReveal: self.presetText != nil, segmentedValues: self.segmentedValues) self.peersContentNode = peersContentNode peersContentNode.openSearch = { [weak self] in - let _ = (context.engine.peers.recentlySearchedPeers() + let _ = (_internal_recentlySearchedPeers(postbox: context.stateManager.postbox) |> take(1) |> deliverOnMainQueue).start(next: { peers in if let strongSelf = self { - let searchContentNode = ShareSearchContainerNode(sharedContext: strongSelf.sharedContext, context: context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, controllerInteraction: strongSelf.controllerInteraction!, recentPeers: peers.filter({ $0.peer.peerId.namespace != Namespaces.Peer.SecretChat }).map({ EngineRenderedPeer($0.peer) })) + let searchContentNode = ShareSearchContainerNode(environment: strongSelf.environment, context: context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, controllerInteraction: strongSelf.controllerInteraction!, recentPeers: peers.filter({ $0.peer.peerId.namespace != Namespaces.Peer.SecretChat }).map({ EngineRenderedPeer($0.peer) })) searchContentNode.cancel = { if let strongSelf = self, let peersContentNode = strongSelf.peersContentNode { strongSelf.transitionToContentNode(peersContentNode) @@ -1216,7 +1247,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate transition.updateAlpha(node: self.actionSeparatorNode, alpha: 0.0) transition.updateAlpha(node: self.actionsBackgroundNode, alpha: 0.0) - self.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, forceNativeAppearance: true, account: self.context?.account, sharedContext: self.sharedContext), fastOut: true) + self.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, forceNativeAppearance: true, postbox: self.context?.stateManager.postbox, environment: self.environment), fastOut: true) let timestamp = CACurrentMediaTime() self.shareDisposable.set(signal.start(completed: { [weak self] in let minDelay = 0.6 @@ -1307,7 +1338,7 @@ private final class ShareContextReferenceContentSource: ContextReferenceContentS } } -private func threadList(context: AccountContext, peerId: EnginePeer.Id) -> Signal { +private func threadList(accountPeerId: EnginePeer.Id, postbox: Postbox, peerId: EnginePeer.Id) -> Signal { let viewKey: PostboxViewKey = .messageHistoryThreadIndex( id: peerId, summaryComponents: ChatListEntrySummaryComponents( @@ -1315,10 +1346,10 @@ private func threadList(context: AccountContext, peerId: EnginePeer.Id) -> Signa ) ) - return context.account.postbox.combinedView(keys: [viewKey]) + return postbox.combinedView(keys: [viewKey]) |> mapToSignal { view -> Signal in - return context.account.postbox.transaction { transaction -> CombinedView in - if let peer = transaction.getPeer(context.account.peerId) { + return postbox.transaction { transaction -> CombinedView in + if let peer = transaction.getPeer(accountPeerId) { transaction.updatePeersInternal([peer]) { current, _ in return current ?? peer } diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index 5195c536fe..6b0a824fb7 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -91,7 +91,8 @@ final class ShareControllerGridSectionNode: ASDisplayNode { } final class ShareControllerPeerGridItem: GridItem { - let context: AccountContext + let environment: ShareControllerEnvironment + let context: ShareControllerAccountContext let theme: PresentationTheme let strings: PresentationStrings let peer: EngineRenderedPeer? @@ -103,7 +104,8 @@ final class ShareControllerPeerGridItem: GridItem { let section: GridSection? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { + self.environment = environment self.context = context self.theme = theme self.strings = strings @@ -124,7 +126,7 @@ final class ShareControllerPeerGridItem: GridItem { func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode { let node = ShareControllerPeerGridItemNode() node.controllerInteraction = self.controllerInteraction - node.setup(context: self.context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.topicId, threadData: self.threadData, search: self.search, synchronousLoad: synchronousLoad, force: false) + node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.topicId, threadData: self.threadData, search: self.search, synchronousLoad: synchronousLoad, force: false) return node } @@ -134,12 +136,12 @@ final class ShareControllerPeerGridItem: GridItem { return } node.controllerInteraction = self.controllerInteraction - node.setup(context: self.context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.topicId, threadData: self.threadData, search: self.search, synchronousLoad: false, force: false) + node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.topicId, threadData: self.threadData, search: self.search, synchronousLoad: false, force: false) } } final class ShareControllerPeerGridItemNode: GridItemNode { - private var currentState: (AccountContext, PresentationTheme, PresentationStrings, EngineRenderedPeer?, Bool, EnginePeer.Presence?, Int64?, MessageHistoryThreadData?)? + private var currentState: (ShareControllerEnvironment, ShareControllerAccountContext, PresentationTheme, PresentationStrings, EngineRenderedPeer?, Bool, EnginePeer.Presence?, Int64?, MessageHistoryThreadData?)? private let peerNode: SelectablePeerNode private var presenceManager: PeerPresenceStatusManager? @@ -149,7 +151,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { private var absoluteLocation: (CGRect, CGSize)? var peerId: EnginePeer.Id? { - return self.currentState?.3?.peerId + return self.currentState?.4?.peerId } override init() { @@ -159,7 +161,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.toggleSelection = { [weak self] in if let strongSelf = self { - if let (_, _, _, maybePeer, search, _, _, _) = strongSelf.currentState, let peer = maybePeer { + if let (_, _, _, _, maybePeer, search, _, _, _) = strongSelf.currentState, let peer = maybePeer { if let _ = peer.peers[peer.peerId] { strongSelf.controllerInteraction?.togglePeer(peer, search) } @@ -171,7 +173,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { guard let strongSelf = self, let currentState = strongSelf.currentState else { return } - strongSelf.setup(context: currentState.0, theme: currentState.1, strings: currentState.2, peer: currentState.3, presence: currentState.5, topicId: currentState.6, threadData: currentState.7, search: currentState.4, synchronousLoad: false, force: true) + strongSelf.setup(environment: currentState.0, context: currentState.1, theme: currentState.2, strings: currentState.3, peer: currentState.4, presence: currentState.6, topicId: currentState.7, threadData: currentState.8, search: currentState.5, synchronousLoad: false, force: true) }) } @@ -183,13 +185,13 @@ final class ShareControllerPeerGridItemNode: GridItemNode { } } - func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?, search: Bool, synchronousLoad: Bool, force: Bool) { - if force || self.currentState == nil || self.currentState!.0 !== context || self.currentState!.2 !== theme || self.currentState!.3 != peer || self.currentState!.5 != presence || self.currentState!.6 != topicId { + func setup(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, presence: EnginePeer.Presence?, topicId: Int64?, threadData: MessageHistoryThreadData?, search: Bool, synchronousLoad: Bool, force: Bool) { + if force || self.currentState == nil || self.currentState!.1 !== context || self.currentState!.3 !== theme || self.currentState!.4 != peer || self.currentState!.6 != presence || self.currentState!.7 != topicId { let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor) let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) var online = false - if case let .user(peer) = peer?.peer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId { + if case let .user(peer) = peer?.peer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.accountPeerId { let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp) if case .online = relativeStatus { online = true @@ -198,7 +200,27 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.theme = itemTheme if let peer = peer { - self.peerNode.setup(context: context, theme: theme, strings: strings, peer: peer, customTitle: threadData?.info.title, iconId: threadData?.info.icon, iconColor: threadData?.info.iconColor ?? 0, online: online, synchronousLoad: synchronousLoad) + let resolveInlineStickers = context.resolveInlineStickers + self.peerNode.setup( + accountPeerId: context.accountPeerId, + postbox: context.stateManager.postbox, + network: context.stateManager.network, + energyUsageSettings: environment.energyUsageSettings, + contentSettings: context.contentSettings, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + resolveInlineStickers: { fileIds in + return resolveInlineStickers(fileIds) + }, + theme: theme, + strings: strings, + peer: peer, + customTitle: threadData?.info.title, + iconId: threadData?.info.icon, + iconColor: threadData?.info.iconColor ?? 0, + online: online, + synchronousLoad: synchronousLoad + ) if let shimmerNode = self.placeholderNode { self.placeholderNode = nil shimmerNode.removeFromSupernode() @@ -230,7 +252,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) } - self.currentState = (context, theme, strings, peer, search, presence, topicId, threadData) + self.currentState = (environment, context, theme, strings, peer, search, presence, topicId, threadData) self.setNeedsLayout() if let presence = presence { self.presenceManager?.reset(presence: presence) @@ -241,7 +263,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { func updateSelection(animated: Bool) { var selected = false - if let controllerInteraction = self.controllerInteraction, let (_, _, _, maybePeer, _, _, _, _) = self.currentState, let peer = maybePeer { + if let controllerInteraction = self.controllerInteraction, let (_, _, _, _, maybePeer, _, _, _, _) = self.currentState, let peer = maybePeer { selected = controllerInteraction.selectedPeerIds.contains(peer.peerId) } @@ -255,7 +277,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.frame = bounds self.placeholderNode?.frame = bounds - if let (_, theme, _, _, _, _, _, _) = self.currentState, let shimmerNode = self.placeholderNode { + if let (_, _, theme, _, _, _, _, _, _) = self.currentState, let shimmerNode = self.placeholderNode { var shapes: [ShimmerEffectNode.Shape] = [] let titleLineWidth: CGFloat = 56.0 diff --git a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift index 0efd076ebe..074eb85846 100644 --- a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift @@ -9,7 +9,8 @@ import ChatListSearchRecentPeersNode import AccountContext final class ShareControllerRecentPeersGridItem: GridItem { - let context: AccountContext + let environment: ShareControllerEnvironment + let context: ShareControllerAccountContext let theme: PresentationTheme let strings: PresentationStrings let controllerInteraction: ShareControllerInteraction @@ -17,7 +18,8 @@ final class ShareControllerRecentPeersGridItem: GridItem { let section: GridSection? = nil let fillsRowWithHeight: (CGFloat, Bool)? = (102.0, true) - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ShareControllerInteraction) { + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ShareControllerInteraction) { + self.environment = environment self.context = context self.theme = theme self.strings = strings @@ -27,7 +29,7 @@ final class ShareControllerRecentPeersGridItem: GridItem { func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode { let node = ShareControllerRecentPeersGridItemNode() node.controllerInteraction = self.controllerInteraction - node.setup(context: self.context, theme: self.theme, strings: self.strings) + node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings) return node } @@ -37,12 +39,12 @@ final class ShareControllerRecentPeersGridItem: GridItem { return } node.controllerInteraction = self.controllerInteraction - node.setup(context: self.context, theme: self.theme, strings: self.strings) + node.setup(environment: self.environment, context: self.context, theme: self.theme, strings: self.strings) } } final class ShareControllerRecentPeersGridItemNode: GridItemNode { - private var currentState: (AccountContext, PresentationTheme, PresentationStrings)? + private var currentState: (ShareControllerAccountContext, PresentationTheme, PresentationStrings)? var controllerInteraction: ShareControllerInteraction? @@ -52,18 +54,34 @@ final class ShareControllerRecentPeersGridItemNode: GridItemNode { super.init() } - func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings) { + func setup(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings) { if self.currentState == nil || self.currentState!.0 !== context || self.currentState!.1 !== theme { let peersNode: ChatListSearchRecentPeersNode if let currentPeersNode = self.peersNode { peersNode = currentPeersNode peersNode.updateThemeAndStrings(theme: theme, strings: strings) } else { - peersNode = ChatListSearchRecentPeersNode(context: context, theme: theme, mode: .actionSheet, strings: strings, peerSelected: { [weak self] peer in - self?.controllerInteraction?.togglePeer(EngineRenderedPeer(peer: peer), true) - }, peerContextAction: { _, _, gesture, _ in gesture?.cancel() }, isPeerSelected: { [weak self] peerId in - return self?.controllerInteraction?.selectedPeerIds.contains(peerId) ?? false - }, share: true) + peersNode = ChatListSearchRecentPeersNode( + accountPeerId: context.accountPeerId, + postbox: context.stateManager.postbox, + network: context.stateManager.network, + energyUsageSettings: environment.energyUsageSettings, + contentSettings: context.contentSettings, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + resolveInlineStickers: context.resolveInlineStickers, + theme: theme, + mode: .actionSheet, + strings: strings, + peerSelected: { [weak self] peer in + self?.controllerInteraction?.togglePeer(EngineRenderedPeer(peer: peer), true) + }, + peerContextAction: { _, _, gesture, _ in gesture?.cancel() }, + isPeerSelected: { [weak self] peerId in + return self?.controllerInteraction?.selectedPeerIds.contains(peerId) ?? false + }, + share: true + ) self.peersNode = peersNode self.addSubnode(peersNode) } diff --git a/submodules/ShareController/Sources/ShareLoadingContainerNode.swift b/submodules/ShareController/Sources/ShareLoadingContainerNode.swift index dd015ecea0..c643f1ef46 100644 --- a/submodules/ShareController/Sources/ShareLoadingContainerNode.swift +++ b/submodules/ShareController/Sources/ShareLoadingContainerNode.swift @@ -11,6 +11,7 @@ import TelegramAnimatedStickerNode import AppBundle import TelegramUniversalVideoContent import TelegramCore +import Postbox import AccountContext private func fileSize(_ path: String, useTotalFileAllocatedSize: Bool = false) -> Int64? { @@ -231,7 +232,7 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte return self.elapsedTime + 3.0 + 0.15 } - public init(theme: PresentationTheme, strings: PresentationStrings, forceNativeAppearance: Bool, account: Account?, sharedContext: SharedAccountContext) { + public init(theme: PresentationTheme, strings: PresentationStrings, forceNativeAppearance: Bool, postbox: Postbox?, environment: ShareControllerEnvironment) { self.theme = theme self.strings = strings @@ -272,14 +273,14 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte } })) - if let account = account, let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) { + if let postbox, let mediaManager = environment.mediaManager, let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) { let decoration = ChatBubbleVideoDecoration(corners: ImageCorners(), nativeSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, backgroundColor: .black) let dummyFile = TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [], preloadSize: nil)]) let videoContent = NativeVideoContent(id: .message(1, EngineMedia.Id(namespace: 0, id: 1)), userLocation: .other, fileReference: .standalone(media: dummyFile), streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black, storeAfterDownload: nil) - let videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: sharedContext.mediaManager.audioSession, manager: sharedContext.mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded) + let videoNode = UniversalVideoNode(postbox: postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded) videoNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: 2.0)) videoNode.alpha = 0.01 self.videoNode = videoNode diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index b04eb7317e..87b80936eb 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -75,8 +75,8 @@ private struct SharePeerEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.threadId, threadData: self.threadData, controllerInteraction: interfaceInteraction, search: false) + func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { + return ShareControllerPeerGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: self.threadId, threadData: self.threadData, controllerInteraction: interfaceInteraction, search: false) } } @@ -89,19 +89,19 @@ private struct ShareGridTransaction { private let avatarFont = avatarPlaceholderFont(size: 17.0) -private func preparedGridEntryTransition(context: AccountContext, from fromEntries: [SharePeerEntry], to toEntries: [SharePeerEntry], interfaceInteraction: ShareControllerInteraction) -> ShareGridTransaction { +private func preparedGridEntryTransition(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, from fromEntries: [SharePeerEntry], to toEntries: [SharePeerEntry], interfaceInteraction: ShareControllerInteraction) -> ShareGridTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction)) } return ShareGridTransaction(deletions: deletions, insertions: insertions, updates: updates, animated: false) } final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { - private let sharedContext: SharedAccountContext - private let context: AccountContext + private let environment: ShareControllerEnvironment + private let context: ShareControllerAccountContext private var theme: PresentationTheme private let themePromise: Promise private let strings: PresentationStrings @@ -152,8 +152,8 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { } private let tick = ValuePromise(0) - init(sharedContext: SharedAccountContext, context: AccountContext, switchableAccounts: [AccountWithInfo], theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, peers: [(EngineRenderedPeer, EnginePeer.Presence?)], accountPeer: EnginePeer, controllerInteraction: ShareControllerInteraction, externalShare: Bool, switchToAnotherAccount: @escaping () -> Void, debugAction: @escaping () -> Void, extendedInitialReveal: Bool, segmentedValues: [ShareControllerSegmentedValue]?) { - self.sharedContext = sharedContext + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, switchableAccounts: [ShareControllerSwitchableAccount], theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, peers: [(EngineRenderedPeer, EnginePeer.Presence?)], accountPeer: EnginePeer, controllerInteraction: ShareControllerInteraction, externalShare: Bool, switchToAnotherAccount: @escaping () -> Void, debugAction: @escaping () -> Void, extendedInitialReveal: Bool, segmentedValues: [ShareControllerSegmentedValue]?) { + self.environment = environment self.context = context self.theme = theme self.themePromise = Promise() @@ -213,9 +213,18 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { self.contentTitleAccountNode = AvatarNode(font: avatarFont) var hasOtherAccounts = false - if switchableAccounts.count > 1, let info = switchableAccounts.first(where: { $0.account.id == context.account.id }) { + if switchableAccounts.count > 1, let info = switchableAccounts.first(where: { $0.account.accountId == context.accountId }) { hasOtherAccounts = true - self.contentTitleAccountNode.setPeer(context: context, theme: theme, peer: EnginePeer(info.peer), emptyColor: nil, synchronousLoad: false) + self.contentTitleAccountNode.setPeer( + accountPeerId: context.accountPeerId, + postbox: context.stateManager.postbox, + network: context.stateManager.network, + contentSettings: context.contentSettings, + theme: theme, + peer: EnginePeer(info.peer), + emptyColor: nil, + synchronousLoad: false + ) } else { self.contentTitleAccountNode.isHidden = true } @@ -284,7 +293,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { strongSelf.entries = entries let firstTime = previousEntries == nil - let transition = preparedGridEntryTransition(context: context, from: previousEntries ?? [], to: entries, interfaceInteraction: controllerInteraction) + let transition = preparedGridEntryTransition(environment: environment, context: context, from: previousEntries ?? [], to: entries, interfaceInteraction: controllerInteraction) strongSelf.enqueueTransition(transition, firstTime: firstTime) } })) diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index 3b166ed261..c612976c22 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -83,17 +83,17 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable { } } - func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { + func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { switch self { case let .topPeers(theme, strings): - return ShareControllerRecentPeersGridItem(context: context, theme: theme, strings: strings, controllerInteraction: interfaceInteraction) + return ShareControllerRecentPeersGridItem(environment: environment, context: context, theme: theme, strings: strings, controllerInteraction: interfaceInteraction) case let .peer(_, theme, peer, associatedPeer, presence, strings): var peers: [EnginePeer.Id: EnginePeer] = [peer.id: peer] if let associatedPeer = associatedPeer { peers[associatedPeer.id] = associatedPeer } let peer = EngineRenderedPeer(peerId: peer.id, peers: peers, associatedMedia: [:]) - return ShareControllerPeerGridItem(context: context, theme: theme, strings: strings, peer: peer, presence: presence, topicId: nil, threadData: nil, controllerInteraction: interfaceInteraction, sectionTitle: strings.DialogList_SearchSectionRecent, search: true) + return ShareControllerPeerGridItem(environment: environment, context: context, theme: theme, strings: strings, peer: peer, presence: presence, topicId: nil, threadData: nil, controllerInteraction: interfaceInteraction, sectionTitle: strings.DialogList_SearchSectionRecent, search: true) } } } @@ -130,8 +130,8 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: nil, threadData: nil, controllerInteraction: interfaceInteraction, search: true) + func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { + return ShareControllerPeerGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, topicId: nil, threadData: nil, controllerInteraction: interfaceInteraction, search: true) } } @@ -143,29 +143,29 @@ private struct ShareSearchGridTransaction { let crossFade: Bool } -private func preparedGridEntryTransition(context: AccountContext, from fromEntries: [ShareSearchPeerEntry], to toEntries: [ShareSearchPeerEntry], interfaceInteraction: ShareControllerInteraction, crossFade: Bool) -> ShareSearchGridTransaction { +private func preparedGridEntryTransition(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, from fromEntries: [ShareSearchPeerEntry], to toEntries: [ShareSearchPeerEntry], interfaceInteraction: ShareControllerInteraction, crossFade: Bool) -> ShareSearchGridTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction)) } return ShareSearchGridTransaction(deletions: deletions, insertions: insertions, updates: updates, animated: false, crossFade: crossFade) } -private func preparedRecentEntryTransition(context: AccountContext, from fromEntries: [ShareSearchRecentEntry], to toEntries: [ShareSearchRecentEntry], interfaceInteraction: ShareControllerInteraction) -> ShareSearchGridTransaction { +private func preparedRecentEntryTransition(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, from fromEntries: [ShareSearchRecentEntry], to toEntries: [ShareSearchRecentEntry], interfaceInteraction: ShareControllerInteraction) -> ShareSearchGridTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction)) } return ShareSearchGridTransaction(deletions: deletions, insertions: insertions, updates: updates, animated: false, crossFade: false) } final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { - private let sharedContext: SharedAccountContext - private let context: AccountContext + private let environment: ShareControllerEnvironment + private let context: ShareControllerAccountContext private var theme: PresentationTheme private let themePromise: Promise private let strings: PresentationStrings @@ -197,8 +197,8 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { private let searchQuery = ValuePromise("", ignoreRepeated: true) private let searchDisposable = MetaDisposable() - init(sharedContext: SharedAccountContext, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ShareControllerInteraction, recentPeers recentPeerList: [EngineRenderedPeer]) { - self.sharedContext = sharedContext + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ShareControllerInteraction, recentPeers recentPeerList: [EngineRenderedPeer]) { + self.environment = environment self.context = context self.theme = theme self.themePromise = Promise() @@ -247,11 +247,11 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { let foundItems = combineLatest(self.searchQuery.get(), self.themePromise.get()) |> mapToSignal { query, theme -> Signal<([ShareSearchPeerEntry]?, Bool), NoError> in if !query.isEmpty { - let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1) - let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased()) + let accountPeer = context.stateManager.postbox.loadedPeerWithId(context.accountPeerId) |> take(1) + let foundLocalPeers = context.stateManager.postbox.searchPeers(query: query.lowercased()) let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> = .single(([], [], true)) |> then( - context.engine.contacts.searchRemotePeers(query: query) + _internal_searchPeers(accountPeerId: context.accountPeerId, postbox: context.stateManager.postbox, network: context.stateManager.network, query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue()) |> map { a, b -> ([FoundPeer], [FoundPeer], Bool) in return (a, b, false) @@ -329,7 +329,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { let firstTime = previousEntries.0 == nil let crossFade = !firstTime && previousEntries.1 && !isPlaceholder - let transition = preparedGridEntryTransition(context: context, from: previousEntries.0 ?? [], to: entries ?? [], interfaceInteraction: controllerInteraction, crossFade: crossFade) + let transition = preparedGridEntryTransition(environment: environment, context: context, from: previousEntries.0 ?? [], to: entries ?? [], interfaceInteraction: controllerInteraction, crossFade: crossFade) strongSelf.enqueueTransition(transition, firstTime: firstTime) if (previousEntries.0 == nil) != (entries == nil) { @@ -350,7 +350,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { self?.searchQuery.set(text) } - let hasRecentPeers = context.engine.peers.recentPeers() + let hasRecentPeers = _internal_recentPeers(accountPeerId: context.accountPeerId, postbox: context.stateManager.postbox) |> map { value -> Bool in switch value { case let .peers(peers): @@ -384,7 +384,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { strongSelf.recentEntries = entries let firstTime = previousEntries == nil - let transition = preparedRecentEntryTransition(context: context, from: previousEntries ?? [], to: entries, interfaceInteraction: controllerInteraction) + let transition = preparedRecentEntryTransition(environment: environment, context: context, from: previousEntries ?? [], to: entries, interfaceInteraction: controllerInteraction) strongSelf.enqueueRecentTransition(transition, firstTime: firstTime) } })) diff --git a/submodules/ShareController/Sources/ShareTopicGridItem.swift b/submodules/ShareController/Sources/ShareTopicGridItem.swift index b3a4af9703..de756596ae 100644 --- a/submodules/ShareController/Sources/ShareTopicGridItem.swift +++ b/submodules/ShareController/Sources/ShareTopicGridItem.swift @@ -14,7 +14,8 @@ import ComponentFlow import EmojiStatusComponent final class ShareTopicGridItem: GridItem { - let context: AccountContext + let environment: ShareControllerEnvironment + let context: ShareControllerAccountContext let theme: PresentationTheme let strings: PresentationStrings let peer: EngineRenderedPeer? @@ -24,7 +25,8 @@ final class ShareTopicGridItem: GridItem { let section: GridSection? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, id: Int64, threadInfo: MessageHistoryThreadData, controllerInteraction: ShareControllerInteraction) { + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer?, id: Int64, threadInfo: MessageHistoryThreadData, controllerInteraction: ShareControllerInteraction) { + self.environment = environment self.context = context self.theme = theme self.strings = strings @@ -46,7 +48,7 @@ final class ShareTopicGridItem: GridItem { } final class ShareTopicGridItemNode: GridItemNode { - private var currentState: (AccountContext, PresentationTheme, PresentationStrings, EngineRenderedPeer?, MessageHistoryThreadData?)? + private var currentState: (ShareControllerAccountContext, PresentationTheme, PresentationStrings, EngineRenderedPeer?, MessageHistoryThreadData?)? private let iconView: ComponentView private let textNode: ImmediateTextNode @@ -113,7 +115,9 @@ final class ShareTopicGridItemNode: GridItemNode { let iconSize = self.iconView.update( transition: .easeInOut(duration: 0.2), component: AnyComponent(EmojiStatusComponent( - context: item.context, + postbox: item.context.stateManager.postbox, + energyUsageSettings: item.environment.energyUsageSettings, + resolveInlineStickers: item.context.resolveInlineStickers, animationCache: item.context.animationCache, animationRenderer: item.context.animationRenderer, content: iconContent, diff --git a/submodules/ShareController/Sources/ShareTopicsContainerNode.swift b/submodules/ShareController/Sources/ShareTopicsContainerNode.swift index bf68b640af..5319df1494 100644 --- a/submodules/ShareController/Sources/ShareTopicsContainerNode.swift +++ b/submodules/ShareController/Sources/ShareTopicsContainerNode.swift @@ -52,8 +52,8 @@ private struct ShareTopicEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareTopicGridItem(context: context, theme: self.theme, strings: self.strings, peer: self.peer, id: self.id, threadInfo: self.threadData, controllerInteraction: interfaceInteraction) + func item(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { + return ShareTopicGridItem(environment: environment, context: context, theme: self.theme, strings: self.strings, peer: self.peer, id: self.id, threadInfo: self.threadData, controllerInteraction: interfaceInteraction) } } @@ -64,12 +64,12 @@ private struct ShareGridTransaction { let animated: Bool } -private func preparedGridEntryTransition(context: AccountContext, from fromEntries: [ShareTopicEntry], to toEntries: [ShareTopicEntry], interfaceInteraction: ShareControllerInteraction) -> ShareGridTransaction { +private func preparedGridEntryTransition(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, from fromEntries: [ShareTopicEntry], to toEntries: [ShareTopicEntry], interfaceInteraction: ShareControllerInteraction) -> ShareGridTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interfaceInteraction: interfaceInteraction)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(environment: environment, context: context, interfaceInteraction: interfaceInteraction)) } return ShareGridTransaction(deletions: deletions, insertions: insertions, updates: updates, animated: false) } @@ -157,8 +157,8 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { } - private let sharedContext: SharedAccountContext - private let context: AccountContext + private let environment: ShareControllerEnvironment + private let context: ShareControllerAccountContext private var theme: PresentationTheme private let themePromise: Promise private let strings: PresentationStrings @@ -183,8 +183,8 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { var backPressed: () -> Void = {} - init(sharedContext: SharedAccountContext, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, topics: Signal, controllerInteraction: ShareControllerInteraction) { - self.sharedContext = sharedContext + init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, topics: Signal, controllerInteraction: ShareControllerInteraction) { + self.environment = environment self.context = context self.theme = theme self.themePromise = Promise() @@ -245,7 +245,7 @@ final class ShareTopicsContainerNode: ASDisplayNode, ShareContentContainerNode { strongSelf.entries = entries let firstTime = previousEntries == nil - let transition = preparedGridEntryTransition(context: context, from: previousEntries ?? [], to: entries, interfaceInteraction: controllerInteraction) + let transition = preparedGridEntryTransition(environment: environment, context: context, from: previousEntries ?? [], to: entries, interfaceInteraction: controllerInteraction) strongSelf.enqueueTransition(transition, firstTime: firstTime) } })) diff --git a/submodules/ShareItems/Impl/Sources/TGItemProviderSignals.m b/submodules/ShareItems/Impl/Sources/TGItemProviderSignals.m index 7c28f02c63..35f8ec5068 100644 --- a/submodules/ShareItems/Impl/Sources/TGItemProviderSignals.m +++ b/submodules/ShareItems/Impl/Sources/TGItemProviderSignals.m @@ -173,9 +173,6 @@ static CGSize TGFitSize(CGSize size, CGSize maxSize) { return [[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { bool preferAsFile = false; -#if DEBUG - preferAsFile = true; -#endif CGSize maxSize = CGSizeMake(1280.0, 1280.0); NSDictionary *imageOptions = @{ diff --git a/submodules/ShareItems/Sources/ShareItems.swift b/submodules/ShareItems/Sources/ShareItems.swift index 0eb575e6e5..890b8800e8 100644 --- a/submodules/ShareItems/Sources/ShareItems.swift +++ b/submodules/ShareItems/Sources/ShareItems.swift @@ -52,12 +52,12 @@ public enum PreparedShareItemError { case fileTooBig(Int64) } -private func preparedShareItem(account: Account, to peerId: PeerId, value: [String: Any]) -> Signal { +private func preparedShareItem(postbox: Postbox, network: Network, to peerId: PeerId, value: [String: Any]) -> Signal { if let imageData = value["scaledImageData"] as? Data, let dimensions = value["scaledImageDimensions"] as? NSValue { let diminsionsSize = dimensions.cgSizeValue return .single(.preparing(false)) |> then( - standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(diminsionsSize.width), height: Int32(diminsionsSize.height))) + standaloneUploadedImage(postbox: postbox, network: network, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(diminsionsSize.width), height: Int32(diminsionsSize.height))) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -76,7 +76,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri if let scaledImage = scalePhotoImage(image, dimensions: dimensions), let imageData = scaledImage.jpegData(compressionQuality: 0.52) { return .single(.preparing(false)) |> then( - standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))) + standaloneUploadedImage(postbox: postbox, network: network, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -146,7 +146,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true) let resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: asset.url.path, adjustments: resourceAdjustments) - return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: finalDuration, size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags, preloadSize: nil)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024) + return standaloneUploadedFile(postbox: postbox, network: network, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: finalDuration, size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags, preloadSize: nil)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -217,7 +217,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri mimeType = "animation/gif" attributes = [.ImageSize(size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))), .Animated, .FileName(fileName: fileName ?? "animation.gif")] } - return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: attributes, hintFileIsLarge: data.count > 10 * 1024 * 1024) + return standaloneUploadedFile(postbox: postbox, network: network, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: attributes, hintFileIsLarge: data.count > 10 * 1024 * 1024) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -236,7 +236,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri let imageData = scaledImage.jpegData(compressionQuality: 0.54)! return .single(.preparing(false)) |> then( - standaloneUploadedImage(account: account, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(scaledImage.size.width), height: Int32(scaledImage.size.height))) + standaloneUploadedImage(postbox: postbox, network: network, peerId: peerId, text: "", data: imageData, dimensions: PixelDimensions(width: Int32(scaledImage.size.width), height: Int32(scaledImage.size.height))) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -259,7 +259,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri let long = data.count > Int32(1.5 * 1024 * 1024) return .single(.preparing(long)) |> then( - standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 10 * 1024 * 1024) + standaloneUploadedFile(postbox: postbox, network: network, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 10 * 1024 * 1024) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -290,7 +290,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri let long = audioData.count > Int32(1.5 * 1024 * 1024) return .single(.preparing(long)) |> then( - standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform?.makeData()), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024) + standaloneUploadedFile(postbox: postbox, network: network, peerId: peerId, text: "", source: .data(audioData), mimeType: mimeType, attributes: [.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: artist, waveform: waveform?.makeData()), .FileName(fileName: fileName)], hintFileIsLarge: audioData.count > 10 * 1024 * 1024) |> mapError { _ -> PreparedShareItemError in return .generic } @@ -345,7 +345,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri } } -public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [MTSignal], additionalText: String) -> Signal { +public func preparedShareItems(postbox: Postbox, network: Network, to peerId: PeerId, dataItems: [MTSignal]) -> Signal { var dataSignals: Signal<[String: Any], PreparedShareItemError> = .complete() for dataItem in dataItems { let wrappedSignal: Signal<[String: Any], NoError> = Signal { subscriber in @@ -374,7 +374,7 @@ public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [ }) |> mapToSignal { items -> Signal<[PreparedShareItem], PreparedShareItemError> in return combineLatest(items.map { - preparedShareItem(account: account, to: peerId, value: $0) + preparedShareItem(postbox: postbox, network: network, to: peerId, value: $0) }) } @@ -384,21 +384,18 @@ public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [ var progresses: [Float] = [] for item in items { switch item { - case let .preparing(long): - return .preparing(long) - case let .progress(value): - progresses.append(value) - case let .userInteractionRequired(value): - return .userInteractionRequired([value]) - case let .done(content): - result.append(content) - progresses.append(1.0) + case let .preparing(long): + return .preparing(long) + case let .progress(value): + progresses.append(value) + case let .userInteractionRequired(value): + return .userInteractionRequired([value]) + case let .done(content): + result.append(content) + progresses.append(1.0) } } if result.count == items.count { - if !additionalText.isEmpty { - result.insert(PreparedShareItemContent.text(additionalText), at: 0) - } return .done(result) } else { let value = progresses.reduce(0.0, +) / Float(progresses.count) @@ -414,8 +411,8 @@ public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [ }) } -public func sentShareItems(account: Account, to peerIds: [PeerId], threadIds: [PeerId: Int64], items: [PreparedShareItemContent], silently: Bool) -> Signal { - var messages: [EnqueueMessage] = [] +public func sentShareItems(accountPeerId: PeerId, postbox: Postbox, network: Network, stateManager: AccountStateManager, auxiliaryMethods: AccountAuxiliaryMethods, to peerIds: [PeerId], threadIds: [PeerId: Int64], items: [PreparedShareItemContent], silently: Bool, additionalText: String) -> Signal { + var messages: [StandaloneSendEnqueueMessage] = [] var groupingKey: Int64? var mediaTypes: (photo: Int, video: Int, music: Int, other: Int) = (0, 0, 0, 0) if items.count > 1 { @@ -450,45 +447,82 @@ public func sentShareItems(account: Account, to peerIds: [PeerId], threadIds: [P groupingKey = Int64.random(in: Int64.min ... Int64.max) } - var attributes: [MessageAttribute] = [] - if silently { - attributes.append(NotificationInfoMessageAttribute(flags: .muted)) - } - - var mediaMessages: [EnqueueMessage] = [] + var mediaMessageCount = 0 + var consumedText = false for item in items { switch item { - case let .text(text): - messages.append(.message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - case let .media(media): - switch media { - case let .media(reference): - let message: EnqueueMessage = .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: reference, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: groupingKey, correlationId: nil, bubbleUpEmojiOrStickersets: []) - messages.append(message) - mediaMessages.append(message) - - } - if let _ = groupingKey, mediaMessages.count % 10 == 0 { - groupingKey = Int64.random(in: Int64.min ... Int64.max) - } + case let .text(text): + var message = StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: text, + entities: [] + )), + replyToMessageId: nil + ) + message.isSilent = silently + messages.append(message) + case let .media(media): + switch media { + case let .media(reference): + var message = StandaloneSendEnqueueMessage( + content: .arbitraryMedia( + media: reference, + text: StandaloneSendEnqueueMessage.Text( + string: additionalText, + entities: [] + ) + ), + replyToMessageId: nil + ) + consumedText = true + message.isSilent = silently + message.groupingKey = groupingKey + messages.append(message) + mediaMessageCount += 1 + } + if let _ = groupingKey, mediaMessageCount % 10 == 0 { + groupingKey = Int64.random(in: Int64.min ... Int64.max) + } } } - return enqueueMessagesToMultiplePeers(account: account, peerIds: peerIds, threadIds: threadIds, messages: messages) - |> castError(Void.self) - |> mapToSignal { messageIds -> Signal in - return TelegramEngine(account: account).data.subscribe(EngineDataMap( - messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init) - )) - |> castError(Void.self) - |> mapToSignal { messages -> Signal in - for (_, message) in messages { - if let message = message, message.flags.contains(.Unsent) { - return .complete() - } + if !consumedText && !additionalText.isEmpty { + var message = StandaloneSendEnqueueMessage( + content: .text(text: StandaloneSendEnqueueMessage.Text( + string: additionalText, + entities: [] + )), + replyToMessageId: nil + ) + message.isSilent = silently + messages.append(message) + } + + var peerSignals: Signal = .single(0.0) + for peerId in peerIds { + peerSignals = peerSignals |> then(standaloneSendEnqueueMessages( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + stateManager: stateManager, + auxiliaryMethods: auxiliaryMethods, + peerId: peerId, + threadId: threadIds[peerId], + messages: messages + ) + |> mapToSignal { status -> Signal in + switch status { + case let .progress(progress): + //return .single(progress) + let _ = progress + return .complete() + case .done: + return .complete() } - return .single(1.0) - } - |> take(1) + }) + } + return peerSignals + |> mapError { _ -> Void in + return Void() } } diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 94617c7388..b31e673c37 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -527,7 +527,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD }) }))) - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .extracted(ChannelStatsContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) controller.presentInGlobalOverlay(contextController) } return controller diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 119159e2a9..9d6d35e915 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -838,7 +838,7 @@ private final class StickerPackContainer: ASDisplayNode { } }))) - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(StickerPackContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(StickerPackContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) self.presentInGlobalOverlay(contextController, nil) } diff --git a/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift b/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift index 56c2e313e9..973cf3743f 100644 --- a/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift +++ b/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift @@ -46,20 +46,29 @@ public final class AnimatedStickerNodeLocalFileSource: AnimatedStickerNodeSource } public final class AnimatedStickerResourceSource: AnimatedStickerNodeSource { - public let account: Account + public let postbox: Postbox public let resource: MediaResource public let fitzModifier: EmojiFitzModifier? public let isVideo: Bool - public init(account: Account, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil, isVideo: Bool = false) { - self.account = account + public convenience init(account: Account, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil, isVideo: Bool = false) { + self.init( + postbox: account.postbox, + resource: resource, + fitzModifier: fitzModifier, + isVideo: isVideo + ) + } + + public init(postbox: Postbox, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil, isVideo: Bool = false) { + self.postbox = postbox self.resource = resource self.fitzModifier = fitzModifier self.isVideo = isVideo } public func cachedDataPath(width: Int, height: Int) -> Signal<(String, Bool), NoError> { - return chatMessageAnimationData(mediaBox: self.account.postbox.mediaBox, resource: self.resource, fitzModifier: self.fitzModifier, isVideo: self.isVideo, width: width, height: height, synchronousLoad: false) + return chatMessageAnimationData(mediaBox: self.postbox.mediaBox, resource: self.resource, fitzModifier: self.fitzModifier, isVideo: self.isVideo, width: width, height: height, synchronousLoad: false) |> filter { data in return data.size != 0 } @@ -69,7 +78,7 @@ public final class AnimatedStickerResourceSource: AnimatedStickerNodeSource { } public func directDataPath(attemptSynchronously: Bool) -> Signal { - return self.account.postbox.mediaBox.resourceData(self.resource, attemptSynchronously: attemptSynchronously) + return self.postbox.mediaBox.resourceData(self.resource, attemptSynchronously: attemptSynchronously) |> map { data -> String? in if data.complete { return data.path diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index 815fb8e78e..5052903899 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -596,7 +596,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi let items = self.contextMenuSpeedItems(scheduleTooltip: { change in scheduledTooltip = change }) - let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture) + let contextController = ContextController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture) contextController.dismissed = { [weak self] in if let scheduledTooltip, let self, let rate = self.playbackBaseRate { self.setRate?(rate, scheduledTooltip) diff --git a/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift index 4ab979bc37..5944f985df 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift @@ -656,7 +656,7 @@ public final class MediaStreamComponent: CombinedComponent { } } - let contextController = ContextController(account: call.accountContext.account, presentationData: presentationData.withUpdated(theme: defaultDarkPresentationTheme), source: .reference(ReferenceContentSource(sourceView: anchorView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + let contextController = ContextController(presentationData: presentationData.withUpdated(theme: defaultDarkPresentationTheme), source: .reference(ReferenceContentSource(sourceView: anchorView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) /*contextController.passthroughTouchEvent = { sourceView, point in guard let strongSelf = self else { return .ignore diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index e8ad0b7393..ec1abfe31d 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1799,7 +1799,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController dismissPromise.set(true) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(source), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(source), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) contextController.useComplexItemsTransitionAnimation = true strongSelf.controller?.presentInGlobalOverlay(contextController) }, getPeerVideo: { [weak self] endpointId, position in @@ -2488,7 +2488,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController private func openSettingsMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { let items: Signal<[ContextMenuItem], NoError> = self.contextMenuMainItems() if let controller = self.controller { - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData.withUpdated(theme: self.darkTheme), source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceNode: self.optionsButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData.withUpdated(theme: self.darkTheme), source: .reference(VoiceChatContextReferenceContentSource(controller: controller, sourceNode: self.optionsButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) controller.presentInGlobalOverlay(contextController) } } @@ -6311,7 +6311,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController let signal = Signal { [weak self] subscriber in let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: account, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: account.postbox, adjustments: adjustments) } else { return nil } diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 8bb8f2e23f..4b6a33219c 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -685,12 +685,12 @@ public enum AccountNetworkState: Equatable { } public final class AccountAuxiliaryMethods { - public let fetchResource: (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal? + public let fetchResource: (Postbox, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal? public let fetchResourceMediaReferenceHash: (MediaResource) -> Signal public let prepareSecretThumbnailData: (MediaResourceData) -> (PixelDimensions, Data)? public let backgroundUpload: (Postbox, Network, MediaResource) -> Signal - public init(fetchResource: @escaping (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?, backgroundUpload: @escaping (Postbox, Network, MediaResource) -> Signal) { + public init(fetchResource: @escaping (Postbox, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?, backgroundUpload: @escaping (Postbox, Network, MediaResource) -> Signal) { self.fetchResource = fetchResource self.fetchResourceMediaReferenceHash = fetchResourceMediaReferenceHash self.prepareSecretThumbnailData = prepareSecretThumbnailData @@ -1378,7 +1378,7 @@ public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil) { account.postbox.mediaBox.fetchResource = { [weak account] resource, intervals, parameters -> Signal in if let strongAccount = account { - if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, intervals, parameters) { + if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount.postbox, resource, intervals, parameters) { return result } else if let result = fetchResource(account: strongAccount, resource: resource, intervals: intervals, parameters: parameters) { return result @@ -1475,6 +1475,9 @@ public func standaloneStateManager( |> mapToSignal { phoneNumber in Logger.shared.log("StandaloneStateManager", "received phone number") + let mediaReferenceRevalidationContext = MediaReferenceRevalidationContext() + let networkStatsContext = NetworkStatsContext(postbox: postbox) + return initializedNetwork( accountId: id, arguments: networkArguments, @@ -1492,6 +1495,31 @@ public func standaloneStateManager( |> map { network -> AccountStateManager? in Logger.shared.log("StandaloneStateManager", "received network") + postbox.mediaBox.fetchResource = { resource, intervals, parameters -> Signal in + if let result = auxiliaryMethods.fetchResource( + postbox, + resource, + intervals, + parameters + ) { + return result + } else if let result = fetchResource( + accountPeerId: authorizedState.peerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + isTestingEnvironment: authorizedState.isTestingEnvironment, + resource: resource, + intervals: intervals, + parameters: parameters + ) { + return result + } else { + return .never() + } + } + return AccountStateManager( accountPeerId: authorizedState.peerId, accountManager: accountManager, diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 4f10dd7311..7018b6b226 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -484,15 +484,15 @@ func _internal_setForumChannelPinnedTopics(account: Account, id: EnginePeer.Id, } } -func _internal_setChannelForumMode(account: Account, peerId: PeerId, isForum: Bool) -> Signal { - return account.postbox.transaction { transaction -> Api.InputChannel? in +func _internal_setChannelForumMode(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, isForum: Bool) -> Signal { + return postbox.transaction { transaction -> Api.InputChannel? in return transaction.getPeer(peerId).flatMap(apiInputChannel) } |> mapToSignal { inputChannel -> Signal in guard let inputChannel = inputChannel else { return .complete() } - return account.network.request(Api.functions.channels.toggleForum(channel: inputChannel, enabled: isForum ? .boolTrue : .boolFalse)) + return network.request(Api.functions.channels.toggleForum(channel: inputChannel, enabled: isForum ? .boolTrue : .boolFalse)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -501,7 +501,7 @@ func _internal_setChannelForumMode(account: Account, peerId: PeerId, isForum: Bo guard let result = result else { return .complete() } - account.stateManager.addUpdates(result) + stateManager.addUpdates(result) return .complete() } diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index ef7baa9c43..fb92377aca 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -104,6 +104,7 @@ public struct StandaloneSendEnqueueMessage { public var replyToMessageId: MessageId? public var forwardOptions: ForwardOptions? public var isSilent: Bool = false + public var groupingKey: Int64? = nil public init( content: Content, @@ -114,11 +115,16 @@ public struct StandaloneSendEnqueueMessage { } } -public func standaloneSendEnqueueMessages(account: Account, peerId: PeerId, messages: [StandaloneSendEnqueueMessage]) -> Signal { - #if !DEBUG - error - #endif - +public func standaloneSendEnqueueMessages( + accountPeerId: PeerId, + postbox: Postbox, + network: Network, + stateManager: AccountStateManager, + auxiliaryMethods: AccountAuxiliaryMethods, + peerId: PeerId, + threadId: Int64?, + messages: [StandaloneSendEnqueueMessage] +) -> Signal { let signals: [Signal] = messages.map { message in var attributes: [MessageAttribute] = [] var text: String = "" @@ -167,7 +173,9 @@ public func standaloneSendEnqueueMessages(account: Account, peerId: PeerId, mess attributes.append(NotificationInfoMessageAttribute(flags: .muted)) } - let content = messageContentToUpload(accountPeerId: account.peerId, network: account.network, postbox: account.postbox, auxiliaryMethods: account.auxiliaryMethods, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, revalidationContext: account.mediaReferenceRevalidationContext, forceReupload: false, isGrouped: false, passFetchProgress: true, forceNoBigParts: false, peerId: peerId, messageId: nil, attributes: attributes, text: text, media: media) + let content = messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: { _, _, _, _ in + return .single(nil) + }, messageMediaPreuploadManager: MessageMediaPreuploadManager(), revalidationContext: MediaReferenceRevalidationContext(), forceReupload: false, isGrouped: false, passFetchProgress: true, forceNoBigParts: false, peerId: peerId, messageId: nil, attributes: attributes, text: text, media: media) let contentResult: Signal switch content { case let .signal(value, _): @@ -197,31 +205,29 @@ public func standaloneSendEnqueueMessages(account: Account, peerId: PeerId, mess } if allDone { var sendSignals: [Signal] = [] - sendSignals.removeAll() for content in allResults { + var text: String = "" switch content.content { - case let .text(text): - let _ = text - preconditionFailure() - case let .media(inputMedia, text): - let _ = inputMedia - let _ = text - preconditionFailure() - case let .forward(source): - let _ = source - preconditionFailure() - case let .chatContextResult(result): - let _ = result - preconditionFailure() - case let .secretMedia(inputFile, size, key): - let _ = inputFile - let _ = size - let _ = key - preconditionFailure() - case .messageScreenshot: - return .single(.done) + case let .text(textValue): + text = textValue + case let .media(_, textValue): + text = textValue + default: + break } + + sendSignals.append(sendUploadedMessageContent( + postbox: postbox, + network: network, + stateManager: stateManager, + accountPeerId: stateManager.accountPeerId, + peerId: peerId, + content: content, + text: text, + attributes: [], + threadId: threadId + )) } return combineLatest(sendSignals) @@ -245,7 +251,9 @@ private func sendUploadedMessageContent(postbox: Postbox, network: Network, stat var uniqueId: Int64 = 0 var forwardSourceInfoAttribute: ForwardSourceInfoAttribute? var messageEntities: [Api.MessageEntity]? - var replyMessageId: Int32? + var replyMessageId: Int32? = threadId.flatMap { threadId in + makeThreadIdMessageId(peerId: peerId, threadId: threadId).id + } var replyToStoryId: StoryId? var scheduleTime: Int32? var sendAsPeerId: PeerId? @@ -287,6 +295,10 @@ private func sendUploadedMessageContent(postbox: Postbox, network: Network, stat } } + if uniqueId == 0 { + uniqueId = Int64.random(in: Int64.min ... Int64.max) + } + if case .forward = content.content { } else { flags |= (1 << 7) diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift index 10b6b52cf1..10c2105e85 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift @@ -52,19 +52,19 @@ private func uploadedThumbnail(network: Network, postbox: Postbox, data: Data) - } } -public func standaloneUploadedImage(account: Account, peerId: PeerId, text: String, data: Data, thumbnailData: Data? = nil, dimensions: PixelDimensions) -> Signal { - return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image, userContentType: .image), hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: false) +public func standaloneUploadedImage(postbox: Postbox, network: Network, peerId: PeerId, text: String, data: Data, thumbnailData: Data? = nil, dimensions: PixelDimensions) -> Signal { + return multipartUpload(network: network, postbox: postbox, source: .data(data), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image, userContentType: .image), hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: false) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { next -> Signal in switch next { case let .inputFile(inputFile): - return account.postbox.transaction { transaction -> Api.InputPeer? in + return postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } |> mapError { _ -> StandaloneUploadMediaError in } |> mapToSignal { inputPeer -> Signal in if let inputPeer = inputPeer { - return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil))) + return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil))) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { media -> Signal in switch media { @@ -84,7 +84,7 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri } } case let .inputSecretFile(file, _, key): - return account.postbox.transaction { transaction -> Api.InputEncryptedChat? in + return postbox.transaction { transaction -> Api.InputEncryptedChat? in if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { return Api.InputEncryptedChat.inputEncryptedChat(chatId: Int32(peer.id.id._internalGetInt64Value()), accessHash: peer.accessHash) } @@ -95,7 +95,7 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri guard let inputChat = inputChat else { return .fail(.generic) } - return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file)) + return network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file)) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { result -> Signal in @@ -113,15 +113,15 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri } } -public func standaloneUploadedFile(account: Account, peerId: PeerId, text: String, source: MultipartUploadSource, thumbnailData: Data? = nil, mimeType: String, attributes: [TelegramMediaFileAttribute], hintFileIsLarge: Bool) -> Signal { - let upload = multipartUpload(network: account.network, postbox: account.postbox, source: source, encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes), userContentType: nil), hintFileSize: nil, hintFileIsLarge: hintFileIsLarge, forceNoBigParts: false) +public func standaloneUploadedFile(postbox: Postbox, network: Network, peerId: PeerId, text: String, source: MultipartUploadSource, thumbnailData: Data? = nil, mimeType: String, attributes: [TelegramMediaFileAttribute], hintFileIsLarge: Bool) -> Signal { + let upload = multipartUpload(network: network, postbox: postbox, source: source, encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes), userContentType: nil), hintFileSize: nil, hintFileIsLarge: hintFileIsLarge, forceNoBigParts: false) |> mapError { _ -> StandaloneUploadMediaError in return .generic } let uploadThumbnail: Signal if let thumbnailData = thumbnailData { uploadThumbnail = .single(.pending) |> then( - uploadedThumbnail(network: account.network, postbox: account.postbox, data: thumbnailData) + uploadedThumbnail(network: network, postbox: postbox, data: thumbnailData) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> map { result in if let result = result { @@ -147,7 +147,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin default: switch result { case let .inputFile(inputFile): - return account.postbox.transaction { transaction -> Api.InputPeer? in + return postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } |> mapError { _ -> StandaloneUploadMediaError in } @@ -158,7 +158,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin if let _ = thumbnailFile { flags |= 1 << 2 } - return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil))) + return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil))) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { media -> Signal in switch media { @@ -178,7 +178,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin } } case let .inputSecretFile(file, _, key): - return account.postbox.transaction { transaction -> Api.InputEncryptedChat? in + return postbox.transaction { transaction -> Api.InputEncryptedChat? in if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { return Api.InputEncryptedChat.inputEncryptedChat(chatId: Int32(peer.id.id._internalGetInt64Value()), accessHash: peer.accessHash) } @@ -189,7 +189,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin guard let inputChat = inputChat else { return .fail(.generic) } - return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file)) + return network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file)) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { result -> Signal in switch result { diff --git a/submodules/TelegramCore/Sources/State/Fetch.swift b/submodules/TelegramCore/Sources/State/Fetch.swift index 22e1683026..bdc2ef909b 100644 --- a/submodules/TelegramCore/Sources/State/Fetch.swift +++ b/submodules/TelegramCore/Sources/State/Fetch.swift @@ -22,8 +22,30 @@ private final class MediaResourceDataCopyFile : MediaResourceDataFetchCopyLocalI } } -public func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal { - return multipartFetch(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, networkStatsContext: account.networkStatsContext, resource: resource, datacenterId: datacenterId, size: size, intervals: intervals, parameters: parameters) +func fetchCloudMediaLocation( + accountPeerId: PeerId, + postbox: Postbox, + network: Network, + mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, + networkStatsContext: NetworkStatsContext, + resource: TelegramMediaResource, + datacenterId: Int, + size: Int64?, + intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, + parameters: MediaResourceFetchParameters? +) -> Signal { + return multipartFetch( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + resource: resource, + datacenterId: datacenterId, + size: size, + intervals: intervals, + parameters: parameters + ) } private func fetchLocalFileResource(path: String, move: Bool) -> Signal { @@ -40,21 +62,76 @@ private func fetchLocalFileResource(path: String, move: Bool) -> Signal, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal? { + return fetchResource( + accountPeerId: account.peerId, + postbox: account.postbox, + network: account.network, + mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, + networkStatsContext: account.networkStatsContext, + isTestingEnvironment: account.testingEnvironment, + resource: resource, + intervals: intervals, + parameters: parameters + ) +} + +func fetchResource( + accountPeerId: PeerId, + postbox: Postbox, + network: Network, + mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, + networkStatsContext: NetworkStatsContext, + isTestingEnvironment: Bool, + resource: MediaResource, + intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, + parameters: MediaResourceFetchParameters? +) -> Signal? { if let _ = resource as? EmptyMediaResource { return .single(.reset) |> then(.never()) } else if let secretFileResource = resource as? SecretFileMediaResource { return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) - |> then(fetchSecretFileResource(account: account, resource: secretFileResource, intervals: intervals, parameters: parameters)) + |> then(fetchSecretFileResource( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + resource: secretFileResource, + intervals: intervals, + parameters: parameters + )) } else if let cloudResource = resource as? TelegramMultipartFetchableResource { return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) - |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters)) + |> then(fetchCloudMediaLocation( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + resource: cloudResource, + datacenterId: cloudResource.datacenterId, + size: resource.size == 0 ? nil : resource.size, + intervals: intervals, + parameters: parameters + )) } else if let webFileResource = resource as? MediaResourceWithWebFileReference { - return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: account.testingEnvironment) + return currentWebDocumentsHostDatacenterId(postbox: postbox, isTestingEnvironment: isTestingEnvironment) |> castError(MediaResourceDataFetchError.self) |> mapToSignal { datacenterId -> Signal in return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) - |> then(fetchCloudMediaLocation(account: account, resource: webFileResource, datacenterId: Int(datacenterId), size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters)) + |> then(fetchCloudMediaLocation( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + resource: webFileResource, + datacenterId: Int(datacenterId), + size: resource.size == 0 ? nil : resource.size, + intervals: intervals, + parameters: parameters + )) } } else if let localFileResource = resource as? LocalFileReferenceMediaResource { return fetchLocalFileResource(path: localFileResource.localFilePath, move: localFileResource.isUniquelyReferencedTemporaryFile) @@ -63,7 +140,7 @@ func fetchResource(account: Account, resource: MediaResource, intervals: Signal< return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchHttpResource(url: httpReference.url)) } else if let wallpaperResource = resource as? WallpaperDataResource { - return getWallpaper(network: account.network, slug: wallpaperResource.slug) + return getWallpaper(network: network, slug: wallpaperResource.slug) |> mapError { _ -> MediaResourceDataFetchError in return .generic } @@ -75,13 +152,24 @@ func fetchResource(account: Account, resource: MediaResource, intervals: Signal< return .fail(.generic) } return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) - |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: MediaResourceFetchParameters( - tag: nil, - info: TelegramCloudMediaResourceFetchInfo(reference: .standalone(resource: file.file.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), - location: nil, - contentType: .other, - isRandomAccessAllowed: true - ))) + |> then(fetchCloudMediaLocation( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + resource: cloudResource, + datacenterId: cloudResource.datacenterId, + size: resource.size == 0 ? nil : resource.size, + intervals: intervals, + parameters: MediaResourceFetchParameters( + tag: nil, + info: TelegramCloudMediaResourceFetchInfo(reference: .standalone(resource: file.file.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), + location: nil, + contentType: .other, + isRandomAccessAllowed: true + ) + )) } } return nil diff --git a/submodules/TelegramCore/Sources/State/FetchSecretFileResource.swift b/submodules/TelegramCore/Sources/State/FetchSecretFileResource.swift index 7784f5ac42..011005fd32 100644 --- a/submodules/TelegramCore/Sources/State/FetchSecretFileResource.swift +++ b/submodules/TelegramCore/Sources/State/FetchSecretFileResource.swift @@ -3,7 +3,28 @@ import Postbox import SwiftSignalKit import MtProtoKit - -func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal { - return multipartFetch(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, networkStatsContext: account.networkStatsContext, resource: resource, datacenterId: resource.datacenterId, size: resource.size, intervals: intervals, parameters: parameters, encryptionKey: resource.key, decryptedSize: resource.decryptedSize) +func fetchSecretFileResource( + accountPeerId: PeerId, + postbox: Postbox, + network: Network, + mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, + networkStatsContext: NetworkStatsContext, + resource: SecretFileMediaResource, + intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, + parameters: MediaResourceFetchParameters? +) -> Signal { + return multipartFetch( + accountPeerId: accountPeerId, + postbox: postbox, + network: network, + mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, + networkStatsContext: networkStatsContext, + resource: resource, + datacenterId: resource.datacenterId, + size: resource.size, + intervals: intervals, + parameters: parameters, + encryptionKey: resource.key, + decryptedSize: resource.decryptedSize + ) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift index 01bb54a501..913d5b67da 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Contacts/TelegramEngineContacts.swift @@ -47,7 +47,7 @@ public extension TelegramEngine { } public func searchRemotePeers(query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { - return _internal_searchPeers(account: self.account, query: query) + return _internal_searchPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, query: query) } public func searchLocalPeers(query: String) -> Signal<[EngineRenderedPeer], NoError> { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift index bd57e5fa11..53924d3223 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift @@ -131,10 +131,10 @@ public extension TelegramEngine { public struct Item { } - private let account: Account + private let postbox: Postbox - init(account: Account) { - self.account = account + public init(postbox: Postbox) { + self.postbox = postbox } private func _subscribe(items: [AnyPostboxViewDataItem]) -> Signal<[Any], NoError> { @@ -144,7 +144,7 @@ public extension TelegramEngine { keys.insert(key) } } - return self.account.postbox.combinedView(keys: Array(keys)) + return self.postbox.combinedView(keys: Array(keys)) |> map { views -> [Any] in var results: [Any] = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift b/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift index 681f0dd9f7..8d48f8789e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift @@ -5,10 +5,12 @@ import TelegramApi public extension TelegramEngine { final class HistoryImport { - private let account: Account + private let postbox: Postbox + private let network: Network - init(account: Account) { - self.account = account + public init(postbox: Postbox, network: Network) { + self.postbox = postbox + self.network = network } public struct Session { @@ -37,7 +39,7 @@ public extension TelegramEngine { } public func getInfo(header: String) -> Signal { - return self.account.network.request(Api.functions.messages.checkHistoryImport(importHead: header)) + return self.network.request(Api.functions.messages.checkHistoryImport(importHead: header)) |> mapError { _ -> GetInfoError in return .generic } @@ -56,15 +58,16 @@ public extension TelegramEngine { } public func initSession(peerId: PeerId, file: TempBoxFile, mediaCount: Int32) -> Signal { - let account = self.account - return multipartUpload(network: self.account.network, postbox: self.account.postbox, source: .tempFile(file), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: true, useLargerParts: true, increaseParallelParts: true, useMultiplexedRequests: false, useCompression: true) + let postbox = self.postbox + let network = self.network + return multipartUpload(network: network, postbox: postbox, source: .tempFile(file), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: true, useLargerParts: true, increaseParallelParts: true, useMultiplexedRequests: false, useCompression: true) |> mapError { _ -> InitImportError in return .generic } |> mapToSignal { result -> Signal in switch result { case let .inputFile(inputFile): - return account.postbox.transaction { transaction -> Api.InputPeer? in + return postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } |> castError(InitImportError.self) @@ -72,7 +75,7 @@ public extension TelegramEngine { guard let inputPeer = inputPeer else { return .fail(.generic) } - return account.network.request(Api.functions.messages.initHistoryImport(peer: inputPeer, file: inputFile, mediaCount: mediaCount), automaticFloodWait: false) + return network.request(Api.functions.messages.initHistoryImport(peer: inputPeer, file: inputFile, mediaCount: mediaCount), automaticFloodWait: false) |> mapError { error -> InitImportError in if error.errorDescription == "CHAT_ADMIN_REQUIRED" { return .chatAdminRequired @@ -123,8 +126,9 @@ public extension TelegramEngine { forceNoBigParts = false } - let account = self.account - return multipartUpload(network: self.account.network, postbox: self.account.postbox, source: .tempFile(file), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: forceNoBigParts, useLargerParts: true, useMultiplexedRequests: true) + let postbox = self.postbox + let network = self.network + return multipartUpload(network: network, postbox: postbox, source: .tempFile(file), encrypt: false, tag: nil, hintFileSize: nil, hintFileIsLarge: false, forceNoBigParts: forceNoBigParts, useLargerParts: true, useMultiplexedRequests: true) |> mapError { _ -> UploadMediaError in return .generic } @@ -156,7 +160,7 @@ public extension TelegramEngine { case .inputSecretFile: return .fail(.generic) } - return account.network.request(Api.functions.messages.uploadImportedMedia(peer: session.inputPeer, importId: session.id, fileName: fileName, media: inputMedia)) + return network.request(Api.functions.messages.uploadImportedMedia(peer: session.inputPeer, importId: session.id, fileName: fileName, media: inputMedia)) |> mapError { error -> UploadMediaError in switch error.errorDescription { case "CHAT_ADMIN_REQUIRED": @@ -181,7 +185,7 @@ public extension TelegramEngine { } public func startImport(session: Session) -> Signal { - return self.account.network.request(Api.functions.messages.startHistoryImport(peer: session.inputPeer, importId: session.id)) + return self.network.request(Api.functions.messages.startHistoryImport(peer: session.inputPeer, importId: session.id)) |> mapError { _ -> StartImportError in return .generic } @@ -209,8 +213,9 @@ public extension TelegramEngine { } public func checkPeerImport(peerId: PeerId) -> Signal { - let account = self.account - return self.account.postbox.transaction { transaction -> Peer? in + let postbox = self.postbox + let network = self.network + return postbox.transaction { transaction -> Peer? in return transaction.getPeer(peerId) } |> castError(CheckPeerImportError.self) @@ -222,7 +227,7 @@ public extension TelegramEngine { return .fail(.generic) } - return account.network.request(Api.functions.messages.checkHistoryImportPeer(peer: inputPeer)) + return network.request(Api.functions.messages.checkHistoryImportPeer(peer: inputPeer)) |> mapError { error -> CheckPeerImportError in if error.errorDescription == "CHAT_ADMIN_REQUIRED" { return .chatAdminRequired diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ExportMessageLink.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ExportMessageLink.swift index 2be0e7f592..1d64aec790 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ExportMessageLink.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ExportMessageLink.swift @@ -3,8 +3,8 @@ import Postbox import TelegramApi import SwiftSignalKit -func _internal_exportMessageLink(account: Account, peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal { - return account.postbox.transaction { transaction -> (Peer, MessageId)? in +public func _internal_exportMessageLink(postbox: Postbox, network: Network, peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal { + return postbox.transaction { transaction -> (Peer, MessageId)? in let peer: Peer? = transaction.getPeer(messageId.peerId) if let peer = peer { return (peer, messageId) @@ -22,7 +22,7 @@ func _internal_exportMessageLink(account: Account, peerId: PeerId, messageId: Me if isThread { flags |= 1 << 1 } - return account.network.request(Api.functions.channels.exportMessageLink(flags: flags, channel: input, id: sourceMessageId.id)) |> mapError { _ in return } + return network.request(Api.functions.channels.exportMessageLink(flags: flags, channel: input, id: sourceMessageId.id)) |> mapError { _ in return } |> map { res in switch res { case let .exportedMessageLink(link, _): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 637b49fa0e..640594f509 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -217,7 +217,7 @@ public extension TelegramEngine { } public func exportMessageLink(peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal { - return _internal_exportMessageLink(account: self.account, peerId: peerId, messageId: messageId, isThread: isThread) + return _internal_exportMessageLink(postbox: self.account.postbox, network: self.account.network, peerId: peerId, messageId: messageId, isThread: isThread) } public func enqueueOutgoingMessage( diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelCreation.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelCreation.swift index c197c42061..554c91f901 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelCreation.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelCreation.swift @@ -17,8 +17,8 @@ public enum CreateChannelMode { case supergroup(isForum: Bool) } -private func createChannel(account: Account, title: String, description: String?, username: String?, mode: CreateChannelMode, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal { - return account.postbox.transaction { transaction -> Signal in +private func createChannel(postbox: Postbox, network: Network, stateManager: AccountStateManager, title: String, description: String?, username: String?, mode: CreateChannelMode, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal { + return postbox.transaction { transaction -> Signal in var flags: Int32 = 0 switch mode { case .channel: @@ -43,7 +43,7 @@ private func createChannel(account: Account, title: String, description: String? transaction.clearItemCacheCollection(collectionId: Namespaces.CachedItemCollection.cachedGroupCallDisplayAsPeers) - return account.network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address, ttlPeriod: nil), automaticFloodWait: false) + return network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address, ttlPeriod: nil), automaticFloodWait: false) |> mapError { error -> CreateChannelError in if error.errorCode == 406 { return .serverProvided(error.errorDescription) @@ -58,9 +58,9 @@ private func createChannel(account: Account, title: String, description: String? } } |> mapToSignal { updates -> Signal in - account.stateManager.addUpdates(updates) + stateManager.addUpdates(updates) if let message = updates.messages.first, let peerId = apiMessagePeerId(message) { - return account.postbox.multiplePeersView([peerId]) + return postbox.multiplePeersView([peerId]) |> filter { view in return view.peers[peerId] != nil } @@ -72,7 +72,7 @@ private func createChannel(account: Account, title: String, description: String? |> timeout(5.0, queue: Queue.concurrentDefaultQueue(), alternate: .fail(.generic)) |> mapToSignal { peerId -> Signal in if title.contains("*forum") { - return _internal_setChannelForumMode(account: account, peerId: peerId, isForum: true) + return _internal_setChannelForumMode(postbox: postbox, network: network, stateManager: stateManager, peerId: peerId, isForum: true) |> castError(CreateChannelError.self) |> map { _ -> PeerId in } @@ -91,11 +91,11 @@ private func createChannel(account: Account, title: String, description: String? } func _internal_createChannel(account: Account, title: String, description: String?, username: String?) -> Signal { - return createChannel(account: account, title: title, description: description, username: nil, mode: .channel) + return createChannel(postbox: account.postbox, network: account.network, stateManager: account.stateManager, title: title, description: description, username: nil, mode: .channel) } -func _internal_createSupergroup(account: Account, title: String, description: String?, username: String?, isForum: Bool, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal { - return createChannel(account: account, title: title, description: description, username: username, mode: .supergroup(isForum: isForum), location: location, isForHistoryImport: isForHistoryImport) +public func _internal_createSupergroup(postbox: Postbox, network: Network, stateManager: AccountStateManager, title: String, description: String?, username: String?, isForum: Bool, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal { + return createChannel(postbox: postbox, network: network, stateManager: stateManager, title: title, description: description, username: username, mode: .supergroup(isForum: isForum), location: location, isForHistoryImport: isForHistoryImport) } public enum DeleteChannelError { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift index 74b375c562..28598bd218 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentPeers.swift @@ -12,17 +12,17 @@ func cachedRecentPeersEntryId() -> ItemCacheEntryId { return ItemCacheEntryId(collectionId: 101, key: CachedRecentPeers.cacheKey()) } -func _internal_recentPeers(account: Account) -> Signal { +public func _internal_recentPeers(accountPeerId: EnginePeer.Id, postbox: Postbox) -> Signal { let key = PostboxViewKey.cachedItem(cachedRecentPeersEntryId()) - return account.postbox.combinedView(keys: [key]) + return postbox.combinedView(keys: [key]) |> mapToSignal { views -> Signal in if let value = (views.views[key] as? CachedItemView)?.value?.get(CachedRecentPeers.self) { if value.enabled { - return account.postbox.multiplePeersView(value.ids) + return postbox.multiplePeersView(value.ids) |> map { view -> RecentPeers in var peers: [Peer] = [] for id in value.ids { - if let peer = view.peers[id], id != account.peerId { + if let peer = view.peers[id], id != accountPeerId { peers.append(peer) } } @@ -44,7 +44,7 @@ public func _internal_getRecentPeers(transaction: Transaction) -> [PeerId] { return entry.ids } -func _internal_managedUpdatedRecentPeers(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { +public func _internal_managedUpdatedRecentPeers(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { let key = PostboxViewKey.cachedItem(cachedRecentPeersEntryId()) let peersEnabled = postbox.combinedView(keys: [key]) |> map { views -> Bool in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentlySearchedPeerIds.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentlySearchedPeerIds.swift index 56cf52a787..3b1bc04dcf 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentlySearchedPeerIds.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RecentlySearchedPeerIds.swift @@ -35,7 +35,7 @@ public struct RecentlySearchedPeer: Equatable { public let subpeerSummary: RecentlySearchedPeerSubpeerSummary? } -func _internal_recentlySearchedPeers(postbox: Postbox) -> Signal<[RecentlySearchedPeer], NoError> { +public func _internal_recentlySearchedPeers(postbox: Postbox) -> Signal<[RecentlySearchedPeer], NoError> { return postbox.combinedView(keys: [.orderedItemList(id: Namespaces.OrderedItemList.RecentlySearchedPeerIds)]) |> mapToSignal { view -> Signal<[RecentlySearchedPeer], NoError> in var peerIds: [PeerId] = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index a4bedb6de6..b1c40319bd 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -19,10 +19,8 @@ public struct FoundPeer: Equatable { } } -func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { - let accountPeerId = account.peerId - - let searchResult = account.network.request(Api.functions.contacts.search(q: query, limit: 20), automaticFloodWait: false) +public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, network: Network, query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { + let searchResult = network.request(Api.functions.contacts.search(q: query, limit: 20), automaticFloodWait: false) |> map(Optional.init) |> `catch` { _ in return Signal.single(nil) @@ -32,7 +30,7 @@ func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPee if let result = result { switch result { case let .found(myResults, results, chats, users): - return account.postbox.transaction { transaction -> ([FoundPeer], [FoundPeer]) in + return postbox.transaction { transaction -> ([FoundPeer], [FoundPeer]) in var subscribers: [PeerId: Int32] = [:] let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 6875dfa3bf..bfa6899af4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -343,7 +343,7 @@ public extension TelegramEngine { } public func createSupergroup(title: String, description: String?, username: String? = nil, isForum: Bool = false, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false) -> Signal { - return _internal_createSupergroup(account: self.account, title: title, description: description, username: username, isForum: isForum, location: location, isForHistoryImport: isForHistoryImport) + return _internal_createSupergroup(postbox: self.account.postbox, network: self.account.network, stateManager: account.stateManager, title: title, description: description, username: username, isForum: isForum, location: location, isForHistoryImport: isForHistoryImport) } public func deleteChannel(peerId: PeerId) -> Signal { @@ -502,7 +502,7 @@ public extension TelegramEngine { } public func recentPeers() -> Signal { - return _internal_recentPeers(account: self.account) + return _internal_recentPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox) } public func managedUpdatedRecentPeers() -> Signal { @@ -926,7 +926,7 @@ public extension TelegramEngine { } public func ensurePeerIsLocallyAvailable(peer: EnginePeer) -> Signal { - return _internal_storedMessageFromSearchPeer(account: self.account, peer: peer._asPeer()) + return _internal_storedMessageFromSearchPeer(postbox: self.account.postbox, peer: peer._asPeer()) |> map { result -> EnginePeer in return EnginePeer(result) } @@ -978,7 +978,7 @@ public extension TelegramEngine { } public func setChannelForumMode(id: EnginePeer.Id, isForum: Bool) -> Signal { - return _internal_setChannelForumMode(account: self.account, peerId: id, isForum: isForum) + return _internal_setChannelForumMode(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: id, isForum: isForum) } public func createForumChannelTopic(id: EnginePeer.Id, title: String, iconColor: Int32, iconFileId: Int64?) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index 017a5c1683..264528a33e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -193,7 +193,7 @@ public extension TelegramEngine { } } -func _internal_resolveInlineStickers(postbox: Postbox, network: Network, fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> { +public func _internal_resolveInlineStickers(postbox: Postbox, network: Network, fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> { return postbox.transaction { transaction -> [Int64: TelegramMediaFile] in var cachedFiles: [Int64: TelegramMediaFile] = [:] for fileId in fileIds { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift b/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift index f6660a6f11..01ada4bf65 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift @@ -57,7 +57,7 @@ public final class TelegramEngine { }() public lazy var historyImport: HistoryImport = { - return HistoryImport(account: self.account) + return HistoryImport(postbox: self.account.postbox, network: self.account.network) }() public lazy var contacts: Contacts = { @@ -73,7 +73,7 @@ public final class TelegramEngine { }() public lazy var data: EngineData = { - return EngineData(account: self.account) + return EngineData(postbox: self.account.postbox) }() public lazy var orderedLists: OrderedLists = { diff --git a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift index 949b95a37c..6362c49c48 100644 --- a/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift +++ b/submodules/TelegramCore/Sources/Utils/StoredMessageFromSearchPeer.swift @@ -2,8 +2,8 @@ import Foundation import Postbox import SwiftSignalKit -func _internal_storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal { - return account.postbox.transaction { transaction -> Peer in +public func _internal_storedMessageFromSearchPeer(postbox: Postbox, peer: Peer) -> Signal { + return postbox.transaction { transaction -> Peer in if transaction.getPeer(peer.id) == nil { updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in return updatedPeer diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 794b2bfcdf..d99d78209f 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -379,6 +379,11 @@ swift_library( "//submodules/TelegramUI/Components/PeerReportScreen", "//submodules/Utils/VolumeButtons", "//submodules/ChatContextQuery", + "//submodules/TelegramUI/Components/TelegramUIDeclareEncodables", + "//submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods", + "//submodules/TelegramUI/Components/PeerSelectionController", + "//submodules/TelegramUI/Components/Chat/AccessoryPanelNode", + "//submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode", ] + select({ "@build_bazel_rules_apple//apple:ios_armv7": [], "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, diff --git a/submodules/TelegramUI/Components/Chat/AccessoryPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/AccessoryPanelNode/BUILD new file mode 100644 index 0000000000..c894723555 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/AccessoryPanelNode/BUILD @@ -0,0 +1,20 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AccessoryPanelNode", + module_name = "AccessoryPanelNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/ChatPresentationInterfaceState", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Chat/AccessoryPanelNode/Sources/AccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/AccessoryPanelNode/Sources/AccessoryPanelNode.swift new file mode 100644 index 0000000000..17ff62255f --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/AccessoryPanelNode/Sources/AccessoryPanelNode.swift @@ -0,0 +1,23 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import ChatPresentationInterfaceState + +open class AccessoryPanelNode: ASDisplayNode { + open var originalFrameBeforeDismissed: CGRect? + open var dismiss: (() -> Void)? + open var interfaceInteraction: ChatPanelInterfaceInteraction? + + open func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + } + + open func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) { + } + + open func animateIn() { + } + + open func animateOut() { + } +} diff --git a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD new file mode 100644 index 0000000000..0faa548442 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD @@ -0,0 +1,36 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ForwardAccessoryPanelNode", + module_name = "ForwardAccessoryPanelNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Display", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/AccountContext", + "//submodules/LocalizedPeerData", + "//submodules/AlertUI", + "//submodules/PresentationDataUtils", + "//submodules/TextFormat", + "//submodules/Markdown", + "//submodules/TelegramNotices", + "//submodules/ChatPresentationInterfaceState", + "//submodules/TelegramUI/Components/TextNodeWithEntities", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/TelegramUI/Components/Chat/AccessoryPanelNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift similarity index 94% rename from submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift rename to submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift index efc8bb8395..f04add6746 100644 --- a/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift @@ -18,6 +18,7 @@ import ChatPresentationInterfaceState import TextNodeWithEntities import AnimationCache import MultiAnimationRenderer +import AccessoryPanelNode func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (text: String, entities: [MessageTextEntity], isMedia: Bool) { for media in message.media { @@ -81,9 +82,9 @@ func textStringForForwardedMessage(_ message: Message, strings: PresentationStri return (message.text, message.textEntitiesAttribute?.entities ?? [], false) } -final class ForwardAccessoryPanelNode: AccessoryPanelNode { +public final class ForwardAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() - let messageIds: [MessageId] + public let messageIds: [MessageId] private var messages: [Message] = [] private var authors: String? private var sourcePeer: (peerId: PeerId, displayTitle: String)? @@ -106,7 +107,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { private var validLayout: (size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState)? - init(context: AccountContext, messageIds: [MessageId], theme: PresentationTheme, strings: PresentationStrings, fontSize: PresentationFontSize, nameDisplayOrder: PresentationPersonNameOrder, forwardOptionsState: ChatInterfaceForwardOptionsState?, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { + public init(context: AccountContext, messageIds: [MessageId], theme: PresentationTheme, strings: PresentationStrings, fontSize: PresentationFontSize, nameDisplayOrder: PresentationPersonNameOrder, forwardOptionsState: ChatInterfaceForwardOptionsState?, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { self.context = context self.messageIds = messageIds self.theme = theme @@ -275,25 +276,25 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.messageDisposable.dispose() } - override func didLoad() { + override public func didLoad() { super.didLoad() self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) } - override func animateIn() { + override public func animateIn() { self.iconNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) } - override func animateOut() { + override public func animateOut() { self.iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) } - override func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + override public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { self.updateThemeAndStrings(theme: theme, strings: strings, forwardOptionsState: self.forwardOptionsState) } - func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, forwardOptionsState: ChatInterfaceForwardOptionsState?, force: Bool = false) { + public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, forwardOptionsState: ChatInterfaceForwardOptionsState?, force: Bool = false) { if force || self.theme !== theme || self.strings !== strings || self.forwardOptionsState != forwardOptionsState { self.theme = theme self.strings = strings @@ -327,11 +328,11 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { } } - override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { return CGSize(width: constrainedSize.width, height: 45.0) } - override func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) { + override public func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) { self.validLayout = (size, inset, interfaceState) let bounds = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 45.0)) @@ -360,7 +361,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 25.0), size: textSize) } - @objc func closePressed() { + @objc private func closePressed() { guard let (peerId, peerDisplayTitle) = self.sourcePeer else { return } @@ -399,7 +400,7 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode { } private var previousTapTimestamp: Double? - @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { let timestamp = CFAbsoluteTimeGetCurrent() if let previousTapTimestamp = self.previousTapTimestamp, previousTapTimestamp + 1.0 > timestamp { diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 6f36589a07..720a374434 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -2040,7 +2040,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { }))) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceView: sourceView, sourceRect: sourceRect)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.interaction?.presentGlobalOverlayController(contextController, nil) }) } diff --git a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift index 841391d83b..918bf280aa 100644 --- a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift +++ b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift @@ -638,7 +638,6 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { let items = ContextController.Items(content: .list(itemList)) let controller = ContextController( - account: component.context.account, presentationData: presentationData, source: .extracted(LinkListContextExtractedContentSource(contentView: sourceView)), items: .single(items), diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/BUILD b/submodules/TelegramUI/Components/EmojiStatusComponent/BUILD index 946cb68b8c..ac13c220d9 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/BUILD +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/BUILD @@ -24,6 +24,7 @@ swift_library( "//submodules/TextFormat:TextFormat", "//submodules/lottie-ios:Lottie", "//submodules/GZip:GZip", + "//submodules/TelegramUIPreferences", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift index 97b2dcc296..fdcc76a0dd 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift @@ -14,6 +14,7 @@ import TextFormat import Lottie import GZip import HierarchyTrackingLayer +import TelegramUIPreferences public final class EmojiStatusComponent: Component { public typealias EnvironmentType = Empty @@ -52,7 +53,9 @@ public final class EmojiStatusComponent: Component { case image(image: UIImage?) } - public let context: AccountContext + public let postbox: Postbox + public let energyUsageSettings: EnergyUsageSettings + public let resolveInlineStickers: ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> public let animationCache: AnimationCache public let animationRenderer: MultiAnimationRenderer public let content: Content @@ -61,7 +64,7 @@ public final class EmojiStatusComponent: Component { public let action: (() -> Void)? public let emojiFileUpdated: ((TelegramMediaFile?) -> Void)? - public init( + public convenience init( context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, @@ -71,7 +74,37 @@ public final class EmojiStatusComponent: Component { action: (() -> Void)?, emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil ) { - self.context = context + self.init( + postbox: context.account.postbox, + energyUsageSettings: context.sharedContext.energyUsageSettings, + resolveInlineStickers: { fileIds in + return context.engine.stickers.resolveInlineStickers(fileIds: fileIds) + }, + animationCache: animationCache, + animationRenderer: animationRenderer, + content: content, + isVisibleForAnimations: isVisibleForAnimations, + useSharedAnimation: useSharedAnimation, + action: action, + emojiFileUpdated: emojiFileUpdated + ) + } + + public init( + postbox: Postbox, + energyUsageSettings: EnergyUsageSettings, + resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + content: Content, + isVisibleForAnimations: Bool, + useSharedAnimation: Bool = false, + action: (() -> Void)?, + emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil + ) { + self.postbox = postbox + self.energyUsageSettings = energyUsageSettings + self.resolveInlineStickers = resolveInlineStickers self.animationCache = animationCache self.animationRenderer = animationRenderer self.content = content @@ -83,7 +116,9 @@ public final class EmojiStatusComponent: Component { public func withVisibleForAnimations(_ isVisibleForAnimations: Bool) -> EmojiStatusComponent { return EmojiStatusComponent( - context: self.context, + postbox: self.postbox, + energyUsageSettings: self.energyUsageSettings, + resolveInlineStickers: self.resolveInlineStickers, animationCache: self.animationCache, animationRenderer: self.animationRenderer, content: self.content, @@ -95,7 +130,10 @@ public final class EmojiStatusComponent: Component { } public static func ==(lhs: EmojiStatusComponent, rhs: EmojiStatusComponent) -> Bool { - if lhs.context !== rhs.context { + if lhs.postbox !== rhs.postbox { + return false + } + if lhs.energyUsageSettings != rhs.energyUsageSettings { return false } if lhs.animationCache !== rhs.animationCache { @@ -419,7 +457,15 @@ public final class EmojiStatusComponent: Component { loopCount = value } animationLayer = InlineStickerItemLayer( - context: component.context, + context: .custom(InlineStickerItemLayer.Context.Custom( + postbox: component.postbox, + energyUsageSettings: { + return component.energyUsageSettings + }, + resolveInlineStickers: { fileIds in + return component.resolveInlineStickers(fileIds) + } + )), userLocation: .other, attemptSynchronousLoad: false, emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiFile.fileId.id, file: emojiFile), @@ -508,7 +554,7 @@ public final class EmojiStatusComponent: Component { }*/ } else { if self.emojiFileDisposable == nil { - self.emojiFileDisposable = (component.context.engine.stickers.resolveInlineStickers(fileIds: [emojiFileId]) + self.emojiFileDisposable = (component.resolveInlineStickers([emojiFileId]) |> deliverOnMainQueue).start(next: { [weak self] result in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD b/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD index 37de259bd2..225ce0e5a6 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/BUILD @@ -24,6 +24,7 @@ swift_library( "//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", "//submodules/ShimmerEffect:ShimmerEffect", + "//submodules/TelegramUIPreferences", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index 97b5bf614e..2b7f91ec4f 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -15,6 +15,7 @@ import VideoAnimationCache import MultiAnimationRenderer import ShimmerEffect import TextFormat +import TelegramUIPreferences public func generateTopicIcon(title: String, backgroundColors: [UIColor], strokeColors: [UIColor], size: CGSize) -> UIImage? { let realSize = size @@ -97,8 +98,20 @@ public extension AnimationCacheAnimationType { } public func animationCacheFetchFile(context: AccountContext, userLocation: MediaResourceUserLocation, userContentType: MediaResourceUserContentType, resource: MediaResourceReference, type: AnimationCacheAnimationType, keyframeOnly: Bool, customColor: UIColor?) -> (AnimationCacheFetchOptions) -> Disposable { + return animationCacheFetchFile( + postbox: context.account.postbox, + userLocation: userLocation, + userContentType: userContentType, + resource: resource, + type: type, + keyframeOnly: keyframeOnly, + customColor: customColor + ) +} + +public func animationCacheFetchFile(postbox: Postbox, userLocation: MediaResourceUserLocation, userContentType: MediaResourceUserContentType, resource: MediaResourceReference, type: AnimationCacheAnimationType, keyframeOnly: Bool, customColor: UIColor?) -> (AnimationCacheFetchOptions) -> Disposable { return { options in - let source = AnimatedStickerResourceSource(account: context.account, resource: resource.resource, fitzModifier: nil, isVideo: false) + let source = AnimatedStickerResourceSource(postbox: postbox, resource: resource.resource, fitzModifier: nil, isVideo: false) let dataDisposable = source.directDataPath(attemptSynchronously: false).start(next: { result in guard let result = result else { @@ -119,7 +132,7 @@ public func animationCacheFetchFile(context: AccountContext, userLocation: Media } }) - let fetchDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: userLocation, userContentType: userContentType, reference: resource).start() + let fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: userContentType, reference: resource).start() return ActionDisposable { dataDisposable.dispose() @@ -129,6 +142,74 @@ public func animationCacheFetchFile(context: AccountContext, userLocation: Media } public final class InlineStickerItemLayer: MultiAnimationRenderTarget { + public enum Context: Equatable { + public final class Custom: Equatable { + public let postbox: Postbox + public let energyUsageSettings: () -> EnergyUsageSettings + public let resolveInlineStickers: ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> + + public init(postbox: Postbox, energyUsageSettings: @escaping () -> EnergyUsageSettings, resolveInlineStickers: @escaping ([Int64]) -> Signal<[Int64: TelegramMediaFile], NoError>) { + self.postbox = postbox + self.energyUsageSettings = energyUsageSettings + self.resolveInlineStickers = resolveInlineStickers + } + + public static func ==(lhs: Custom, rhs: Custom) -> Bool { + if lhs.postbox !== rhs.postbox { + return false + } + return true + } + } + + case account(AccountContext) + case custom(Custom) + + var postbox: Postbox { + switch self { + case let .account(account): + return account.account.postbox + case let .custom(custom): + return custom.postbox + } + } + + var energyUsageSettings: EnergyUsageSettings { + switch self { + case let .account(account): + return account.sharedContext.energyUsageSettings + case let .custom(custom): + return custom.energyUsageSettings() + } + } + + func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> { + switch self { + case let .account(account): + return account.engine.stickers.resolveInlineStickers(fileIds: fileIds) + case let .custom(custom): + return custom.resolveInlineStickers(fileIds) + } + } + + public static func ==(lhs: Context, rhs: Context) -> Bool { + switch lhs { + case let .account(lhsContext): + if case let .account(rhsContext) = rhs, lhsContext === rhsContext { + return true + } else { + return false + } + case let .custom(custom): + if case .custom(custom) = rhs { + return true + } else { + return false + } + } + } + } + public static let queue = Queue() public struct Key: Hashable { @@ -141,7 +222,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } } - private let context: AccountContext + private let context: InlineStickerItemLayer.Context private let userLocation: MediaResourceUserLocation private let emoji: ChatTextInputTextCustomEmojiAttribute private let cache: AnimationCache @@ -189,7 +270,24 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } } - public init(context: AccountContext, userLocation: MediaResourceUserLocation, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool = false, placeholderColor: UIColor, pointSize: CGSize, dynamicColor: UIColor? = nil, loopCount: Int? = nil) { + public convenience init(context: AccountContext, userLocation: MediaResourceUserLocation, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool = false, placeholderColor: UIColor, pointSize: CGSize, dynamicColor: UIColor? = nil, loopCount: Int? = nil) { + self.init( + context: .account(context), + userLocation: userLocation, + attemptSynchronousLoad: attemptSynchronousLoad, + emoji: emoji, + file: file, + cache: cache, + renderer: renderer, + unique: unique, + placeholderColor: placeholderColor, + pointSize: pointSize, + dynamicColor: dynamicColor, + loopCount: loopCount + ) + } + + public init(context: InlineStickerItemLayer.Context, userLocation: MediaResourceUserLocation, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, unique: Bool = false, placeholderColor: UIColor, pointSize: CGSize, dynamicColor: UIColor? = nil, loopCount: Int? = nil) { self.context = context self.userLocation = userLocation self.emoji = emoji @@ -211,7 +309,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } else if let file = file { self.updateFile(file: file, attemptSynchronousLoad: attemptSynchronousLoad) } else { - self.infoDisposable = (context.engine.stickers.resolveInlineStickers(fileIds: [emoji.fileId]) + self.infoDisposable = (context.resolveInlineStickers(fileIds: [emoji.fileId]) |> deliverOnMainQueue).start(next: { [weak self] files in guard let strongSelf = self else { return @@ -335,7 +433,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { let pointSize = self.pointSize let placeholderColor = self.placeholderColor let isThumbnailCancelled = Atomic(value: false) - self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(context: self.context, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true, customColor: isTemplate ? .white : nil), completion: { [weak self] result, isFinal in + self.loadDisposable = self.renderer.loadFirstFrame(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: animationCacheFetchFile(postbox: self.context.postbox, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: true, customColor: isTemplate ? .white : nil), completion: { [weak self] result, isFinal in if !result { MultiAnimationRendererImpl.firstFrameQueue.async { let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: pointSize, scale: min(2.0, UIScreenScale), imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: placeholderColor) @@ -377,10 +475,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { if file.isAnimatedSticker || file.isVideoSticker || file.isVideoEmoji { let keyframeOnly = self.pixelSize.width >= 120.0 - self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly, customColor: isTemplate ? .white : nil)) + self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: animationCacheFetchFile(postbox: self.context.postbox, userLocation: self.userLocation, userContentType: .sticker, resource: .media(media: .standalone(media: file), resource: file.resource), type: AnimationCacheAnimationType(file: file), keyframeOnly: keyframeOnly, customColor: isTemplate ? .white : nil)) } else { self.disposable = renderer.add(target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, unique: self.unique, size: self.pixelSize, fetch: { options in - let dataDisposable = context.account.postbox.mediaBox.resourceData(file.resource).start(next: { result in + let dataDisposable = context.postbox.mediaBox.resourceData(file.resource).start(next: { result in guard result.complete else { return } @@ -388,7 +486,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { cacheStillSticker(path: result.path, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, customColor: isTemplate ? .white : nil) }) - let fetchDisposable = freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: self.userLocation, fileReference: .customEmoji(media: file), resource: file.resource).start() + let fetchDisposable = freeMediaFileResourceInteractiveFetched(postbox: context.postbox, userLocation: self.userLocation, fileReference: .customEmoji(media: file), resource: file.resource).start() return ActionDisposable { dataDisposable.dispose() @@ -447,13 +545,26 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { public final class EmojiTextAttachmentView: UIView { private let contentLayer: InlineStickerItemLayer - public init(context: AccountContext, userLocation: MediaResourceUserLocation, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor, pointSize: CGSize) { + public convenience init(context: AccountContext, userLocation: MediaResourceUserLocation, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor, pointSize: CGSize) { + self.init( + context: .account(context), + userLocation: userLocation, + emoji: emoji, + file: file, + cache: cache, + renderer: renderer, + placeholderColor: placeholderColor, + pointSize: pointSize + ) + } + + public init(context: InlineStickerItemLayer.Context, userLocation: MediaResourceUserLocation, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor, pointSize: CGSize) { self.contentLayer = InlineStickerItemLayer(context: context, userLocation: userLocation, attemptSynchronousLoad: true, emoji: emoji, file: file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: pointSize) super.init(frame: CGRect()) self.layer.addSublayer(self.contentLayer) - self.contentLayer.isVisibleForAnimations = context.sharedContext.energyUsageSettings.loopEmoji + self.contentLayer.isVisibleForAnimations = context.energyUsageSettings.loopEmoji } required public init?(coder: NSCoder) { diff --git a/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift b/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift index efcdbc69b1..a9c14e92e5 100644 --- a/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift +++ b/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift @@ -244,13 +244,16 @@ public func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedM }) if let parentController = parentController { parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, _, text, account, silently in - return legacyAssetPickerEnqueueMessages(context: context, account: account, signals: signals!) + guard let account = account as? ShareControllerAppAccountContext else { + return .single(.done) + } + return legacyAssetPickerEnqueueMessages(context: context, account: account.context.account, signals: signals!) |> `catch` { _ -> Signal<[LegacyAssetPickerEnqueueMessage], ShareControllerError> in return .single([]) } |> mapToSignal { messages -> Signal in let resultSignals = peerIds.map({ peerId in - return enqueueMessages(account: account, peerId: peerId, messages: messages.map { $0.message }) + return enqueueMessages(account: account.context.account, peerId: peerId, messages: messages.map { $0.message }) |> castError(ShareControllerError.self) |> mapToSignal { _ -> Signal in return .complete() diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index a66568f0fb..35c983c2a2 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -7,6 +7,7 @@ import MetalKit import Display import SwiftSignalKit import TelegramCore +import Postbox public func mediaEditorGenerateGradientImage(size: CGSize, colors: [UIColor]) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, 1.0) @@ -54,7 +55,7 @@ final class MediaEditorComposer { private let drawingImage: CIImage? private var entities: [MediaEditorComposerEntity] - init(account: Account, values: MediaEditorValues, dimensions: CGSize, outputDimensions: CGSize, textScale: CGFloat) { + init(postbox: Postbox, values: MediaEditorValues, dimensions: CGSize, outputDimensions: CGSize, textScale: CGFloat) { self.values = values self.dimensions = dimensions self.outputDimensions = outputDimensions @@ -79,7 +80,7 @@ final class MediaEditorComposer { var entities: [MediaEditorComposerEntity] = [] for entity in values.entities { - entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, textScale: textScale, entity: entity.entity, colorSpace: colorSpace)) + entities.append(contentsOf: composerEntitiesForDrawingEntity(postbox: postbox, textScale: textScale, entity: entity.entity, colorSpace: colorSpace)) } self.entities = entities @@ -182,7 +183,7 @@ final class MediaEditorComposer { } } -public func makeEditorImageComposition(context: CIContext, account: Account, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, textScale: CGFloat, completion: @escaping (UIImage?) -> Void) { +public func makeEditorImageComposition(context: CIContext, postbox: Postbox, inputImage: UIImage, dimensions: CGSize, values: MediaEditorValues, time: CMTime, textScale: CGFloat, completion: @escaping (UIImage?) -> Void) { let colorSpace = CGColorSpaceCreateDeviceRGB() let inputImage = CIImage(image: inputImage, options: [.colorSpace: colorSpace])! let gradientImage: CIImage @@ -199,7 +200,7 @@ public func makeEditorImageComposition(context: CIContext, account: Account, inp var entities: [MediaEditorComposerEntity] = [] for entity in values.entities { - entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, textScale: textScale, entity: entity.entity, colorSpace: colorSpace)) + entities.append(contentsOf: composerEntitiesForDrawingEntity(postbox: postbox, textScale: textScale, entity: entity.entity, colorSpace: colorSpace)) } makeEditorImageFrameComposition(context: context, inputImage: inputImage, gradientImage: gradientImage, drawingImage: drawingImage, dimensions: dimensions, outputDimensions: dimensions, values: values, entities: entities, time: time, textScale: textScale, completion: { ciImage in diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index 380bb6da7c..e858552819 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -7,6 +7,7 @@ import MetalKit import Display import SwiftSignalKit import TelegramCore +import Postbox import AnimatedStickerNode import TelegramAnimatedStickerNode import YuvConversion @@ -60,7 +61,7 @@ private func prerenderTextTransformations(entity: DrawingEntity, image: UIImage, return MediaEditorComposerStaticEntity(image: CIImage(image: newImage, options: [.colorSpace: colorSpace])!, position: position, scale: 1.0, rotation: 0.0, baseSize: nil, baseDrawingSize: CGSize(width: 1080, height: 1920), mirrored: false) } -func composerEntitiesForDrawingEntity(account: Account, textScale: CGFloat, entity: DrawingEntity, colorSpace: CGColorSpace, tintColor: UIColor? = nil) -> [MediaEditorComposerEntity] { +func composerEntitiesForDrawingEntity(postbox: Postbox, textScale: CGFloat, entity: DrawingEntity, colorSpace: CGColorSpace, tintColor: UIColor? = nil) -> [MediaEditorComposerEntity] { if let entity = entity as? DrawingStickerEntity { let content: MediaEditorComposerStickerEntity.Content switch entity.content { @@ -73,7 +74,7 @@ func composerEntitiesForDrawingEntity(account: Account, textScale: CGFloat, enti case .dualVideoReference: return [] } - return [MediaEditorComposerStickerEntity(account: account, content: content, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: entity.baseSize, mirrored: entity.mirrored, colorSpace: colorSpace, tintColor: tintColor, isStatic: entity.isExplicitlyStatic)] + return [MediaEditorComposerStickerEntity(postbox: postbox, content: content, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: entity.baseSize, mirrored: entity.mirrored, colorSpace: colorSpace, tintColor: tintColor, isStatic: entity.isExplicitlyStatic)] } else if let renderImage = entity.renderImage, let image = CIImage(image: renderImage, options: [.colorSpace: colorSpace]) { if let entity = entity as? DrawingBubbleEntity { return [MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: 1.0, rotation: entity.rotation, baseSize: entity.size, mirrored: false)] @@ -86,7 +87,7 @@ func composerEntitiesForDrawingEntity(account: Account, textScale: CGFloat, enti entities.append(prerenderTextTransformations(entity: entity, image: renderImage, textScale: textScale, colorSpace: colorSpace)) if let renderSubEntities = entity.renderSubEntities { for subEntity in renderSubEntities { - entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, textScale: textScale, entity: subEntity, colorSpace: colorSpace, tintColor: entity.color.toUIColor())) + entities.append(contentsOf: composerEntitiesForDrawingEntity(postbox: postbox, textScale: textScale, entity: subEntity, colorSpace: colorSpace, tintColor: entity.color.toUIColor())) } } return entities @@ -146,7 +147,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { } } - let account: Account + let postbox: Postbox let content: Content let position: CGPoint let scale: CGFloat @@ -181,8 +182,8 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { var imagePixelBuffer: CVPixelBuffer? let imagePromise = Promise() - init(account: Account, content: Content, position: CGPoint, scale: CGFloat, rotation: CGFloat, baseSize: CGSize, mirrored: Bool, colorSpace: CGColorSpace, tintColor: UIColor?, isStatic: Bool) { - self.account = account + init(postbox: Postbox, content: Content, position: CGPoint, scale: CGFloat, rotation: CGFloat, baseSize: CGSize, mirrored: Bool, colorSpace: CGColorSpace, tintColor: UIColor?, isStatic: Bool) { + self.postbox = postbox self.content = content self.position = position self.scale = scale @@ -200,8 +201,8 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { self.isAnimated = true self.isVideoSticker = file.isVideoSticker || file.mimeType == "video/webm" - self.source = AnimatedStickerResourceSource(account: account, resource: file.resource, isVideo: isVideoSticker) - let pathPrefix = account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id) + self.source = AnimatedStickerResourceSource(postbox: postbox, resource: file.resource, isVideo: isVideoSticker) + let pathPrefix = postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id) if let source = self.source { let fitToSize: CGSize if self.isStatic { @@ -251,7 +252,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { } } else { self.isAnimated = false - self.disposables.add((chatMessageSticker(account: account, userLocation: .other, file: file, small: false, fetched: true, onlyFullSize: true, thumbnail: false, synchronousLoad: false, colorSpace: self.colorSpace) + self.disposables.add((chatMessageSticker(postbox: postbox, userLocation: .other, file: file, small: false, fetched: true, onlyFullSize: true, thumbnail: false, synchronousLoad: false, colorSpace: self.colorSpace) |> deliverOn(self.queue)).start(next: { [weak self] generator in if let self { let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: baseSize, boundingSize: baseSize, intrinsicInsets: UIEdgeInsets())) @@ -276,7 +277,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity { private func setupVideoOutput() { if case let .video(file) = self.content { - if let path = self.account.postbox.mediaBox.completedResourcePath(file.resource, pathExtension: "mp4") { + if let path = self.postbox.mediaBox.completedResourcePath(file.resource, pathExtension: "mp4") { let url = URL(fileURLWithPath: path) let asset = AVURLAsset(url: url) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index b1123a0ebc..51af7d8dd0 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -3,6 +3,7 @@ import AVFoundation import MetalKit import SwiftSignalKit import TelegramCore +import Postbox enum ExportWriterStatus { case unknown @@ -258,7 +259,7 @@ public final class MediaEditorVideoExport { public private(set) var internalStatus: Status = .idle - private let account: Account + private let postbox: Postbox private let subject: Subject private let configuration: Configuration private let textScale: CGFloat @@ -296,8 +297,8 @@ public final class MediaEditorVideoExport { private let semaphore = DispatchSemaphore(value: 0) - public init(account: Account, subject: Subject, configuration: Configuration, outputPath: String, textScale: CGFloat = 1.0) { - self.account = account + public init(postbox: Postbox, subject: Subject, configuration: Configuration, outputPath: String, textScale: CGFloat = 1.0) { + self.postbox = postbox self.subject = subject self.configuration = configuration self.outputPath = outputPath @@ -356,7 +357,7 @@ public final class MediaEditorVideoExport { guard self.composer == nil else { return } - self.composer = MediaEditorComposer(account: self.account, values: self.configuration.values, dimensions: self.configuration.composerDimensions, outputDimensions: self.configuration.dimensions, textScale: self.textScale) + self.composer = MediaEditorComposer(postbox: self.postbox, values: self.configuration.values, dimensions: self.configuration.composerDimensions, outputDimensions: self.configuration.dimensions, textScale: self.textScale) } private func setupWithAsset(_ asset: AVAsset, additionalAsset: AVAsset?) { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 8c441d6184..fbb3a427c0 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3673,7 +3673,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } }))) - let contextController = ContextController(account: self.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) self.present(contextController, in: .window(.root)) } @@ -3867,7 +3867,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let resultImage = mediaEditor.resultImage { mediaEditor.seek(0.0, andPlay: false) - makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, textScale: 2.0, completion: { resultImage in + makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, textScale: 2.0, completion: { resultImage in guard let resultImage else { return } @@ -4187,7 +4187,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate inputImage = UIImage() } - makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: inputImage, dimensions: storyDimensions, values: mediaEditor.values, time: firstFrameTime, textScale: 2.0, completion: { [weak self] coverImage in + makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: inputImage, dimensions: storyDimensions, values: mediaEditor.values, time: firstFrameTime, textScale: 2.0, completion: { [weak self] coverImage in if let self { Logger.shared.log("MediaEditor", "Completed with video \(videoResult)") self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions), mediaAreas, caption, self.state.privacy, stickers, { [weak self] finished in @@ -4210,7 +4210,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let image = mediaEditor.resultImage { self.saveDraft(id: randomId) - makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { [weak self] resultImage in + makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { [weak self] resultImage in if let self, let resultImage { Logger.shared.log("MediaEditor", "Completed with image \(resultImage)") self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas, caption, self.state.privacy, stickers, { [weak self] finished in @@ -4340,7 +4340,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } let configuration = recommendedVideoExportConfiguration(values: mediaEditor.values, duration: duration, forceFullHd: true, frameRate: 60.0) let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).mp4" - let videoExport = MediaEditorVideoExport(account: self.context.account, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0) + let videoExport = MediaEditorVideoExport(postbox: self.context.account.postbox, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0) self.videoExport = videoExport videoExport.start() @@ -4373,7 +4373,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } else { if let image = mediaEditor.resultImage { Queue.concurrentDefaultQueue().async { - makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { resultImage in + makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, textScale: 2.0, completion: { resultImage in if let data = resultImage?.jpegData(compressionQuality: 0.8) { let outputPath = NSTemporaryDirectory() + "\(Int64.random(in: 0 ..< .max)).jpg" try? data.write(to: URL(fileURLWithPath: outputPath)) diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 2c36d7c134..300d6d3290 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -1006,7 +1006,7 @@ public final class MessageInputPanelComponent: Component { break } }, - longPressAction: component.sendMessageOptionsAction, + longPressAction: inputActionButtonMode == .send ? component.sendMessageOptionsAction : nil, switchMediaInputMode: { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift index 79be05eae0..5782429165 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift @@ -191,7 +191,7 @@ final class PeerInfoStoryGridScreenComponent: Component { } } - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + let contextController = ContextController(presentationData: presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) contextController.passthroughTouchEvent = { [weak self] sourceView, point in guard let self else { return .ignore diff --git a/submodules/TelegramUI/Components/PeerSelectionController/BUILD b/submodules/TelegramUI/Components/PeerSelectionController/BUILD new file mode 100644 index 0000000000..8cce950b84 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerSelectionController/BUILD @@ -0,0 +1,41 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerSelectionController", + module_name = "PeerSelectionController", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/ProgressNavigationButtonNode", + "//submodules/AccountContext", + "//submodules/SearchUI", + "//submodules/ChatListUI", + "//submodules/SearchBarNode", + "//submodules/ContactListUI", + "//submodules/SegmentedControlNode", + "//submodules/AttachmentTextInputPanelNode", + "//submodules/ChatPresentationInterfaceState", + "//submodules/ChatSendMessageActionUI", + "//submodules/ChatTextLinkEditUI", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode", + "//submodules/SolidRoundedButtonNode", + "//submodules/ContextUI", + "//submodules/TextFormat", + "//submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift similarity index 99% rename from submodules/TelegramUI/Sources/PeerSelectionController.swift rename to submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index a057d019b3..74091ddf1c 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -296,7 +296,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.peerSelectionNode.requestOpenPeerFromSearch = { [weak self] peer, threadId in if let strongSelf = self { - strongSelf.openMessageFromSearchDisposable.set((strongSelf.context.engine.peers.ensurePeerIsLocallyAvailable(peer: peer) + strongSelf.openMessageFromSearchDisposable.set((_internal_storedMessageFromSearchPeer(postbox: strongSelf.context.account.postbox, peer: peer._asPeer()) |> deliverOnMainQueue).start(completed: { [weak strongSelf] in if let strongSelf = strongSelf, let peerSelected = strongSelf.peerSelected { if case let .channel(peer) = peer, peer.flags.contains(.isForum), threadId == nil, strongSelf.selectForumThreads { diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift similarity index 98% rename from submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift rename to submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index c6a3f26ca2..6902208146 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -23,6 +23,7 @@ import TelegramAnimatedStickerNode import SolidRoundedButtonNode import ContextUI import TextFormat +import ForwardAccessoryPanelNode final class PeerSelectionControllerNode: ASDisplayNode { private let context: AccountContext @@ -363,17 +364,26 @@ final class PeerSelectionControllerNode: ASDisplayNode { return ChatControllerSubject.ForwardOptions(hideNames: state.interfaceState.forwardOptionsState?.hideNames ?? false, hideCaptions: state.interfaceState.forwardOptionsState?.hideCaptions ?? false) } |> distinctUntilChanged - - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.context.account.peerId), subject: .forwardedMessages(peerIds: peerIds, ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true)) + + let chatController = strongSelf.context.sharedContext.makeChatController( + context: strongSelf.context, + chatLocation: .peer(id: strongSelf.context.account.peerId), + subject: .forwardedMessages(peerIds: peerIds, ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), + botStart: nil, + mode: .standard(previewing: true) + ) chatController.canReadHistory.set(false) let messageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] let messagesCount: Signal - if let chatController = chatController as? ChatControllerImpl, messageIds.count > 1 { + if messageIds.count > 1 { messagesCount = .single(messageIds.count) |> then( - chatController.presentationInterfaceStatePromise.get() + chatController.presentationInterfaceStateSignal |> map { state -> Int in + guard let state = state as? ChatPresentationInterfaceState else { + return 1 + } return state.interfaceState.selectionState?.selectedIds.count ?? 1 } ) @@ -509,7 +519,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { guard let strongSelf = self else { return } - if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds { + if let selectedMessageIds = chatController?.selectedMessageIds { var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] forwardMessageIds = forwardMessageIds.filter { selectedMessageIds.contains($0) } strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(forwardMessageIds) }) }) @@ -522,9 +532,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { return items } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(content: .list($0)) }) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(content: .list($0)) }) contextController.dismissedForCancel = { [weak chatController] in - if let selectedMessageIds = (chatController as? ChatControllerImpl)?.selectedMessageIds { + if let selectedMessageIds = chatController?.selectedMessageIds { var forwardMessageIds = strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] forwardMessageIds = forwardMessageIds.filter { selectedMessageIds.contains($0) } strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(forwardMessageIds) }) }) @@ -1419,7 +1429,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } } -func stringForAdminRights(strings: PresentationStrings, adminRights: TelegramChatAdminRights, isChannel: Bool) -> String { +public func stringForAdminRights(strings: PresentationStrings, adminRights: TelegramChatAdminRights, isChannel: Bool) -> String { var rights: [String] = [] func append(_ string: String) { rights.append("• \(string)") diff --git a/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/BUILD b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/BUILD new file mode 100644 index 0000000000..1fc9bca113 --- /dev/null +++ b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/BUILD @@ -0,0 +1,25 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "FetchVideoMediaResource", + module_name = "FetchVideoMediaResource", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Postbox", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramCore", + "//submodules/LegacyComponents", + "//submodules/FFMpegBinding", + "//submodules/LocalMediaResources", + "//submodules/LegacyMediaPickerUI", + "//submodules/TelegramUI/Components/MediaEditor", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift similarity index 98% rename from submodules/TelegramUI/Sources/FetchVideoMediaResource.swift rename to submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift index 50a09090fb..50877a9a20 100644 --- a/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift +++ b/submodules/TelegramUI/Components/Resources/FetchVideoMediaResource/Sources/FetchVideoMediaResource.swift @@ -208,7 +208,7 @@ private final class FetchVideoLibraryMediaResourceContext { private let throttlingContext = FetchVideoLibraryMediaResourceContext() -public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibraryMediaResource) -> Signal { +public func fetchVideoLibraryMediaResource(postbox: Postbox, resource: VideoLibraryMediaResource) -> Signal { let signal = Signal { subscriber in subscriber.putNext(.reset) let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [resource.localIdentifier], options: nil) @@ -248,7 +248,7 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr Logger.shared.log("FetchVideoResource", "Requesting video export") let configuration = recommendedVideoExportConfiguration(values: mediaEditorValues, duration: 5.0, image: true, frameRate: 30.0) - let videoExport = MediaEditorVideoExport(account: account, subject: .image(image), configuration: configuration, outputPath: tempFile.path) + let videoExport = MediaEditorVideoExport(postbox: postbox, subject: .image(image), configuration: configuration, outputPath: tempFile.path) videoExport.start() let statusDisposable = videoExport.status.start(next: { status in @@ -339,7 +339,7 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr if let mediaEditorValues { let duration: Double = avAsset.duration.seconds let configuration = recommendedVideoExportConfiguration(values: mediaEditorValues, duration: duration, frameRate: 30.0) - let videoExport = MediaEditorVideoExport(account: account, subject: .video(avAsset), configuration: configuration, outputPath: tempFile.path) + let videoExport = MediaEditorVideoExport(postbox: postbox, subject: .video(avAsset), configuration: configuration, outputPath: tempFile.path) videoExport.start() let statusDisposable = videoExport.status.start(next: { status in @@ -387,7 +387,7 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr } else { let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: account, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: postbox, adjustments: adjustments) } else { return nil } @@ -464,7 +464,7 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr return throttlingContext.wrap(priority: .default, signal: signal) } -func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideoMediaResource) -> Signal { +public func fetchLocalFileVideoMediaResource(postbox: Postbox, resource: LocalFileVideoMediaResource) -> Signal { let signal = Signal { subscriber in subscriber.putNext(.reset) @@ -497,7 +497,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo subject = .video(avAsset) } - let videoExport = MediaEditorVideoExport(account: account, subject: subject, configuration: configuration, outputPath: tempFile.path) + let videoExport = MediaEditorVideoExport(postbox: postbox, subject: subject, configuration: configuration, outputPath: tempFile.path) videoExport.start() let statusDisposable = videoExport.status.start(next: { status in @@ -550,7 +550,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo } else { let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: account, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: postbox, adjustments: adjustments) } else { return nil } @@ -726,7 +726,7 @@ public func fetchVideoLibraryMediaResourceHash(resource: VideoLibraryMediaResour } } -func fetchLocalFileGifMediaResource(resource: LocalFileGifMediaResource) -> Signal { +public func fetchLocalFileGifMediaResource(resource: LocalFileGifMediaResource) -> Signal { return Signal { subscriber in subscriber.putNext(.reset) diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/BUILD b/submodules/TelegramUI/Components/ShareExtensionContext/BUILD new file mode 100644 index 0000000000..b3ca371358 --- /dev/null +++ b/submodules/TelegramUI/Components/ShareExtensionContext/BUILD @@ -0,0 +1,45 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ShareExtensionContext", + module_name = "ShareExtensionContext", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/AccountContext", + "//submodules/ShareController", + "//submodules/LegacyUI", + "//submodules/PeerInfoUI", + "//submodules/ShareItems", + "//submodules/ShareItems/Impl:ShareItemsImpl", + "//submodules/SettingsUI", + "//submodules/OpenSSLEncryptionProvider", + "//submodules/AppLock", + "//submodules/OverlayStatusController", + "//submodules/PresentationDataUtils", + "//submodules/ChatImportUI", + "//third-party/ZipArchive", + "//submodules/ActivityIndicator", + "//submodules/DebugSettingsUI", + "//submodules/ManagedFile", + "//submodules/TelegramUI/Components/TelegramUIDeclareEncodables", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods", + "//submodules/TelegramUI/Components/PeerSelectionController", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift new file mode 100644 index 0000000000..1d248edf0c --- /dev/null +++ b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift @@ -0,0 +1,1297 @@ +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import SwiftSignalKit +import Postbox +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import ShareController +import LegacyUI +import PeerInfoUI +import ShareItems +import ShareItemsImpl +import SettingsUI +import OpenSSLEncryptionProvider +import AppLock +import Intents +import MobileCoreServices +import OverlayStatusController +import PresentationDataUtils +import ChatImportUI +import ZipArchive +import ActivityIndicator +import DebugSettingsUI +import ManagedFile +import TelegramUIDeclareEncodables +import AnimationCache +import MultiAnimationRenderer +import TelegramUIDeclareEncodables +import TelegramAccountAuxiliaryMethods +import PeerSelectionController + +private var installedSharedLogger = false + +private func setupSharedLogger(rootPath: String, path: String) { + if !installedSharedLogger { + installedSharedLogger = true + Logger.setSharedLogger(Logger(rootPath: rootPath, basePath: path)) + } +} + +private enum ShareAuthorizationError { + case unauthorized +} + +private final class ShareControllerEnvironmentExtension: ShareControllerEnvironment { + let presentationData: PresentationData + var updatedPresentationData: Signal { + return .single(self.presentationData) + } + var isMainApp: Bool { + return false + } + var energyUsageSettings: EnergyUsageSettings { + return .default + } + + var mediaManager: MediaManager? { + return nil + } + + var accounts: [ShareControllerAccountContextExtension] = [] + + init(presentationData: PresentationData) { + self.presentationData = presentationData + } + + func setAccountUserInterfaceInUse(id: AccountRecordId) -> Disposable { + if let account = self.accounts.first(where: { $0.accountId == id }) { + let shouldKeepConnection = account.stateManager.network.shouldKeepConnection + shouldKeepConnection.set(.single(true)) + return ActionDisposable { + shouldKeepConnection.set(.single(false)) + } + } else { + return EmptyDisposable + } + } + + func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id]) { + } +} + +private final class ShareControllerAccountContextExtension: ShareControllerAccountContext { + let accountId: AccountRecordId + let accountPeerId: EnginePeer.Id + let stateManager: AccountStateManager + let animationCache: AnimationCache + let animationRenderer: MultiAnimationRenderer + let contentSettings: ContentSettings + let appConfiguration: AppConfiguration + + init( + accountId: AccountRecordId, + stateManager: AccountStateManager, + contentSettings: ContentSettings, + appConfiguration: AppConfiguration + ) { + self.accountId = accountId + self.accountPeerId = stateManager.accountPeerId + self.stateManager = stateManager + let cacheStorageBox = stateManager.postbox.mediaBox.cacheStorageBox + self.animationCache = AnimationCacheImpl(basePath: stateManager.postbox.mediaBox.basePath + "/animation-cache", allocateTempFile: { + return TempBox.shared.tempFile(fileName: "file").path + }, updateStorageStats: { path, size in + if let pathData = path.data(using: .utf8) { + cacheStorageBox.update(id: pathData, size: size) + } + }) + self.animationRenderer = MultiAnimationRendererImpl() + self.contentSettings = contentSettings + self.appConfiguration = appConfiguration + } + + func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> { + return _internal_resolveInlineStickers(postbox: self.stateManager.postbox, network: self.stateManager.network, fileIds: fileIds) + } +} + +public struct ShareRootControllerInitializationData { + public let appBundleId: String + public let appBuildType: TelegramAppBuildType + public let appGroupPath: String + public let apiId: Int32 + public let apiHash: String + public let languagesCategory: String + public let encryptionParameters: (Data, Data) + public let appVersion: String + public let bundleData: Data? + public let useBetaFeatures: Bool + + public init(appBundleId: String, appBuildType: TelegramAppBuildType, appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?, useBetaFeatures: Bool) { + self.appBundleId = appBundleId + self.appBuildType = appBuildType + self.appGroupPath = appGroupPath + self.apiId = apiId + self.apiHash = apiHash + self.languagesCategory = languagesCategory + self.encryptionParameters = encryptionParameters + self.appVersion = appVersion + self.bundleData = bundleData + self.useBetaFeatures = useBetaFeatures + } +} + +private func extractTextFileHeader(path: String) -> String? { + guard let file = ManagedFile(queue: nil, path: path, mode: .read) else { + return nil + } + guard let size = file.getSize() else { + return nil + } + + let limit: Int64 = 3000 + + var data = file.readData(count: Int(min(size, limit))) + let additionalCapacity = min(10, max(0, Int(size) - data.count)) + + for alignment in 0 ... additionalCapacity { + if alignment != 0 { + data.append(file.readData(count: 1)) + } + if let text = String(data: data, encoding: .utf8) { + return text + } else { + continue + } + } + return nil +} + +public class ShareRootControllerImpl { + private let initializationData: ShareRootControllerInitializationData + private let getExtensionContext: () -> NSExtensionContext? + + private var mainWindow: Window1? + private var currentShareController: ShareController? + private var currentPasscodeController: ViewController? + + private let disposable = MetaDisposable() + private var observer1: AnyObject? + private var observer2: AnyObject? + + private weak var navigationController: NavigationController? + + public init(initializationData: ShareRootControllerInitializationData, getExtensionContext: @escaping () -> NSExtensionContext?) { + self.initializationData = initializationData + self.getExtensionContext = getExtensionContext + } + + deinit { + self.disposable.dispose() + if let observer = self.observer1 { + NotificationCenter.default.removeObserver(observer) + } + if let observer = self.observer2 { + NotificationCenter.default.removeObserver(observer) + } + } + + public func loadView() { + telegramUIDeclareEncodables() + } + + public func viewWillAppear() { + } + + public func viewWillDisappear() { + self.disposable.dispose() + } + + public func viewDidLayoutSubviews(view: UIView, traitCollection: UITraitCollection) { + if self.mainWindow == nil { + let mainWindow = Window1(hostView: childWindowHostView(parent: view), statusBarHost: nil) + mainWindow.hostView.eventView.backgroundColor = UIColor.clear + mainWindow.hostView.eventView.isHidden = false + self.mainWindow = mainWindow + + let bounds = view.bounds + + view.addSubview(mainWindow.hostView.containerView) + mainWindow.hostView.containerView.frame = bounds + + let rootPath = rootPathForBasePath(self.initializationData.appGroupPath) + performAppGroupUpgrades(appGroupPath: self.initializationData.appGroupPath, rootPath: rootPath) + + TempBox.initializeShared(basePath: rootPath, processType: "share", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) + + let logsPath = rootPath + "/logs/share-logs" + let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) + + setupSharedLogger(rootPath: rootPath, path: logsPath) + + let applicationBindings = TelegramApplicationBindings(isMainApp: false, appBundleId: self.initializationData.appBundleId, appBuildType: self.initializationData.appBuildType, containerPath: self.initializationData.appGroupPath, appSpecificScheme: "tg", openUrl: { _ in + }, openUniversalUrl: { _, completion in + completion.completion(false) + return + }, canOpenUrl: { _ in + return false + }, getTopWindow: { + return nil + }, displayNotification: { _ in + + }, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in + }, pushIdleTimerExtension: { + return EmptyDisposable + }, openSettings: { + }, openAppStorePage: { + }, openSubscriptions: { + }, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { + return nil + }, presentNativeController: { _ in + }, dismissNativeController: { + }, getAvailableAlternateIcons: { + return [] + }, getAlternateIconName: { + return nil + }, requestSetAlternateIconName: { _, f in + f(false) + }, forceOrientation: { _ in + }) + + let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false) + initializeAccountManagement() + + var initialPresentationDataAndSettings: InitialPresentationDataAndSettings? + let semaphore = DispatchSemaphore(value: 0) + let systemUserInterfaceStyle: WindowUserInterfaceStyle + if #available(iOSApplicationExtension 12.0, iOS 12.0, *) { + systemUserInterfaceStyle = WindowUserInterfaceStyle(style: traitCollection.userInterfaceStyle) + } else { + systemUserInterfaceStyle = .light + } + let _ = currentPresentationDataAndSettings(accountManager: accountManager, systemUserInterfaceStyle: systemUserInterfaceStyle).start(next: { value in + initialPresentationDataAndSettings = value + semaphore.signal() + }) + semaphore.wait() + + let presentationDataPromise = Promise() + + let appLockContext = AppLockContextImpl(rootPath: rootPath, window: nil, rootController: nil, applicationBindings: applicationBindings, accountManager: accountManager, presentationDataSignal: presentationDataPromise.get(), lockIconInitialFrame: { + return nil + }) + let presentationData = initialPresentationDataAndSettings!.presentationData + presentationDataPromise.set(.single(presentationData)) + + var immediatePeerId: PeerId? + if #available(iOS 13.2, *), let sendMessageIntent = self.getExtensionContext()?.intent as? INSendMessageIntent { + if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") { + let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2)) + if let peerId = Int64(string) { + immediatePeerId = PeerId(peerId) + } + } + } + + /*let account: Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> = internalContext.sharedContext.accountManager.transaction { transaction -> (SharedAccountContextImpl, LoggingSettings) in + return (internalContext.sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings) + } + |> castError(ShareAuthorizationError.self) + |> mapToSignal { sharedContext, loggingSettings -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in + Logger.shared.logToFile = loggingSettings.logToFile + Logger.shared.logToConsole = loggingSettings.logToConsole + + Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData + + return combineLatest(sharedContext.activeAccountsWithInfo, accountManager.transaction { transaction -> (Set, PeerId?) in + let accountRecords = Set(transaction.getRecords().map { record in + return record.id + }) + let intentsSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.intentsSettings)?.get(IntentsSettings.self) ?? IntentsSettings.defaultSettings + return (accountRecords, intentsSettings.account) + }) + |> castError(ShareAuthorizationError.self) + |> take(1) + |> mapToSignal { primaryAndAccounts, validAccountIdsAndIntentsAccountId -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in + var (maybePrimary, accounts) = primaryAndAccounts + let (validAccountIds, intentsAccountId) = validAccountIdsAndIntentsAccountId + for i in (0 ..< accounts.count).reversed() { + if !validAccountIds.contains(accounts[i].account.id) { + accounts.remove(at: i) + } + } + + if let _ = immediatePeerId, let intentsAccountId = intentsAccountId { + for account in accounts { + if account.peer.id == intentsAccountId { + maybePrimary = account.account.id + } + } + } + + guard let primary = maybePrimary, validAccountIds.contains(primary) else { + return .fail(.unauthorized) + } + + guard let info = accounts.first(where: { $0.account.id == primary }) else { + return .fail(.unauthorized) + } + + return .single((sharedContext, info.account, Array(accounts))) + } + } + |> take(1)*/ + + let environment = ShareControllerEnvironmentExtension(presentationData: presentationData) + let initializationData = self.initializationData + + let accountData: Signal<(ShareControllerEnvironment, ShareControllerAccountContext, [ShareControllerSwitchableAccount]), NoError> = accountManager.accountRecords() + |> take(1) + |> mapToSignal { view -> Signal<(ShareControllerEnvironment, ShareControllerAccountContext, [ShareControllerSwitchableAccount]), NoError> in + var signals: [Signal<(AccountRecordId, AccountStateManager, Peer)?, NoError>] = [] + for record in view.records { + if record.attributes.contains(where: { attribute in + if case .loggedOut = attribute { + return true + } else { + return false + } + }) { + continue + } + + let networkArguments = NetworkInitializationArguments( + apiId: initializationData.apiId, + apiHash: initializationData.apiHash, + languagesCategory: initializationData.languagesCategory, + appVersion: initializationData.appVersion, + voipMaxLayer: 0, + voipVersions: [], + appData: .single(nil), + autolockDeadine: .single(nil), + encryptionProvider: OpenSSLEncryptionProvider(), + deviceModelName: nil, + useBetaFeatures: initializationData.useBetaFeatures, + isICloudEnabled: false + ) + + signals.append(standaloneStateManager( + accountManager: accountManager, + networkArguments: networkArguments, + id: record.id, + encryptionParameters: ValueBoxEncryptionParameters( + forceEncryptionIfNoSet: false, + key: ValueBoxEncryptionParameters.Key(data: initializationData.encryptionParameters.0)!, + salt: ValueBoxEncryptionParameters.Salt(data: initializationData.encryptionParameters.1)! + ), + rootPath: rootPath, + auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: nil) + ) + |> mapToSignal { result -> Signal<(AccountRecordId, AccountStateManager, Peer)?, NoError> in + if let result { + return result.postbox.transaction { transaction -> (AccountRecordId, AccountStateManager, Peer)? in + guard let peer = transaction.getPeer(result.accountPeerId) else { + return nil + } + + return (record.id, result, peer) + } + } else { + return .single(nil) + } + }) + } + return combineLatest(signals) + |> mapToSignal { stateManagers -> Signal<(ShareControllerEnvironment, ShareControllerAccountContext, [ShareControllerSwitchableAccount]), NoError> in + var allAccounts: [ShareControllerSwitchableAccount] = [] + for data in stateManagers { + guard let (id, stateManager, peer) = data else { + continue + } + //TODO:content settings + allAccounts.append(ShareControllerSwitchableAccount( + account: ShareControllerAccountContextExtension( + accountId: id, + stateManager: stateManager, + contentSettings: .default, + appConfiguration: .defaultValue + ), + peer: peer + )) + } + + guard let currentAccount = allAccounts.first(where: { $0.account.accountId == view.currentRecord?.id }) else { + return .never() + } + + return .single((environment, currentAccount.account, allAccounts)) + } + } + + let applicationInterface: Signal<(ShareControllerEnvironment, ShareControllerAccountContext, PostboxAccessChallengeData, [ShareControllerSwitchableAccount]), ShareAuthorizationError> = accountData + |> castError(ShareAuthorizationError.self) + |> mapToSignal { data -> Signal<(ShareControllerEnvironment, ShareControllerAccountContext, PostboxAccessChallengeData, [ShareControllerSwitchableAccount]), ShareAuthorizationError> in + let (environment, context, otherAccounts) = data + + let limitsConfigurationAndContentSettings = TelegramEngine.EngineData(postbox: context.stateManager.postbox).get( + TelegramEngine.EngineData.Item.Configuration.Limits(), + TelegramEngine.EngineData.Item.Configuration.ContentSettings(), + TelegramEngine.EngineData.Item.Configuration.App() + ) + + return combineLatest(accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), limitsConfigurationAndContentSettings, accountManager.accessChallengeData()) + |> take(1) + |> deliverOnMainQueue + |> castError(ShareAuthorizationError.self) + |> map { sharedData, limitsConfigurationAndContentSettings, data -> (ShareControllerEnvironment, ShareControllerAccountContext, PostboxAccessChallengeData, [ShareControllerSwitchableAccount]) in + updateLegacyLocalization(strings: environment.presentationData.strings) + + return (environment, context, data.data, otherAccounts) + } + } + |> deliverOnMainQueue + |> afterNext { [weak self] environment, context, accessChallengeData, otherAccounts in + (environment as? ShareControllerEnvironmentExtension)?.accounts = otherAccounts.compactMap { $0.account as? ShareControllerAccountContextExtension } + + initializeLegacyComponents(application: nil, currentSizeClassGetter: { return .compact }, currentHorizontalClassGetter: { return .compact }, documentsPath: "", currentApplicationBounds: { return CGRect() }, canOpenUrl: { _ in return false}, openUrl: { _ in }) + + let displayShare: () -> Void = { + var cancelImpl: (() -> Void)? + let _ = cancelImpl + + let beginShare: () -> Void = { + let requestUserInteraction: ([UnpreparedShareItemContent]) -> Signal<[PreparedShareItemContent], NoError> = { content in + return Signal { [weak self] subscriber in + switch content[0] { + case let .contact(data): + #if !DEBUG + //qwefqwfqwefw + #endif + let _ = data + let _ = self + /*let controller = deviceContactInfoController(context: context, subject: .filter(peer: nil, contactId: nil, contactData: data, completion: { peer, contactData in + let phone = contactData.basicData.phoneNumbers[0].value + if let vCardData = contactData.serializedVCard() { + subscriber.putNext([.media(.media(.standalone(media: TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: vCardData))))]) + } + subscriber.putCompletion() + }), completed: nil, cancelled: { + cancelImpl?() + }) + + if let strongSelf = self, let window = strongSelf.mainWindow { + controller.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet) + window.present(controller, on: .root) + }*/ + break + } + return EmptyDisposable + } |> runOn(Queue.mainQueue()) + } + + let sentItems: ([PeerId], [PeerId: Int64], [PreparedShareItemContent], ShareControllerAccountContext, Bool, String) -> Signal = { peerIds, threadIds, contents, account, silently, additionalText in + let sentItems = sentShareItems(accountPeerId: account.accountPeerId, postbox: account.stateManager.postbox, network: account.stateManager.network, stateManager: account.stateManager, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: nil), to: peerIds, threadIds: threadIds, items: contents, silently: silently, additionalText: additionalText) + |> `catch` { _ -> Signal< + Float, NoError> in + return .complete() + } + return sentItems + |> map { value -> ShareControllerExternalStatus in + return .progress(value) + } + |> then(.single(.done)) + } + + let shareController = ShareController(environment: environment, currentContext: context, subject: .fromExternal({ peerIds, threadIds, additionalText, account, silently in + if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, !inputItems.isEmpty, !peerIds.isEmpty { + let rawSignals = TGItemProviderSignals.itemSignals(forInputItems: inputItems)! + return preparedShareItems(postbox: account.stateManager.postbox, network: account.stateManager.network, to: peerIds[0], dataItems: rawSignals) + |> map(Optional.init) + |> `catch` { error -> Signal in + switch error { + case .generic: + return .single(nil) + case let .fileTooBig(size): + return .fail(.fileTooBig(size)) + } + } + |> mapToSignal { state -> Signal in + guard let state = state else { + return .single(.done) + } + switch state { + case let .preparing(long): + return .single(.preparing(long)) + case let .progress(value): + return .single(.progress(value)) + case let .userInteractionRequired(value): + return requestUserInteraction(value) + |> castError(ShareControllerError.self) + |> mapToSignal { contents -> Signal in + return sentItems(peerIds, threadIds, contents, account, silently, additionalText) + |> castError(ShareControllerError.self) + } + case let .done(contents): + return sentItems(peerIds, threadIds, contents, account, silently, additionalText) + |> castError(ShareControllerError.self) + } + } + } else { + return .single(.done) + } + }), fromForeignApp: true, externalShare: false, switchableAccounts: otherAccounts, immediatePeerId: immediatePeerId) + shareController.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet) + shareController.dismissed = { _ in + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + } + /*shareController.debugAction = { + guard let strongSelf = self else { + return + } + let presentationData = environment.presentationData + let navigationController = NavigationController(mode: .single, theme: NavigationControllerTheme(presentationTheme: presentationData.theme)) + strongSelf.navigationController = navigationController + navigationController.viewControllers = [debugController(sharedContext: context.sharedContext, context: context)] + strongSelf.mainWindow?.present(navigationController, on: .root) + }*/ + + cancelImpl = { [weak shareController] in + shareController?.dismiss(completion: { [weak self] in + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + }) + } + + if let strongSelf = self { + if let currentShareController = strongSelf.currentShareController { + currentShareController.dismiss() + } + if let navigationController = strongSelf.navigationController { + navigationController.dismiss(animated: false) + } + strongSelf.currentShareController = shareController + strongSelf.mainWindow?.present(shareController, on: .root) + } + + //TODO + //context.account.resetStateManagement() + } + + if !"".isEmpty {/* if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, inputItems.count == 1, let item = inputItems[0] as? NSExtensionItem, let attachments = item.attachments { + for attachment in attachments { + if attachment.hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) { + attachment.loadItem(forTypeIdentifier: kUTTypeFileURL as String, completionHandler: { result, error in + Queue.mainQueue().async { + guard let url = result as? URL, url.isFileURL else { + beginShare() + return + } + guard let fileName = url.pathComponents.last else { + beginShare() + return + } + let fileExtension = (fileName as NSString).pathExtension + + var archivePathValue: String? + var otherEntries: [(SSZipEntry, String, TelegramEngine.HistoryImport.MediaType)] = [] + var mainFile: TempBoxFile? + + let appConfiguration = context.appConfiguration + + /* + history_import_filters: { + "zip": { + "main_file_patterns": [ + "_chat\\.txt", + "KakaoTalkChats\\.txt", + "Talk_.*?\\.txt" + ] + }, + "txt": { + "patterns": [ + "^\\[LINE\\]" + ] + } + } + */ + + if fileExtension.lowercased() == "zip" { + let archivePath = url.path + archivePathValue = archivePath + + guard let entries = SSZipArchive.getEntriesForFile(atPath: archivePath) else { + beginShare() + return + } + + var mainFileNameExpressions: [String] = [ + "_chat\\.txt", + "KakaoTalkChats\\.txt", + "Talk_.*?\\.txt", + ] + + if let data = appConfiguration.data, let dict = data["history_import_filters"] as? [String: Any] { + if let zip = dict["zip"] as? [String: Any] { + if let patterns = zip["main_file_patterns"] as? [String] { + mainFileNameExpressions = patterns + } + } + } + + let mainFileNames: [NSRegularExpression] = mainFileNameExpressions.compactMap { string -> NSRegularExpression? in + return try? NSRegularExpression(pattern: string) + } + + var maybeMainFileName: String? + mainFileLoop: for entry in entries { + let entryFileName = entry.path.replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "..", with: "_") + let fullRange = NSRange(entryFileName.startIndex ..< entryFileName.endIndex, in: entryFileName) + for expression in mainFileNames { + if expression.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { + maybeMainFileName = entryFileName + break mainFileLoop + } + } + } + + guard let mainFileName = maybeMainFileName else { + beginShare() + return + } + + let photoRegex = try! NSRegularExpression(pattern: ".*?\\.jpg") + let videoRegex = try! NSRegularExpression(pattern: "[\\d]+-VIDEO-.*?\\.mp4") + let stickerRegex = try! NSRegularExpression(pattern: "[\\d]+-STICKER-.*?\\.webp") + let voiceRegex = try! NSRegularExpression(pattern: "[\\d]+-AUDIO-.*?\\.opus") + + do { + for entry in entries { + let entryPath = entry.path.replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "..", with: "_") + if entryPath.isEmpty { + continue + } + let tempFile = TempBox.shared.tempFile(fileName: entryPath) + if entryPath == mainFileName { + if SSZipArchive.extractFileFromArchive(atPath: archivePath, filePath: entry.path, toPath: tempFile.path) { + mainFile = tempFile + } + } else { + let entryFileName = (entryPath as NSString).lastPathComponent + if !entryFileName.isEmpty { + let mediaType: TelegramEngine.HistoryImport.MediaType + let fullRange = NSRange(entryFileName.startIndex ..< entryFileName.endIndex, in: entryFileName) + if photoRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { + mediaType = .photo + } else if videoRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { + mediaType = .video + } else if stickerRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { + mediaType = .sticker + } else if voiceRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { + mediaType = .voice + } else { + mediaType = .file + } + otherEntries.append((entry, entryFileName, mediaType)) + } + } + } + } + } else if fileExtension.lowercased() == "txt" { + var fileScanExpressions: [String] = [ + "^\\[LINE\\]", + ] + + if let data = appConfiguration.data, let dict = data["history_import_filters"] as? [String: Any] { + if let zip = dict["txt"] as? [String: Any] { + if let patterns = zip["patterns"] as? [String] { + fileScanExpressions = patterns + } + } + } + + let filePatterns: [NSRegularExpression] = fileScanExpressions.compactMap { string -> NSRegularExpression? in + return try? NSRegularExpression(pattern: string) + } + + if let mainFileTextHeader = extractTextFileHeader(path: url.path) { + let fullRange = NSRange(mainFileTextHeader.startIndex ..< mainFileTextHeader.endIndex, in: mainFileTextHeader) + var foundMatch = false + for pattern in filePatterns { + if pattern.firstMatch(in: mainFileTextHeader, options: [], range: fullRange) != nil { + foundMatch = true + break + } + } + if !foundMatch { + beginShare() + return + } + } else { + beginShare() + return + } + + let tempFile = TempBox.shared.tempFile(fileName: "History.txt") + if let _ = try? FileManager.default.copyItem(atPath: url.path, toPath: tempFile.path) { + mainFile = tempFile + } else { + beginShare() + return + } + } + + if let mainFile = mainFile, let mainFileHeader = extractTextFileHeader(path :mainFile.path) { + final class TempController: ViewController { + override public var _presentedInModal: Bool { + get { + return true + } set(value) { + } + } + + private let activityIndicator: ActivityIndicator + + init(environment: ShareControllerEnvironment) { + let presentationData = environment.presentationData + + self.activityIndicator = ActivityIndicator(type: .custom(presentationData.theme.list.itemAccentColor, 22.0, 1.0, false)) + + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) + + self.title = presentationData.strings.ChatImport_Title + self.navigationItem.setLeftBarButton(UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func cancelPressed() { + //self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + } + + override func displayNodeDidLoad() { + super.displayNodeDidLoad() + + self.displayNode.addSubnode(self.activityIndicator) + } + + override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0)) + let navigationHeight = self.navigationLayout(layout: layout).navigationFrame.maxY + transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - indicatorSize.width) / 2.0), y: navigationHeight + floor((layout.size.height - navigationHeight - indicatorSize.height) / 2.0)), size: indicatorSize)) + } + } + + let presentationData = environment.presentationData + let navigationController = NavigationController(mode: .single, theme: NavigationControllerTheme(presentationTheme: presentationData.theme)) + strongSelf.navigationController = navigationController + navigationController.viewControllers = [TempController(environment: environment)] + strongSelf.mainWindow?.present(navigationController, on: .root) + + let _ = (TelegramEngine.HistoryImport(postbox: context.stateManager.postbox, network: context.stateManager.network).getInfo(header: mainFileHeader) + |> deliverOnMainQueue).start(next: { parseInfo in + switch parseInfo { + case let .group(groupTitle): + var attemptSelectionImpl: ((EnginePeer) -> Void)? + var createNewGroupImpl: (() -> Void)? + let controller = PeerSelectionControllerImpl(PeerSelectionControllerParams(context: context, filter: [.onlyGroups, .onlyManageable, .excludeDisabled, .doNotSearchMessages], hasContactSelector: false, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in + attemptSelectionImpl?(peer) + }, createNewGroup: { + createNewGroupImpl?() + }, pretendPresentedInModal: true, selectForumThreads: false)) + + controller.customDismiss = { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + } + + controller.peerSelected = { peer, _ in + attemptSelectionImpl?(peer) + } + + controller.navigationPresentation = .default + + let beginWithPeer: (PeerId) -> Void = { peerId in + navigationController.view.endEditing(true) + navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + }, peerId: peerId, archivePath: archivePathValue, mainEntry: mainFile, otherEntries: otherEntries)) + } + + attemptSelectionImpl = { peer in + var errorText: String? + if case let .channel(channel) = peer { + if channel.hasPermission(.changeInfo), (channel.flags.contains(.isCreator) || channel.adminRights != nil) { + } else { + errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin + } + } else if case let .legacyGroup(group) = peer { + switch group.role { + case .creator: + break + default: + errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin + } + } else { + errorText = presentationData.strings.ChatImport_SelectionErrorGroupGeneric + } + + if let errorText = errorText { + let presentationData = environment.presentationData + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + })]) + strongSelf.mainWindow?.present(controller, on: .root) + } else { + controller.inProgress = true + let _ = (context.engine.historyImport.checkPeerImport(peerId: peer.id) + |> deliverOnMainQueue).start(next: { result in + controller.inProgress = false + + let presentationData = environment.presentationData + + var errorText: String? + if case let .channel(channel) = peer { + if channel.hasPermission(.changeInfo), (channel.flags.contains(.isCreator) || channel.adminRights != nil) { + } else { + errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin + } + } else if case let .legacyGroup(group) = peer { + switch group.role { + case .creator: + break + default: + errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin + } + } else if case .user = peer { + } else { + errorText = presentationData.strings.ChatImport_SelectionErrorGroupGeneric + } + + if let errorText = errorText { + let presentationData = environment.presentationData + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + })]) + strongSelf.mainWindow?.present(controller, on: .root) + } else { + let presentationData = environment.presentationData + let text: String + switch result { + case .allowed: + if let groupTitle = groupTitle { + text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithTitle(groupTitle, peer.debugDisplayTitle).string + } else { + text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithoutTitle(peer.debugDisplayTitle).string + } + case let .alert(textValue): + text = textValue + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { + beginWithPeer(peer.id) + })], parseMarkdown: true) + strongSelf.mainWindow?.present(controller, on: .root) + } + }, error: { error in + controller.inProgress = false + + let presentationData = environment.presentationData + let errorText: String + switch error { + case .generic: + errorText = presentationData.strings.Login_UnknownError + case .chatAdminRequired: + errorText = presentationData.strings.ChatImportActivity_ErrorNotAdmin + case .invalidChatType: + errorText = presentationData.strings.ChatImportActivity_ErrorInvalidChatType + case .userBlocked: + errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked + case .limitExceeded: + errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded + case .notMutualContact: + errorText = presentationData.strings.ChatImport_UserErrorNotMutual + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + })]) + strongSelf.mainWindow?.present(controller, on: .root) + }) + } + } + + createNewGroupImpl = { + let presentationData = environment.presentationData + let resolvedGroupTitle: String + if let groupTitle = groupTitle { + resolvedGroupTitle = groupTitle + } else { + resolvedGroupTitle = "Group" + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_CreateGroupAlertTitle, text: presentationData.strings.ChatImport_CreateGroupAlertText(resolvedGroupTitle).string, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_CreateGroupAlertImportAction, action: { + var signal: Signal = _internal_createSupergroup(postbox: context.stateManager.postbox, network: context.stateManager.network, stateManager: context.stateManager, title: resolvedGroupTitle, description: nil, username: nil, isForum: false, isForHistoryImport: true) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let progressSignal = Signal { subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + if let strongSelf = self { + strongSelf.mainWindow?.present(controller, on: .root) + } + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.15, queue: Queue.mainQueue()) + let progressDisposable = progressSignal.start() + + signal = signal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + let _ = (signal + |> deliverOnMainQueue).start(next: { peerId in + if let peerId = peerId { + beginWithPeer(peerId) + } else { + } + }) + }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + })], actionLayout: .vertical, parseMarkdown: true) + strongSelf.mainWindow?.present(controller, on: .root) + } + + navigationController.viewControllers = [controller] + case let .privateChat(title): + let presentationData = environment.presentationData + + var attemptSelectionImpl: ((EnginePeer) -> Void)? + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeDisabled, .doNotSearchMessages, .excludeSecretChats], hasChatListSelector: false, hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in + attemptSelectionImpl?(peer) + }, pretendPresentedInModal: true, selectForumThreads: true)) + + controller.customDismiss = { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + } + + controller.peerSelected = { peer, _ in + attemptSelectionImpl?(peer) + } + + controller.navigationPresentation = .default + + let beginWithPeer: (PeerId) -> Void = { peerId in + navigationController.view.endEditing(true) + navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + }, peerId: peerId, archivePath: archivePathValue, mainEntry: mainFile, otherEntries: otherEntries)) + } + + attemptSelectionImpl = { [weak controller] peer in + controller?.inProgress = true + let _ = (context.engine.historyImport.checkPeerImport(peerId: peer.id) + |> deliverOnMainQueue).start(next: { result in + controller?.inProgress = false + + let presentationData = environment.presentationData + let text: String + switch result { + case .allowed: + if let title = title { + text = presentationData.strings.ChatImport_SelectionConfirmationUserWithTitle(title, peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string + } else { + text = presentationData.strings.ChatImport_SelectionConfirmationUserWithoutTitle(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string + } + case let .alert(textValue): + text = textValue + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { + beginWithPeer(peer.id) + })], parseMarkdown: true) + strongSelf.mainWindow?.present(controller, on: .root) + }, error: { error in + controller?.inProgress = false + + let presentationData = environment.presentationData + let errorText: String + switch error { + case .generic: + errorText = presentationData.strings.Login_UnknownError + case .chatAdminRequired: + errorText = presentationData.strings.ChatImportActivity_ErrorNotAdmin + case .invalidChatType: + errorText = presentationData.strings.ChatImportActivity_ErrorInvalidChatType + case .userBlocked: + errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked + case .limitExceeded: + errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded + case .notMutualContact: + errorText = presentationData.strings.ChatImport_UserErrorNotMutual + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + })]) + strongSelf.mainWindow?.present(controller, on: .root) + }) + } + + navigationController.viewControllers = [controller] + case let .unknown(peerTitle): + var attemptSelectionImpl: ((EnginePeer) -> Void)? + var createNewGroupImpl: (() -> Void)? + let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeDisabled, .doNotSearchMessages], hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in + attemptSelectionImpl?(peer) + }, createNewGroup: { + createNewGroupImpl?() + }, pretendPresentedInModal: true, selectForumThreads: true)) + + controller.customDismiss = { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + } + + controller.peerSelected = { peer, _ in + attemptSelectionImpl?(peer) + } + + controller.navigationPresentation = .default + + let beginWithPeer: (EnginePeer.Id) -> Void = { peerId in + navigationController.view.endEditing(true) + navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + }, peerId: peerId, archivePath: archivePathValue, mainEntry: mainFile, otherEntries: otherEntries)) + } + + attemptSelectionImpl = { [weak controller] peer in + controller?.inProgress = true + let _ = (context.engine.historyImport.checkPeerImport(peerId: peer.id) + |> deliverOnMainQueue).start(next: { result in + controller?.inProgress = false + + let presentationData = environment.presentationData + + var errorText: String? + if case let .channel(channel) = peer { + if channel.hasPermission(.changeInfo), (channel.flags.contains(.isCreator) || channel.adminRights != nil) { + } else { + errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin + } + } else if case let .legacyGroup(group) = peer { + switch group.role { + case .creator: + break + default: + errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin + } + } else if case .user = peer { + } else { + errorText = presentationData.strings.ChatImport_SelectionErrorGroupGeneric + } + + if let errorText = errorText { + let presentationData = environment.presentationData + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + })]) + strongSelf.mainWindow?.present(controller, on: .root) + } else { + let presentationData = environment.presentationData + if case .user = peer { + let text: String + switch result { + case .allowed: + if let title = peerTitle { + text = presentationData.strings.ChatImport_SelectionConfirmationUserWithTitle(title, peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string + } else { + text = presentationData.strings.ChatImport_SelectionConfirmationUserWithoutTitle(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string + } + case let .alert(textValue): + text = textValue + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { + beginWithPeer(peer.id) + })], parseMarkdown: true) + strongSelf.mainWindow?.present(controller, on: .root) + } else { + let text: String + switch result { + case .allowed: + if let groupTitle = peerTitle { + text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithTitle(groupTitle, peer.debugDisplayTitle).string + } else { + text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithoutTitle(peer.debugDisplayTitle).string + } + case let .alert(textValue): + text = textValue + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { + beginWithPeer(peer.id) + })], parseMarkdown: true) + strongSelf.mainWindow?.present(controller, on: .root) + } + } + }, error: { error in + controller?.inProgress = false + + let presentationData = environment.presentationData + let errorText: String + switch error { + case .generic: + errorText = presentationData.strings.Login_UnknownError + case .chatAdminRequired: + errorText = presentationData.strings.ChatImportActivity_ErrorNotAdmin + case .invalidChatType: + errorText = presentationData.strings.ChatImportActivity_ErrorInvalidChatType + case .userBlocked: + errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked + case .limitExceeded: + errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded + case .notMutualContact: + errorText = presentationData.strings.ChatImport_UserErrorNotMutual + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + })]) + strongSelf.mainWindow?.present(controller, on: .root) + }) + } + + createNewGroupImpl = { + let presentationData = environment.presentationData + let resolvedGroupTitle: String + if let groupTitle = peerTitle { + resolvedGroupTitle = groupTitle + } else { + resolvedGroupTitle = "Group" + } + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_CreateGroupAlertTitle, text: presentationData.strings.ChatImport_CreateGroupAlertText(resolvedGroupTitle).string, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_CreateGroupAlertImportAction, action: { + var signal: Signal = _internal_createSupergroup(postbox: context.stateManager.postbox, network: context.stateManager.network, stateManager: context.stateManager, title: resolvedGroupTitle, description: nil, username: nil, isForum: false, isForHistoryImport: true) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let progressSignal = Signal { subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + if let strongSelf = self { + strongSelf.mainWindow?.present(controller, on: .root) + } + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.15, queue: Queue.mainQueue()) + let progressDisposable = progressSignal.start() + + signal = signal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + let _ = (signal + |> deliverOnMainQueue).start(next: { peerId in + if let peerId = peerId { + beginWithPeer(peerId) + } else { + } + }) + }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + })], actionLayout: .vertical, parseMarkdown: true) + strongSelf.mainWindow?.present(controller, on: .root) + } + + navigationController.viewControllers = [controller] + } + }, error: { _ in + beginShare() + }) + } else { + beginShare() + return + } + } + }) + return + } + } + beginShare()*/ + } else { + beginShare() + } + } + + let modalPresentation: Bool + if #available(iOSApplicationExtension 13.0, iOS 13.0, *) { + modalPresentation = true + } else { + modalPresentation = false + } + + let _ = passcodeEntryController( + accountManager: accountManager, + applicationBindings: applicationBindings, + presentationData: environment.presentationData, + updatedPresentationData: .single(environment.presentationData), + statusBarHost: nil, + appLockContext: appLockContext, + animateIn: true, + modalPresentation: modalPresentation, + completion: { value in + if value { + displayShare() + } else { + Queue.mainQueue().after(0.5, { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + }) + } + } + ).start(next: { controller in + guard let strongSelf = self, let controller = controller else { + return + } + + if let currentPasscodeController = strongSelf.currentPasscodeController { + currentPasscodeController.dismiss() + } + strongSelf.currentPasscodeController = controller + strongSelf.mainWindow?.present(controller, on: .root) + }) + } + + self.disposable.set(applicationInterface.start(next: { _, _, _, _ in }, error: { [weak self] error in + guard let strongSelf = self else { + return + } + let presentationData = environment.presentationData + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.Share_AuthTitle, text: presentationData.strings.Share_AuthDescription, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + //inForeground.set(false) + self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) + })]) + strongSelf.mainWindow?.present(controller, on: .root) + }, completed: {})) + } + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index b697b39f71..dbfa4df0af 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -2062,7 +2062,6 @@ final class StorageUsageScreenComponent: Component { let items = ContextController.Items(content: .list(itemList)) let controller = ContextController( - account: component.context.account, presentationData: presentationData, source: .extracted(StorageUsageListContextExtractedContentSource(contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture) @@ -2594,7 +2593,6 @@ final class StorageUsageScreenComponent: Component { case let .gallery(gallery): gallery.setHintWillBePresentedInPreviewingContext(true) let contextController = ContextController( - account: component.context.account, presentationData: presentationData, source: .controller(StorageUsageListContextGalleryContentSourceImpl( controller: gallery, @@ -2698,7 +2696,6 @@ final class StorageUsageScreenComponent: Component { let items = ContextController.Items(content: .list(itemList)) let controller = ContextController( - account: component.context.account, presentationData: presentationData, source: .extracted(StorageUsageListContextExtractedContentSource(contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture) @@ -3249,7 +3246,6 @@ final class StorageUsageScreenComponent: Component { let source: ContextContentSource = .reference(StorageUsageContextReferenceContentSource(sourceView: sourceLabelView)) let contextController = ContextController( - account: context.account, presentationData: presentationData, source: source, items: items, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index a319cc9e8b..2f4049e26c 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -1676,6 +1676,7 @@ private final class StoryContainerScreenComponent: Component { for (id, itemSetView) in self.visibleItemSetViews { if !validIds.contains(id) { removedIds.append(id) + itemSetView.view.parentState = nil itemSetView.removeFromSuperview() if let view = itemSetView.view.view as? StoryItemSetContainerComponent.View { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 6061714ea9..750fdfbe45 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2243,6 +2243,17 @@ public final class StoryItemSetContainerComponent: Component { if !likeButtonView.isDescendant(of: self) { return } + if likeButtonView.isHidden { + return + } + if likeButtonView.alpha == 0.0 { + return + } + if component.slice.peer.isService { + return + } else if case .unsupported = component.slice.item.storyItem.media { + return + } let tooltipScreen = TooltipScreen( account: component.context.account, @@ -3103,7 +3114,6 @@ public final class StoryItemSetContainerComponent: Component { let items = ContextController.Items(content: .list(itemList)) let controller = ContextController( - account: component.context.account, presentationData: presentationData, source: .extracted(ListContextExtractedContentSource(contentView: sourceView)), items: .single(items), @@ -3700,7 +3710,7 @@ public final class StoryItemSetContainerComponent: Component { strings: component.strings, theme: component.theme, text: component.slice.item.storyItem.text, - entities: component.slice.item.storyItem.entities, + entities: component.slice.peer.isPremium ? component.slice.item.storyItem.entities : [], entityFiles: component.slice.item.entityFiles, action: { [weak self] action in guard let self, let component = self.component else { @@ -5354,7 +5364,7 @@ public final class StoryItemSetContainerComponent: Component { let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: gesture) contextController.dismissed = { [weak self] in guard let self else { return @@ -5638,7 +5648,7 @@ public final class StoryItemSetContainerComponent: Component { let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: gesture) contextController.dismissed = { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index fd7737d758..e2d7352c02 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -433,7 +433,7 @@ final class StoryItemSetContainerSendMessage { let contextItems = ContextController.Items(content: .list(items)) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .top)), items: .single(contextItems), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .top)), items: .single(contextItems), gesture: gesture) contextController.dismissed = { [weak view] in guard let view else { return diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift index 29de8c3989..e941e52f40 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift @@ -272,6 +272,10 @@ final class StoryItemSetViewListComponent: Component { var eventCycleState: EventCycleState? + var totalCount: Int? { + return self.viewListState?.totalCount + } + var hasContent: Bool = false var hasContentUpdated: ((Bool) -> Void)? @@ -1208,7 +1212,7 @@ final class StoryItemSetViewListComponent: Component { let contextItems = ContextController.Items(content: .list(items)) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: nil) + let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: nil) sourceView.alpha = 0.5 contextController.dismissed = { [weak self, weak sourceView] in @@ -1280,7 +1284,7 @@ final class StoryItemSetViewListComponent: Component { let titleText: String if let views = component.storyItem.views, views.seenCount != 0 { if component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { - titleText = component.strings.Story_Footer_Views(Int32(views.seenCount)) + titleText = component.strings.Story_ViewList_ViewerCount(Int32(views.seenCount)) } else { titleText = component.strings.Story_ViewList_TitleViewers } @@ -1361,12 +1365,22 @@ final class StoryItemSetViewListComponent: Component { if !component.hasPremium, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { } else { if let views = component.storyItem.views, views.hasList { - if views.seenCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { - displayModeSelector = true - displaySearchBar = true - } - if views.reactedCount >= 10 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { - displaySortSelector = true + if let currentContentView = self.currentContentView, let totalCount = currentContentView.totalCount { + if totalCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { + displayModeSelector = true + displaySearchBar = true + } + if (views.reactedCount >= 10 && totalCount >= 20) || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { + displaySortSelector = true + } + } else { + if views.seenCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { + displayModeSelector = true + displaySearchBar = true + } + if (views.reactedCount >= 10 && views.seenCount >= 20) || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { + displaySortSelector = true + } } } if let privacy = component.storyItem.privacy, case .everyone = privacy.base { diff --git a/submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods/BUILD b/submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods/BUILD new file mode 100644 index 0000000000..0f5bf919fa --- /dev/null +++ b/submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods/BUILD @@ -0,0 +1,32 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "TelegramAccountAuxiliaryMethods", + module_name = "TelegramAccountAuxiliaryMethods", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/MediaResources", + "//submodules/PassportUI", + "//submodules/OpenInExternalAppUI", + "//submodules/MusicAlbumArtResources", + "//submodules/LocalMediaResources", + "//submodules/LocationResources", + "//submodules/ChatInterfaceState", + "//submodules/WallpaperResources", + "//submodules/AppBundle", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/ICloudResources", + "//submodules/TelegramUI/Components/Resources/FetchVideoMediaResource", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift b/submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods/Sources/TelegramAccountAuxiliaryMethods.swift similarity index 80% rename from submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift rename to submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods/Sources/TelegramAccountAuxiliaryMethods.swift index 2009206428..603b22bea7 100644 --- a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift +++ b/submodules/TelegramUI/Components/TelegramAccountAuxiliaryMethods/Sources/TelegramAccountAuxiliaryMethods.swift @@ -12,13 +12,15 @@ import WallpaperResources import AppBundle import SwiftSignalKit import ICloudResources +import FetchVideoMediaResource +import Display -func makeTelegramAccountAuxiliaryMethods(appDelegate: AppDelegate?) -> AccountAuxiliaryMethods { - return AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in +public func makeTelegramAccountAuxiliaryMethods(uploadInBackground: ((Postbox, MediaResource) -> Signal)?) -> AccountAuxiliaryMethods { + return AccountAuxiliaryMethods(fetchResource: { postbox, resource, ranges, _ in if let resource = resource as? VideoLibraryMediaResource { - return fetchVideoLibraryMediaResource(account: account, resource: resource) + return fetchVideoLibraryMediaResource(postbox: postbox, resource: resource) } else if let resource = resource as? LocalFileVideoMediaResource { - return fetchLocalFileVideoMediaResource(account: account, resource: resource) + return fetchLocalFileVideoMediaResource(postbox: postbox, resource: resource) } else if let resource = resource as? LocalFileGifMediaResource { return fetchLocalFileGifMediaResource(resource: resource) } else if let photoLibraryResource = resource as? PhotoLibraryMediaResource { @@ -26,7 +28,7 @@ func makeTelegramAccountAuxiliaryMethods(appDelegate: AppDelegate?) -> AccountAu } else if let resource = resource as? ICloudFileResource { return fetchICloudFileResource(resource: resource) } else if let resource = resource as? SecureIdLocalImageResource { - return fetchSecureIdLocalImageResource(postbox: account.postbox, resource: resource) + return fetchSecureIdLocalImageResource(postbox: postbox, resource: resource) } else if let resource = resource as? BundleResource { return Signal { subscriber in subscriber.putNext(.reset) @@ -111,9 +113,24 @@ func makeTelegramAccountAuxiliaryMethods(appDelegate: AppDelegate?) -> AccountAu return (PixelDimensions(size), data) } }, backgroundUpload: { postbox, _, resource in - if let appDelegate { - return appDelegate.uploadInBackround(postbox: postbox, resource: resource) + if let uploadInBackground { + return uploadInBackground(postbox, resource) } return .single(nil) }) } + +private func prepareSecretThumbnailData(_ data: EngineMediaResource.ResourceData) -> (CGSize, Data)? { + if data.isComplete, let image = UIImage(contentsOfFile: data.path) { + if image.size.width < 100 && image.size.height < 100 { + if let resultData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { + return (image.size, resultData) + } + } + let scaledSize = image.size.fitted(CGSize(width: 90.0, height: 90.0)) + if let scaledImage = generateScaledImage(image: image, size: scaledSize, scale: 1.0), let scaledData = scaledImage.jpegData(compressionQuality: 0.4) { + return (scaledSize, scaledData) + } + } + return nil +} diff --git a/submodules/TelegramUI/Components/TelegramUIDeclareEncodables/BUILD b/submodules/TelegramUI/Components/TelegramUIDeclareEncodables/BUILD new file mode 100644 index 0000000000..5faf3562fe --- /dev/null +++ b/submodules/TelegramUI/Components/TelegramUIDeclareEncodables/BUILD @@ -0,0 +1,32 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "TelegramUIDeclareEncodables", + module_name = "TelegramUIDeclareEncodables", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Postbox", + "//submodules/TemporaryCachedPeerDataManager", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramNotices", + "//submodules/InstantPageUI", + "//submodules/AccountContext", + "//submodules/LocalMediaResources", + "//submodules/WebSearchUI", + "//submodules/InstantPageCache", + "//submodules/SettingsUI", + "//submodules/WallpaperResources", + "//submodules/MediaResources", + "//submodules/LocationUI", + "//submodules/ChatInterfaceState", + "//submodules/ICloudResources", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/DeclareEncodables.swift b/submodules/TelegramUI/Components/TelegramUIDeclareEncodables/Sources/TelegramUIDeclareEncodables.swift similarity index 100% rename from submodules/TelegramUI/Sources/DeclareEncodables.swift rename to submodules/TelegramUI/Components/TelegramUIDeclareEncodables/Sources/TelegramUIDeclareEncodables.swift diff --git a/submodules/TelegramUI/Sources/AccessoryPanelNode.swift b/submodules/TelegramUI/Sources/AccessoryPanelNode.swift deleted file mode 100644 index 0a2d8c54d8..0000000000 --- a/submodules/TelegramUI/Sources/AccessoryPanelNode.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Foundation -import UIKit -import AsyncDisplayKit -import TelegramPresentationData -import ChatPresentationInterfaceState - -class AccessoryPanelNode: ASDisplayNode { - var originalFrameBeforeDismissed: CGRect? - var dismiss: (() -> Void)? - var interfaceInteraction: ChatPanelInterfaceInteraction? - - func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { - } - - func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) { - } - - func animateIn() { - } - - func animateOut() { - } -} diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 421bd18988..335b1ce473 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -39,6 +39,7 @@ import AuthorizationUI import ManagedFile import DeviceProximity import MediaEditor +import TelegramUIDeclareEncodables #if canImport(AppCenter) import AppCenter diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9ec5046275..56b47abba6 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -100,6 +100,7 @@ import VolumeButtons import ChatAvatarNavigationNode import ChatContextQuery import PeerReportScreen +import PeerSelectionController #if DEBUG import os.signpost @@ -265,9 +266,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private let presentationReady = Promise() private var presentationInterfaceState: ChatPresentationInterfaceState - var presentationInterfaceStatePromise: ValuePromise + let presentationInterfaceStatePromise: ValuePromise + public var presentationInterfaceStateSignal: Signal { + return self.presentationInterfaceStatePromise.get() |> map { $0 } + } - var selectedMessageIds: Set? { + public var selectedMessageIds: Set? { return self.presentationInterfaceState.interfaceState.selectionState?.selectedIds } @@ -1424,7 +1428,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.canReadHistory.set(false) - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: actionsSignal, recognizer: recognizer, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: source, items: actionsSignal, recognizer: recognizer, gesture: gesture) controller.dismissed = { [weak self] in self?.canReadHistory.set(true) } @@ -1761,7 +1765,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.canReadHistory.set(false) - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageReactionContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message, contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatMessageReactionContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message, contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture) controller.dismissed = { [weak self] in self?.canReadHistory.set(true) } @@ -3357,7 +3361,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message._asMessage(), selectAll: true)), items: .single(ContextController.Items(content: .list(actions))), recognizer: nil) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message._asMessage(), selectAll: true)), items: .single(ContextController.Items(content: .list(actions))), recognizer: nil) strongSelf.currentContextController = controller strongSelf.forEachController({ controller in if let controller = controller as? TooltipScreen { @@ -3435,7 +3439,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: topMessage, selectAll: true)), items: .single(ContextController.Items(content: .list(actions))), recognizer: nil) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: topMessage, selectAll: true)), items: .single(ContextController.Items(content: .list(actions))), recognizer: nil) strongSelf.currentContextController = controller strongSelf.forEachController({ controller in if let controller = controller as? TooltipScreen { @@ -3941,7 +3945,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) contextController.dismissed = { [weak self] in self?.canReadHistory.set(true) } @@ -4716,7 +4720,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) contextController.dismissed = { [weak self] in self?.canReadHistory.set(true) } @@ -7996,7 +8000,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ))) let items = ContextController.Items(content: .list(menuItems)) - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageNavigationButtonContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, contentNode: strongSelf.chatDisplayNode.navigateButtons.mentionsButton.containerNode)), items: .single(items), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatMessageNavigationButtonContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, contentNode: strongSelf.chatDisplayNode.navigateButtons.mentionsButton.containerNode)), items: .single(items), recognizer: nil, gesture: gesture) strongSelf.forEachController({ controller in if let controller = controller as? TooltipScreen { @@ -8163,7 +8167,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ))) let items = ContextController.Items(content: .list(menuItems)) - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageNavigationButtonContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, contentNode: strongSelf.chatDisplayNode.navigateButtons.reactionsButton.containerNode)), items: .single(items), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatMessageNavigationButtonContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, contentNode: strongSelf.chatDisplayNode.navigateButtons.reactionsButton.containerNode)), items: .single(items), recognizer: nil, gesture: gesture) strongSelf.forEachController({ controller in if let controller = controller as? TooltipScreen { @@ -8645,7 +8649,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(content: .list($0)) }) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(content: .list($0)) }) contextController.dismissed = { [weak self] in self?.canReadHistory.set(true) } @@ -10377,7 +10381,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) }, joinGroupCall: { [weak self] activeCall in guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { @@ -10462,7 +10466,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: node.view, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture, workaroundUseLegacyImplementation: true) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: node.view, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture, workaroundUseLegacyImplementation: true) contextController.dismissed = { [weak self] in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(interactive: true, { @@ -16159,7 +16163,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } ) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 1b04b0a951..e0313b77e5 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -25,6 +25,8 @@ import ChatInputNode import ChatEntityKeyboardInputNode import ChatControllerInteraction import ChatAvatarNavigationNode +import AccessoryPanelNode +import ForwardAccessoryPanelNode final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem { let itemNode: OverlayMediaItemNode diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift index 24c2418e41..b09e9d5e1a 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift @@ -5,6 +5,8 @@ import TelegramCore import AccountContext import ChatPresentationInterfaceState import ChatControllerInteraction +import AccessoryPanelNode +import ForwardAccessoryPanelNode func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? { if let _ = chatPresentationInterfaceState.interfaceState.selectionState { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 6263a02a4f..b47d8bf023 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -942,7 +942,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { return } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(actions))), recognizer: recognizer, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(actions))), recognizer: recognizer, gesture: gesture) controller.window?.presentInGlobalOverlay(contextController) } diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 9659de86ed..4b9246fbca 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -253,7 +253,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe if let message = peerData.messages.first { let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerData.peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list([]))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list([]))), gesture: gesture) presentInGlobalOverlay(contextController) } else { gesture?.cancel() diff --git a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift index 919d13cd24..0c5d6f3666 100644 --- a/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTranslationPanelNode.swift @@ -339,7 +339,7 @@ final class ChatTranslationPanelNode: ASDisplayNode { } if let controller = self.interfaceInteraction?.chatController() { - let contextController = ContextController(account: self.context.account, presentationData: presentationData, source: .reference(TranslationContextReferenceContentSource(controller: controller, sourceNode: node)), items: items, gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(TranslationContextReferenceContentSource(controller: controller, sourceNode: node)), items: items, gesture: gesture) self.interfaceInteraction?.presentGlobalOverlayController(contextController, nil) } } diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index 39ce3b3b2e..88091d0eb9 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -529,7 +529,7 @@ public func createChannelController(context: AccountContext, mode: CreateChannel let signal = Signal { subscriber in let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: context.account.postbox, adjustments: adjustments) } else { return nil } diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index 0add97cc14..e85dadea05 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -964,7 +964,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] let signal = Signal { subscriber in let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: context.account.postbox, adjustments: adjustments) } else { return nil } @@ -1246,7 +1246,6 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] let source: ContextContentSource = .reference(CreateGroupContextReferenceContentSource(sourceView: sourceNode.labelNode.view)) let contextController = ContextController( - account: context.account, presentationData: presentationData, source: source, items: items, diff --git a/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift index 0c7657eeca..ced54568b6 100644 --- a/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift @@ -17,6 +17,7 @@ import TextNodeWithEntities import AnimationCache import MultiAnimationRenderer import TextFormat +import AccessoryPanelNode final class EditAccessoryPanelNode: AccessoryPanelNode { let dateTimeFormat: PresentationDateTimeFormat diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index 544bae4f5e..ad6774419f 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -1092,7 +1092,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { scheduledTooltip = change }) - let contextController = ContextController(account: self.account, presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: .single(false))), items: items, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: .single(false))), items: items, gesture: gesture) contextController.dismissed = { [weak self] in if let scheduledTooltip, let self, let rate = self.currentRate { self.presentAudioRateTooltip(baseRate: rate, changeType: scheduledTooltip) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 172e03f3fb..a6d3b9c797 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2517,7 +2517,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }))) } - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) strongSelf.controller?.window?.presentInGlobalOverlay(controller) }) }, openMessageReactionContextMenu: { _, _, _, _ in @@ -2673,7 +2673,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro switch previewData { case let .gallery(gallery): gallery.setHintWillBePresentedInPreviewingContext(true) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node, sourceRect: rect)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node, sourceRect: rect)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) case .instantPage: break @@ -2907,7 +2907,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default) })) ] - let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) controller.presentInGlobalOverlay(contextController) } @@ -3763,7 +3763,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, synchronousLoad: true) galleryController.setHintWillBePresentedInPreviewingContext(true) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) } @@ -4926,7 +4926,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.view.endEditing(true) if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode { - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items), tip: tip)), gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items), tip: tip)), gesture: gesture) contextController.dismissed = { [weak self] in if let strongSelf = self { strongSelf.state = strongSelf.state.withHighlightedButton(nil) @@ -5645,7 +5645,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if let sourceNode = self.headerNode.buttonNodes[.more]?.referenceNode { let items = mainItemsImpl?() ?? .single([]) - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) contextController.dismissed = { [weak self] in if let strongSelf = self { strongSelf.state = strongSelf.state.withHighlightedButton(nil) @@ -6224,7 +6224,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } }) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) strongSelf.controller?.present(contextController, in: .window(.root)) }) } @@ -6620,7 +6620,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller { - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) contextController.dismissed = { [weak self] in if let strongSelf = self { strongSelf.state = strongSelf.state.withHighlightedButton(nil) @@ -6716,7 +6716,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller { - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) contextController.dismissed = { [weak self] in if let strongSelf = self { strongSelf.state = strongSelf.state.withHighlightedButton(nil) @@ -7660,7 +7660,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro videoResource = Signal { [weak self] subscriber in let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(account: account, adjustments: adjustments) + return LegacyPaintEntityRenderer(postbox: account.postbox, adjustments: adjustments) } else { return nil } @@ -8459,7 +8459,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let accountContext = self.context.sharedContext.makeTempAccountContext(account: selectedAccount) let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, location: .chatList(groupId: EngineChatList.Group(.root)), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) - let contextController = ContextController(account: accountContext.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: { [weak self] in + let contextController = ContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: { [weak self] in self?.logoutAccount(id: id) }) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) self.controller?.presentInGlobalOverlay(contextController) @@ -8967,7 +8967,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }))) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) contextController.passthroughTouchEvent = { sourceView, point in guard let strongSelf = self else { return .ignore @@ -9084,7 +9084,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: .message(id: .id(index.id), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) } ) @@ -10388,7 +10388,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc })) }))) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + let contextController = ContextController(presentationData: presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) parentController.presentInGlobalOverlay(contextController) }) } @@ -10483,7 +10483,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc })))*/ } - let controller = ContextController(account: primary.0.account, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } diff --git a/submodules/TelegramUI/Sources/PrepareSecretThumbnailData.swift b/submodules/TelegramUI/Sources/PrepareSecretThumbnailData.swift index 58b471333f..bfac43b181 100644 --- a/submodules/TelegramUI/Sources/PrepareSecretThumbnailData.swift +++ b/submodules/TelegramUI/Sources/PrepareSecretThumbnailData.swift @@ -3,17 +3,4 @@ import UIKit import Display import TelegramCore -func prepareSecretThumbnailData(_ data: EngineMediaResource.ResourceData) -> (CGSize, Data)? { - if data.isComplete, let image = UIImage(contentsOfFile: data.path) { - if image.size.width < 100 && image.size.height < 100 { - if let resultData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { - return (image.size, resultData) - } - } - let scaledSize = image.size.fitted(CGSize(width: 90.0, height: 90.0)) - if let scaledImage = generateScaledImage(image: image, size: scaledSize, scale: 1.0), let scaledData = scaledImage.jpegData(compressionQuality: 0.4) { - return (scaledSize, scaledData) - } - } - return nil -} + diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 0186b7bd2f..e665bc4c19 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -16,6 +16,7 @@ import ChatPresentationInterfaceState import TextNodeWithEntities import AnimationCache import MultiAnimationRenderer +import AccessoryPanelNode final class ReplyAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift index 7ad6a0979e..e0f73bbae7 100644 --- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift @@ -1,1149 +1,3 @@ +import Foundation import UIKit -import AsyncDisplayKit -import Display -import TelegramCore -import SwiftSignalKit -import Postbox -import TelegramPresentationData -import TelegramUIPreferences -import AccountContext -import ShareController -import LegacyUI -import PeerInfoUI -import ShareItems -import ShareItemsImpl -import SettingsUI -import OpenSSLEncryptionProvider -import AppLock -import Intents -import MobileCoreServices -import OverlayStatusController -import PresentationDataUtils -import ChatImportUI -import ZipArchive -import ActivityIndicator -import DebugSettingsUI -import ManagedFile -private let inForeground = ValuePromise(false, ignoreRepeated: true) - -private final class InternalContext { - let sharedContext: SharedAccountContextImpl - let wakeupManager: SharedWakeupManager - - init(sharedContext: SharedAccountContextImpl) { - self.sharedContext = sharedContext - self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, acquireIdleExtension: { - return nil - }, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: .single(nil), watchTasks: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in - return sharedContext.accountUserInterfaceInUse(id) - }) - } -} - -private var globalInternalContext: InternalContext? - -private var installedSharedLogger = false - -private func setupSharedLogger(rootPath: String, path: String) { - if !installedSharedLogger { - installedSharedLogger = true - Logger.setSharedLogger(Logger(rootPath: rootPath, basePath: path)) - } -} - -private enum ShareAuthorizationError { - case unauthorized -} - -public struct ShareRootControllerInitializationData { - public let appBundleId: String - public let appBuildType: TelegramAppBuildType - public let appGroupPath: String - public let apiId: Int32 - public let apiHash: String - public let languagesCategory: String - public let encryptionParameters: (Data, Data) - public let appVersion: String - public let bundleData: Data? - public let useBetaFeatures: Bool - - public init(appBundleId: String, appBuildType: TelegramAppBuildType, appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?, useBetaFeatures: Bool) { - self.appBundleId = appBundleId - self.appBuildType = appBuildType - self.appGroupPath = appGroupPath - self.apiId = apiId - self.apiHash = apiHash - self.languagesCategory = languagesCategory - self.encryptionParameters = encryptionParameters - self.appVersion = appVersion - self.bundleData = bundleData - self.useBetaFeatures = useBetaFeatures - } -} - -private func extractTextFileHeader(path: String) -> String? { - guard let file = ManagedFile(queue: nil, path: path, mode: .read) else { - return nil - } - guard let size = file.getSize() else { - return nil - } - - let limit: Int64 = 3000 - - var data = file.readData(count: Int(min(size, limit))) - let additionalCapacity = min(10, max(0, Int(size) - data.count)) - - for alignment in 0 ... additionalCapacity { - if alignment != 0 { - data.append(file.readData(count: 1)) - } - if let text = String(data: data, encoding: .utf8) { - return text - } else { - continue - } - } - return nil -} - -public class ShareRootControllerImpl { - private let initializationData: ShareRootControllerInitializationData - private let getExtensionContext: () -> NSExtensionContext? - - private var mainWindow: Window1? - private var currentShareController: ShareController? - private var currentPasscodeController: ViewController? - - private let disposable = MetaDisposable() - private var observer1: AnyObject? - private var observer2: AnyObject? - - private weak var navigationController: NavigationController? - - public init(initializationData: ShareRootControllerInitializationData, getExtensionContext: @escaping () -> NSExtensionContext?) { - self.initializationData = initializationData - self.getExtensionContext = getExtensionContext - } - - deinit { - self.disposable.dispose() - if let observer = self.observer1 { - NotificationCenter.default.removeObserver(observer) - } - if let observer = self.observer2 { - NotificationCenter.default.removeObserver(observer) - } - } - - public func loadView() { - telegramUIDeclareEncodables() - - if #available(iOSApplicationExtension 8.2, iOS 8.2, *) { - self.observer1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSExtensionHostDidBecomeActive, object: nil, queue: nil, using: { _ in - inForeground.set(true) - }) - - self.observer2 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSExtensionHostWillResignActive, object: nil, queue: nil, using: { _ in - inForeground.set(false) - }) - } - } - - public func viewWillAppear() { - inForeground.set(true) - } - - public func viewWillDisappear() { - self.disposable.dispose() - inForeground.set(false) - } - - public func viewDidLayoutSubviews(view: UIView, traitCollection: UITraitCollection) { - if self.mainWindow == nil { - let mainWindow = Window1(hostView: childWindowHostView(parent: view), statusBarHost: nil) - mainWindow.hostView.eventView.backgroundColor = UIColor.clear - mainWindow.hostView.eventView.isHidden = false - self.mainWindow = mainWindow - - let bounds = view.bounds - - view.addSubview(mainWindow.hostView.containerView) - mainWindow.hostView.containerView.frame = bounds - - let rootPath = rootPathForBasePath(self.initializationData.appGroupPath) - performAppGroupUpgrades(appGroupPath: self.initializationData.appGroupPath, rootPath: rootPath) - - TempBox.initializeShared(basePath: rootPath, processType: "share", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) - - let logsPath = rootPath + "/logs/share-logs" - let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) - - setupSharedLogger(rootPath: rootPath, path: logsPath) - - let applicationBindings = TelegramApplicationBindings(isMainApp: false, appBundleId: self.initializationData.appBundleId, appBuildType: self.initializationData.appBuildType, containerPath: self.initializationData.appGroupPath, appSpecificScheme: "tg", openUrl: { _ in - }, openUniversalUrl: { _, completion in - completion.completion(false) - return - }, canOpenUrl: { _ in - return false - }, getTopWindow: { - return nil - }, displayNotification: { _ in - - }, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in - }, pushIdleTimerExtension: { - return EmptyDisposable - }, openSettings: { - }, openAppStorePage: { - }, openSubscriptions: { - }, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { - return nil - }, presentNativeController: { _ in - }, dismissNativeController: { - }, getAvailableAlternateIcons: { - return [] - }, getAlternateIconName: { - return nil - }, requestSetAlternateIconName: { _, f in - f(false) - }, forceOrientation: { _ in - }) - - let internalContext: InternalContext - - let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false) - - if let globalInternalContext = globalInternalContext { - internalContext = globalInternalContext - } else { - initializeAccountManagement() - var initialPresentationDataAndSettings: InitialPresentationDataAndSettings? - let semaphore = DispatchSemaphore(value: 0) - let systemUserInterfaceStyle: WindowUserInterfaceStyle - if #available(iOSApplicationExtension 12.0, iOS 12.0, *) { - systemUserInterfaceStyle = WindowUserInterfaceStyle(style: traitCollection.userInterfaceStyle) - } else { - systemUserInterfaceStyle = .light - } - let _ = currentPresentationDataAndSettings(accountManager: accountManager, systemUserInterfaceStyle: systemUserInterfaceStyle).start(next: { value in - initialPresentationDataAndSettings = value - semaphore.signal() - }) - semaphore.wait() - - let presentationDataPromise = Promise() - - let appLockContext = AppLockContextImpl(rootPath: rootPath, window: nil, rootController: nil, applicationBindings: applicationBindings, accountManager: accountManager, presentationDataSignal: presentationDataPromise.get(), lockIconInitialFrame: { - return nil - }) - - let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: self.initializationData.useBetaFeatures, isICloudEnabled: false), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil) - presentationDataPromise.set(sharedContext.presentationData) - internalContext = InternalContext(sharedContext: sharedContext) - globalInternalContext = internalContext - } - - var immediatePeerId: PeerId? - if #available(iOS 13.2, *), let sendMessageIntent = self.getExtensionContext()?.intent as? INSendMessageIntent { - if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") { - let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2)) - if let peerId = Int64(string) { - immediatePeerId = PeerId(peerId) - } - } - } - - let account: Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> = internalContext.sharedContext.accountManager.transaction { transaction -> (SharedAccountContextImpl, LoggingSettings) in - return (internalContext.sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings) - } - |> castError(ShareAuthorizationError.self) - |> mapToSignal { sharedContext, loggingSettings -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in - Logger.shared.logToFile = loggingSettings.logToFile - Logger.shared.logToConsole = loggingSettings.logToConsole - - Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData - - return combineLatest(sharedContext.activeAccountsWithInfo, accountManager.transaction { transaction -> (Set, PeerId?) in - let accountRecords = Set(transaction.getRecords().map { record in - return record.id - }) - let intentsSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.intentsSettings)?.get(IntentsSettings.self) ?? IntentsSettings.defaultSettings - return (accountRecords, intentsSettings.account) - }) - |> castError(ShareAuthorizationError.self) - |> take(1) - |> mapToSignal { primaryAndAccounts, validAccountIdsAndIntentsAccountId -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in - var (maybePrimary, accounts) = primaryAndAccounts - let (validAccountIds, intentsAccountId) = validAccountIdsAndIntentsAccountId - for i in (0 ..< accounts.count).reversed() { - if !validAccountIds.contains(accounts[i].account.id) { - accounts.remove(at: i) - } - } - - if let _ = immediatePeerId, let intentsAccountId = intentsAccountId { - for account in accounts { - if account.peer.id == intentsAccountId { - maybePrimary = account.account.id - } - } - } - - guard let primary = maybePrimary, validAccountIds.contains(primary) else { - return .fail(.unauthorized) - } - - guard let info = accounts.first(where: { $0.account.id == primary }) else { - return .fail(.unauthorized) - } - - return .single((sharedContext, info.account, Array(accounts))) - } - } - |> take(1) - - let applicationInterface = account - |> mapToSignal { sharedContext, account, otherAccounts -> Signal<(AccountContext, PostboxAccessChallengeData, [AccountWithInfo]), ShareAuthorizationError> in - let limitsConfigurationAndContentSettings = TelegramEngine(account: account).data.get( - TelegramEngine.EngineData.Item.Configuration.Limits(), - TelegramEngine.EngineData.Item.Configuration.ContentSettings(), - TelegramEngine.EngineData.Item.Configuration.App() - ) - - return combineLatest(sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), limitsConfigurationAndContentSettings, sharedContext.accountManager.accessChallengeData()) - |> take(1) - |> deliverOnMainQueue - |> castError(ShareAuthorizationError.self) - |> map { sharedData, limitsConfigurationAndContentSettings, data -> (AccountContext, PostboxAccessChallengeData, [AccountWithInfo]) in - updateLegacyLocalization(strings: sharedContext.currentPresentationData.with({ $0 }).strings) - let context = AccountContextImpl(sharedContext: sharedContext, account: account, limitsConfiguration: limitsConfigurationAndContentSettings.0._asLimits(), contentSettings: limitsConfigurationAndContentSettings.1, appConfiguration: limitsConfigurationAndContentSettings.2) - return (context, data.data, otherAccounts) - } - } - |> deliverOnMainQueue - |> afterNext { [weak self] context, accessChallengeData, otherAccounts in - setupLegacyComponents(context: context) - initializeLegacyComponents(application: nil, currentSizeClassGetter: { return .compact }, currentHorizontalClassGetter: { return .compact }, documentsPath: "", currentApplicationBounds: { return CGRect() }, canOpenUrl: { _ in return false}, openUrl: { _ in }) - - let displayShare: () -> Void = { - var cancelImpl: (() -> Void)? - - let beginShare: () -> Void = { - let requestUserInteraction: ([UnpreparedShareItemContent]) -> Signal<[PreparedShareItemContent], NoError> = { content in - return Signal { [weak self] subscriber in - switch content[0] { - case let .contact(data): - let controller = deviceContactInfoController(context: context, subject: .filter(peer: nil, contactId: nil, contactData: data, completion: { peer, contactData in - let phone = contactData.basicData.phoneNumbers[0].value - if let vCardData = contactData.serializedVCard() { - subscriber.putNext([.media(.media(.standalone(media: TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: vCardData))))]) - } - subscriber.putCompletion() - }), completed: nil, cancelled: { - cancelImpl?() - }) - - if let strongSelf = self, let window = strongSelf.mainWindow { - controller.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet) - window.present(controller, on: .root) - } - break - } - return EmptyDisposable - } |> runOn(Queue.mainQueue()) - } - - let sentItems: ([PeerId], [PeerId: Int64], [PreparedShareItemContent], Account, Bool) -> Signal = { peerIds, threadIds, contents, account, silently in - let sentItems = sentShareItems(account: account, to: peerIds, threadIds: threadIds, items: contents, silently: silently) - |> `catch` { _ -> Signal< - Float, NoError> in - return .complete() - } - return sentItems - |> map { value -> ShareControllerExternalStatus in - return .progress(value) - } - |> then(.single(.done)) - } - - let shareController = ShareController(context: context, subject: .fromExternal({ peerIds, threadIds, additionalText, account, silently in - if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, !inputItems.isEmpty, !peerIds.isEmpty { - let rawSignals = TGItemProviderSignals.itemSignals(forInputItems: inputItems)! - return preparedShareItems(account: account, to: peerIds[0], dataItems: rawSignals, additionalText: additionalText) - |> map(Optional.init) - |> `catch` { error -> Signal in - switch error { - case .generic: - return .single(nil) - case let .fileTooBig(size): - return .fail(.fileTooBig(size)) - } - } - |> mapToSignal { state -> Signal in - guard let state = state else { - return .single(.done) - } - switch state { - case let .preparing(long): - return .single(.preparing(long)) - case let .progress(value): - return .single(.progress(value)) - case let .userInteractionRequired(value): - return requestUserInteraction(value) - |> castError(ShareControllerError.self) - |> mapToSignal { contents -> Signal in - return sentItems(peerIds, threadIds, contents, account, silently) - |> castError(ShareControllerError.self) - } - case let .done(contents): - return sentItems(peerIds, threadIds, contents, account, silently) - |> castError(ShareControllerError.self) - } - } - } else { - return .single(.done) - } - }), fromForeignApp: true, externalShare: false, switchableAccounts: otherAccounts, immediatePeerId: immediatePeerId) - shareController.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet) - shareController.dismissed = { _ in - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - } - shareController.debugAction = { - guard let strongSelf = self else { - return - } - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let navigationController = NavigationController(mode: .single, theme: NavigationControllerTheme(presentationTheme: presentationData.theme)) - strongSelf.navigationController = navigationController - navigationController.viewControllers = [debugController(sharedContext: context.sharedContext, context: context)] - strongSelf.mainWindow?.present(navigationController, on: .root) - } - - cancelImpl = { [weak shareController] in - shareController?.dismiss(completion: { [weak self] in - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - }) - } - - if let strongSelf = self { - if let currentShareController = strongSelf.currentShareController { - currentShareController.dismiss() - } - if let navigationController = strongSelf.navigationController { - navigationController.dismiss(animated: false) - } - strongSelf.currentShareController = shareController - strongSelf.mainWindow?.present(shareController, on: .root) - } - - context.account.resetStateManagement() - } - - if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, inputItems.count == 1, let item = inputItems[0] as? NSExtensionItem, let attachments = item.attachments { - for attachment in attachments { - if attachment.hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) { - attachment.loadItem(forTypeIdentifier: kUTTypeFileURL as String, completionHandler: { result, error in - Queue.mainQueue().async { - guard let url = result as? URL, url.isFileURL else { - beginShare() - return - } - guard let fileName = url.pathComponents.last else { - beginShare() - return - } - let fileExtension = (fileName as NSString).pathExtension - - var archivePathValue: String? - var otherEntries: [(SSZipEntry, String, TelegramEngine.HistoryImport.MediaType)] = [] - var mainFile: TempBoxFile? - - let appConfiguration = context.currentAppConfiguration.with({ $0 }) - - /* - history_import_filters: { - "zip": { - "main_file_patterns": [ - "_chat\\.txt", - "KakaoTalkChats\\.txt", - "Talk_.*?\\.txt" - ] - }, - "txt": { - "patterns": [ - "^\\[LINE\\]" - ] - } - } - */ - - if fileExtension.lowercased() == "zip" { - let archivePath = url.path - archivePathValue = archivePath - - guard let entries = SSZipArchive.getEntriesForFile(atPath: archivePath) else { - beginShare() - return - } - - var mainFileNameExpressions: [String] = [ - "_chat\\.txt", - "KakaoTalkChats\\.txt", - "Talk_.*?\\.txt", - ] - - if let data = appConfiguration.data, let dict = data["history_import_filters"] as? [String: Any] { - if let zip = dict["zip"] as? [String: Any] { - if let patterns = zip["main_file_patterns"] as? [String] { - mainFileNameExpressions = patterns - } - } - } - - let mainFileNames: [NSRegularExpression] = mainFileNameExpressions.compactMap { string -> NSRegularExpression? in - return try? NSRegularExpression(pattern: string) - } - - var maybeMainFileName: String? - mainFileLoop: for entry in entries { - let entryFileName = entry.path.replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "..", with: "_") - let fullRange = NSRange(entryFileName.startIndex ..< entryFileName.endIndex, in: entryFileName) - for expression in mainFileNames { - if expression.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { - maybeMainFileName = entryFileName - break mainFileLoop - } - } - } - - guard let mainFileName = maybeMainFileName else { - beginShare() - return - } - - let photoRegex = try! NSRegularExpression(pattern: ".*?\\.jpg") - let videoRegex = try! NSRegularExpression(pattern: "[\\d]+-VIDEO-.*?\\.mp4") - let stickerRegex = try! NSRegularExpression(pattern: "[\\d]+-STICKER-.*?\\.webp") - let voiceRegex = try! NSRegularExpression(pattern: "[\\d]+-AUDIO-.*?\\.opus") - - do { - for entry in entries { - let entryPath = entry.path.replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "..", with: "_") - if entryPath.isEmpty { - continue - } - let tempFile = TempBox.shared.tempFile(fileName: entryPath) - if entryPath == mainFileName { - if SSZipArchive.extractFileFromArchive(atPath: archivePath, filePath: entry.path, toPath: tempFile.path) { - mainFile = tempFile - } - } else { - let entryFileName = (entryPath as NSString).lastPathComponent - if !entryFileName.isEmpty { - let mediaType: TelegramEngine.HistoryImport.MediaType - let fullRange = NSRange(entryFileName.startIndex ..< entryFileName.endIndex, in: entryFileName) - if photoRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { - mediaType = .photo - } else if videoRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { - mediaType = .video - } else if stickerRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { - mediaType = .sticker - } else if voiceRegex.firstMatch(in: entryFileName, options: [], range: fullRange) != nil { - mediaType = .voice - } else { - mediaType = .file - } - otherEntries.append((entry, entryFileName, mediaType)) - } - } - } - } - } else if fileExtension.lowercased() == "txt" { - var fileScanExpressions: [String] = [ - "^\\[LINE\\]", - ] - - if let data = appConfiguration.data, let dict = data["history_import_filters"] as? [String: Any] { - if let zip = dict["txt"] as? [String: Any] { - if let patterns = zip["patterns"] as? [String] { - fileScanExpressions = patterns - } - } - } - - let filePatterns: [NSRegularExpression] = fileScanExpressions.compactMap { string -> NSRegularExpression? in - return try? NSRegularExpression(pattern: string) - } - - if let mainFileTextHeader = extractTextFileHeader(path: url.path) { - let fullRange = NSRange(mainFileTextHeader.startIndex ..< mainFileTextHeader.endIndex, in: mainFileTextHeader) - var foundMatch = false - for pattern in filePatterns { - if pattern.firstMatch(in: mainFileTextHeader, options: [], range: fullRange) != nil { - foundMatch = true - break - } - } - if !foundMatch { - beginShare() - return - } - } else { - beginShare() - return - } - - let tempFile = TempBox.shared.tempFile(fileName: "History.txt") - if let _ = try? FileManager.default.copyItem(atPath: url.path, toPath: tempFile.path) { - mainFile = tempFile - } else { - beginShare() - return - } - } - - if let mainFile = mainFile, let mainFileHeader = extractTextFileHeader(path :mainFile.path) { - final class TempController: ViewController { - override public var _presentedInModal: Bool { - get { - return true - } set(value) { - } - } - - private let activityIndicator: ActivityIndicator - - init(context: AccountContext) { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - - self.activityIndicator = ActivityIndicator(type: .custom(presentationData.theme.list.itemAccentColor, 22.0, 1.0, false)) - - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) - - self.title = presentationData.strings.ChatImport_Title - self.navigationItem.setLeftBarButton(UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false) - } - - required public init(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc private func cancelPressed() { - //self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - } - - override func displayNodeDidLoad() { - super.displayNodeDidLoad() - - self.displayNode.addSubnode(self.activityIndicator) - } - - override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { - super.containerLayoutUpdated(layout, transition: transition) - - let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0)) - let navigationHeight = self.navigationLayout(layout: layout).navigationFrame.maxY - transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - indicatorSize.width) / 2.0), y: navigationHeight + floor((layout.size.height - navigationHeight - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - } - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let navigationController = NavigationController(mode: .single, theme: NavigationControllerTheme(presentationTheme: presentationData.theme)) - strongSelf.navigationController = navigationController - navigationController.viewControllers = [TempController(context: context)] - strongSelf.mainWindow?.present(navigationController, on: .root) - - let _ = (context.engine.historyImport.getInfo(header: mainFileHeader) - |> deliverOnMainQueue).start(next: { parseInfo in - switch parseInfo { - case let .group(groupTitle): - var attemptSelectionImpl: ((EnginePeer) -> Void)? - var createNewGroupImpl: (() -> Void)? - let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyGroups, .onlyManageable, .excludeDisabled, .doNotSearchMessages], hasContactSelector: false, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in - attemptSelectionImpl?(peer) - }, createNewGroup: { - createNewGroupImpl?() - }, pretendPresentedInModal: true, selectForumThreads: false)) - - controller.customDismiss = { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - } - - controller.peerSelected = { peer, _ in - attemptSelectionImpl?(peer) - } - - controller.navigationPresentation = .default - - let beginWithPeer: (PeerId) -> Void = { peerId in - navigationController.view.endEditing(true) - navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - }, peerId: peerId, archivePath: archivePathValue, mainEntry: mainFile, otherEntries: otherEntries)) - } - - attemptSelectionImpl = { peer in - var errorText: String? - if case let .channel(channel) = peer { - if channel.hasPermission(.changeInfo), (channel.flags.contains(.isCreator) || channel.adminRights != nil) { - } else { - errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin - } - } else if case let .legacyGroup(group) = peer { - switch group.role { - case .creator: - break - default: - errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin - } - } else { - errorText = presentationData.strings.ChatImport_SelectionErrorGroupGeneric - } - - if let errorText = errorText { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - })]) - strongSelf.mainWindow?.present(controller, on: .root) - } else { - controller.inProgress = true - let _ = (context.engine.historyImport.checkPeerImport(peerId: peer.id) - |> deliverOnMainQueue).start(next: { result in - controller.inProgress = false - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - - var errorText: String? - if case let .channel(channel) = peer { - if channel.hasPermission(.changeInfo), (channel.flags.contains(.isCreator) || channel.adminRights != nil) { - } else { - errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin - } - } else if case let .legacyGroup(group) = peer { - switch group.role { - case .creator: - break - default: - errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin - } - } else if case .user = peer { - } else { - errorText = presentationData.strings.ChatImport_SelectionErrorGroupGeneric - } - - if let errorText = errorText { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - })]) - strongSelf.mainWindow?.present(controller, on: .root) - } else { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let text: String - switch result { - case .allowed: - if let groupTitle = groupTitle { - text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithTitle(groupTitle, peer.debugDisplayTitle).string - } else { - text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithoutTitle(peer.debugDisplayTitle).string - } - case let .alert(textValue): - text = textValue - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { - beginWithPeer(peer.id) - })], parseMarkdown: true) - strongSelf.mainWindow?.present(controller, on: .root) - } - }, error: { error in - controller.inProgress = false - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let errorText: String - switch error { - case .generic: - errorText = presentationData.strings.Login_UnknownError - case .chatAdminRequired: - errorText = presentationData.strings.ChatImportActivity_ErrorNotAdmin - case .invalidChatType: - errorText = presentationData.strings.ChatImportActivity_ErrorInvalidChatType - case .userBlocked: - errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked - case .limitExceeded: - errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded - case .notMutualContact: - errorText = presentationData.strings.ChatImport_UserErrorNotMutual - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - })]) - strongSelf.mainWindow?.present(controller, on: .root) - }) - } - } - - createNewGroupImpl = { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let resolvedGroupTitle: String - if let groupTitle = groupTitle { - resolvedGroupTitle = groupTitle - } else { - resolvedGroupTitle = "Group" - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_CreateGroupAlertTitle, text: presentationData.strings.ChatImport_CreateGroupAlertText(resolvedGroupTitle).string, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_CreateGroupAlertImportAction, action: { - var signal: Signal = context.engine.peers.createSupergroup(title: resolvedGroupTitle, description: nil, isForHistoryImport: true) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let progressSignal = Signal { subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) - if let strongSelf = self { - strongSelf.mainWindow?.present(controller, on: .root) - } - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - signal = signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - let _ = (signal - |> deliverOnMainQueue).start(next: { peerId in - if let peerId = peerId { - beginWithPeer(peerId) - } else { - } - }) - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - })], actionLayout: .vertical, parseMarkdown: true) - strongSelf.mainWindow?.present(controller, on: .root) - } - - navigationController.viewControllers = [controller] - case let .privateChat(title): - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - - var attemptSelectionImpl: ((EnginePeer) -> Void)? - let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyPrivateChats, .excludeDisabled, .doNotSearchMessages, .excludeSecretChats], hasChatListSelector: false, hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in - attemptSelectionImpl?(peer) - }, pretendPresentedInModal: true, selectForumThreads: true)) - - controller.customDismiss = { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - } - - controller.peerSelected = { peer, _ in - attemptSelectionImpl?(peer) - } - - controller.navigationPresentation = .default - - let beginWithPeer: (PeerId) -> Void = { peerId in - navigationController.view.endEditing(true) - navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - }, peerId: peerId, archivePath: archivePathValue, mainEntry: mainFile, otherEntries: otherEntries)) - } - - attemptSelectionImpl = { [weak controller] peer in - controller?.inProgress = true - let _ = (context.engine.historyImport.checkPeerImport(peerId: peer.id) - |> deliverOnMainQueue).start(next: { result in - controller?.inProgress = false - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let text: String - switch result { - case .allowed: - if let title = title { - text = presentationData.strings.ChatImport_SelectionConfirmationUserWithTitle(title, peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string - } else { - text = presentationData.strings.ChatImport_SelectionConfirmationUserWithoutTitle(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string - } - case let .alert(textValue): - text = textValue - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { - beginWithPeer(peer.id) - })], parseMarkdown: true) - strongSelf.mainWindow?.present(controller, on: .root) - }, error: { error in - controller?.inProgress = false - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let errorText: String - switch error { - case .generic: - errorText = presentationData.strings.Login_UnknownError - case .chatAdminRequired: - errorText = presentationData.strings.ChatImportActivity_ErrorNotAdmin - case .invalidChatType: - errorText = presentationData.strings.ChatImportActivity_ErrorInvalidChatType - case .userBlocked: - errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked - case .limitExceeded: - errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded - case .notMutualContact: - errorText = presentationData.strings.ChatImport_UserErrorNotMutual - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - })]) - strongSelf.mainWindow?.present(controller, on: .root) - }) - } - - navigationController.viewControllers = [controller] - case let .unknown(peerTitle): - var attemptSelectionImpl: ((EnginePeer) -> Void)? - var createNewGroupImpl: (() -> Void)? - let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeDisabled, .doNotSearchMessages], hasContactSelector: true, hasGlobalSearch: false, title: presentationData.strings.ChatImport_Title, attemptSelection: { peer, _ in - attemptSelectionImpl?(peer) - }, createNewGroup: { - createNewGroupImpl?() - }, pretendPresentedInModal: true, selectForumThreads: true)) - - controller.customDismiss = { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - } - - controller.peerSelected = { peer, _ in - attemptSelectionImpl?(peer) - } - - controller.navigationPresentation = .default - - let beginWithPeer: (EnginePeer.Id) -> Void = { peerId in - navigationController.view.endEditing(true) - navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - }, peerId: peerId, archivePath: archivePathValue, mainEntry: mainFile, otherEntries: otherEntries)) - } - - attemptSelectionImpl = { [weak controller] peer in - controller?.inProgress = true - let _ = (context.engine.historyImport.checkPeerImport(peerId: peer.id) - |> deliverOnMainQueue).start(next: { result in - controller?.inProgress = false - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - - var errorText: String? - if case let .channel(channel) = peer { - if channel.hasPermission(.changeInfo), (channel.flags.contains(.isCreator) || channel.adminRights != nil) { - } else { - errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin - } - } else if case let .legacyGroup(group) = peer { - switch group.role { - case .creator: - break - default: - errorText = presentationData.strings.ChatImport_SelectionErrorNotAdmin - } - } else if case .user = peer { - } else { - errorText = presentationData.strings.ChatImport_SelectionErrorGroupGeneric - } - - if let errorText = errorText { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - })]) - strongSelf.mainWindow?.present(controller, on: .root) - } else { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - if case .user = peer { - let text: String - switch result { - case .allowed: - if let title = peerTitle { - text = presentationData.strings.ChatImport_SelectionConfirmationUserWithTitle(title, peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string - } else { - text = presentationData.strings.ChatImport_SelectionConfirmationUserWithoutTitle(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string - } - case let .alert(textValue): - text = textValue - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { - beginWithPeer(peer.id) - })], parseMarkdown: true) - strongSelf.mainWindow?.present(controller, on: .root) - } else { - let text: String - switch result { - case .allowed: - if let groupTitle = peerTitle { - text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithTitle(groupTitle, peer.debugDisplayTitle).string - } else { - text = presentationData.strings.ChatImport_SelectionConfirmationGroupWithoutTitle(peer.debugDisplayTitle).string - } - case let .alert(textValue): - text = textValue - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_SelectionConfirmationAlertTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_SelectionConfirmationAlertImportAction, action: { - beginWithPeer(peer.id) - })], parseMarkdown: true) - strongSelf.mainWindow?.present(controller, on: .root) - } - } - }, error: { error in - controller?.inProgress = false - - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let errorText: String - switch error { - case .generic: - errorText = presentationData.strings.Login_UnknownError - case .chatAdminRequired: - errorText = presentationData.strings.ChatImportActivity_ErrorNotAdmin - case .invalidChatType: - errorText = presentationData.strings.ChatImportActivity_ErrorInvalidChatType - case .userBlocked: - errorText = presentationData.strings.ChatImportActivity_ErrorUserBlocked - case .limitExceeded: - errorText = presentationData.strings.ChatImportActivity_ErrorLimitExceeded - case .notMutualContact: - errorText = presentationData.strings.ChatImport_UserErrorNotMutual - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - })]) - strongSelf.mainWindow?.present(controller, on: .root) - }) - } - - createNewGroupImpl = { - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let resolvedGroupTitle: String - if let groupTitle = peerTitle { - resolvedGroupTitle = groupTitle - } else { - resolvedGroupTitle = "Group" - } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatImport_CreateGroupAlertTitle, text: presentationData.strings.ChatImport_CreateGroupAlertText(resolvedGroupTitle).string, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatImport_CreateGroupAlertImportAction, action: { - var signal: Signal = context.engine.peers.createSupergroup(title: resolvedGroupTitle, description: nil, isForHistoryImport: true) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let progressSignal = Signal { subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) - if let strongSelf = self { - strongSelf.mainWindow?.present(controller, on: .root) - } - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - signal = signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - let _ = (signal - |> deliverOnMainQueue).start(next: { peerId in - if let peerId = peerId { - beginWithPeer(peerId) - } else { - } - }) - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - })], actionLayout: .vertical, parseMarkdown: true) - strongSelf.mainWindow?.present(controller, on: .root) - } - - navigationController.viewControllers = [controller] - } - }, error: { _ in - beginShare() - }) - } else { - beginShare() - return - } - } - }) - return - } - } - beginShare() - } else { - beginShare() - } - } - - let modalPresentation: Bool - if #available(iOSApplicationExtension 13.0, iOS 13.0, *) { - modalPresentation = true - } else { - modalPresentation = false - } - - let _ = passcodeEntryController(context: context, animateIn: true, modalPresentation: modalPresentation, completion: { value in - if value { - displayShare() - } else { - Queue.mainQueue().after(0.5, { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - }) - } - }).start(next: { controller in - guard let strongSelf = self, let controller = controller else { - return - } - - if let currentPasscodeController = strongSelf.currentPasscodeController { - currentPasscodeController.dismiss() - } - strongSelf.currentPasscodeController = controller - strongSelf.mainWindow?.present(controller, on: .root) - }) - } - - self.disposable.set(applicationInterface.start(next: { _, _, _ in }, error: { [weak self] error in - guard let strongSelf = self else { - return - } - let presentationData = internalContext.sharedContext.currentPresentationData.with { $0 } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.Share_AuthTitle, text: presentationData.strings.Share_AuthDescription, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - inForeground.set(false) - self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) - })]) - strongSelf.mainWindow?.present(controller, on: .root) - }, completed: {})) - } - } -} diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 9fdc0e3ac1..5c768a3f50 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -39,6 +39,8 @@ import AttachmentTextInputPanelNode import ChatEntityKeyboardInputNode import HashtagSearchUI import PeerInfoStoryGridScreen +import TelegramAccountAuxiliaryMethods +import PeerSelectionController private final class AccountUserInterfaceInUseContext { let subscribers = Bag<(Bool) -> Void>() @@ -506,7 +508,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { var addedAuthSignal: Signal = .single(nil) for (id, attributes) in records { if self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == id}) == nil { - addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(appDelegate: appDelegate)) + addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround)) |> mapToSignal { result -> Signal in switch result { case let .authorized(account): @@ -528,7 +530,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { } } if let authRecord = authRecord, authRecord.0 != self.activeAccountsValue?.currentAuth?.id { - addedAuthSignal = accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: authRecord.0, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: authRecord.1, backupData: nil, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(appDelegate: appDelegate)) + addedAuthSignal = accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: authRecord.0, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: authRecord.1, backupData: nil, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround)) |> mapToSignal { result -> Signal in switch result { case let .unauthorized(account): @@ -894,7 +896,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return true }) - let _ = managedCleanupAccounts(networkArguments: networkArguments, accountManager: self.accountManager, rootPath: rootPath, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(appDelegate: appDelegate), encryptionParameters: encryptionParameters).start() + let _ = managedCleanupAccounts(networkArguments: networkArguments, accountManager: self.accountManager, rootPath: rootPath, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: appDelegate?.uploadInBackround), encryptionParameters: encryptionParameters).start() self.updateNotificationTokensRegistration() diff --git a/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift index 2083075499..ba7fb6ee99 100644 --- a/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift @@ -8,6 +8,7 @@ import TelegramPresentationData import AccountContext import TelegramStringFormatting import ChatPresentationInterfaceState +import AccessoryPanelNode final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { private let webpageDisposable = MetaDisposable() diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 8c866d11e1..dce54e4139 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1274,7 +1274,7 @@ public final class WebAppController: ViewController, AttachmentContainable { return ContextController.Items(content: .list(items)) } - let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(WebAppContextReferenceContentSource(controller: self, sourceNode: node)), items: items, gesture: gesture) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(WebAppContextReferenceContentSource(controller: self, sourceNode: node)), items: items, gesture: gesture) self.presentInGlobalOverlay(contextController) }