Various fixes

This commit is contained in:
Peter 2018-12-01 02:42:58 +04:00
parent f6a88c9c13
commit c8a943a017
22 changed files with 271 additions and 125 deletions

View File

@ -68,7 +68,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: true, theme: defaultPresentationTheme, present: { c, a in presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: true, theme: defaultPresentationTheme, present: { c, a in
self?.view.endEditing(true) self?.view.endEditing(true)
self?.present(c, in: .window(.root), with: a) self?.present(c, in: .window(.root), with: a)
}, completion: { image in }, openCurrent: nil, completion: { image in
self?.controllerNode.currentPhoto = image self?.controllerNode.currentPhoto = image
}) })
}) })

View File

@ -168,15 +168,15 @@ public final class CallController: ViewController {
} }
self.peerDisposable = (account.postbox.peerView(id: self.call.peerId) self.peerDisposable = (account.postbox.peerView(id: self.call.peerId)
|> deliverOnMainQueue).start(next: { [weak self] view in |> deliverOnMainQueue).start(next: { [weak self] view in
if let strongSelf = self { if let strongSelf = self {
if let peer = view.peers[view.peerId] { if let peer = view.peers[view.peerId] {
strongSelf.peer = peer strongSelf.peer = peer
strongSelf.controllerNode.updatePeer(peer: peer) strongSelf.controllerNode.updatePeer(peer: peer)
strongSelf._ready.set(.single(true)) strongSelf._ready.set(.single(true))
}
} }
}) }
})
self.controllerNode.isMuted = self.isMuted self.controllerNode.isMuted = self.isMuted

View File

