mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Sharing refactoring
This commit is contained in:
parent
1faf0a9d39
commit
77a5857a25
@ -1191,7 +1191,8 @@ swift_library(
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/TelegramUI:TelegramUI"
|
||||
"//submodules/TelegramUI:TelegramUI",
|
||||
"//submodules/TelegramUI/Components/ShareExtensionContext"
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import UIKit
|
||||
import TelegramUI
|
||||
import BuildConfig
|
||||
import ShareExtensionContext
|
||||
|
||||
@objc(ShareRootController)
|
||||
class ShareRootController: UIViewController {
|
||||
|
@ -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";
|
||||
|
@ -659,6 +659,9 @@ public protocol ChatController: ViewController {
|
||||
|
||||
var purposefulAction: (() -> Void)? { get set }
|
||||
|
||||
var selectedMessageIds: Set<EngineMessage.Id>? { get }
|
||||
var presentationInterfaceStateSignal: Signal<Any, NoError> { get }
|
||||
|
||||
func updatePresentationMode(_ mode: ChatControllerPresentationMode)
|
||||
func beginMessageSearch(_ query: String)
|
||||
func displayPromoAnnouncement(text: String)
|
||||
|
@ -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<FetchResourceSourceType, FetchResourceError> {
|
||||
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)
|
||||
}
|
||||
|
@ -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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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<PresentationData, NoError>
|
||||
|
||||
public init(
|
||||
accountPeerId: EnginePeer.Id,
|
||||
postbox: Postbox,
|
||||
network: Network,
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
presentationData: PresentationData,
|
||||
updatedPresentationData: Signal<PresentationData, NoError>
|
||||
) {
|
||||
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 }
|
||||
|
@ -11,6 +11,7 @@ swift_library(
|
||||
],
|
||||
deps = [
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/AvatarNode:AvatarNode",
|
||||
|
@ -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
|
||||
|
||||
|
@ -1061,7 +1061,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
avatarVideo = Signal<TelegramMediaResource?, NoError> { 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
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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<UIImage?, NoError> {
|
||||
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<UIImage?, NoError> {
|
||||
let iconSignal: Signal<UIImage?, NoError>
|
||||
|
||||
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<UIImage?, NoError> 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
|
||||
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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<ContextController.Items, NoError>
|
||||
@ -2520,8 +2518,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
|
||||
public var getOverlayViews: (() -> [UIView])?
|
||||
|
||||
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, workaroundUseLegacyImplementation: Bool = false) {
|
||||
self.account = account
|
||||
public init(presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, 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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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<UIImage>()
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<ViewController?, NoError> {
|
||||
return context.sharedContext.accountManager.transaction { transaction -> PostboxAccessChallengeData in
|
||||
public func passcodeEntryController(
|
||||
context: AccountContext,
|
||||
animateIn: Bool = true,
|
||||
modalPresentation: Bool = false,
|
||||
completion: @escaping (Bool) -> Void
|
||||
) -> Signal<ViewController?, NoError> {
|
||||
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<TelegramAccountManagerTypes>,
|
||||
applicationBindings: TelegramApplicationBindings,
|
||||
presentationData: PresentationData,
|
||||
updatedPresentationData: Signal<PresentationData, NoError>,
|
||||
statusBarHost: StatusBarHost?,
|
||||
appLockContext: AppLockContext,
|
||||
animateIn: Bool = true,
|
||||
modalPresentation: Bool = false,
|
||||
completion: @escaping (Bool) -> Void
|
||||
) -> Signal<ViewController?, NoError> {
|
||||
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
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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",
|
||||
|
@ -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<ShareControllerExternalStatus, ShareControllerError>)
|
||||
case fromExternal(([PeerId], [PeerId: Int64], String, ShareControllerAccountContext, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
|
||||
}
|
||||
|
||||
private enum ExternalShareItem {
|
||||
@ -297,6 +300,121 @@ private func collectExternalShareItems(strings: PresentationStrings, dateTimeFor
|
||||
})
|
||||
}
|
||||
|
||||
public protocol ShareControllerEnvironment: AnyObject {
|
||||
var presentationData: PresentationData { get }
|
||||
var updatedPresentationData: Signal<PresentationData, NoError> { 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<PresentationData, NoError> {
|
||||
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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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<ShareState, ShareControllerError> {
|
||||
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<ShareState, ShareControllerError> 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<ShareState, ShareControllerError> {
|
||||
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<ShareState, ShareControllerError> 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<Float, NoError>] = messages.compactMap { message -> Signal<Float, NoError>? 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
|
||||
|
@ -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<Bool>(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<ContextController.Items, NoError> =
|
||||
@ -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<EngineChatList, NoError> {
|
||||
private func threadList(accountPeerId: EnginePeer.Id, postbox: Postbox, peerId: EnginePeer.Id) -> Signal<EngineChatList, NoError> {
|
||||
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<CombinedView, NoError> 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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<PresentationTheme>
|
||||
private let strings: PresentationStrings
|
||||
@ -152,8 +152,8 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
}
|
||||
private let tick = ValuePromise<Int>(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)
|
||||
}
|
||||
}))
|
||||
|
@ -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<PresentationTheme>
|
||||
private let strings: PresentationStrings
|
||||
@ -197,8 +197,8 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
||||
private let searchQuery = ValuePromise<String>("", 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<PresentationTheme>()
|
||||
@ -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)
|
||||
}
|
||||
}))
|
||||
|
@ -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<Empty>
|
||||
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,
|
||||
|
@ -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<PresentationTheme>
|
||||
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<EngineChatList, NoError>, controllerInteraction: ShareControllerInteraction) {
|
||||
self.sharedContext = sharedContext
|
||||
init(environment: ShareControllerEnvironment, context: ShareControllerAccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, topics: Signal<EngineChatList, NoError>, 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)
|
||||
}
|
||||
}))
|
||||
|
@ -173,9 +173,6 @@ static CGSize TGFitSize(CGSize size, CGSize maxSize) {
|
||||
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber)
|
||||
{
|
||||
bool preferAsFile = false;
|
||||
#if DEBUG
|
||||
preferAsFile = true;
|
||||
#endif
|
||||
|
||||
CGSize maxSize = CGSizeMake(1280.0, 1280.0);
|
||||
NSDictionary *imageOptions = @{
|
||||
|
@ -52,12 +52,12 @@ public enum PreparedShareItemError {
|
||||
case fileTooBig(Int64)
|
||||
}
|
||||
|
||||
private func preparedShareItem(account: Account, to peerId: PeerId, value: [String: Any]) -> Signal<PreparedShareItem, PreparedShareItemError> {
|
||||
private func preparedShareItem(postbox: Postbox, network: Network, to peerId: PeerId, value: [String: Any]) -> Signal<PreparedShareItem, PreparedShareItemError> {
|
||||
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<PreparedShareItems, PreparedShareItemError> {
|
||||
public func preparedShareItems(postbox: Postbox, network: Network, to peerId: PeerId, dataItems: [MTSignal]) -> Signal<PreparedShareItems, PreparedShareItemError> {
|
||||
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<Float, Void> {
|
||||
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<Float, Void> {
|
||||
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<Float, Void> in
|
||||
return TelegramEngine(account: account).data.subscribe(EngineDataMap(
|
||||
messageIds.map(TelegramEngine.EngineData.Item.Messages.Message.init)
|
||||
))
|
||||
|> castError(Void.self)
|
||||
|> mapToSignal { messages -> Signal<Float, Void> 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<Float, StandaloneSendMessagesError> = .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<Float, StandaloneSendMessagesError> 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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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<String?, NoError> {
|
||||
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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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<TelegramMediaResource, UploadPeerPhotoError> { [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
|
||||
}
|
||||
|
@ -685,12 +685,12 @@ public enum AccountNetworkState: Equatable {
|
||||
}
|
||||
|
||||
public final class AccountAuxiliaryMethods {
|
||||
public let fetchResource: (Account, MediaResource, Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>?
|
||||
public let fetchResource: (Postbox, MediaResource, Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>?
|
||||
public let fetchResourceMediaReferenceHash: (MediaResource) -> Signal<Data?, NoError>
|
||||
public let prepareSecretThumbnailData: (MediaResourceData) -> (PixelDimensions, Data)?
|
||||
public let backgroundUpload: (Postbox, Network, MediaResource) -> Signal<String?, NoError>
|
||||
|
||||
public init(fetchResource: @escaping (Account, MediaResource, Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal<Data?, NoError>, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?, backgroundUpload: @escaping (Postbox, Network, MediaResource) -> Signal<String?, NoError>) {
|
||||
public init(fetchResource: @escaping (Postbox, MediaResource, Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal<Data?, NoError>, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?, backgroundUpload: @escaping (Postbox, Network, MediaResource) -> Signal<String?, NoError>) {
|
||||
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<MediaResourceDataFetchResult, MediaResourceDataFetchError> 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<MediaResourceDataFetchResult, MediaResourceDataFetchError> 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,
|
||||
|
@ -484,15 +484,15 @@ func _internal_setForumChannelPinnedTopics(account: Account, id: EnginePeer.Id,
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_setChannelForumMode(account: Account, peerId: PeerId, isForum: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
||||
func _internal_setChannelForumMode(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, isForum: Bool) -> Signal<Never, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputChannel? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputChannel)
|
||||
}
|
||||
|> mapToSignal { inputChannel -> Signal<Never, NoError> 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<Api.Updates?, NoError> 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()
|
||||
}
|
||||
|
@ -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<StandaloneSendMessageStatus, StandaloneSendMessagesError> {
|
||||
#if !DEBUG
|
||||
error
|
||||
#endif
|
||||
|
||||
public func standaloneSendEnqueueMessages(
|
||||
accountPeerId: PeerId,
|
||||
postbox: Postbox,
|
||||
network: Network,
|
||||
stateManager: AccountStateManager,
|
||||
auxiliaryMethods: AccountAuxiliaryMethods,
|
||||
peerId: PeerId,
|
||||
threadId: Int64?,
|
||||
messages: [StandaloneSendEnqueueMessage]
|
||||
) -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> {
|
||||
let signals: [Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>] = 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<PendingMessageUploadedContentResult, PendingMessageUploadError>
|
||||
switch content {
|
||||
case let .signal(value, _):
|
||||
@ -197,31 +205,29 @@ public func standaloneSendEnqueueMessages(account: Account, peerId: PeerId, mess
|
||||
}
|
||||
if allDone {
|
||||
var sendSignals: [Signal<Never, StandaloneSendMessagesError>] = []
|
||||
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)
|
||||
|
@ -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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
|
||||
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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
|
||||
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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> 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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> 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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> 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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> 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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
|
||||
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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
|
||||
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<StandaloneUploadMediaThumbnailResult, StandaloneUploadMediaError>
|
||||
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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> 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<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||
switch result {
|
||||
|
@ -22,8 +22,30 @@ private final class MediaResourceDataCopyFile : MediaResourceDataFetchCopyLocalI
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
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<Int64>, MediaBoxFetchPriority)], NoError>,
|
||||
parameters: MediaResourceFetchParameters?
|
||||
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
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<MediaResourceDataFetchResult, NoError> {
|
||||
@ -40,21 +62,76 @@ private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaRes
|
||||
}
|
||||
|
||||
func fetchResource(account: Account, resource: MediaResource, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>? {
|
||||
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<Int64>, MediaBoxFetchPriority)], NoError>,
|
||||
parameters: MediaResourceFetchParameters?
|
||||
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>? {
|
||||
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<MediaResourceDataFetchResult, MediaResourceDataFetchError> 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
|
||||
|
@ -3,7 +3,28 @@ import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKit
|
||||
|
||||
|
||||
func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
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<Int64>, MediaBoxFetchPriority)], NoError>,
|
||||
parameters: MediaResourceFetchParameters?
|
||||
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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] = []
|
||||
|
||||
|
@ -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<ParsedInfo, GetInfoError> {
|
||||
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<Session, InitImportError> {
|
||||
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<Session, InitImportError> 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<Never, StartImportError> {
|
||||
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<CheckPeerImportResult, CheckPeerImportError> {
|
||||
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
|
||||
|
@ -3,8 +3,8 @@ import Postbox
|
||||
import TelegramApi
|
||||
import SwiftSignalKit
|
||||
|
||||
func _internal_exportMessageLink(account: Account, peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal<String?, NoError> {
|
||||
return account.postbox.transaction { transaction -> (Peer, MessageId)? in
|
||||
public func _internal_exportMessageLink(postbox: Postbox, network: Network, peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal<String?, NoError> {
|
||||
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, _):
|
||||
|
@ -217,7 +217,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
public func exportMessageLink(peerId: PeerId, messageId: MessageId, isThread: Bool = false) -> Signal<String?, NoError> {
|
||||
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(
|
||||
|
@ -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<PeerId, CreateChannelError> {
|
||||
return account.postbox.transaction { transaction -> Signal<PeerId, CreateChannelError> 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<PeerId, CreateChannelError> {
|
||||
return postbox.transaction { transaction -> Signal<PeerId, CreateChannelError> 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<PeerId, CreateChannelError> 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<PeerId, CreateChannelError> 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<PeerId, CreateChannelError> {
|
||||
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<PeerId, CreateChannelError> {
|
||||
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<PeerId, CreateChannelError> {
|
||||
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 {
|
||||
|
@ -12,17 +12,17 @@ func cachedRecentPeersEntryId() -> ItemCacheEntryId {
|
||||
return ItemCacheEntryId(collectionId: 101, key: CachedRecentPeers.cacheKey())
|
||||
}
|
||||
|
||||
func _internal_recentPeers(account: Account) -> Signal<RecentPeers, NoError> {
|
||||
public func _internal_recentPeers(accountPeerId: EnginePeer.Id, postbox: Postbox) -> Signal<RecentPeers, NoError> {
|
||||
let key = PostboxViewKey.cachedItem(cachedRecentPeersEntryId())
|
||||
return account.postbox.combinedView(keys: [key])
|
||||
return postbox.combinedView(keys: [key])
|
||||
|> mapToSignal { views -> Signal<RecentPeers, NoError> 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<Void, NoError> {
|
||||
public func _internal_managedUpdatedRecentPeers(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
let key = PostboxViewKey.cachedItem(cachedRecentPeersEntryId())
|
||||
let peersEnabled = postbox.combinedView(keys: [key])
|
||||
|> map { views -> Bool in
|
||||
|
@ -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] = []
|
||||
|
@ -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<Api.contacts.Found?, NoError>.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)
|
||||
|
@ -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<PeerId, CreateChannelError> {
|
||||
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<Void, DeleteChannelError> {
|
||||
@ -502,7 +502,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
public func recentPeers() -> Signal<RecentPeers, NoError> {
|
||||
return _internal_recentPeers(account: self.account)
|
||||
return _internal_recentPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox)
|
||||
}
|
||||
|
||||
public func managedUpdatedRecentPeers() -> Signal<Void, NoError> {
|
||||
@ -926,7 +926,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
public func ensurePeerIsLocallyAvailable(peer: EnginePeer) -> Signal<EnginePeer, NoError> {
|
||||
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<Never, NoError> {
|
||||
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<Int64, CreateForumChannelTopicError> {
|
||||
|
@ -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 {
|
||||
|
@ -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 = {
|
||||
|
@ -2,8 +2,8 @@ import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
func _internal_storedMessageFromSearchPeer(account: Account, peer: Peer) -> Signal<Peer, NoError> {
|
||||
return account.postbox.transaction { transaction -> Peer in
|
||||
public func _internal_storedMessageFromSearchPeer(postbox: Postbox, peer: Peer) -> Signal<Peer, NoError> {
|
||||
return postbox.transaction { transaction -> Peer in
|
||||
if transaction.getPeer(peer.id) == nil {
|
||||
updatePeersCustom(transaction: transaction, peers: [peer], update: { _, updatedPeer in
|
||||
return updatedPeer
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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 {
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -24,6 +24,7 @@ swift_library(
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/lottie-ios:Lottie",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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
|
||||
|
@ -24,6 +24,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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<Bool>(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) {
|
||||
|
@ -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<ShareControllerExternalStatus, ShareControllerError> 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<ShareControllerExternalStatus, ShareControllerError> in
|
||||
return .complete()
|
||||
|
@ -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
|
||||
|
@ -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<UIImage>()
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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?) {
|
||||
|
@ -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))
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user