mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
f6a88c9c13
commit
c8a943a017
@ -68,7 +68,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: true, theme: defaultPresentationTheme, present: { c, a in
|
||||
self?.view.endEditing(true)
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, completion: { image in
|
||||
}, openCurrent: nil, completion: { image in
|
||||
self?.controllerNode.currentPhoto = image
|
||||
})
|
||||
})
|
||||
|
@ -4,9 +4,9 @@ import AVFoundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
public final class CallKitIntegration {
|
||||
private let providerDelegate: AnyObject
|
||||
private var sharedProviderDelegate: AnyObject?
|
||||
|
||||
public final class CallKitIntegration {
|
||||
public static var isAvailable: Bool {
|
||||
#if targetEnvironment(simulator)
|
||||
return false
|
||||
@ -34,7 +34,10 @@ public final class CallKitIntegration {
|
||||
#else
|
||||
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
@ -43,31 +46,31 @@ public final class CallKitIntegration {
|
||||
|
||||
func startCall(peerId: PeerId, displayTitle: String) {
|
||||
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) {
|
||||
if #available(iOSApplicationExtension 10.0, *) {
|
||||
(self.providerDelegate as! CallKitProviderDelegate).answerCall(uuid: uuid)
|
||||
(sharedProviderDelegate as? CallKitProviderDelegate)?.answerCall(uuid: uuid)
|
||||
}
|
||||
}
|
||||
|
||||
func dropCall(uuid: UUID) {
|
||||
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)?) {
|
||||
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) {
|
||||
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 callController = CXCallController()
|
||||
|
||||
private let startCall: (UUID, String) -> Signal<Bool, NoError>
|
||||
private let answerCall: (UUID) -> Void
|
||||
private let endCall: (UUID) -> Signal<Bool, NoError>
|
||||
private let setCallMuted: (UUID, Bool) -> Void
|
||||
private let audioSessionActivationChanged: (Bool) -> Void
|
||||
private var startCall: ((UUID, String) -> Signal<Bool, NoError>)?
|
||||
private var answerCall: ((UUID) -> Void)?
|
||||
private var endCall: ((UUID) -> Signal<Bool, NoError>)?
|
||||
private var setCallMuted: ((UUID, Bool) -> Void)?
|
||||
private var audioSessionActivationChanged: ((Bool) -> Void)?
|
||||
|
||||
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.startCall = startCall
|
||||
self.answerCall = answerCall
|
||||
self.endCall = endCall
|
||||
self.setCallMuted = setCallMuted
|
||||
self.audioSessionActivationChanged = audioSessionActivationChanged
|
||||
|
||||
self.provider = CXProvider(configuration: CallKitProviderDelegate.providerConfiguration)
|
||||
|
||||
super.init()
|
||||
|
||||
self.provider.setDelegate(self, queue: nil)
|
||||
}
|
||||
|
||||
static var providerConfiguration: CXProviderConfiguration {
|
||||
@ -184,19 +189,16 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
}
|
||||
|
||||
func providerDidReset(_ provider: CXProvider) {
|
||||
/*stopAudio()
|
||||
|
||||
for call in callManager.calls {
|
||||
call.end()
|
||||
}
|
||||
|
||||
callManager.removeAllCalls()*/
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
||||
guard let startCall = self.startCall else {
|
||||
action.fail()
|
||||
return
|
||||
}
|
||||
let disposable = MetaDisposable()
|
||||
self.disposableSet.add(disposable)
|
||||
disposable.set((self.startCall(action.callUUID, action.handle.value)
|
||||
disposable.set((startCall(action.callUUID, action.handle.value)
|
||||
|> deliverOnMainQueue
|
||||
|> afterDisposed { [weak self, weak disposable] in
|
||||
if let strongSelf = self, let disposable = disposable {
|
||||
@ -212,15 +214,22 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
||||
guard let endCall = self.endCall else {
|
||||
action.fail()
|
||||
return
|
||||
}
|
||||
let disposable = MetaDisposable()
|
||||
self.disposableSet.add(disposable)
|
||||
disposable.set((self.endCall(action.callUUID)
|
||||
disposable.set((endCall(action.callUUID)
|
||||
|> deliverOnMainQueue
|
||||
|> afterDisposed { [weak self, weak disposable] in
|
||||
if let strongSelf = self, let disposable = disposable {
|
||||
@ -236,18 +245,22 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
||||
self.audioSessionActivationChanged(true)
|
||||
self.audioSessionActivePromise.set(true)
|
||||
self.audioSessionActivationChanged?(true)
|
||||
self.audioSessionActivePromise?.set(true)
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
||||
self.audioSessionActivationChanged(false)
|
||||
self.audioSessionActivePromise.set(false)
|
||||
self.audioSessionActivationChanged?(false)
|
||||
self.audioSessionActivePromise?.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1727,7 +1727,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID
|
||||
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
|
||||
return state.updatedPinnedMessageId(pinnedMessageId).updatedPinnedMessage(pinnedMessage).updatedPeerIsBlocked(peerIsBlocked).updatedCanReportPeer(canReport).updatedCallsAvailable(callsAvailable).updatedCallsPrivate(callsPrivate).updatedTitlePanelContext({ context in
|
||||
if pinnedMessageId != nil {
|
||||
|
@ -260,6 +260,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strongSelf.requestLayout(.animated(duration: 0.1, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
var lastSendTimestamp = 0.0
|
||||
self.textInputPanelNode?.sendMessage = { [weak self] in
|
||||
if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode {
|
||||
if textInputPanelNode.textInputNode?.isFirstResponder() ?? false {
|
||||
@ -274,6 +275,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if let _ = effectivePresentationInterfaceState.interfaceState.editMessage {
|
||||
strongSelf.interfaceInteraction?.editMessage()
|
||||
} else {
|
||||
let timestamp = CACurrentMediaTime()
|
||||
if lastSendTimestamp + 0.15 > timestamp {
|
||||
return
|
||||
}
|
||||
lastSendTimestamp = timestamp
|
||||
|
||||
strongSelf.updateTypingActivity(false)
|
||||
|
||||
var messages: [EnqueueMessage] = []
|
||||
|
@ -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.scrollToTop = { [weak self] in
|
||||
self?.chatListDisplayNode.chatListNode.scrollToPosition(.top)
|
||||
self?.chatListDisplayNode.scrollToTop()
|
||||
}
|
||||
self.scrollToTopWithTabBar = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -197,4 +197,12 @@ class ChatListControllerNode: ASDisplayNode {
|
||||
self.searchDisplayController = nil
|
||||
}
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
if let searchDisplayController = self.searchDisplayController {
|
||||
searchDisplayController.contentNode.scrollToTop()
|
||||
} else {
|
||||
self.chatListNode.scrollToPosition(.top)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.recentListNode = ListView()
|
||||
self.recentListNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
|
||||
self.statePromise = ValuePromise(self.stateValue, ignoreRepeated: true)
|
||||
|
||||
@ -1015,6 +1017,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
|
||||
private func updateTheme(theme: PresentationTheme) {
|
||||
self.backgroundColor = theme.chatList.backgroundColor
|
||||
self.recentListNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||
self.listNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,9 +183,21 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
||||
}
|
||||
}
|
||||
|
||||
if let fromView = fromView, fromView.filteredEntries.isEmpty {
|
||||
var fromEmptyView = false
|
||||
if let fromView = fromView {
|
||||
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))
|
||||
|
@ -58,7 +58,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
}
|
||||
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
|
||||
@ -71,7 +71,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
if itemInfos.count == 2 {
|
||||
if proportions == "ww" && averageAspectRatio > 1.4 * maxAspectRatio && itemInfos[1].aspectRatio - itemInfos[0].aspectRatio < 0.2 {
|
||||
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].position = [.top, .left, .right]
|
||||
@ -80,7 +80,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
itemInfos[1].position = [.bottom, .left, .right]
|
||||
} else if proportions == "ww" || proportions == "qq" {
|
||||
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].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].position = [.top, .right, .bottom]
|
||||
} 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 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].position = [.top, .left, .bottom]
|
||||
@ -117,7 +117,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
itemInfos[2].position = [.right, .bottom]
|
||||
} else {
|
||||
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].position = [.top, .left, .right]
|
||||
|
||||
@ -156,8 +156,8 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
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))
|
||||
let h0 = floorToScreenPixels(w / itemInfos[1].aspectRatio)
|
||||
let h1 = floorToScreenPixels(w / itemInfos[2].aspectRatio)
|
||||
let h0 = floor(w / itemInfos[1].aspectRatio)
|
||||
let h1 = floor(w / itemInfos[2].aspectRatio)
|
||||
let h2 = h - h0 - h1 - 2.0 * spacing
|
||||
w = max(minWidth, min(maxSize.width - w0 - spacing, w))
|
||||
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([croppedRatios subarrayWithRange:NSMakeRange(0, firstLine)]), multiHeight([croppedRatios subarrayWithRange:NSMakeRange(firstLine, croppedRatios.count - firstLine)])])
|
||||
}
|
||||
|
||||
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([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([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 optimalDiff: CGFloat = 0.0
|
||||
for attempt in attempts {
|
||||
@ -252,7 +246,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
var minLineHeight: CGFloat = .greatestFiniteMagnitude
|
||||
var maxLineHeight: CGFloat = 0.0
|
||||
for h in attempt.heights {
|
||||
totalHeight += h
|
||||
totalHeight += floor(h)
|
||||
if totalHeight < minLineHeight {
|
||||
minLineHeight = totalHeight
|
||||
}
|
||||
@ -284,7 +278,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
if let optimal = optimal {
|
||||
for i in 0 ..< optimal.lineCounts.count {
|
||||
let count = optimal.lineCounts[i]
|
||||
let lineHeight = optimal.heights[i]
|
||||
let lineHeight = ceil(optimal.heights[i])
|
||||
var x: CGFloat = 0.0
|
||||
|
||||
var positionFlags: MosaicItemPosition = []
|
||||
@ -310,7 +304,7 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
}
|
||||
|
||||
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].position = innerPositionFlags
|
||||
|
||||
@ -320,6 +314,31 @@ func chatMessageBubbleMosaicLayout(maxSize: CGSize, itemSizes: [CGSize]) -> ([(C
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|> map { status in
|
||||
switch status.mediaStatus {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
if !voice {
|
||||
if !voice && !message.flags.isSending {
|
||||
return FileMediaResourceStatus(mediaStatus: .fetchStatus(.Local), fetchStatus: status.fetchStatus)
|
||||
} else {
|
||||
return FileMediaResourceStatus(mediaStatus: .fetchStatus(fetchStatus), fetchStatus: status.fetchStatus)
|
||||
@ -619,7 +619,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
let state: RadialStatusNodeState
|
||||
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 streamingStatusBackgroundColor: UIColor = incoming ? bubbleTheme.incomingMediaInactiveControlColor : bubbleTheme.outgoingMediaInactiveControlColor
|
||||
switch resourceStatus.fetchStatus {
|
||||
|
@ -429,7 +429,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
strongSelf.currentImageArguments = arguments
|
||||
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 videoNode = strongSelf.videoNode {
|
||||
@ -581,8 +585,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
||||
if progressRequired {
|
||||
if self.statusNode == nil {
|
||||
let statusNode = RadialStatusNode(backgroundNodeColor: theme.chat.bubble.mediaOverlayControlBackgroundColor)
|
||||
statusNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: radialStatusSize, height: radialStatusSize))
|
||||
statusNode.position = self.imageNode.position
|
||||
let imagePosition = 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.addSubnode(statusNode)
|
||||
}
|
||||
|
@ -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.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.instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0))
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ final class ContactListNode: ASDisplayNode {
|
||||
super.init()
|
||||
|
||||
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.selectionStatePromise.set(.single(selectionState))
|
||||
|
@ -444,7 +444,7 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName
|
||||
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)
|
||||
mixin.didFinishWithImage = { image in
|
||||
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
|
||||
let _ = currentAvatarMixin.swap(nil)
|
||||
legacyController?.dismiss()
|
||||
|
@ -3,7 +3,7 @@ import Display
|
||||
import SwiftSignalKit
|
||||
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)
|
||||
legacyController.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
@ -16,7 +16,7 @@ func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: P
|
||||
|
||||
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)
|
||||
mixin.didFinishWithImage = { image in
|
||||
guard let image = image else {
|
||||
@ -24,6 +24,9 @@ func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: P
|
||||
}
|
||||
completion(image)
|
||||
}
|
||||
mixin.didFinishWithView = { [weak legacyController] in
|
||||
openCurrent?()
|
||||
}
|
||||
mixin.didDismiss = { [weak legacyController] in
|
||||
let _ = holder.swap(nil)
|
||||
legacyController?.dismiss()
|
||||
|
@ -261,7 +261,11 @@ public final class PresentationCallManager {
|
||||
return .alreadyInProgress(call.peerId)
|
||||
}
|
||||
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 {
|
||||
return
|
||||
}
|
||||
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
|
||||
@ -275,8 +279,8 @@ public final class PresentationCallManager {
|
||||
return EmptyDisposable
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
let postbox = self.account.postbox
|
||||
self.startCallDisposable.set((accessEnabledSignal
|
||||
let postbox = strongSelf.account.postbox
|
||||
strongSelf.startCallDisposable.set((accessEnabledSignal
|
||||
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
|
||||
if !accessEnabled {
|
||||
return .single(nil)
|
||||
@ -285,14 +289,37 @@ public final class PresentationCallManager {
|
||||
|> take(1)
|
||||
|> map(Optional.init)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
|> 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 {
|
||||
let _ = self.startCall(peerId: peerId).start()
|
||||
begin()
|
||||
}
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
@ -6,4 +6,5 @@ public extension PresentationSurfaceLevel {
|
||||
static let calls = PresentationSurfaceLevel(rawValue: 1)
|
||||
static let overlayMedia = PresentationSurfaceLevel(rawValue: 2)
|
||||
static let notifications = PresentationSurfaceLevel(rawValue: 3)
|
||||
static let passcode = PresentationSurfaceLevel(rawValue: 4)
|
||||
}
|
||||
|
@ -30,4 +30,7 @@ class SearchDisplayControllerContentNode: ASDisplayNode {
|
||||
func previewViewAndActionAtLocation(_ location: CGPoint) -> (UIView, Any)? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +114,9 @@ public final class TelegramApplicationContext {
|
||||
public var isCurrent: Bool = false {
|
||||
didSet {
|
||||
self.mediaManager?.isCurrent = self.isCurrent
|
||||
if !self.isCurrent {
|
||||
self.callManager = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,9 +50,8 @@ public class TransformImageNode: ASDisplayNode {
|
||||
public func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, attemptSynchronously: Bool = false, dispatchOnDisplayLink: Bool = true) {
|
||||
let argumentsPromise = self.argumentsPromise
|
||||
|
||||
var shouldAttemptSynchronously = attemptSynchronously
|
||||
let result = combineLatest(signal, argumentsPromise.get())
|
||||
|> mapToSignal { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> in
|
||||
let data = combineLatest(signal, argumentsPromise.get())
|
||||
/*|> mapToSignal { transform, arguments -> Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> in
|
||||
let result: Signal<((TransformImageArguments) -> DrawingContext?, TransformImageArguments), NoError> = .single((transform, arguments))
|
||||
if shouldAttemptSynchronously {
|
||||
shouldAttemptSynchronously = false
|
||||
@ -61,7 +60,17 @@ public class TransformImageNode: ASDisplayNode {
|
||||
return result
|
||||
|> 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
|
||||
return deferred {
|
||||
if let context = transform(arguments) {
|
||||
@ -102,7 +111,7 @@ public class TransformImageNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
if dispatchOnDisplayLink {
|
||||
if dispatchOnDisplayLink && !attemptSynchronously {
|
||||
displayLinkDispatcher.dispatch {
|
||||
apply()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user