@ -4,9 +4,9 @@ import AVFoundation
import Postbox import Postbox
import SwiftSignalKit import SwiftSignalKit
public final class CallKitIntegration { private var sharedProviderDelegate: AnyObject?
private let providerDelegate: AnyObject
public final class CallKitIntegration {
public static var isAvailable: Bool { public static var isAvailable: Bool {
#if targetEnvironment(simulator) #if targetEnvironment(simulator)
return false return false
@ -34,7 +34,10 @@ public final class CallKitIntegration {
#else #else
if #available(iOSApplicationExtension 10.0, *) { if #available(iOSApplicationExtension 10.0, *) {
self.providerDelegate = CallKitProviderDelegate(audioSessionActivePromise: self.audioSessionActivePromise, startCall: startCall, answerCall: answerCall, endCall: endCall, setCallMuted: setCallMuted, audioSessionActivationChanged: audioSessionActivationChanged) if sharedProviderDelegate == nil {
sharedProviderDelegate = CallKitProviderDelegate()
}
(sharedProviderDelegate as? CallKitProviderDelegate)?.setup(audioSessionActivePromise: self.audioSessionActivePromise, startCall: startCall, answerCall: answerCall, endCall: endCall, setCallMuted: setCallMuted, audioSessionActivationChanged: audioSessionActivationChanged)
} else { } else {
return nil return nil
} }
@ -43,31 +46,31 @@ public final class CallKitIntegration {
func startCall(peerId: PeerId, displayTitle: String) { func startCall(peerId: PeerId, displayTitle: String) {
if #available(iOSApplicationExtension 10.0, *) { if #available(iOSApplicationExtension 10.0, *) {
(self.providerDelegate as! CallKitProviderDelegate).startCall(peerId: peerId, displayTitle: displayTitle) (sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(peerId: peerId, displayTitle: displayTitle)
} }
} }
func answerCall(uuid: UUID) { func answerCall(uuid: UUID) {
if #available(iOSApplicationExtension 10.0, *) { if #available(iOSApplicationExtension 10.0, *) {
(self.providerDelegate as! CallKitProviderDelegate).answerCall(uuid: uuid) (sharedProviderDelegate as? CallKitProviderDelegate)?.answerCall(uuid: uuid)
} }
} }
func dropCall(uuid: UUID) { func dropCall(uuid: UUID) {
if #available(iOSApplicationExtension 10.0, *) { if #available(iOSApplicationExtension 10.0, *) {
(self.providerDelegate as! CallKitProviderDelegate).dropCall(uuid: uuid) (sharedProviderDelegate as? CallKitProviderDelegate)?.dropCall(uuid: uuid)
} }
} }
func reportIncomingCall(uuid: UUID, handle: String, displayTitle: String, completion: ((NSError?) -> Void)?) { func reportIncomingCall(uuid: UUID, handle: String, displayTitle: String, completion: ((NSError?) -> Void)?) {
if #available(iOSApplicationExtension 10.0, *) { if #available(iOSApplicationExtension 10.0, *) {
(self.providerDelegate as! CallKitProviderDelegate).reportIncomingCall(uuid: uuid, handle: handle, displayTitle: displayTitle, completion: completion) (sharedProviderDelegate as? CallKitProviderDelegate)?.reportIncomingCall(uuid: uuid, handle: handle, displayTitle: displayTitle, completion: completion)
} }
} }
func reportOutgoingCallConnected(uuid: UUID, at date: Date) { func reportOutgoingCallConnected(uuid: UUID, at date: Date) {
if #available(iOSApplicationExtension 10.0, *) { if #available(iOSApplicationExtension 10.0, *) {
(self.providerDelegate as! CallKitProviderDelegate).reportOutgoingCallConnected(uuid: uuid, at: date) (sharedProviderDelegate as? CallKitProviderDelegate)?.reportOutgoingCallConnected(uuid: uuid, at: date)
} }
} }
} }
@ -77,29 +80,31 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
private let provider: CXProvider private let provider: CXProvider
private let callController = CXCallController() private let callController = CXCallController()
private let startCall: (UUID, String) -> Signal<Bool, NoError> private var startCall: ((UUID, String) -> Signal<Bool, NoError>)?
private let answerCall: (UUID) -> Void private var answerCall: ((UUID) -> Void)?
private let endCall: (UUID) -> Signal<Bool, NoError> private var endCall: ((UUID) -> Signal<Bool, NoError>)?
private let setCallMuted: (UUID, Bool) -> Void private var setCallMuted: ((UUID, Bool) -> Void)?
private let audioSessionActivationChanged: (Bool) -> Void private var audioSessionActivationChanged: ((Bool) -> Void)?
private let disposableSet = DisposableSet() private let disposableSet = DisposableSet()
fileprivate let audioSessionActivePromise: ValuePromise<Bool> fileprivate var audioSessionActivePromise: ValuePromise<Bool>?
init(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (UUID, String) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { override init() {
self.provider = CXProvider(configuration: CallKitProviderDelegate.providerConfiguration)
super.init()
self.provider.setDelegate(self, queue: nil)
}
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (UUID, String) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
self.audioSessionActivePromise = audioSessionActivePromise self.audioSessionActivePromise = audioSessionActivePromise
self.startCall = startCall self.startCall = startCall
self.answerCall = answerCall self.answerCall = answerCall
self.endCall = endCall self.endCall = endCall
self.setCallMuted = setCallMuted self.setCallMuted = setCallMuted
self.audioSessionActivationChanged = audioSessionActivationChanged self.audioSessionActivationChanged = audioSessionActivationChanged
self.provider = CXProvider(configuration: CallKitProviderDelegate.providerConfiguration)
super.init()
self.provider.setDelegate(self, queue: nil)
} }
static var providerConfiguration: CXProviderConfiguration { static var providerConfiguration: CXProviderConfiguration {
@ -184,19 +189,16 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
} }
func providerDidReset(_ provider: CXProvider) { func providerDidReset(_ provider: CXProvider) {
/*stopAudio()
for call in callManager.calls {
call.end()
}
callManager.removeAllCalls()*/
} }
func provider(_ provider: CXProvider, perform action: CXStartCallAction) { func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
guard let startCall = self.startCall else {
action.fail()
return
}
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.disposableSet.add(disposable) self.disposableSet.add(disposable)
disposable.set((self.startCall(action.callUUID, action.handle.value) disposable.set((startCall(action.callUUID, action.handle.value)
|> deliverOnMainQueue |> deliverOnMainQueue
|> afterDisposed { [weak self, weak disposable] in |> afterDisposed { [weak self, weak disposable] in
if let strongSelf = self, let disposable = disposable { if let strongSelf = self, let disposable = disposable {
@ -212,42 +214,53 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
} }
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
self.answerCall(action.callUUID) guard let answerCall = self.answerCall else {
action.fail()
return
}
answerCall(action.callUUID)
action.fulfill() action.fulfill()
} }
func provider(_ provider: CXProvider, perform action: CXEndCallAction) { func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
guard let endCall = self.endCall else {
action.fail()
return
}
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.disposableSet.add(disposable) self.disposableSet.add(disposable)
disposable.set((self.endCall(action.callUUID) disposable.set((endCall(action.callUUID)
|> deliverOnMainQueue |> deliverOnMainQueue
|> afterDisposed { [weak self, weak disposable] in |> afterDisposed { [weak self, weak disposable] in
if let strongSelf = self, let disposable = disposable { if let strongSelf = self, let disposable = disposable {
strongSelf.disposableSet.remove(disposable) strongSelf.disposableSet.remove(disposable)
} }
}).start(next: { result in }).start(next: { result in
if result { if result {
action.fulfill(withDateEnded: Date()) action.fulfill(withDateEnded: Date())
} else { } else {
action.fail() action.fail()
} }
})) }))
} }
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
self.setCallMuted(action.uuid, action.isMuted) guard let setCallMuted = self.setCallMuted else {
action.fail()
return
}
setCallMuted(action.uuid, action.isMuted)
action.fulfill() action.fulfill()
} }
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
self.audioSessionActivationChanged(true) self.audioSessionActivationChanged?(true)
self.audioSessionActivePromise.set(true) self.audioSessionActivePromise?.set(true)
} }
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
self.audioSessionActivationChanged(false) self.audioSessionActivationChanged?(false)
self.audioSessionActivePromise.set(false) self.audioSessionActivePromise?.set(false)
} }
} }

View File

