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
|
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
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ import AVFoundation
|
|||||||
import Postbox
|
import Postbox
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
private var sharedProviderDelegate: AnyObject?
|
||||||
|
|
||||||
public final class CallKitIntegration {
|
public final class CallKitIntegration {
|
||||||
private let providerDelegate: AnyObject
|
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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] = []
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user