@ -1727,7 +1727,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID
pinnedMessageUpdated = true pinnedMessageUpdated = true
} }
if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || strongSelf.presentationInterfaceState.canReportPeer != canReport || pinnedMessageUpdated { let callsDataUpdated = strongSelf.presentationInterfaceState.callsAvailable != callsAvailable || strongSelf.presentationInterfaceState.callsPrivate != callsPrivate
if strongSelf.presentationInterfaceState.pinnedMessageId != pinnedMessageId || strongSelf.presentationInterfaceState.pinnedMessage?.stableVersion != pinnedMessage?.stableVersion || strongSelf.presentationInterfaceState.peerIsBlocked != peerIsBlocked || strongSelf.presentationInterfaceState.canReportPeer != canReport || pinnedMessageUpdated || callsDataUpdated {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
return state.updatedPinnedMessageId(pinnedMessageId).updatedPinnedMessage(pinnedMessage).updatedPeerIsBlocked(peerIsBlocked).updatedCanReportPeer(canReport).updatedCallsAvailable(callsAvailable).updatedCallsPrivate(callsPrivate).updatedTitlePanelContext({ context in return state.updatedPinnedMessageId(pinnedMessageId).updatedPinnedMessage(pinnedMessage).updatedPeerIsBlocked(peerIsBlocked).updatedCanReportPeer(canReport).updatedCallsAvailable(callsAvailable).updatedCallsPrivate(callsPrivate).updatedTitlePanelContext({ context in
if pinnedMessageId != nil { if pinnedMessageId != nil {

View File

@ -260,6 +260,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
strongSelf.requestLayout(.animated(duration: 0.1, curve: .easeInOut)) strongSelf.requestLayout(.animated(duration: 0.1, curve: .easeInOut))
} }
} }
var lastSendTimestamp = 0.0
self.textInputPanelNode?.sendMessage = { [weak self] in self.textInputPanelNode?.sendMessage = { [weak self] in
if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode { if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode {
if textInputPanelNode.textInputNode?.isFirstResponder() ?? false { if textInputPanelNode.textInputNode?.isFirstResponder() ?? false {
@ -274,6 +275,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if let _ = effectivePresentationInterfaceState.interfaceState.editMessage { if let _ = effectivePresentationInterfaceState.interfaceState.editMessage {
strongSelf.interfaceInteraction?.editMessage() strongSelf.interfaceInteraction?.editMessage()
} else { } else {
let timestamp = CACurrentMediaTime()
if lastSendTimestamp + 0.15 > timestamp {
return
}
lastSendTimestamp = timestamp
strongSelf.updateTypingActivity(false) strongSelf.updateTypingActivity(false)
var messages: [EnqueueMessage] = [] var messages: [EnqueueMessage] = []

View File

@ -78,7 +78,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.DialogList_Title, style: .plain, target: nil, action: nil) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.DialogList_Title, style: .plain, target: nil, action: nil)
self.scrollToTop = { [weak self] in self.scrollToTop = { [weak self] in
self?.chatListDisplayNode.chatListNode.scrollToPosition(.top) self?.chatListDisplayNode.scrollToTop()
} }
self.scrollToTopWithTabBar = { [weak self] in self.scrollToTopWithTabBar = { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {

View File

@ -197,4 +197,12 @@ class ChatListControllerNode: ASDisplayNode {
self.searchDisplayController = nil self.searchDisplayController = nil
} }
} }
func scrollToTop() {
if let searchDisplayController = self.searchDisplayController {
searchDisplayController.contentNode.scrollToTop()
} else {
self.chatListNode.scrollToPosition(.top)
}
}
} }

View File

@ -586,7 +586,9 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)) self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))
self.recentListNode = ListView() self.recentListNode = ListView()
self.recentListNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
self.listNode = ListView() self.listNode = ListView()
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
self.statePromise = ValuePromise(self.stateValue, ignoreRepeated: true) self.statePromise = ValuePromise(self.stateValue, ignoreRepeated: true)
@ -1015,6 +1017,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
private func updateTheme(theme: PresentationTheme) { private func updateTheme(theme: PresentationTheme) {
self.backgroundColor = theme.chatList.backgroundColor self.backgroundColor = theme.chatList.backgroundColor
self.recentListNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
self.listNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
} }
private func updateState(_ f: (ChatListSearchContainerNodeState) -> ChatListSearchContainerNodeState) { private func updateState(_ f: (ChatListSearchContainerNodeState) -> ChatListSearchContainerNodeState) {
@ -1188,4 +1192,12 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
} }
} }
} }
override func scrollToTop() {
if !self.listNode.isHidden {
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
} else {
self.recentListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
}
} }

View File

@ -183,9 +183,21 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
} }
} }
if let fromView = fromView, fromView.filteredEntries.isEmpty { var fromEmptyView = false
options.remove(.AnimateInsertion) if let fromView = fromView {
options.remove(.AnimateAlpha) if fromView.filteredEntries.isEmpty {
options.remove(.AnimateInsertion)
options.remove(.AnimateAlpha)
fromEmptyView = true
}
} else {
fromEmptyView = true
}
if fromEmptyView && scrollToItem == nil && toView.filteredEntries.count >= 2 {
if case .SearchEntry = toView.filteredEntries[toView.filteredEntries.count - 1] {
scrollToItem = ListViewScrollToItem(index: 1, position: .top(0.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
}
} }
subscriber.putNext(ChatListNodeViewTransition(chatListView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange)) subscriber.putNext(ChatListNodeViewTransition(chatListView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange))

View File

@ -58,7 +58,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
} }
averageAspectRatio += aspectRatio averageAspectRatio += aspectRatio
return MosaicItemInfo(index: index, imageSize: itemSize, aspectRatio: aspectRatio , layoutFrame: CGRect(), position: []) return MosaicItemInfo(index: index, imageSize: itemSize, aspectRatio: aspectRatio, layoutFrame: CGRect(), position: [])
} }
let minWidth: CGFloat = 68.0 let minWidth: CGFloat = 68.0
@ -71,7 +71,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
if itemInfos.count == 2 { if itemInfos.count == 2 {
if proportions == "ww" && averageAspectRatio > 1.4 * maxAspectRatio && itemInfos[1].aspectRatio - itemInfos[0].aspectRatio < 0.2 { if proportions == "ww" && averageAspectRatio > 1.4 * maxAspectRatio && itemInfos[1].aspectRatio - itemInfos[0].aspectRatio < 0.2 {
let width = maxSize.width let width = maxSize.width
let height = floorToScreenPixels(min(width / itemInfos[0].aspectRatio, min(width / itemInfos[1].aspectRatio, (maxSize.height - spacing) / 2.0))) let height = floor(min(width / itemInfos[0].aspectRatio, min(width / itemInfos[1].aspectRatio, (maxSize.height - spacing) / 2.0)))
itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: width, height: height) itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
itemInfos[0].position = [.top, .left, .right] itemInfos[0].position = [.top, .left, .right]
@ -80,7 +80,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
itemInfos[1].position = [.bottom, .left, .right] itemInfos[1].position = [.bottom, .left, .right]
} else if proportions == "ww" || proportions == "qq" { } else if proportions == "ww" || proportions == "qq" {
let width = (maxSize.width - spacing) / 2.0 let width = (maxSize.width - spacing) / 2.0
let height = floorToScreenPixels(min(width / itemInfos[0].aspectRatio, min(width / itemInfos[1].aspectRatio, maxSize.height))) let height = floor(min(width / itemInfos[0].aspectRatio, min(width / itemInfos[1].aspectRatio, maxSize.height)))
itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: width, height: height) itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
itemInfos[0].position = [.top, .left, .bottom] itemInfos[0].position = [.top, .left, .bottom]
@ -88,9 +88,9 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
itemInfos[1].layoutFrame = CGRect(x: width + spacing, y: 0.0, width: width, height: height) itemInfos[1].layoutFrame = CGRect(x: width + spacing, y: 0.0, width: width, height: height)
itemInfos[1].position = [.top, .right, .bottom] itemInfos[1].position = [.top, .right, .bottom]
} else { } else {
let secondWidth = floorToScreenPixels(min(0.5 * (maxSize.width - spacing), round((maxSize.width - spacing) / itemInfos[0].aspectRatio / (1.0 / itemInfos[0].aspectRatio + 1.0 / itemInfos[1].aspectRatio)))) let secondWidth = floor(min(0.5 * (maxSize.width - spacing), round((maxSize.width - spacing) / itemInfos[0].aspectRatio / (1.0 / itemInfos[0].aspectRatio + 1.0 / itemInfos[1].aspectRatio))))
let firstWidth = maxSize.width - secondWidth - spacing let firstWidth = maxSize.width - secondWidth - spacing
let height = floorToScreenPixels(min(maxSize.height, round(min(firstWidth / itemInfos[0].aspectRatio, secondWidth / itemInfos[1].aspectRatio)))) let height = floor(min(maxSize.height, round(min(firstWidth / itemInfos[0].aspectRatio, secondWidth / itemInfos[1].aspectRatio))))
itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: firstWidth, height: height) itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: firstWidth, height: height)
itemInfos[0].position = [.top, .left, .bottom] itemInfos[0].position = [.top, .left, .bottom]
@ -117,7 +117,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
itemInfos[2].position = [.right, .bottom] itemInfos[2].position = [.right, .bottom]
} else { } else {
var width = maxSize.width var width = maxSize.width
let firstHeight = floorToScreenPixels(min(width / itemInfos[0].aspectRatio, (maxSize.height - spacing) * 0.66)) let firstHeight = floor(min(width / itemInfos[0].aspectRatio, (maxSize.height - spacing) * 0.66))
itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: width, height: firstHeight) itemInfos[0].layoutFrame = CGRect(x: 0.0, y: 0.0, width: width, height: firstHeight)
itemInfos[0].position = [.top, .left, .right] itemInfos[0].position = [.top, .left, .right]
@ -156,8 +156,8 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
itemInfos[0].position = [.top, .left, .bottom] itemInfos[0].position = [.top, .left, .bottom]
var w = round((maxSize.height - 2 * spacing) / (1.0 / itemInfos[1].aspectRatio + 1.0 / itemInfos[2].aspectRatio + 1.0 / itemInfos[3].aspectRatio)) var w = round((maxSize.height - 2 * spacing) / (1.0 / itemInfos[1].aspectRatio + 1.0 / itemInfos[2].aspectRatio + 1.0 / itemInfos[3].aspectRatio))
let h0 = floorToScreenPixels(w / itemInfos[1].aspectRatio) let h0 = floor(w / itemInfos[1].aspectRatio)
let h1 = floorToScreenPixels(w / itemInfos[2].aspectRatio) let h1 = floor(w / itemInfos[2].aspectRatio)
let h2 = h - h0 - h1 - 2.0 * spacing let h2 = h - h0 - h1 - 2.0 * spacing
w = max(minWidth, min(maxSize.width - w0 - spacing, w)) w = max(minWidth, min(maxSize.width - w0 - spacing, w))
itemInfos[1].layoutFrame = CGRect(x: w0 + spacing, y: 0.0, width: w, height: h0) itemInfos[1].layoutFrame = CGRect(x: w0 + spacing, y: 0.0, width: w, height: h0)
@ -207,8 +207,6 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
} }
addAttempt([firstLine, croppedRatios.count - firstLine], [multiHeight(Array(croppedRatios[0..<firstLine])), multiHeight(Array(croppedRatios[firstLine..<croppedRatios.count]))], &attempts) addAttempt([firstLine, croppedRatios.count - firstLine], [multiHeight(Array(croppedRatios[0..<firstLine])), multiHeight(Array(croppedRatios[firstLine..<croppedRatios.count]))], &attempts)
//addAttempt(@[@(firstLine), @(croppedRatios.count - firstLine)], @[multiHeight([croppedRatios subarrayWithRange:NSMakeRange(0, firstLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine, croppedRatios.count - firstLine)])])
} }
for firstLine in 1 ..< croppedRatios.count - 1 { for firstLine in 1 ..< croppedRatios.count - 1 {
@ -219,8 +217,6 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
} }
addAttempt([firstLine, secondLine, thirdLine], [multiHeight(Array(croppedRatios[0 ..< firstLine])), multiHeight(Array(croppedRatios[firstLine ..< croppedRatios.count - thirdLine])), multiHeight(Array(croppedRatios[firstLine + secondLine ..< croppedRatios.count]))], &attempts) addAttempt([firstLine, secondLine, thirdLine], [multiHeight(Array(croppedRatios[0 ..< firstLine])), multiHeight(Array(croppedRatios[firstLine ..< croppedRatios.count - thirdLine])), multiHeight(Array(croppedRatios[firstLine + secondLine ..< croppedRatios.count]))], &attempts)
//addAttempt(@[@(firstLine), @(secondLine), @(thirdLine)], @[multiHeight([croppedRatios subarrayWithRange:NSMakeRange(0, firstLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine, croppedRatios.count - firstLine - thirdLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine + secondLine, croppedRatios.count - firstLine - secondLine)])])
} }
} }
@ -237,14 +233,12 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
} }
addAttempt([firstLine, secondLine, thirdLine, fourthLine], [multiHeight(Array(croppedRatios[0 ..< firstLine])), multiHeight(Array(croppedRatios[firstLine ..< croppedRatios.count - thirdLine - fourthLine])), multiHeight(Array(croppedRatios[firstLine + secondLine ..< croppedRatios.count - fourthLine])), multiHeight(Array(croppedRatios[firstLine + secondLine + thirdLine ..< croppedRatios.count]))], &attempts) addAttempt([firstLine, secondLine, thirdLine, fourthLine], [multiHeight(Array(croppedRatios[0 ..< firstLine])), multiHeight(Array(croppedRatios[firstLine ..< croppedRatios.count - thirdLine - fourthLine])), multiHeight(Array(croppedRatios[firstLine + secondLine ..< croppedRatios.count - fourthLine])), multiHeight(Array(croppedRatios[firstLine + secondLine + thirdLine ..< croppedRatios.count]))], &attempts)
//addAttempt(@[@(firstLine), @(secondLine), @(thirdLine), @(fourthLine)], @[multiHeight([croppedRatios subarrayWithRange:NSMakeRange(0, firstLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine, croppedRatios.count - firstLine - thirdLine - fourthLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine + secondLine, croppedRatios.count - firstLine - secondLine - fourthLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine + secondLine + thirdLine, croppedRatios.count - firstLine - secondLine - thirdLine)])])
} }
} }
} }
} }
let maxHeight = maxSize.width / 3.0 * 4.0 let maxHeight = floor(maxSize.width / 3.0 * 4.0)
var optimal: MosaicLayoutAttempt? = nil var optimal: MosaicLayoutAttempt? = nil
var optimalDiff: CGFloat = 0.0 var optimalDiff: CGFloat = 0.0
for attempt in attempts { for attempt in attempts {
@ -252,7 +246,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
var minLineHeight: CGFloat = .greatestFiniteMagnitude var minLineHeight: CGFloat = .greatestFiniteMagnitude
var maxLineHeight: CGFloat = 0.0 var maxLineHeight: CGFloat = 0.0
for h in attempt.heights { for h in attempt.heights {
totalHeight += h totalHeight += floor(h)
if totalHeight < minLineHeight { if totalHeight < minLineHeight {
minLineHeight = totalHeight minLineHeight = totalHeight
} }
@ -284,7 +278,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
if let optimal = optimal { if let optimal = optimal {
for i in 0 ..< optimal.lineCounts.count { for i in 0 ..< optimal.lineCounts.count {
let count = optimal.lineCounts[i] let count = optimal.lineCounts[i]
let lineHeight = optimal.heights[i] let lineHeight = ceil(optimal.heights[i])
var x: CGFloat = 0.0 var x: CGFloat = 0.0
var positionFlags: MosaicItemPosition = [] var positionFlags: MosaicItemPosition = []
@ -310,7 +304,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
} }
let ratio = croppedRatios[index] let ratio = croppedRatios[index]
let width = ratio * lineHeight let width = ceil(ratio * lineHeight)
itemInfos[index].layoutFrame = CGRect(x: x, y: y, width: width, height: lineHeight) itemInfos[index].layoutFrame = CGRect(x: x, y: y, width: width, height: lineHeight)
itemInfos[index].position = innerPositionFlags itemInfos[index].position = innerPositionFlags
@ -320,6 +314,31 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
y += lineHeight + spacing y += lineHeight + spacing
} }
index = 0
var maxWidth: CGFloat = 0.0
for i in 0 ..< optimal.lineCounts.count {
let count = optimal.lineCounts[i]
for k in 0 ..< count {
if k == count - 1 {
maxWidth = max(maxWidth, itemInfos[index].layoutFrame.maxX)
}
index += 1
}
}
index = 0
for i in 0 ..< optimal.lineCounts.count {
let count = optimal.lineCounts[i]
for k in 0 ..< count {
if k == count - 1 {
var frame = itemInfos[index].layoutFrame
frame.size.width = max(frame.width, maxWidth - frame.minX)
itemInfos[index].layoutFrame = frame
}
index += 1
}
}
} }
} }

View File

@ -258,7 +258,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|> map { status in |> map { status in
switch status.mediaStatus { switch status.mediaStatus {
case let .fetchStatus(fetchStatus): case let .fetchStatus(fetchStatus):
if !voice { if !voice && !message.flags.isSending {
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus) return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
} else { } else {
return FileMediaResourceStatus(mediaStatus: .fetchStatus(fetchStatus), fetchStatus: status.fetchStatus) return FileMediaResourceStatus(mediaStatus: .fetchStatus(fetchStatus), fetchStatus: status.fetchStatus)
@ -619,7 +619,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let state: RadialStatusNodeState let state: RadialStatusNodeState
var streamingState: RadialStatusNodeState = .none var streamingState: RadialStatusNodeState = .none
if isAudio && !isVoice { let isSending = message.flags.isSending
if isAudio && !isVoice && !isSending {
let streamingStatusForegroundColor: UIColor = incoming ? bubbleTheme.incomingAccentControlColor : bubbleTheme.outgoingAccentControlColor let streamingStatusForegroundColor: UIColor = incoming ? bubbleTheme.incomingAccentControlColor : bubbleTheme.outgoingAccentControlColor
let streamingStatusBackgroundColor: UIColor = incoming ? bubbleTheme.incomingMediaInactiveControlColor : bubbleTheme.outgoingMediaInactiveControlColor let streamingStatusBackgroundColor: UIColor = incoming ? bubbleTheme.incomingMediaInactiveControlColor : bubbleTheme.outgoingMediaInactiveControlColor
switch resourceStatus.fetchStatus { switch resourceStatus.fetchStatus {

View File

@ -429,7 +429,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
strongSelf.currentImageArguments = arguments strongSelf.currentImageArguments = arguments
imageApply() imageApply()
strongSelf.statusNode?.position = CGPoint(x: imageFrame.midX, y: imageFrame.midY) if let statusNode = strongSelf.statusNode {
var statusFrame = statusNode.frame
statusFrame.origin.x = floor(imageFrame.midX - statusFrame.width / 2.0)
statusFrame.origin.y = floor(imageFrame.midY - statusFrame.height / 2.0)
}
if let replaceVideoNode = replaceVideoNode { if let replaceVideoNode = replaceVideoNode {
if let videoNode = strongSelf.videoNode { if let videoNode = strongSelf.videoNode {
@ -581,8 +585,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if progressRequired { if progressRequired {
if self.statusNode == nil { if self.statusNode == nil {
let statusNode = RadialStatusNode(backgroundNodeColor: theme.chat.bubble.mediaOverlayControlBackgroundColor) let statusNode = RadialStatusNode(backgroundNodeColor: theme.chat.bubble.mediaOverlayControlBackgroundColor)
statusNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: radialStatusSize, height: radialStatusSize)) let imagePosition = self.imageNode.position
statusNode.position = self.imageNode.position statusNode.frame = CGRect(origin: CGPoint(x: floor(imagePosition.x - radialStatusSize / 2.0), y: floor(imagePosition.y - radialStatusSize / 2.0)), size: CGSize(width: radialStatusSize, height: radialStatusSize))
self.statusNode = statusNode self.statusNode = statusNode
self.addSubnode(statusNode) self.addSubnode(statusNode)
} }

View File

@ -66,7 +66,7 @@ struct ChatMessageItemLayoutConstants {
self.bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 40.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0)) self.bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 40.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0))
self.text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0)) self.text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0))
self.image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 1.5, left: 1.5, bottom: 1.5, right: 1.5), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 17.0, mergedCornerRadius: 5.0, contentMergedCornerRadius: 5.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 74.0, height: 74.0)) self.image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 1.0 + UIScreenPixel, left: 1.0 + UIScreenPixel, bottom: 1.0 + UIScreenPixel, right: 1.0 + UIScreenPixel), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 17.0, mergedCornerRadius: 5.0, contentMergedCornerRadius: 5.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 74.0, height: 74.0))
self.file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0)) self.file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0))
self.instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0)) self.instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0))
} }

View File

@ -587,7 +587,7 @@ final class ContactListNode: ASDisplayNode {
super.init() super.init()
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
//self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
self.selectionStateValue = selectionState self.selectionStateValue = selectionState
self.selectionStatePromise.set(.single(selectionState)) self.selectionStatePromise.set(.single(selectionState))

View File

@ -444,7 +444,7 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName
hasPhotos = true hasPhotos = true
} }
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: hasPhotos, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false)! let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: hasPhotos, hasViewButton: hasPhotos, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
let _ = currentAvatarMixin.swap(mixin) let _ = currentAvatarMixin.swap(mixin)
mixin.didFinishWithImage = { image in mixin.didFinishWithImage = { image in
if let image = image { if let image = image {
@ -488,6 +488,27 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName
} }
})) }))
} }
mixin.didFinishWithView = {
let _ = currentAvatarMixin.swap(nil)
let _ = (account.postbox.loadedPeerWithId(account.peerId)
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
if peer.smallProfileImage != nil {
let galleryController = AvatarGalleryController(account: account, peer: peer, replaceRootController: { controller, ready in
})
/*hiddenAvatarRepresentationDisposable.set((galleryController.hiddenMedia |> deliverOnMainQueue).start(next: { entry in
avatarAndNameInfoContext.hiddenAvatarRepresentation = entry?.representations.first
updateHiddenAvatarImpl?()
}))*/
presentControllerImpl?(galleryController, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
return nil
}))
} else {
changeProfilePhotoImpl?()
}
})
}
mixin.didDismiss = { [weak legacyController] in mixin.didDismiss = { [weak legacyController] in
let _ = currentAvatarMixin.swap(nil) let _ = currentAvatarMixin.swap(nil)
legacyController?.dismiss() legacyController?.dismiss()

View File

@ -53,7 +53,7 @@ func messageFileMediaResourceStatus(account: Account, file: TelegramMediaFile, m
return status?.status return status?.status
} }
if message.flags.isSending { if message.flags.isSending {
return combineLatest(messageMediaFileStatus(account: account, messageId: message.id, file: file), account.pendingMessageManager.pendingMessageStatus(message.id), playbackStatus) return combineLatest(messageMediaFileStatus(account: account, messageId: message.id, file: file), account.pendingMessageManager.pendingMessageStatus(message.id), playbackStatus)
|> map { resourceStatus, pendingStatus, playbackStatus -> FileMediaResourceStatus in |> map { resourceStatus, pendingStatus, playbackStatus -> FileMediaResourceStatus in
let mediaStatus: FileMediaResourceMediaStatus let mediaStatus: FileMediaResourceMediaStatus

View File

@ -3,7 +3,7 @@ import Display
import SwiftSignalKit import SwiftSignalKit
import LegacyComponents import LegacyComponents
func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, completion: @escaping (UIImage) -> Void) { func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void) {
let legacyController = LegacyController(presentation: .custom, theme: theme) let legacyController = LegacyController(presentation: .custom, theme: theme)
legacyController.statusBar.statusBarStyle = .Ignore legacyController.statusBar.statusBarStyle = .Ignore
@ -16,7 +16,7 @@ func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: P
present(legacyController, nil) present(legacyController, nil)
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: false, hasViewButton: false, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false, signup: signup)! let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: false, hasViewButton: openCurrent != nil, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false, signup: signup)!
let _ = holder.swap(mixin) let _ = holder.swap(mixin)
mixin.didFinishWithImage = { image in mixin.didFinishWithImage = { image in
guard let image = image else { guard let image = image else {
@ -24,6 +24,9 @@ func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: P
} }
completion(image) completion(image)
} }
mixin.didFinishWithView = { [weak legacyController] in
openCurrent?()
}
mixin.didDismiss = { [weak legacyController] in mixin.didDismiss = { [weak legacyController] in
let _ = holder.swap(nil) let _ = holder.swap(nil)
legacyController?.dismiss() legacyController?.dismiss()

View File

@ -261,38 +261,65 @@ public final class PresentationCallManager {
return .alreadyInProgress(call.peerId) return .alreadyInProgress(call.peerId)
} }
if let _ = callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings?.0) { if let _ = callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings?.0) {
let (presentationData, present, openSettings) = self.getDeviceAccessData() let begin: () -> Void = { [weak self] in
guard let strongSelf = self else {
let accessEnabledSignal: Signal<Bool, NoError> = Signal { subscriber in
DeviceAccess.authorizeAccess(to: .microphone(.voiceCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
}, { value in
subscriber.putNext(value)
subscriber.putCompletion()
})
return EmptyDisposable
}
|> runOn(Queue.mainQueue())
let postbox = self.account.postbox
self.startCallDisposable.set((accessEnabledSignal
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
if !accessEnabled {
return .single(nil)
}
return postbox.loadedPeerWithId(peerId)
|> take(1)
|> map(Optional.init)
}
|> deliverOnMainQueue).start(next: { [weak self] peer in
guard let strongSelf = self, let peer = peer else {
return return
} }
strongSelf.callKitIntegration?.startCall(peerId: peerId, displayTitle: peer.displayTitle) let (presentationData, present, openSettings) = strongSelf.getDeviceAccessData()
}))
let accessEnabledSignal: Signal<Bool, NoError> = Signal { subscriber in
DeviceAccess.authorizeAccess(to: .microphone(.voiceCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
}, { value in
subscriber.putNext(value)
subscriber.putCompletion()
})
return EmptyDisposable
}
|> runOn(Queue.mainQueue())
let postbox = strongSelf.account.postbox
strongSelf.startCallDisposable.set((accessEnabledSignal
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
if !accessEnabled {
return .single(nil)
}
return postbox.loadedPeerWithId(peerId)
|> take(1)
|> map(Optional.init)
}
|> deliverOnMainQueue).start(next: { peer in
guard let strongSelf = self, let peer = peer else {
return
}
strongSelf.callKitIntegration?.startCall(peerId: peerId, displayTitle: peer.displayTitle)
}))
}
if let currentCall = self.currentCall {
self.callKitIntegration?.dropCall(uuid: currentCall.internalId)
self.startCallDisposable.set((currentCall.hangUp()
|> deliverOnMainQueue).start(next: { _ in
begin()
}))
} else {
begin()
}
} else { } else {
let _ = self.startCall(peerId: peerId).start() let begin: () -> Void = { [weak self] in
guard let strongSelf = self else {
return
}
let _ = strongSelf.startCall(peerId: peerId).start()
}
if let currentCall = self.currentCall {
self.startCallDisposable.set((currentCall.hangUp()
|> deliverOnMainQueue).start(next: { _ in
begin()
}))
} else {
begin()
}
} }
return .requested return .requested
} }

View File

@ -6,4 +6,5 @@ public extension PresentationSurfaceLevel {
static let calls = PresentationSurfaceLevel(rawValue: 1) static let calls = PresentationSurfaceLevel(rawValue: 1)
static let overlayMedia = PresentationSurfaceLevel(rawValue: 2) static let overlayMedia = PresentationSurfaceLevel(rawValue: 2)
static let notifications = PresentationSurfaceLevel(rawValue: 3) static let notifications = PresentationSurfaceLevel(rawValue: 3)
static let passcode = PresentationSurfaceLevel(rawValue: 4)
} }

View File

@ -30,4 +30,7 @@ class SearchDisplayControllerContentNode: ASDisplayNode {
func previewViewAndActionAtLocation(_ location: CGPoint) -> (UIView, Any)? { func previewViewAndActionAtLocation(_ location: CGPoint) -> (UIView, Any)? {
return nil return nil
} }
func scrollToTop() {
}
} }

View File

@ -114,6 +114,9 @@ public final class TelegramApplicationContext {
public var isCurrent: Bool = false { public var isCurrent: Bool = false {
didSet { didSet {
self.mediaManager?.isCurrent = self.isCurrent self.mediaManager?.isCurrent = self.isCurrent
if !self.isCurrent {
self.callManager = nil
}
} }
} }

View File

@ -50,9 +50,8 @@ public class TransformImageNode: ASDisplayNode {
public func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, attemptSynchronously: Bool = false, dispatchOnDisplayLink: Bool = true) { public func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, attemptSynchronously: Bool = false, dispatchOnDisplayLink: Bool = true) {
let argumentsPromise = self.argumentsPromise let argumentsPromise = self.argumentsPromise
var shouldAttemptSynchronously = attemptSynchronously let data = combineLatest(signal, argumentsPromise.get())
let result = combineLatest(signal, argumentsPromise.get()) /*|> mapToSignal { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> in
|> mapToSignal { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> in
let result: Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> = .single((transform, arguments)) let result: Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> = .single((transform, arguments))
if shouldAttemptSynchronously { if shouldAttemptSynchronously {
shouldAttemptSynchronously = false shouldAttemptSynchronously = false
@ -61,7 +60,17 @@ public class TransformImageNode: ASDisplayNode {
return result return result
|> deliverOn(Queue.concurrentDefaultQueue()) |> deliverOn(Queue.concurrentDefaultQueue())
} }
}*/
let resultData: Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError>
if attemptSynchronously {
resultData = data
} else {
resultData = data
|> deliverOn(Queue.concurrentDefaultQueue())
} }
let result = resultData
|> mapToThrottled { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments, UIImage?)?, NoError> in |> mapToThrottled { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments, UIImage?)?, NoError> in
return deferred { return deferred {
if let context = transform(arguments) { if let context = transform(arguments) {
@ -102,7 +111,7 @@ public class TransformImageNode: ASDisplayNode {
} }
} }
} }
if dispatchOnDisplayLink { if dispatchOnDisplayLink && !attemptSynchronously {
displayLinkDispatcher.dispatch { displayLinkDispatcher.dispatch {
apply() apply()
} }