mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios into mac-video-calls-temp
# Conflicts: # submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h # submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
This commit is contained in:
commit
287e37850c
@ -2446,6 +2446,7 @@ Unused sets are archived when you add more.";
|
||||
"Call.StatusBar" = "Touch to return to call %@";
|
||||
|
||||
"Call.ParticipantVersionOutdatedError" = "%@'s app does not support calls. They need to update their app before you can call them.";
|
||||
"Call.ParticipantVideoVersionOutdatedError" = "%@'s app does not support video calls. They need to update their app before you can call them.";
|
||||
|
||||
"Privacy.Calls" = "Voice Calls";
|
||||
|
||||
@ -2755,6 +2756,7 @@ Unused sets are archived when you add more.";
|
||||
|
||||
"Channel.EditAdmin.CannotEdit" = "You cannot edit the rights of this admin.";
|
||||
"Call.RateCall" = "Rate This Call";
|
||||
"Call.ShareStats" = "Share Statistics";
|
||||
|
||||
"Settings.ApplyProxyAlert" = "Are you sure you want to enable this proxy?\nServer: %1$@\nPort: %2$@\n\nYou can change your proxy server later it in the Settings (Data and Storage).";
|
||||
"Settings.ApplyProxyAlertCredentials" = "Are you sure you want to enable this proxy?\nServer: %1$@\nPort: %2$@\nUsername: %3$@\nPassword: %4$@\n\nYou can change your proxy server later it in the Settings (Data and Storage).";
|
||||
@ -4002,6 +4004,8 @@ Unused sets are archived when you add more.";
|
||||
"CallFeedback.ReasonSilentLocal" = "I couldn't hear the other side";
|
||||
"CallFeedback.ReasonSilentRemote" = "The other side couldn't hear me";
|
||||
"CallFeedback.ReasonDropped" = "Call ended unexpectedly";
|
||||
"CallFeedback.VideoReasonDistorted" = "Video was distorted";
|
||||
"CallFeedback.VideoReasonLowQuality" = "Video was pixelated";
|
||||
"CallFeedback.AddComment" = "Add an optional comment";
|
||||
"CallFeedback.IncludeLogs" = "Include technical information";
|
||||
"CallFeedback.IncludeLogsInfo" = "This won't reveal the contents of your conversation, but will help us fix the issue sooner.";
|
||||
|
0
Wallet/Strings/fa.lproj/Localizable.strings
Normal file
0
Wallet/Strings/fa.lproj/Localizable.strings
Normal file
0
Wallet/Strings/pl.lproj/Localizable.strings
Normal file
0
Wallet/Strings/pl.lproj/Localizable.strings
Normal file
0
Wallet/Strings/uz.lproj/Localizable.strings
Normal file
0
Wallet/Strings/uz.lproj/Localizable.strings
Normal file
@ -1 +1 @@
|
||||
2.2.0
|
||||
3.4.1
|
||||
|
@ -531,7 +531,7 @@ public protocol SharedAccountContext: class {
|
||||
func makeComposeController(context: AccountContext) -> ViewController
|
||||
func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
|
||||
func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController
|
||||
func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem
|
||||
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem
|
||||
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||
func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController?
|
||||
func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController
|
||||
|
@ -94,19 +94,22 @@ public final class PresentationCallVideoView {
|
||||
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
||||
|
||||
public let getOrientation: () -> Orientation
|
||||
public let setOnOrientationUpdated: (((Orientation) -> Void)?) -> Void
|
||||
public let getAspect: () -> CGFloat
|
||||
public let setOnOrientationUpdated: (((Orientation, CGFloat) -> Void)?) -> Void
|
||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||
|
||||
public init(
|
||||
view: UIView,
|
||||
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
||||
getOrientation: @escaping () -> Orientation,
|
||||
setOnOrientationUpdated: @escaping (((Orientation) -> Void)?) -> Void,
|
||||
getAspect: @escaping () -> CGFloat,
|
||||
setOnOrientationUpdated: @escaping (((Orientation, CGFloat) -> Void)?) -> Void,
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||
) {
|
||||
self.view = view
|
||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||
self.getOrientation = getOrientation
|
||||
self.getAspect = getAspect
|
||||
self.setOnOrientationUpdated = setOnOrientationUpdated
|
||||
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
||||
}
|
||||
@ -137,6 +140,7 @@ public protocol PresentationCall: class {
|
||||
func toggleIsMuted()
|
||||
func setIsMuted(_ value: Bool)
|
||||
func requestVideo()
|
||||
func setRequestedVideoAspect(_ aspect: Float)
|
||||
func disableVideo()
|
||||
func setOutgoingVideoIsPaused(_ isPaused: Bool)
|
||||
func switchVideoCamera()
|
||||
|
@ -139,8 +139,8 @@ class CallListCallItem: ListViewItem {
|
||||
for media in self.topMessage.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||
break
|
||||
isVideo = isVideoValue
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -762,7 +762,30 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
)
|
||||
)
|
||||
let location: SearchMessagesLocation
|
||||
location = .general(tags: nil)
|
||||
let messageTags: MessageTags?
|
||||
if query.hasPrefix("%media ") {
|
||||
messageTags = .photoOrVideo
|
||||
} else if query.hasPrefix("%photo ") {
|
||||
messageTags = .photo
|
||||
} else if query.hasPrefix("%video ") {
|
||||
messageTags = .video
|
||||
} else if query.hasPrefix("%file ") {
|
||||
messageTags = .file
|
||||
} else if query.hasPrefix("%music ") {
|
||||
messageTags = .music
|
||||
} else if query.hasPrefix("%link ") {
|
||||
messageTags = .webPage
|
||||
} else if query.hasPrefix("%gif ") {
|
||||
messageTags = .gif
|
||||
} else {
|
||||
messageTags = nil
|
||||
}
|
||||
location = .general(tags: messageTags)
|
||||
|
||||
var finalQuery = query
|
||||
if let _ = messageTags, let index = finalQuery.firstIndex(of: " ") {
|
||||
finalQuery = String(finalQuery.suffix(from: finalQuery.index(after: index)))
|
||||
}
|
||||
|
||||
updateSearchContext { _ in
|
||||
return (nil, true)
|
||||
@ -771,22 +794,22 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if filter.contains(.doNotSearchMessages) {
|
||||
foundRemoteMessages = .single((([], [:], 0), false))
|
||||
} else {
|
||||
if !query.isEmpty {
|
||||
if !finalQuery.isEmpty {
|
||||
addAppLogEvent(postbox: context.account.postbox, time: Date().timeIntervalSince1970, type: "search_global_query", peerId: nil, data: .dictionary([:]))
|
||||
}
|
||||
|
||||
let searchSignal = searchMessages(account: context.account, location: location, query: query, state: nil, limit: 50)
|
||||
let searchSignal = searchMessages(account: context.account, location: location, query: finalQuery, state: nil, limit: 50)
|
||||
|> map { result, updatedState -> ChatListSearchMessagesResult in
|
||||
return ChatListSearchMessagesResult(query: query, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
|
||||
return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
|
||||
}
|
||||
|
||||
let loadMore = searchContext.get()
|
||||
|> mapToSignal { searchContext -> Signal<(([Message], [PeerId: CombinedPeerReadState], Int32), Bool), NoError> in
|
||||
if let searchContext = searchContext {
|
||||
if let _ = searchContext.loadMoreIndex {
|
||||
return searchMessages(account: context.account, location: location, query: query, state: searchContext.result.state, limit: 80)
|
||||
return searchMessages(account: context.account, location: location, query: finalQuery, state: searchContext.result.state, limit: 80)
|
||||
|> map { result, updatedState -> ChatListSearchMessagesResult in
|
||||
return ChatListSearchMessagesResult(query: query, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
|
||||
return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
|
||||
}
|
||||
|> mapToSignal { foundMessages -> Signal<(([Message], [PeerId: CombinedPeerReadState], Int32), Bool), NoError> in
|
||||
updateSearchContext { previous in
|
||||
@ -818,7 +841,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
|
||||
let resolvedMessage = .single(nil)
|
||||
|> then(context.sharedContext.resolveUrl(account: context.account, url: query)
|
||||
|> then(context.sharedContext.resolveUrl(account: context.account, url: finalQuery)
|
||||
|> mapToSignal { resolvedUrl -> Signal<Message?, NoError> in
|
||||
if case let .channelMessage(peerId, messageId) = resolvedUrl {
|
||||
return downloadMessage(postbox: context.account.postbox, network: context.account.network, messageId: messageId)
|
||||
@ -904,60 +927,63 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
globalExpandType = .none
|
||||
}
|
||||
|
||||
let lowercasedQuery = query.lowercased()
|
||||
if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
|
||||
if !existingPeerIds.contains(accountPeer.id), filteredPeer(accountPeer, accountPeer) {
|
||||
existingPeerIds.insert(accountPeer.id)
|
||||
entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfLocalPeers = 0
|
||||
for renderedPeer in foundLocalPeers.peers {
|
||||
if case .expand = localExpandType, numberOfLocalPeers >= 5 {
|
||||
break
|
||||
if let _ = messageTags {
|
||||
} else {
|
||||
let lowercasedQuery = finalQuery.lowercased()
|
||||
if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
|
||||
if !existingPeerIds.contains(accountPeer.id), filteredPeer(accountPeer, accountPeer) {
|
||||
existingPeerIds.insert(accountPeer.id)
|
||||
entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != context.account.peerId, filteredPeer(peer, accountPeer) {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
var associatedPeer: Peer?
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
associatedPeer = renderedPeer.peers[associatedPeerId]
|
||||
var numberOfLocalPeers = 0
|
||||
for renderedPeer in foundLocalPeers.peers {
|
||||
if case .expand = localExpandType, numberOfLocalPeers >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != context.account.peerId, filteredPeer(peer, accountPeer) {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
var associatedPeer: Peer?
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
associatedPeer = renderedPeer.peers[associatedPeerId]
|
||||
}
|
||||
entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
numberOfLocalPeers += 1
|
||||
}
|
||||
entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
}
|
||||
}
|
||||
|
||||
for peer in foundRemotePeers.0 {
|
||||
if case .expand = localExpandType, numberOfLocalPeers >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
numberOfLocalPeers += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for peer in foundRemotePeers.0 {
|
||||
if case .expand = localExpandType, numberOfLocalPeers >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
numberOfLocalPeers += 1
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfGlobalPeers = 0
|
||||
index = 0
|
||||
for peer in foundRemotePeers.1 {
|
||||
if case .expand = globalExpandType, numberOfGlobalPeers >= 3 {
|
||||
break
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType))
|
||||
index += 1
|
||||
numberOfGlobalPeers += 1
|
||||
var numberOfGlobalPeers = 0
|
||||
index = 0
|
||||
for peer in foundRemotePeers.1 {
|
||||
if case .expand = globalExpandType, numberOfGlobalPeers >= 3 {
|
||||
break
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType))
|
||||
index += 1
|
||||
numberOfGlobalPeers += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -986,8 +1012,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
}
|
||||
|
||||
if addContact != nil && isViablePhoneNumber(query) {
|
||||
entries.append(.addContact(query, presentationData.theme, presentationData.strings))
|
||||
if addContact != nil && isViablePhoneNumber(finalQuery) {
|
||||
entries.append(.addContact(finalQuery, presentationData.theme, presentationData.strings))
|
||||
}
|
||||
|
||||
return (entries, isSearching)
|
||||
|
@ -945,8 +945,8 @@ public final class Transaction {
|
||||
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
||||
}
|
||||
|
||||
public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, f)
|
||||
public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
|
||||
}
|
||||
|
||||
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
|
||||
@ -3174,9 +3174,10 @@ public final class Postbox {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||
fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||
var remainingLimit = limit
|
||||
var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
|
||||
while true {
|
||||
while remainingLimit > 0 {
|
||||
let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), limit: 32)
|
||||
for message in messages {
|
||||
let attributes = MessageHistoryTable.renderMessageAttributes(message)
|
||||
@ -3184,6 +3185,7 @@ public final class Postbox {
|
||||
break
|
||||
}
|
||||
}
|
||||
remainingLimit -= messages.count
|
||||
if let last = messages.last {
|
||||
index = last.index
|
||||
} else {
|
||||
|
@ -173,20 +173,20 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let width: CGFloat
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
|
@ -43,6 +43,7 @@ private enum DebugControllerSection: Int32 {
|
||||
case logging
|
||||
case experiments
|
||||
case videoExperiments
|
||||
case videoExperiments2
|
||||
case info
|
||||
}
|
||||
|
||||
@ -73,6 +74,8 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case playerEmbedding(Bool)
|
||||
case playlistPlayback(Bool)
|
||||
case preferredVideoCodec(Int, String, String?, Bool)
|
||||
case disableVideoAspectScaling(Bool)
|
||||
case enableVoipTcp(Bool)
|
||||
case hostInfo(PresentationTheme, String)
|
||||
case versionInfo(PresentationTheme)
|
||||
|
||||
@ -90,6 +93,8 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .preferredVideoCodec:
|
||||
return DebugControllerSection.videoExperiments.rawValue
|
||||
case .disableVideoAspectScaling, .enableVoipTcp:
|
||||
return DebugControllerSection.videoExperiments2.rawValue
|
||||
case .hostInfo, .versionInfo:
|
||||
return DebugControllerSection.info.rawValue
|
||||
}
|
||||
@ -149,10 +154,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 25
|
||||
case let .preferredVideoCodec(index, _, _, _):
|
||||
return 26 + index
|
||||
case .hostInfo:
|
||||
case .disableVideoAspectScaling:
|
||||
return 100
|
||||
case .versionInfo:
|
||||
case .enableVoipTcp:
|
||||
return 101
|
||||
case .hostInfo:
|
||||
return 102
|
||||
case .versionInfo:
|
||||
return 103
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,6 +589,26 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .disableVideoAspectScaling(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Video Cropping Optimization", value: !value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||
settings.disableVideoAspectScaling = !value
|
||||
return settings
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .enableVoipTcp(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Enable VoIP TCP", value: !value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||
settings.enableVoipTcp = value
|
||||
return settings
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .hostInfo(theme, string):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
||||
case let .versionInfo(theme):
|
||||
@ -637,6 +666,9 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
|
||||
for i in 0 ..< codecs.count {
|
||||
entries.append(.preferredVideoCodec(i, codecs[i].0, codecs[i].1, experimentalSettings.preferredVideoCodec == codecs[i].1))
|
||||
}
|
||||
|
||||
entries.append(.disableVideoAspectScaling(experimentalSettings.disableVideoAspectScaling))
|
||||
entries.append(.enableVoipTcp(experimentalSettings.enableVoipTcp))
|
||||
|
||||
if let backupHostOverride = networkSettings?.backupHostOverride {
|
||||
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
|
||||
|
@ -159,7 +159,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil)
|
||||
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
|
||||
var node: ListViewItemNode?
|
||||
if let current = currentNode {
|
||||
|
@ -319,20 +319,20 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let width: CGFloat
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
|
@ -882,7 +882,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
sampleMessages.append(message8)
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
let item = self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: { [weak self] message in
|
||||
let item = self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: { [weak self] message in
|
||||
if message.flags.contains(.Incoming) {
|
||||
self?.updateSection(.accent)
|
||||
self?.requestSectionUpdate?(.accent)
|
||||
|
@ -487,7 +487,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
sampleMessages.append(message8)
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
}
|
||||
|
||||
let width: CGFloat
|
||||
|
@ -165,7 +165,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
}
|
||||
|
||||
var nodes: [ListViewItemNode] = []
|
||||
|
@ -840,28 +840,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
|
||||
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
||||
if let messageNodes = self.messageNodes {
|
||||
// for i in 0 ..< items.count {
|
||||
// let itemNode = messageNodes[i]
|
||||
// items[i].updateNode(async: { $0() }, node: {
|
||||
// return itemNode
|
||||
// }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
||||
// let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
//
|
||||
// itemNode.contentSize = layout.contentSize
|
||||
// itemNode.insets = layout.insets
|
||||
// itemNode.frame = nodeFrame
|
||||
// itemNode.isUserInteractionEnabled = false
|
||||
//
|
||||
// apply(ListViewItemApply(isOnScreen: true))
|
||||
// })
|
||||
// }
|
||||
if let _ = self.messageNodes {
|
||||
} else {
|
||||
var messageNodes: [ListViewItemNode] = []
|
||||
for i in 0 ..< items.count {
|
||||
|
@ -888,7 +888,7 @@ public final class ShareController: ViewController {
|
||||
}
|
||||
|
||||
|
||||
class MessageStoryRenderer {
|
||||
final class MessageStoryRenderer {
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
private let messages: [Message]
|
||||
@ -909,7 +909,7 @@ class MessageStoryRenderer {
|
||||
|
||||
self.instantChatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings()), mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
self.messagesContainerNode.clipsToBounds = true
|
||||
@ -920,8 +920,8 @@ class MessageStoryRenderer {
|
||||
|
||||
self.addressNode = ImmediateTextNode()
|
||||
self.addressNode.displaysAsynchronously = false
|
||||
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(addressName)/\(message.id.id)", font: Font.medium(14.0), textColor: UIColor(rgb: 0xa8b7c4))
|
||||
// self.addressNode.textShadowColor = .black
|
||||
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(addressName)/\(message.id.id)", font: Font.medium(14.0), textColor: UIColor(rgb: 0xffffff))
|
||||
self.addressNode.textShadowColor = UIColor(rgb: 0x929292, alpha: 0.8)
|
||||
|
||||
self.containerNode.addSubnode(self.instantChatBackgroundNode)
|
||||
self.containerNode.addSubnode(self.messagesContainerNode)
|
||||
@ -952,12 +952,7 @@ class MessageStoryRenderer {
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
||||
|
||||
var items: [ListViewItem] = []
|
||||
let sampleMessages: [Message] = self.messages
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
}
|
||||
let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)]
|
||||
|
||||
let inset: CGFloat = 16.0
|
||||
let width = layout.size.width - inset * 2.0
|
||||
|
@ -96,8 +96,10 @@ public extension MessageTags {
|
||||
static let unseenPersonalMessage = MessageTags(rawValue: 1 << 5)
|
||||
static let liveLocation = MessageTags(rawValue: 1 << 6)
|
||||
static let gif = MessageTags(rawValue: 1 << 7)
|
||||
static let photo = MessageTags(rawValue: 1 << 8)
|
||||
static let video = MessageTags(rawValue: 1 << 9)
|
||||
|
||||
static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif]
|
||||
static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif, .photo, .video]
|
||||
}
|
||||
|
||||
public extension GlobalMessageTags {
|
||||
|
@ -2046,39 +2046,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendEncrypted(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SentEncryptedMessage>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1451792525)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.sendEncrypted", parameters: [("peer", peer), ("randomId", randomId), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.SentEncryptedMessage?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendEncryptedFile(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SentEncryptedMessage>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1701831834)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
file.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.sendEncryptedFile", parameters: [("peer", peer), ("randomId", randomId), ("data", data), ("file", file)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.SentEncryptedMessage?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendEncryptedService(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SentEncryptedMessage>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(852769188)
|
||||
@ -3720,6 +3687,41 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendEncrypted(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SentEncryptedMessage>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1157265941)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.sendEncrypted", parameters: [("flags", flags), ("peer", peer), ("randomId", randomId), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.SentEncryptedMessage?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendEncryptedFile(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SentEncryptedMessage>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1431914525)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
file.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.sendEncryptedFile", parameters: [("flags", flags), ("peer", peer), ("randomId", randomId), ("data", data), ("file", file)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.SentEncryptedMessage?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct channels {
|
||||
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
|
@ -24,7 +24,7 @@ protocol CallControllerNodeProtocol: class {
|
||||
var acceptCall: (() -> Void)? { get set }
|
||||
var endCall: (() -> Void)? { get set }
|
||||
var back: (() -> Void)? { get set }
|
||||
var presentCallRating: ((CallId) -> Void)? { get set }
|
||||
var presentCallRating: ((CallId, Bool) -> Void)? { get set }
|
||||
var present: ((ViewController) -> Void)? { get set }
|
||||
var callEnded: ((Bool) -> Void)? { get set }
|
||||
var dismissedInteractively: (() -> Void)? { get set }
|
||||
@ -230,13 +230,13 @@ public final class CallController: ViewController {
|
||||
let _ = self?.dismiss()
|
||||
}
|
||||
|
||||
self.controllerNode.presentCallRating = { [weak self] callId in
|
||||
self.controllerNode.presentCallRating = { [weak self] callId, isVideo in
|
||||
if let strongSelf = self, !strongSelf.presentedCallRating {
|
||||
strongSelf.presentedCallRating = true
|
||||
|
||||
Queue.mainQueue().after(0.5, {
|
||||
let window = strongSelf.window
|
||||
let controller = callRatingController(sharedContext: strongSelf.sharedContext, account: strongSelf.account, callId: callId, userInitiated: false, present: { c, a in
|
||||
let controller = callRatingController(sharedContext: strongSelf.sharedContext, account: strongSelf.account, callId: callId, userInitiated: false, isVideo: isVideo, present: { c, a in
|
||||
if let window = window {
|
||||
c.presentationArguments = a
|
||||
window.present(c, on: .root, blockInteraction: false, completion: {})
|
||||
|
@ -42,18 +42,21 @@ private final class CallVideoNode: ASDisplayNode {
|
||||
private let isFlippedUpdated: (CallVideoNode) -> Void
|
||||
|
||||
private(set) var currentOrientation: PresentationCallVideoView.Orientation
|
||||
private(set) var currentAspect: CGFloat = 0.0
|
||||
|
||||
private var previousVideoHeight: CGFloat?
|
||||
|
||||
init(videoView: PresentationCallVideoView, disabledText: String?, assumeReadyAfterTimeout: Bool, isReadyUpdated: @escaping () -> Void, orientationUpdated: @escaping () -> Void, isFlippedUpdated: @escaping (CallVideoNode) -> Void) {
|
||||
self.isReadyUpdated = isReadyUpdated
|
||||
self.isFlippedUpdated = isFlippedUpdated
|
||||
|
||||
self.videoTransformContainer = ASDisplayNode()
|
||||
self.videoTransformContainer.clipsToBounds = true
|
||||
self.videoView = videoView
|
||||
videoView.view.clipsToBounds = true
|
||||
videoView.view.backgroundColor = .black
|
||||
|
||||
self.currentOrientation = videoView.getOrientation()
|
||||
self.currentAspect = videoView.getAspect()
|
||||
|
||||
self.videoPausedNode = ImmediateTextNode()
|
||||
self.videoPausedNode.alpha = 0.0
|
||||
@ -89,13 +92,14 @@ private final class CallVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
self.videoView.setOnOrientationUpdated { [weak self] orientation in
|
||||
self.videoView.setOnOrientationUpdated { [weak self] orientation, aspect in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.currentOrientation != orientation {
|
||||
if strongSelf.currentOrientation != orientation || strongSelf.currentAspect != aspect {
|
||||
strongSelf.currentOrientation = orientation
|
||||
strongSelf.currentAspect = aspect
|
||||
orientationUpdated()
|
||||
}
|
||||
}
|
||||
@ -164,91 +168,100 @@ private final class CallVideoNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, cornerRadius: CGFloat, deviceOrientation: UIDeviceOrientation, transition: ContainedViewLayoutTransition) {
|
||||
func updateLayout(size: CGSize, cornerRadius: CGFloat, isOutgoing: Bool, deviceOrientation: UIDeviceOrientation, isCompactLayout: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.currentCornerRadius = cornerRadius
|
||||
|
||||
var rotationAngle: CGFloat
|
||||
switch self.currentOrientation {
|
||||
case .rotation0:
|
||||
rotationAngle = 0.0
|
||||
case .rotation90:
|
||||
rotationAngle = -CGFloat.pi / 2.0
|
||||
case .rotation180:
|
||||
rotationAngle = -CGFloat.pi
|
||||
case .rotation270:
|
||||
if isOutgoing {
|
||||
rotationAngle = CGFloat.pi / 2.0
|
||||
}
|
||||
|
||||
var additionalAngle: CGFloat = 0.0
|
||||
switch deviceOrientation {
|
||||
case .portrait:
|
||||
additionalAngle = 0.0
|
||||
case .landscapeLeft:
|
||||
additionalAngle = CGFloat.pi / 2.0
|
||||
case .landscapeRight:
|
||||
additionalAngle = -CGFloat.pi / 2.0
|
||||
case .portraitUpsideDown:
|
||||
rotationAngle = -CGFloat.pi
|
||||
default:
|
||||
additionalAngle = 0.0
|
||||
}
|
||||
rotationAngle += additionalAngle
|
||||
if abs(rotationAngle - (-CGFloat.pi)) < 1.0 {
|
||||
rotationAngle = -CGFloat.pi + 0.001
|
||||
}
|
||||
|
||||
var rotateFrame = abs(rotationAngle.remainder(dividingBy: CGFloat.pi)) > 1.0
|
||||
|
||||
var originalRotateFrame = rotateFrame
|
||||
if size.width > size.height {
|
||||
rotateFrame = !rotateFrame
|
||||
if rotateFrame {
|
||||
originalRotateFrame = true
|
||||
}
|
||||
} else {
|
||||
if rotateFrame {
|
||||
originalRotateFrame = false
|
||||
switch self.currentOrientation {
|
||||
case .rotation0:
|
||||
rotationAngle = 0.0
|
||||
case .rotation90:
|
||||
rotationAngle = CGFloat.pi / 2.0
|
||||
case .rotation180:
|
||||
rotationAngle = CGFloat.pi
|
||||
case .rotation270:
|
||||
rotationAngle = -CGFloat.pi / 2.0
|
||||
}
|
||||
|
||||
var additionalAngle: CGFloat = 0.0
|
||||
switch deviceOrientation {
|
||||
case .portrait:
|
||||
additionalAngle = 0.0
|
||||
case .landscapeLeft:
|
||||
additionalAngle = CGFloat.pi / 2.0
|
||||
case .landscapeRight:
|
||||
additionalAngle = -CGFloat.pi / 2.0
|
||||
case .portraitUpsideDown:
|
||||
rotationAngle = CGFloat.pi
|
||||
default:
|
||||
additionalAngle = 0.0
|
||||
}
|
||||
rotationAngle += additionalAngle
|
||||
if abs(rotationAngle - (-CGFloat.pi)) < 1.0 {
|
||||
rotationAngle = -CGFloat.pi + 0.001
|
||||
}
|
||||
}
|
||||
let videoFrame: CGRect
|
||||
let scale: CGFloat
|
||||
|
||||
let rotateFrame = abs(rotationAngle.remainder(dividingBy: CGFloat.pi)) > 1.0
|
||||
let fittingSize: CGSize
|
||||
if rotateFrame {
|
||||
let frameSize = CGSize(width: size.height, height: size.width).aspectFitted(size)
|
||||
videoFrame = CGRect(origin: CGPoint(x: floor((size.width - frameSize.width) / 2.0), y: floor((size.height - frameSize.height) / 2.0)), size: frameSize)
|
||||
if size.width > size.height {
|
||||
scale = frameSize.height / size.width
|
||||
} else {
|
||||
scale = frameSize.width / size.height
|
||||
}
|
||||
fittingSize = CGSize(width: size.height, height: size.width)
|
||||
} else {
|
||||
videoFrame = CGRect(origin: CGPoint(), size: size)
|
||||
if size.width > size.height {
|
||||
scale = 1.0
|
||||
fittingSize = size
|
||||
}
|
||||
|
||||
let unboundVideoSize = CGSize(width: self.currentAspect * 10000.0, height: 10000.0)
|
||||
|
||||
var fittedVideoSize = unboundVideoSize.fitted(fittingSize)
|
||||
if fittedVideoSize.width < fittingSize.width || fittedVideoSize.height < fittingSize.height {
|
||||
let isVideoPortrait = unboundVideoSize.width < unboundVideoSize.height
|
||||
let isFittingSizePortrait = fittingSize.width < fittingSize.height
|
||||
|
||||
if isCompactLayout && isVideoPortrait == isFittingSizePortrait {
|
||||
fittedVideoSize = unboundVideoSize.aspectFilled(fittingSize)
|
||||
} else {
|
||||
scale = 1.0
|
||||
let maxFittingEdgeDistance: CGFloat
|
||||
if isCompactLayout {
|
||||
maxFittingEdgeDistance = 200.0
|
||||
} else {
|
||||
maxFittingEdgeDistance = 400.0
|
||||
}
|
||||
if fittedVideoSize.width > fittingSize.width - maxFittingEdgeDistance && fittedVideoSize.height > fittingSize.height - maxFittingEdgeDistance {
|
||||
fittedVideoSize = unboundVideoSize.aspectFilled(fittingSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rotatedVideoHeight: CGFloat = max(fittedVideoSize.height, fittedVideoSize.width)
|
||||
|
||||
let videoFrame: CGRect = CGRect(origin: CGPoint(), size: fittedVideoSize)
|
||||
|
||||
let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: size.width - 16.0, height: 100.0))
|
||||
transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - videoPausedSize.width) / 2.0), y: floor((size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
|
||||
|
||||
let previousVideoFrame = self.videoTransformContainer.frame
|
||||
self.videoTransformContainer.bounds = CGRect(origin: CGPoint(), size: size)
|
||||
if transition.isAnimated && !videoFrame.height.isZero && !previousVideoFrame.height.isZero {
|
||||
transition.animateTransformScale(node: self.videoTransformContainer, from: previousVideoFrame.height / size.height, additive: true)
|
||||
self.videoTransformContainer.bounds = CGRect(origin: CGPoint(), size: videoFrame.size)
|
||||
if transition.isAnimated && !videoFrame.height.isZero, let previousVideoHeight = self.previousVideoHeight, !previousVideoHeight.isZero {
|
||||
let scaleDifference = previousVideoHeight / rotatedVideoHeight
|
||||
if abs(scaleDifference - 1.0) > 0.001 {
|
||||
transition.animateTransformScale(node: self.videoTransformContainer, from: scaleDifference, additive: true)
|
||||
}
|
||||
}
|
||||
transition.updatePosition(node: self.videoTransformContainer, position: videoFrame.center)
|
||||
transition.updateSublayerTransformScale(node: self.videoTransformContainer, scale: scale)
|
||||
|
||||
let localVideoSize = originalRotateFrame ? CGSize(width: size.height, height: size.width) : size
|
||||
let localVideoFrame = CGRect(origin: CGPoint(x: floor((size.width - localVideoSize.width) / 2.0), y: floor((size.height - localVideoSize.height) / 2.0)), size: localVideoSize)
|
||||
self.previousVideoHeight = rotatedVideoHeight
|
||||
transition.updatePosition(node: self.videoTransformContainer, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||
transition.updateTransformRotation(view: self.videoTransformContainer.view, angle: rotationAngle)
|
||||
|
||||
let localVideoFrame = CGRect(origin: CGPoint(), size: videoFrame.size)
|
||||
self.videoView.view.bounds = localVideoFrame
|
||||
self.videoView.view.center = localVideoFrame.center
|
||||
transition.updateTransformRotation(view: self.videoView.view, angle: rotationAngle)
|
||||
// TODO: properly fix the issue
|
||||
// On iOS 13 and later metal layer transformation is broken if the layer does not require compositing
|
||||
self.videoView.view.alpha = 0.995
|
||||
|
||||
if let effectView = self.effectView {
|
||||
transition.updateFrame(view: effectView, frame: videoFrame)
|
||||
transition.updateFrame(view: effectView, frame: localVideoFrame)
|
||||
}
|
||||
|
||||
transition.updateCornerRadius(layer: self.layer, cornerRadius: self.currentCornerRadius)
|
||||
@ -392,7 +405,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
var acceptCall: (() -> Void)?
|
||||
var endCall: (() -> Void)?
|
||||
var back: (() -> Void)?
|
||||
var presentCallRating: ((CallId) -> Void)?
|
||||
var presentCallRating: ((CallId, Bool) -> Void)?
|
||||
var callEnded: ((Bool) -> Void)?
|
||||
var dismissedInteractively: (() -> Void)?
|
||||
var present: ((ViewController) -> Void)?
|
||||
@ -419,6 +432,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
private var deviceOrientation: UIDeviceOrientation = .portrait
|
||||
private var orientationDidChangeObserver: NSObjectProtocol?
|
||||
|
||||
private var currentRequestedAspect: CGFloat?
|
||||
|
||||
init(sharedContext: SharedAccountContext, account: Account, presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, shouldStayHiddenUntilConnection: Bool = false, easyDebugAccess: Bool, call: PresentationCall) {
|
||||
self.sharedContext = sharedContext
|
||||
self.account = account
|
||||
@ -922,11 +937,18 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
case let .error(error):
|
||||
let text = self.presentationData.strings.Call_StatusFailed
|
||||
switch error {
|
||||
case .notSupportedByPeer:
|
||||
case let .notSupportedByPeer(isVideo):
|
||||
if !self.displayedVersionOutdatedAlert, let peer = self.peer {
|
||||
self.displayedVersionOutdatedAlert = true
|
||||
|
||||
self.present?(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Call_ParticipantVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
|
||||
let text: String
|
||||
if isVideo {
|
||||
text = self.presentationData.strings.Call_ParticipantVideoVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0
|
||||
} else {
|
||||
text = self.presentationData.strings.Call_ParticipantVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0
|
||||
}
|
||||
|
||||
self.present?(textAlertController(sharedContext: self.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
|
||||
})]))
|
||||
}
|
||||
default:
|
||||
@ -1005,8 +1027,10 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
self.updateButtonsMode()
|
||||
self.updateDimVisibility()
|
||||
|
||||
if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
|
||||
self.displayedCameraTooltip = true
|
||||
if self.incomingVideoViewRequested || self.outgoingVideoViewRequested {
|
||||
if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
|
||||
self.displayedCameraTooltip = true
|
||||
}
|
||||
self.displayedCameraConfirmation = true
|
||||
}
|
||||
if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && (self.toastContent?.isEmpty ?? true) {
|
||||
@ -1019,7 +1043,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
|
||||
let presentRating = reportRating || self.forceReportRating
|
||||
if presentRating {
|
||||
self.presentCallRating?(callId)
|
||||
self.presentCallRating?(callId, self.call.isVideo)
|
||||
}
|
||||
self.callEnded?(presentRating)
|
||||
}
|
||||
@ -1253,12 +1277,12 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
let previewVideoSide = interpolate(from: 350.0, to: 200.0, value: 1.0 - self.pictureInPictureTransitionFraction)
|
||||
var previewVideoSize = layout.size.aspectFitted(CGSize(width: previewVideoSide, height: previewVideoSide))
|
||||
previewVideoSize = CGSize(width: 30.0, height: 45.0).aspectFitted(previewVideoSize)
|
||||
if let minimizedVideoNode = minimizedVideoNode {
|
||||
if let minimizedVideoNode = self.minimizedVideoNode {
|
||||
switch minimizedVideoNode.currentOrientation {
|
||||
case .rotation90, .rotation270:
|
||||
previewVideoSize = CGSize(width: previewVideoSize.height, height: previewVideoSize.width)
|
||||
default:
|
||||
break
|
||||
default:
|
||||
previewVideoSize = CGSize(width: previewVideoSize.height, height: previewVideoSize.width)
|
||||
}
|
||||
}
|
||||
let previewVideoY: CGFloat
|
||||
@ -1305,6 +1329,13 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationBarHeight)
|
||||
|
||||
var mappedDeviceOrientation = self.deviceOrientation
|
||||
var isCompactLayout = true
|
||||
if case .regular = layout.metrics.widthClass, case .regular = layout.metrics.heightClass {
|
||||
mappedDeviceOrientation = .portrait
|
||||
isCompactLayout = false
|
||||
}
|
||||
|
||||
if !self.hasVideoNodes {
|
||||
self.isUIHidden = false
|
||||
}
|
||||
@ -1449,7 +1480,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame)
|
||||
}
|
||||
|
||||
expandedVideoNode.updateLayout(size: expandedVideoNode.frame.size, cornerRadius: 0.0, deviceOrientation: self.deviceOrientation, transition: expandedVideoTransition)
|
||||
expandedVideoNode.updateLayout(size: expandedVideoNode.frame.size, cornerRadius: 0.0, isOutgoing: expandedVideoNode === self.outgoingVideoNodeValue, deviceOrientation: mappedDeviceOrientation, isCompactLayout: isCompactLayout, transition: expandedVideoTransition)
|
||||
|
||||
if self.animateRequestedVideoOnce {
|
||||
self.animateRequestedVideoOnce = false
|
||||
@ -1499,7 +1530,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
self.animationForExpandedVideoSnapshotView = nil
|
||||
}
|
||||
minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame)
|
||||
minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), deviceOrientation: .portrait, transition: minimizedVideoTransition)
|
||||
minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), isOutgoing: minimizedVideoNode === self.outgoingVideoNodeValue, deviceOrientation: .portrait, isCompactLayout: false, transition: minimizedVideoTransition)
|
||||
if transition.isAnimated && didAppear {
|
||||
minimizedVideoNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
|
||||
}
|
||||
@ -1515,6 +1546,43 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
if let debugNode = self.debugNode {
|
||||
transition.updateFrame(node: debugNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
}
|
||||
|
||||
let requestedAspect: CGFloat
|
||||
if case .compact = layout.metrics.widthClass, case .compact = layout.metrics.heightClass {
|
||||
var isIncomingVideoRotated = false
|
||||
var rotationCount = 0
|
||||
|
||||
switch mappedDeviceOrientation {
|
||||
case .portrait:
|
||||
break
|
||||
case .landscapeLeft:
|
||||
rotationCount += 1
|
||||
case .landscapeRight:
|
||||
rotationCount += 1
|
||||
case .portraitUpsideDown:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if rotationCount % 2 != 0 {
|
||||
isIncomingVideoRotated = true
|
||||
}
|
||||
|
||||
if !isIncomingVideoRotated {
|
||||
requestedAspect = layout.size.width / layout.size.height
|
||||
} else {
|
||||
requestedAspect = 0.0
|
||||
}
|
||||
} else {
|
||||
requestedAspect = 0.0
|
||||
}
|
||||
if self.currentRequestedAspect != requestedAspect {
|
||||
self.currentRequestedAspect = requestedAspect
|
||||
if !self.sharedContext.immediateExperimentalUISettings.disableVideoAspectScaling {
|
||||
self.call.setRequestedVideoAspect(Float(requestedAspect))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func keyPressed() {
|
||||
|
@ -12,6 +12,9 @@ import OverlayStatusController
|
||||
import AccountContext
|
||||
|
||||
private enum CallFeedbackReason: Int32, CaseIterable {
|
||||
case videoDistorted
|
||||
case videoLowQuality
|
||||
|
||||
case echo
|
||||
case noise
|
||||
case interruption
|
||||
@ -36,6 +39,19 @@ private enum CallFeedbackReason: Int32, CaseIterable {
|
||||
return "silent_remote"
|
||||
case .dropped:
|
||||
return "dropped"
|
||||
case .videoDistorted:
|
||||
return "distorted_video"
|
||||
case .videoLowQuality:
|
||||
return "pixelated_video"
|
||||
}
|
||||
}
|
||||
|
||||
var isVideoRelated: Bool {
|
||||
switch self {
|
||||
case .videoDistorted, .videoLowQuality:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +71,10 @@ private enum CallFeedbackReason: Int32, CaseIterable {
|
||||
return strings.CallFeedback_ReasonSilentRemote
|
||||
case .dropped:
|
||||
return strings.CallFeedback_ReasonDropped
|
||||
case .videoDistorted:
|
||||
return strings.CallFeedback_VideoReasonDistorted
|
||||
case .videoLowQuality:
|
||||
return strings.CallFeedback_VideoReasonLowQuality
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -214,11 +234,22 @@ private struct CallFeedbackState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func callFeedbackControllerEntries(theme: PresentationTheme, strings: PresentationStrings, state: CallFeedbackState) -> [CallFeedbackControllerEntry] {
|
||||
private func callFeedbackControllerEntries(theme: PresentationTheme, strings: PresentationStrings, state: CallFeedbackState, isVideo: Bool) -> [CallFeedbackControllerEntry] {
|
||||
var entries: [CallFeedbackControllerEntry] = []
|
||||
|
||||
entries.append(.reasonsHeader(theme, strings.CallFeedback_WhatWentWrong))
|
||||
if isVideo {
|
||||
for reason in CallFeedbackReason.allCases {
|
||||
if !reason.isVideoRelated {
|
||||
continue
|
||||
}
|
||||
entries.append(.reason(theme, reason, CallFeedbackReason.localizedString(for: reason, strings: strings), state.reasons.contains(reason)))
|
||||
}
|
||||
}
|
||||
for reason in CallFeedbackReason.allCases {
|
||||
if reason.isVideoRelated {
|
||||
continue
|
||||
}
|
||||
entries.append(.reason(theme, reason, CallFeedbackReason.localizedString(for: reason, strings: strings), state.reasons.contains(reason)))
|
||||
}
|
||||
|
||||
@ -230,7 +261,7 @@ private func callFeedbackControllerEntries(theme: PresentationTheme, strings: Pr
|
||||
return entries
|
||||
}
|
||||
|
||||
public func callFeedbackController(sharedContext: SharedAccountContext, account: Account, callId: CallId, rating: Int, userInitiated: Bool) -> ViewController {
|
||||
public func callFeedbackController(sharedContext: SharedAccountContext, account: Account, callId: CallId, rating: Int, userInitiated: Bool, isVideo: Bool) -> ViewController {
|
||||
let initialState = CallFeedbackState()
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@ -290,7 +321,7 @@ public func callFeedbackController(sharedContext: SharedAccountContext, account:
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.CallFeedback_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: callFeedbackControllerEntries(theme: presentationData.theme, strings: presentationData.strings, state: state), style: .blocks, animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: callFeedbackControllerEntries(theme: presentationData.theme, strings: presentationData.strings, state: state, isVideo: isVideo), style: .blocks, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm
|
||||
let rate = rateCall(account: account, callId: callId, starsCount: Int32(starsCount), comment: comment, userInitiated: userInitiated)
|
||||
if includeLogs {
|
||||
let id = arc4random64()
|
||||
let name = "\(callId.id)_\(callId.accessHash).log"
|
||||
let name = "\(callId.id)_\(callId.accessHash).log.json"
|
||||
let path = callLogsPath(account: account) + "/" + name
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
||||
let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||
@ -266,7 +266,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm
|
||||
}
|
||||
}
|
||||
|
||||
public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, present: @escaping (ViewController, Any) -> Void, push: @escaping (ViewController) -> Void) -> AlertController {
|
||||
public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, isVideo: Bool, present: @escaping (ViewController, Any) -> Void, push: @escaping (ViewController) -> Void) -> AlertController {
|
||||
let presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||
let theme = presentationData.theme
|
||||
let strings = presentationData.strings
|
||||
@ -282,7 +282,7 @@ public func callRatingController(sharedContext: SharedAccountContext, account: A
|
||||
}, apply: { rating in
|
||||
dismissImpl?(true)
|
||||
if rating < 4 {
|
||||
push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated))
|
||||
push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated, isVideo: isVideo))
|
||||
} else {
|
||||
let _ = rateCallAndSendLogs(account: account, callId: callId, starsCount: rating, comment: "", userInitiated: userInitiated, includeLogs: false).start()
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
|
||||
var endCall: (() -> Void)?
|
||||
var setIsVideoPaused: ((Bool) -> Void)?
|
||||
var back: (() -> Void)?
|
||||
var presentCallRating: ((CallId) -> Void)?
|
||||
var presentCallRating: ((CallId, Bool) -> Void)?
|
||||
var callEnded: ((Bool) -> Void)?
|
||||
var dismissedInteractively: (() -> Void)?
|
||||
var present: ((ViewController) -> Void)?
|
||||
@ -307,7 +307,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
|
||||
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
|
||||
let presentRating = reportRating || self.forceReportRating
|
||||
if presentRating {
|
||||
self.presentCallRating?(callId)
|
||||
self.presentCallRating?(callId, false)
|
||||
}
|
||||
self.callEnded?(presentRating)
|
||||
}
|
||||
|
@ -169,6 +169,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
public let isOutgoing: Bool
|
||||
public var isVideo: Bool
|
||||
public var isVideoPossible: Bool
|
||||
private let enableStunMarking: Bool
|
||||
private let enableTCP: Bool
|
||||
public let preferredVideoCodec: String?
|
||||
public let peer: Peer?
|
||||
|
||||
@ -184,6 +186,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
private var callContextState: OngoingCallContextState?
|
||||
private var ongoingContext: OngoingCallContext?
|
||||
private var ongoingContextStateDisposable: Disposable?
|
||||
private var requestedVideoAspect: Float?
|
||||
private var reception: Int32?
|
||||
private var receptionDisposable: Disposable?
|
||||
private var reportedIncomingCall = false
|
||||
@ -265,6 +268,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
updatedNetworkType: Signal<NetworkType, NoError>,
|
||||
startWithVideo: Bool,
|
||||
isVideoPossible: Bool,
|
||||
enableStunMarking: Bool,
|
||||
enableTCP: Bool,
|
||||
preferredVideoCodec: String?
|
||||
) {
|
||||
self.account = account
|
||||
@ -292,6 +297,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.isOutgoing = isOutgoing
|
||||
self.isVideo = initialState?.type == .video
|
||||
self.isVideoPossible = isVideoPossible
|
||||
self.enableStunMarking = enableStunMarking
|
||||
self.enableTCP = enableTCP
|
||||
self.preferredVideoCodec = preferredVideoCodec
|
||||
self.peer = peer
|
||||
self.isVideo = startWithVideo
|
||||
@ -606,9 +613,12 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||
let logName = "\(id.id)_\(id.accessHash)"
|
||||
|
||||
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
||||
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
||||
self.ongoingContext = ongoingContext
|
||||
ongoingContext.setIsMuted(self.isMutedValue)
|
||||
if let requestedVideoAspect = self.requestedVideoAspect {
|
||||
ongoingContext.setRequestedVideoAspect(requestedVideoAspect)
|
||||
}
|
||||
|
||||
self.debugInfoValue.set(ongoingContext.debugInfo())
|
||||
|
||||
@ -848,6 +858,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
}
|
||||
}
|
||||
|
||||
public func setRequestedVideoAspect(_ aspect: Float) {
|
||||
self.requestedVideoAspect = aspect
|
||||
self.ongoingContext?.setRequestedVideoAspect(aspect)
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
@ -909,8 +924,15 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
@ -922,7 +944,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue)
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
@ -971,8 +993,15 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
@ -984,7 +1013,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue)
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
|
@ -17,45 +17,14 @@ private func callKitIntegrationIfEnabled(_ integration: CallKitIntegration?, set
|
||||
return enabled ? integration : nil
|
||||
}
|
||||
|
||||
private func auxiliaryServers(appConfiguration: AppConfiguration) -> [CallAuxiliaryServer] {
|
||||
private func shouldEnableStunMarking(appConfiguration: AppConfiguration) -> Bool {
|
||||
guard let data = appConfiguration.data else {
|
||||
return []
|
||||
return true
|
||||
}
|
||||
guard let servers = data["rtc_servers"] as? [[String: Any]] else {
|
||||
return []
|
||||
guard let enableStunMarking = data["voip_enable_stun_marking"] as? Bool else {
|
||||
return true
|
||||
}
|
||||
var result: [CallAuxiliaryServer] = []
|
||||
for server in servers {
|
||||
guard let host = server["host"] as? String else {
|
||||
continue
|
||||
}
|
||||
guard let portString = server["port"] as? String else {
|
||||
continue
|
||||
}
|
||||
guard let username = server["username"] as? String else {
|
||||
continue
|
||||
}
|
||||
guard let password = server["password"] as? String else {
|
||||
continue
|
||||
}
|
||||
guard let port = Int(portString) else {
|
||||
continue
|
||||
}
|
||||
result.append(CallAuxiliaryServer(
|
||||
host: host,
|
||||
port: port,
|
||||
connection: .stun
|
||||
))
|
||||
result.append(CallAuxiliaryServer(
|
||||
host: host,
|
||||
port: port,
|
||||
connection: .turn(
|
||||
username: username,
|
||||
password: password
|
||||
)
|
||||
))
|
||||
}
|
||||
return result
|
||||
return enableStunMarking
|
||||
}
|
||||
|
||||
private enum CurrentCall {
|
||||
@ -309,11 +278,13 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
isOutgoing: false,
|
||||
peer: firstState.1,
|
||||
proxyServer: strongSelf.proxyServer,
|
||||
auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration),
|
||||
auxiliaryServers: [],
|
||||
currentNetworkType: firstState.4,
|
||||
updatedNetworkType: firstState.0.networkType,
|
||||
startWithVideo: firstState.2.isVideo,
|
||||
isVideoPossible: firstState.2.isVideoPossible,
|
||||
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
||||
enableTCP: experimentalSettings.enableVoipTcp,
|
||||
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
||||
)
|
||||
strongSelf.updateCurrentCall(call)
|
||||
@ -551,11 +522,13 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
||||
isOutgoing: true,
|
||||
peer: nil,
|
||||
proxyServer: strongSelf.proxyServer,
|
||||
auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration),
|
||||
auxiliaryServers: [],
|
||||
currentNetworkType: currentNetworkType,
|
||||
updatedNetworkType: account.networkType,
|
||||
startWithVideo: isVideo,
|
||||
isVideoPossible: isVideoPossible,
|
||||
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
||||
enableTCP: experimentalSettings.enableVoipTcp,
|
||||
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
||||
)
|
||||
strongSelf.updateCurrentCall(call)
|
||||
|
@ -2878,10 +2878,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
}
|
||||
|
||||
// could be the reason for unbounded slowdown, needs investigation
|
||||
/*for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
|
||||
for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
|
||||
var upperMessageId: Int32?
|
||||
var lowerMessageId: Int32?
|
||||
transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, { id, attributes in
|
||||
transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, limit: 200, { id, attributes in
|
||||
for attribute in attributes {
|
||||
if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
||||
if attribute.pts >= pts {
|
||||
@ -2906,7 +2906,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
transaction.removeHole(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, space: .everywhere, range: lowerMessageId ... upperMessageId)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if !peerActivityTimestamps.isEmpty {
|
||||
updatePeerPresenceLastActivities(transaction: transaction, accountPeerId: accountPeerId, activities: peerActivityTimestamps)
|
||||
|
@ -11,44 +11,9 @@ private let minLayer: Int32 = 65
|
||||
public enum CallSessionError: Equatable {
|
||||
case generic
|
||||
case privacyRestricted
|
||||
case notSupportedByPeer
|
||||
case serverProvided(String)
|
||||
case notSupportedByPeer(isVideo: Bool)
|
||||
case serverProvided(text: String)
|
||||
case disconnected
|
||||
|
||||
public static func ==(lhs: CallSessionError, rhs: CallSessionError) -> Bool {
|
||||
switch lhs {
|
||||
case .generic:
|
||||
if case .generic = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .privacyRestricted:
|
||||
if case .privacyRestricted = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .notSupportedByPeer:
|
||||
if case .notSupportedByPeer = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .serverProvided(text):
|
||||
if case .serverProvided(text) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .disconnected:
|
||||
if case .disconnected = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallSessionEndedType {
|
||||
@ -1204,12 +1169,12 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
|
||||
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
||||
switch error.errorDescription {
|
||||
case "PARTICIPANT_VERSION_OUTDATED":
|
||||
return .single(.failed(.notSupportedByPeer))
|
||||
return .single(.failed(.notSupportedByPeer(isVideo: isVideo)))
|
||||
case "USER_PRIVACY_RESTRICTED":
|
||||
return .single(.failed(.privacyRestricted))
|
||||
default:
|
||||
if error.errorCode == 406 {
|
||||
return .single(.failed(.serverProvided(error.errorDescription)))
|
||||
return .single(.failed(.serverProvided(text: error.errorDescription)))
|
||||
} else {
|
||||
return .single(.failed(.generic))
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ import SyncCore
|
||||
func messageFilterForTagMask(_ tagMask: MessageTags) -> Api.MessagesFilter? {
|
||||
if tagMask == .photoOrVideo {
|
||||
return Api.MessagesFilter.inputMessagesFilterPhotoVideo
|
||||
} else if tagMask == .photo {
|
||||
return Api.MessagesFilter.inputMessagesFilterPhotos
|
||||
} else if tagMask == .video {
|
||||
return Api.MessagesFilter.inputMessagesFilterVideo
|
||||
} else if tagMask == .file {
|
||||
return Api.MessagesFilter.inputMessagesFilterDocument
|
||||
} else if tagMask == .music {
|
||||
|
@ -757,6 +757,7 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
|
||||
var viaBotName: String?
|
||||
var entities: [MessageTextEntity]?
|
||||
var muted: Bool = false
|
||||
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
|
||||
@ -769,6 +770,10 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
} else if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
entities = attribute.entities
|
||||
} else if let attribute = attribute as? NotificationInfoMessageAttribute {
|
||||
if attribute.flags.contains(.muted) {
|
||||
muted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -797,6 +802,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
|
||||
return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: decryptedMedia))
|
||||
case .layer46:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -804,6 +812,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
flags |= (1 << 9)
|
||||
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
|
||||
case .layer73:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -818,6 +829,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
|
||||
case .layer101:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -889,6 +903,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
|
||||
if let decryptedMedia = decryptedMedia {
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -913,6 +930,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
|
||||
if let decryptedMedia = decryptedMedia {
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -944,6 +964,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
|
||||
if let decryptedMedia = decryptedMedia {
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -969,6 +992,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
case .layer8:
|
||||
break
|
||||
case .layer46:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -976,6 +1002,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
flags |= (1 << 9)
|
||||
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
|
||||
case .layer73:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -987,6 +1016,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
flags |= (1 << 9)
|
||||
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
|
||||
case .layer101:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1004,6 +1036,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
case .layer8:
|
||||
break
|
||||
case .layer46:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1016,6 +1051,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
|
||||
case .layer73:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1033,6 +1071,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
|
||||
case .layer101:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1055,6 +1096,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
case .layer8:
|
||||
break
|
||||
case .layer46:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1062,6 +1106,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
flags |= (1 << 9)
|
||||
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
|
||||
case .layer73:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1074,6 +1121,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
flags |= (1 << 9)
|
||||
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
|
||||
case .layer101:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1097,11 +1147,17 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
|
||||
return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: .decryptedMessageMediaEmpty))
|
||||
case .layer46:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
|
||||
case .layer73:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1111,6 +1167,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
|
||||
}
|
||||
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
|
||||
case .layer101:
|
||||
if muted {
|
||||
flags |= (1 << 5)
|
||||
}
|
||||
if let _ = viaBotName {
|
||||
flags |= (1 << 11)
|
||||
}
|
||||
@ -1375,7 +1434,7 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos
|
||||
if let state = transaction.getPeerChatState(messageId.peerId) as? SecretChatState, let peer = transaction.getPeer(messageId.peerId) as? TelegramSecretChat {
|
||||
if let message = transaction.getMessage(messageId), let globallyUniqueId = message.globallyUniqueId {
|
||||
let decryptedMessage = boxedDecryptedMessage(transaction: transaction, message: message, globallyUniqueId: globallyUniqueId, uploadedFile: file, thumbnailData: thumbnailData, layer: layer)
|
||||
return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: file, asService: wasDelivered, wasDelivered: wasDelivered)
|
||||
return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: file, silent: message.muted, asService: wasDelivered, wasDelivered: wasDelivered)
|
||||
|> mapToSignal { result in
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let forceRemove: Bool
|
||||
@ -1469,7 +1528,7 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let state = transaction.getPeerChatState(peerId) as? SecretChatState, let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
|
||||
let decryptedMessage = boxedDecryptedSecretMessageAction(action: action)
|
||||
return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: action.globallyUniqueId, file: nil, asService: true, wasDelivered: wasDelivered)
|
||||
return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: action.globallyUniqueId, file: nil, silent: false, asService: true, wasDelivered: wasDelivered)
|
||||
|> mapToSignal { result in
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let forceRemove: Bool
|
||||
@ -1528,7 +1587,7 @@ private enum SendBoxedDecryptedMessageResult {
|
||||
case error(SendBoxedDecryptedMessageError)
|
||||
}
|
||||
|
||||
private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: TelegramSecretChat, state: SecretChatState, operationIndex: Int32, decryptedMessage: BoxedDecryptedMessage, globallyUniqueId: Int64, file: SecretChatOutgoingFile?, asService: Bool, wasDelivered: Bool) -> Signal<SendBoxedDecryptedMessageResult, NoError> {
|
||||
private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: TelegramSecretChat, state: SecretChatState, operationIndex: Int32, decryptedMessage: BoxedDecryptedMessage, globallyUniqueId: Int64, file: SecretChatOutgoingFile?, silent: Bool, asService: Bool, wasDelivered: Bool) -> Signal<SendBoxedDecryptedMessageResult, NoError> {
|
||||
let payload = Buffer()
|
||||
var sequenceInfo: SecretChatOperationSequenceInfo?
|
||||
var maybeParameters: SecretChatEncryptionParameters?
|
||||
@ -1573,6 +1632,11 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer:
|
||||
let sendMessage: Signal<Api.messages.SentEncryptedMessage, MTRpcError>
|
||||
let inputPeer = Api.InputEncryptedChat.inputEncryptedChat(chatId: peer.id.id, accessHash: peer.accessHash)
|
||||
|
||||
var flags: Int32 = 0
|
||||
if silent {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
|
||||
if asService {
|
||||
let actionRandomId: Int64
|
||||
if wasDelivered {
|
||||
@ -1583,9 +1647,9 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer:
|
||||
sendMessage = network.request(Api.functions.messages.sendEncryptedService(peer: inputPeer, randomId: actionRandomId, data: Buffer(data: encryptedPayload)))
|
||||
} else {
|
||||
if let file = file {
|
||||
sendMessage = network.request(Api.functions.messages.sendEncryptedFile(peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload), file: file.reference.apiInputFile))
|
||||
sendMessage = network.request(Api.functions.messages.sendEncryptedFile(flags: flags, peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload), file: file.reference.apiInputFile))
|
||||
} else {
|
||||
sendMessage = network.request(Api.functions.messages.sendEncrypted(peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload)))
|
||||
sendMessage = network.request(Api.functions.messages.sendEncrypted(flags: flags, peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload)))
|
||||
}
|
||||
}
|
||||
return sendMessage
|
||||
|
@ -686,7 +686,7 @@ private func maximumMediaAutoremoveTimeout(_ media: [Media]) -> Int32 {
|
||||
|
||||
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi46.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
|
||||
switch apiMessage {
|
||||
case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId):
|
||||
case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId):
|
||||
var text = message
|
||||
var parsedMedia: [Media] = []
|
||||
var attributes: [MessageAttribute] = []
|
||||
@ -698,6 +698,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
|
||||
}
|
||||
|
||||
if (flags & 1 << 5) != 0 {
|
||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||
}
|
||||
|
||||
if let media = media {
|
||||
switch media {
|
||||
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
|
||||
@ -882,7 +886,7 @@ private func parseEntities(_ entities: [SecretApi73.MessageEntity]) -> TextEntit
|
||||
|
||||
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi73.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
|
||||
switch apiMessage {
|
||||
case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
|
||||
case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
|
||||
var text = message
|
||||
var parsedMedia: [Media] = []
|
||||
var attributes: [MessageAttribute] = []
|
||||
@ -896,6 +900,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
|
||||
}
|
||||
|
||||
if (flags & 1 << 5) != 0 {
|
||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||
}
|
||||
|
||||
if let media = media {
|
||||
switch media {
|
||||
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
|
||||
@ -1116,7 +1124,7 @@ private func parseEntities(_ entities: [SecretApi101.MessageEntity]) -> TextEnti
|
||||
|
||||
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi101.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
|
||||
switch apiMessage {
|
||||
case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
|
||||
case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
|
||||
var text = message
|
||||
var parsedMedia: [Media] = []
|
||||
var attributes: [MessageAttribute] = []
|
||||
@ -1130,6 +1138,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
|
||||
}
|
||||
|
||||
if (flags & 1 << 5) != 0 {
|
||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||
}
|
||||
|
||||
if let media = media {
|
||||
switch media {
|
||||
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
|
||||
|
@ -192,20 +192,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
||||
}
|
||||
}
|
||||
|
||||
let filter: Api.MessagesFilter
|
||||
if let tags = tags {
|
||||
if tags.contains(.file) {
|
||||
filter = .inputMessagesFilterDocument
|
||||
} else if tags.contains(.music) {
|
||||
filter = .inputMessagesFilterMusic
|
||||
} else if tags.contains(.webPage) {
|
||||
filter = .inputMessagesFilterUrl
|
||||
} else {
|
||||
filter = .inputMessagesFilterEmpty
|
||||
}
|
||||
} else {
|
||||
filter = .inputMessagesFilterEmpty
|
||||
}
|
||||
let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty
|
||||
remoteSearchResult = account.postbox.transaction { transaction -> (peer: Peer, additionalPeer: Peer?, from: Peer?)? in
|
||||
guard let peer = transaction.getPeer(peerId) else {
|
||||
return nil
|
||||
|
@ -30,6 +30,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
if let _ = attachment as? TelegramMediaImage {
|
||||
if !isSecret {
|
||||
tags.insert(.photoOrVideo)
|
||||
tags.insert(.photo)
|
||||
}
|
||||
} else if let file = attachment as? TelegramMediaFile {
|
||||
var refinedTag: MessageTags? = .file
|
||||
@ -41,7 +42,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
refinedTag = .voiceOrInstantVideo
|
||||
} else {
|
||||
if !isSecret {
|
||||
refinedTag = .photoOrVideo
|
||||
refinedTag = [.photoOrVideo, .video]
|
||||
} else {
|
||||
refinedTag = nil
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -308,10 +308,13 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.clockFreeMinImage = emptyImage
|
||||
self.dateAndStatusMediaBackground = emptyImage
|
||||
self.dateAndStatusFreeBackground = emptyImage
|
||||
self.incomingDateAndStatusImpressionIcon = emptyImage
|
||||
self.outgoingDateAndStatusImpressionIcon = emptyImage
|
||||
self.mediaImpressionIcon = emptyImage
|
||||
self.freeImpressionIcon = emptyImage
|
||||
|
||||
let impressionCountImage = UIImage(bundleImageName: "Chat/Message/ImpressionCount")!
|
||||
self.incomingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.incoming.secondaryTextColor)!
|
||||
self.outgoingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.outgoing.secondaryTextColor)!
|
||||
self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)!
|
||||
self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)!
|
||||
|
||||
self.radialIndicatorFileIconIncoming = emptyImage
|
||||
self.radialIndicatorFileIconOutgoing = emptyImage
|
||||
} else {
|
||||
|
Binary file not shown.
@ -1731,9 +1731,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, completed: {})
|
||||
}
|
||||
}, rateCall: { [weak self] message, callId in
|
||||
}, rateCall: { [weak self] message, callId, isVideo in
|
||||
if let strongSelf = self {
|
||||
let controller = callRatingController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, callId: callId, userInitiated: true, present: { [weak self] c, a in
|
||||
let controller = callRatingController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, callId: callId, userInitiated: true, isVideo: isVideo, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ public final class ChatControllerInteraction {
|
||||
let navigateToFirstDateMessage: (Int32) -> Void
|
||||
let requestRedeliveryOfFailedMessages: (MessageId) -> Void
|
||||
let addContact: (String) -> Void
|
||||
let rateCall: (Message, CallId) -> Void
|
||||
let rateCall: (Message, CallId, Bool) -> Void
|
||||
let requestSelectMessagePollOptions: (MessageId, [Data]) -> Void
|
||||
let requestOpenMessagePollResults: (MessageId, MediaId) -> Void
|
||||
let openAppStorePage: () -> Void
|
||||
@ -138,7 +138,7 @@ public final class ChatControllerInteraction {
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId, Bool) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
self.openMessage = openMessage
|
||||
self.openPeer = openPeer
|
||||
self.openPeerMention = openPeerMention
|
||||
@ -228,7 +228,7 @@ public final class ChatControllerInteraction {
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
|
@ -1484,7 +1484,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
||||
}
|
||||
|
||||
/*if transition.animateIn || animateIn {
|
||||
if transition.animateIn || animateIn {
|
||||
let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
|
||||
strongSelf.forEachVisibleItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
@ -1502,7 +1502,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||
itemNode.layer.animateScale(from: 0.9, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if let scrolledToIndex = transition.scrolledToIndex {
|
||||
if let strongSelf = self {
|
||||
|
@ -405,20 +405,22 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
|
||||
if data.messageActions.options.contains(.rateCall) {
|
||||
var callId: CallId?
|
||||
var isVideo: Bool = false
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case let .phoneCall(id, discardReason, _, _) = action.action {
|
||||
if let action = media as? TelegramMediaAction, case let .phoneCall(id, discardReason, _, isVideoValue) = action.action {
|
||||
isVideo = isVideoValue
|
||||
if discardReason != .busy && discardReason != .missed {
|
||||
if let logName = callLogNameForId(id: id, account: context.account) {
|
||||
let logsPath = callLogsPath(account: context.account)
|
||||
let logPath = logsPath + "/" + logName
|
||||
let start = logName.index(logName.startIndex, offsetBy: "\(id)".count + 1)
|
||||
let end = logName.index(logName.endIndex, offsetBy: -4)
|
||||
let end = logName.index(logName.endIndex, offsetBy: -4 - 5)
|
||||
let accessHash = logName[start..<end]
|
||||
if let accessHash = Int64(accessHash) {
|
||||
callId = CallId(id: id, accessHash: accessHash)
|
||||
}
|
||||
|
||||
actions.append(.action(ContextMenuActionItem(text: "Share Statistics", icon: { theme in
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Call_ShareStats, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
@ -446,7 +448,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Call_RateCall, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
let _ = controllerInteraction.rateCall(message, callId)
|
||||
let _ = controllerInteraction.rateCall(message, callId, isVideo)
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
}
|
||||
|
@ -1109,7 +1109,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
||||
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
||||
let peach = 0x1F351
|
||||
|
||||
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) || firstScalar.value == peach {
|
||||
|
@ -282,6 +282,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
let currentAdditionalImageBadgeNode = self.additionalImageBadgeNode
|
||||
|
||||
return { presentationData, automaticDownloadSettings, associatedData, attributes, context, controllerInteraction, message, messageRead, title, subtitle, text, entities, mediaAndFlags, mediaBadge, actionIcon, actionTitle, displayLine, layoutConstants, constrainedSize in
|
||||
let isPreview = presentationData.isPreview
|
||||
let fontSize: CGFloat = floor(presentationData.fontSize.baseDisplaySize * 15.0 / 17.0)
|
||||
|
||||
let titleFont = Font.semibold(fontSize)
|
||||
@ -526,7 +527,6 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
let imageMode = !((refineContentImageLayout == nil && refineContentFileLayout == nil && contentInstantVideoSizeAndApply == nil) || preferMediaBeforeText)
|
||||
statusInText = !imageMode
|
||||
|
||||
|
||||
if let count = webpageGalleryMediaCount {
|
||||
additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor, text: NSAttributedString(string: presentationData.strings.Items_NOfM("1", "\(count)").0))
|
||||
skipStandardStatus = imageMode
|
||||
@ -658,7 +658,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var continueActionButtonLayout: ((CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode))?
|
||||
if let actionTitle = actionTitle {
|
||||
if let actionTitle = actionTitle, !isPreview {
|
||||
let buttonImage: UIImage
|
||||
let buttonHighlightedImage: UIImage
|
||||
var buttonIconImage: UIImage?
|
||||
@ -781,6 +781,8 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
strongSelf.lineNode.frame = CGRect(origin: CGPoint(x: 13.0, y: insets.top), size: CGSize(width: 2.0, height: adjustedLineHeight - insets.top - insets.bottom - 2.0))
|
||||
strongSelf.lineNode.isHidden = !displayLine
|
||||
|
||||
strongSelf.textNode.displaysAsynchronously = !isPreview
|
||||
|
||||
let _ = textApply()
|
||||
|
||||
if let imageFrame = imageFrame {
|
||||
|
@ -759,6 +759,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
currentForwardInfo: (Peer?, String?)?,
|
||||
isSelected: Bool?
|
||||
) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) {
|
||||
let isPreview = item.presentationData.isPreview
|
||||
let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: isSelected)
|
||||
|
||||
let fontSize = floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
@ -848,11 +849,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
if hasAvatar {
|
||||
avatarInset = layoutConstants.avatarDiameter
|
||||
} else {
|
||||
avatarInset = 0.0
|
||||
}
|
||||
avatarInset = hasAvatar ? layoutConstants.avatarDiameter : 0.0
|
||||
|
||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||
|
||||
@ -907,7 +904,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
if item.presentationData.isPreview {
|
||||
if isPreview {
|
||||
needShareButton = false
|
||||
}
|
||||
|
||||
@ -992,7 +989,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
}
|
||||
} else if let attribute = attribute as? ReplyMessageAttribute {
|
||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
|
||||
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty && !isPreview {
|
||||
replyMarkup = attribute
|
||||
}
|
||||
}
|
||||
|
@ -496,6 +496,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
let _ = dateApply()
|
||||
|
||||
if let currentImpressionIcon = currentImpressionIcon {
|
||||
currentImpressionIcon.displaysAsynchronously = !presentationData.isPreview
|
||||
if currentImpressionIcon.image !== impressionImage {
|
||||
currentImpressionIcon.image = impressionImage
|
||||
}
|
||||
@ -508,7 +509,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
impressionIcon.removeFromSupernode()
|
||||
strongSelf.impressionIcon = nil
|
||||
}
|
||||
strongSelf.impressionIcon?.displaysAsynchronously = !presentationData.isPreview
|
||||
|
||||
|
||||
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset), size: date.size)
|
||||
|
||||
|
@ -868,7 +868,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
statusNode.backgroundNodeColor = backgroundNodeColor
|
||||
}
|
||||
|
||||
if state != .none && isVoice && self.playbackAudioLevelView == nil {
|
||||
if state != .none && isVoice && self.playbackAudioLevelView == nil && false {
|
||||
let blobFrame = progressFrame.insetBy(dx: -12.0, dy: -12.0)
|
||||
let playbackAudioLevelView = VoiceBlobView(
|
||||
frame: blobFrame,
|
||||
|
@ -425,7 +425,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: { [weak self] in
|
||||
|
@ -3,6 +3,7 @@ import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import SyncCore
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import ContextUI
|
||||
@ -67,8 +68,10 @@ final class ChatSendMessageActionSheetController: ViewController {
|
||||
}
|
||||
|
||||
var reminders = false
|
||||
if case let .peer(peerId) = self.interfaceState.chatLocation, peerId == context.account.peerId {
|
||||
reminders = true
|
||||
var isSecret = false
|
||||
if case let .peer(peerId) = self.interfaceState.chatLocation {
|
||||
reminders = peerId == context.account.peerId
|
||||
isSecret = peerId.namespace == Namespaces.Peer.SecretChat
|
||||
}
|
||||
|
||||
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, reminders: reminders, gesture: gesture, sendButtonFrame: self.sendButtonFrame, textInputNode: self.textInputNode, forwardedCount: forwardedCount, send: { [weak self] in
|
||||
@ -77,7 +80,7 @@ final class ChatSendMessageActionSheetController: ViewController {
|
||||
}, sendSilently: { [weak self] in
|
||||
self?.controllerInteraction?.sendCurrentMessage(true)
|
||||
self?.dismiss(cancel: false)
|
||||
}, schedule: { [weak self] in
|
||||
}, schedule: isSecret ? nil : { [weak self] in
|
||||
self?.controllerInteraction?.scheduleCurrentMessage()
|
||||
self?.dismiss(cancel: false)
|
||||
}, cancel: { [weak self] in
|
||||
|
@ -241,9 +241,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
sendSilently?()
|
||||
}))
|
||||
}
|
||||
contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: reminders ? self.presentationData.strings.Conversation_SendMessage_SetReminder: self.presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: .schedule, hasSeparator: false, action: {
|
||||
schedule?()
|
||||
}))
|
||||
if let _ = schedule {
|
||||
contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: reminders ? self.presentationData.strings.Conversation_SendMessage_SetReminder: self.presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: .schedule, hasSeparator: false, action: {
|
||||
schedule?()
|
||||
}))
|
||||
}
|
||||
self.contentNodes = contentNodes
|
||||
|
||||
super.init()
|
||||
|
@ -84,12 +84,6 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
|
||||
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle)
|
||||
}
|
||||
|
||||
@objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
|
||||
if !self.sendButtonHasApplyIcon && gestureRecognizer.state == .began {
|
||||
//self.sendButtonLongPressed?()
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(theme: PresentationTheme) {
|
||||
self.micButton.updateTheme(theme: theme)
|
||||
self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])
|
||||
|
@ -809,7 +809,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.textPlaceholderNode.frame = CGRect(origin: self.textPlaceholderNode.frame.origin, size: placeholderSize)
|
||||
}
|
||||
|
||||
self.actionButtons.sendButtonLongPressEnabled = peer.id.namespace != Namespaces.Peer.SecretChat && !interfaceState.isScheduledMessages
|
||||
self.actionButtons.sendButtonLongPressEnabled = !interfaceState.isScheduledMessages
|
||||
}
|
||||
|
||||
let sendButtonHasApplyIcon = interfaceState.interfaceState.editMessage != nil
|
||||
|
@ -121,7 +121,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
|
@ -108,7 +108,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
|
@ -1930,7 +1930,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
|
@ -413,7 +413,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
|
@ -38,7 +38,7 @@ private final class PrefetchManagerImpl {
|
||||
|> map { networkType -> MediaAutoDownloadNetworkType in
|
||||
switch networkType {
|
||||
case .none, .cellular:
|
||||
return.cellular
|
||||
return .cellular
|
||||
case .wifi:
|
||||
return .wifi
|
||||
}
|
||||
|
@ -1148,7 +1148,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return PeerSelectionControllerImpl(params)
|
||||
}
|
||||
|
||||
public func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem {
|
||||
public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem {
|
||||
let controllerInteraction: ChatControllerInteraction
|
||||
if tapMessage != nil || clickThroughMessage != nil {
|
||||
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
@ -1171,7 +1171,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
@ -1202,7 +1202,17 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
controllerInteraction = defaultChatControllerInteraction
|
||||
}
|
||||
|
||||
return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil)
|
||||
let content: ChatMessageItemContent
|
||||
let chatLocation: ChatLocation
|
||||
if messages.count > 1 {
|
||||
content = .group(messages: messages.map { ($0, true, .none, ChatMessageEntryAttributes()) })
|
||||
chatLocation = .peer(messages.first!.id.peerId)
|
||||
} else {
|
||||
content = .message(message: messages.first!, read: true, selection: .none, attributes: ChatMessageEntryAttributes())
|
||||
chatLocation = .peer(messages.first!.id.peerId)
|
||||
}
|
||||
|
||||
return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil)
|
||||
}
|
||||
|
||||
public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
|
||||
|
@ -127,25 +127,6 @@ public final class TelegramRootController: NavigationController {
|
||||
self.accountSettingsController = accountSettingsController
|
||||
self.rootTabController = tabBarController
|
||||
self.pushViewController(tabBarController, animated: false)
|
||||
|
||||
// Queue.mainQueue().after(2.0) {
|
||||
// let messageId = MessageId(peerId: PeerId(namespace: 2, id: 1488156064), namespace: 0, id: 528)
|
||||
// let _ = ((self.context.account.postbox.transaction { transaction in
|
||||
// return transaction.getMessage(messageId)
|
||||
// }) |> deliverOnMainQueue).start(next: { [weak self] message in
|
||||
// guard let strongSelf = self, let message = message else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
|
||||
// let renderer = MessageStoryRenderer(context: strongSelf.context, message: message)
|
||||
// let image = renderer.update(layout: layout)
|
||||
//
|
||||
// let node = renderer.containerNode
|
||||
// node.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
// strongSelf.displayNode.addSubnode(node)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
public func updateRootControllers(showCallsTab: Bool) {
|
||||
@ -193,134 +174,3 @@ public final class TelegramRootController: NavigationController {
|
||||
presentedLegacyShortcutCamera(context: self.context, saveCapturedMedia: false, saveEditedPhotos: false, mediaGrouping: true, parentController: controller)
|
||||
}
|
||||
}
|
||||
|
||||
class MessageStoryRenderer {
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
private let message: Message
|
||||
|
||||
let containerNode: ASDisplayNode
|
||||
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
||||
private let messagesContainerNode: ASDisplayNode
|
||||
private var dateHeaderNode: ListViewItemHeaderNode?
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
private let addressNode: ImmediateTextNode
|
||||
|
||||
init(context: AccountContext, message: Message) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.message = message
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.instantChatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings()), mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
self.messagesContainerNode.clipsToBounds = true
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
let peer = message.peers[message.id.peerId]!
|
||||
self.addressNode = ImmediateTextNode()
|
||||
self.addressNode.displaysAsynchronously = false
|
||||
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(peer.addressName ?? "")", font: Font.medium(14.0), textColor: UIColor(rgb: 0xa8b7c4))
|
||||
// self.addressNode.textShadowColor = .black
|
||||
|
||||
self.containerNode.addSubnode(self.instantChatBackgroundNode)
|
||||
self.containerNode.addSubnode(self.messagesContainerNode)
|
||||
self.containerNode.addSubnode(self.addressNode)
|
||||
}
|
||||
|
||||
// func update(layout: ContainerViewLayout, completion: (UIImage?) -> Void) {
|
||||
// self.updateMessagesLayout(layout: layout)
|
||||
//
|
||||
// Queue.mainQueue().after(0.01) {
|
||||
// UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0)
|
||||
// self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true)
|
||||
// let img = UIGraphicsGetImageFromCurrentImageContext()
|
||||
// UIGraphicsEndImageContext()
|
||||
// completion(img)
|
||||
// }
|
||||
// }
|
||||
|
||||
private func updateMessagesLayout(layout: ContainerViewLayout) {
|
||||
let size = layout.size
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.instantChatBackgroundNode.updateLayout(size: size, transition: .immediate)
|
||||
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
let addressLayout = self.addressNode.updateLayout(size)
|
||||
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.message.timestamp, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
||||
|
||||
var items: [ListViewItem] = []
|
||||
let sampleMessages: [Message] = [self.message]
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
}
|
||||
|
||||
let inset: CGFloat = 16.0
|
||||
let width = layout.size.width - inset * 2.0
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
||||
if let messageNodes = self.messageNodes {
|
||||
for i in 0 ..< items.count {
|
||||
let itemNode = messageNodes[i]
|
||||
items[i].updateNode(async: { $0() }, node: {
|
||||
return itemNode
|
||||
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
||||
let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
itemNode.insets = layout.insets
|
||||
itemNode.frame = nodeFrame
|
||||
itemNode.isUserInteractionEnabled = false
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
var messageNodes: [ListViewItemNode] = []
|
||||
for i in 0 ..< items.count {
|
||||
var itemNode: ListViewItemNode?
|
||||
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
|
||||
itemNode = node
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
itemNode!.isUserInteractionEnabled = false
|
||||
messageNodes.append(itemNode!)
|
||||
self.messagesContainerNode.addSubnode(itemNode!)
|
||||
}
|
||||
self.messageNodes = messageNodes
|
||||
}
|
||||
|
||||
var bottomOffset: CGFloat = 0.0
|
||||
if let messageNodes = self.messageNodes {
|
||||
for itemNode in messageNodes {
|
||||
itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size)
|
||||
bottomOffset += itemNode.frame.maxY
|
||||
itemNode.updateFrame(itemNode.frame, within: layout.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0))
|
||||
|
||||
let dateHeaderNode: ListViewItemHeaderNode
|
||||
if let currentDateHeaderNode = self.dateHeaderNode {
|
||||
dateHeaderNode = currentDateHeaderNode
|
||||
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
|
||||
} else {
|
||||
dateHeaderNode = headerItem.node()
|
||||
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
self.messagesContainerNode.addSubnode(dateHeaderNode)
|
||||
self.dateHeaderNode = dateHeaderNode
|
||||
}
|
||||
|
||||
dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))
|
||||
dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
public var playerEmbedding: Bool
|
||||
public var playlistPlayback: Bool
|
||||
public var preferredVideoCodec: String?
|
||||
public var disableVideoAspectScaling: Bool
|
||||
public var enableVoipTcp: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@ -23,7 +25,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
foldersTabAtBottom: false,
|
||||
playerEmbedding: false,
|
||||
playlistPlayback: false,
|
||||
preferredVideoCodec: nil
|
||||
preferredVideoCodec: nil,
|
||||
disableVideoAspectScaling: false,
|
||||
enableVoipTcp: false
|
||||
)
|
||||
}
|
||||
|
||||
@ -36,7 +40,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
foldersTabAtBottom: Bool,
|
||||
playerEmbedding: Bool,
|
||||
playlistPlayback: Bool,
|
||||
preferredVideoCodec: String?
|
||||
preferredVideoCodec: String?,
|
||||
disableVideoAspectScaling: Bool,
|
||||
enableVoipTcp: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@ -47,6 +53,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
self.playerEmbedding = playerEmbedding
|
||||
self.playlistPlayback = playlistPlayback
|
||||
self.preferredVideoCodec = preferredVideoCodec
|
||||
self.disableVideoAspectScaling = disableVideoAspectScaling
|
||||
self.enableVoipTcp = enableVoipTcp
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -59,6 +67,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0
|
||||
self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0
|
||||
self.preferredVideoCodec = decoder.decodeOptionalStringForKey("preferredVideoCodec")
|
||||
self.disableVideoAspectScaling = decoder.decodeInt32ForKey("disableVideoAspectScaling", orElse: 0) != 0
|
||||
self.enableVoipTcp = decoder.decodeInt32ForKey("enableVoipTcp", orElse: 0) != 0
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -73,6 +83,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
||||
if let preferredVideoCodec = self.preferredVideoCodec {
|
||||
encoder.encodeString(preferredVideoCodec, forKey: "preferredVideoCodec")
|
||||
}
|
||||
encoder.encodeInt32(self.disableVideoAspectScaling ? 1 : 0, forKey: "disableVideoAspectScaling")
|
||||
encoder.encodeInt32(self.enableVoipTcp ? 1 : 0, forKey: "enableVoipTcp")
|
||||
}
|
||||
|
||||
public func isEqual(to: PreferencesEntry) -> Bool {
|
||||
|
@ -34,8 +34,6 @@ private func callConnectionDescriptionsWebrtc(_ connection: CallSessionConnectio
|
||||
}
|
||||
}
|
||||
|
||||
private let callLogsLimit = 20
|
||||
|
||||
public func callLogNameForId(id: Int64, account: Account) -> String? {
|
||||
let path = callLogsPath(account: account)
|
||||
let namePrefix = "\(id)_"
|
||||
@ -63,26 +61,25 @@ private func cleanupCallLogs(account: Account) {
|
||||
try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
|
||||
var oldest: (URL, Date)? = nil
|
||||
var oldest: [(URL, Date)] = []
|
||||
var count = 0
|
||||
if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: path), includingPropertiesForKeys: [.contentModificationDateKey], options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants], errorHandler: nil) {
|
||||
for url in enumerator {
|
||||
if let url = url as? URL {
|
||||
if let date = (try? url.resourceValues(forKeys: Set([.contentModificationDateKey])))?.contentModificationDate {
|
||||
if let currentOldest = oldest {
|
||||
if date < currentOldest.1 {
|
||||
oldest = (url, date)
|
||||
}
|
||||
} else {
|
||||
oldest = (url, date)
|
||||
}
|
||||
oldest.append((url, date))
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if count > callLogsLimit, let oldest = oldest {
|
||||
try? fileManager.removeItem(atPath: oldest.0.path)
|
||||
let callLogsLimit = 40
|
||||
if count > callLogsLimit {
|
||||
oldest.sort(by: { $0.1 > $1.1 })
|
||||
while oldest.count > callLogsLimit {
|
||||
try? fileManager.removeItem(atPath: oldest[oldest.count - 1].0.path)
|
||||
oldest.removeLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,6 +268,7 @@ private protocol OngoingCallThreadLocalContextProtocol: class {
|
||||
func nativeSetIsMuted(_ value: Bool)
|
||||
func nativeSetIsLowBatteryLevel(_ value: Bool)
|
||||
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer)
|
||||
func nativeSetRequestedVideoAspect(_ aspect: Float)
|
||||
func nativeDisableVideo()
|
||||
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void)
|
||||
func nativeBeginTermination()
|
||||
@ -309,6 +307,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
||||
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) {
|
||||
}
|
||||
|
||||
func nativeSetRequestedVideoAspect(_ aspect: Float) {
|
||||
}
|
||||
|
||||
func nativeDisableVideo() {
|
||||
}
|
||||
|
||||
@ -347,10 +348,24 @@ public final class OngoingCallVideoCapturer {
|
||||
setOnFirstFrameReceived: { [weak view] f in
|
||||
view?.setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: {
|
||||
return .rotation0
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
return OngoingCallVideoOrientation(view.orientation)
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { _ in
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak view] f in
|
||||
view?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak view] f in
|
||||
view?.setOnIsMirroredUpdated(f)
|
||||
@ -392,6 +407,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
|
||||
self.requestVideo(capturer.impl)
|
||||
}
|
||||
|
||||
func nativeSetRequestedVideoAspect(_ aspect: Float) {
|
||||
self.setRequestedVideoAspect(aspect)
|
||||
}
|
||||
|
||||
func nativeDisableVideo() {
|
||||
self.disableVideo()
|
||||
}
|
||||
@ -456,11 +475,11 @@ private extension OngoingCallVideoOrientation {
|
||||
case .orientation0:
|
||||
self = .rotation0
|
||||
case .orientation90:
|
||||
self = .rotation270
|
||||
self = .rotation90
|
||||
case .orientation180:
|
||||
self = .rotation180
|
||||
case .orientation270:
|
||||
self = .rotation90
|
||||
self = .rotation270
|
||||
@unknown default:
|
||||
self = .rotation0
|
||||
}
|
||||
@ -471,19 +490,22 @@ public final class OngoingCallContextPresentationCallVideoView {
|
||||
public let view: UIView
|
||||
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
||||
public let getOrientation: () -> OngoingCallVideoOrientation
|
||||
public let setOnOrientationUpdated: (((OngoingCallVideoOrientation) -> Void)?) -> Void
|
||||
public let getAspect: () -> CGFloat
|
||||
public let setOnOrientationUpdated: (((OngoingCallVideoOrientation, CGFloat) -> Void)?) -> Void
|
||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||
|
||||
public init(
|
||||
view: UIView,
|
||||
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
||||
getOrientation: @escaping () -> OngoingCallVideoOrientation,
|
||||
setOnOrientationUpdated: @escaping (((OngoingCallVideoOrientation) -> Void)?) -> Void,
|
||||
getAspect: @escaping () -> CGFloat,
|
||||
setOnOrientationUpdated: @escaping (((OngoingCallVideoOrientation, CGFloat) -> Void)?) -> Void,
|
||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||
) {
|
||||
self.view = view
|
||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||
self.getOrientation = getOrientation
|
||||
self.getAspect = getAspect
|
||||
self.setOnOrientationUpdated = setOnOrientationUpdated
|
||||
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
||||
}
|
||||
@ -541,6 +563,9 @@ public final class OngoingCallContext {
|
||||
return OngoingCallThreadLocalContext.maxLayer()
|
||||
}
|
||||
|
||||
private let tempLogFile: TempBoxFile
|
||||
private let tempStatsLogFile: TempBoxFile
|
||||
|
||||
public static func versions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] {
|
||||
var result: [(version: String, supportsVideo: Bool)] = [(OngoingCallThreadLocalContext.version(), false)]
|
||||
if includeExperimental {
|
||||
@ -551,7 +576,7 @@ public final class OngoingCallContext {
|
||||
return result
|
||||
}
|
||||
|
||||
public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal<Bool, NoError>, logName: String, preferredVideoCodec: String?) {
|
||||
public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, enableTCP: Bool, enableStunMarking: Bool, audioSessionActive: Signal<Bool, NoError>, logName: String, preferredVideoCodec: String?) {
|
||||
let _ = setupLogs
|
||||
OngoingCallThreadLocalContext.applyServerConfig(serializedData)
|
||||
|
||||
@ -560,6 +585,11 @@ public final class OngoingCallContext {
|
||||
self.callSessionManager = callSessionManager
|
||||
self.logPath = logName.isEmpty ? "" : callLogsPath(account: self.account) + "/" + logName + ".log"
|
||||
let logPath = self.logPath
|
||||
self.tempLogFile = TempBox.shared.tempFile(fileName: "CallLog.txt")
|
||||
let tempLogPath = self.tempLogFile.path
|
||||
|
||||
self.tempStatsLogFile = TempBox.shared.tempFile(fileName: "CallStats.json")
|
||||
let tempStatsLogPath = self.tempStatsLogFile.path
|
||||
|
||||
let queue = self.queue
|
||||
|
||||
@ -581,11 +611,6 @@ public final class OngoingCallContext {
|
||||
}
|
||||
}
|
||||
|
||||
let screenSize = UIScreen.main.bounds.size
|
||||
let portraitSize = CGSize(width: min(screenSize.width, screenSize.height), height: max(screenSize.width, screenSize.height))
|
||||
let preferredAspectRatio = portraitSize.width / portraitSize.height
|
||||
|
||||
|
||||
let unfilteredConnections = [connections.primary] + connections.alternatives
|
||||
var processedConnections: [CallSessionConnection] = []
|
||||
var filteredConnections: [OngoingCallConnectionDescriptionWebrtc] = []
|
||||
@ -597,9 +622,9 @@ public final class OngoingCallContext {
|
||||
filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection))
|
||||
}
|
||||
|
||||
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in
|
||||
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, allowTCP: enableTCP, enableStunMarking: enableStunMarking, logPath: tempLogPath, statsLogPath: tempStatsLogPath, sendSignalingData: { [weak callSessionManager] data in
|
||||
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
|
||||
}, videoCapturer: video?.impl, preferredAspectRatio: Float(preferredAspectRatio), preferredVideoCodec: preferredVideoCodec)
|
||||
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec)
|
||||
|
||||
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
||||
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
||||
@ -748,10 +773,15 @@ public final class OngoingCallContext {
|
||||
public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise<String?>) {
|
||||
let account = self.account
|
||||
let logPath = self.logPath
|
||||
var statsLogPath = ""
|
||||
if !logPath.isEmpty {
|
||||
statsLogPath = logPath + ".json"
|
||||
}
|
||||
let tempLogPath = self.tempLogFile.path
|
||||
let tempStatsLogPath = self.tempStatsLogFile.path
|
||||
|
||||
self.withContextThenDeallocate { context in
|
||||
context.nativeStop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||
debugLogValue.set(.single(debugLog))
|
||||
let delta = NetworkUsageStatsConnectionsEntry(
|
||||
cellular: NetworkUsageStatsDirectionsEntry(
|
||||
incoming: bytesReceivedMobile,
|
||||
@ -761,17 +791,22 @@ public final class OngoingCallContext {
|
||||
outgoing: bytesSentWifi))
|
||||
updateAccountNetworkUsageStats(account: self.account, category: .call, delta: delta)
|
||||
|
||||
if !logPath.isEmpty, let debugLog = debugLog {
|
||||
if !logPath.isEmpty {
|
||||
let logsPath = callLogsPath(account: account)
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
if let data = debugLog.data(using: .utf8) {
|
||||
let _ = try? data.write(to: URL(fileURLWithPath: logPath))
|
||||
}
|
||||
let _ = try? FileManager.default.moveItem(atPath: tempLogPath, toPath: logPath)
|
||||
}
|
||||
|
||||
if let callId = callId, let debugLog = debugLog {
|
||||
if !statsLogPath.isEmpty {
|
||||
let logsPath = callLogsPath(account: account)
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
let _ = try? FileManager.default.moveItem(atPath: tempStatsLogPath, toPath: statsLogPath)
|
||||
}
|
||||
|
||||
if let callId = callId, !statsLogPath.isEmpty, let data = try? Data(contentsOf: URL(fileURLWithPath: statsLogPath)), let dataString = String(data: data, encoding: .utf8) {
|
||||
debugLogValue.set(.single(dataString))
|
||||
if sendDebugLogs {
|
||||
let _ = saveCallDebugLog(network: self.account.network, callId: callId, log: debugLog).start()
|
||||
let _ = saveCallDebugLog(network: self.account.network, callId: callId, log: dataString).start()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -800,6 +835,12 @@ public final class OngoingCallContext {
|
||||
}
|
||||
}
|
||||
|
||||
public func setRequestedVideoAspect(_ aspect: Float) {
|
||||
self.withContext { context in
|
||||
context.nativeSetRequestedVideoAspect(aspect)
|
||||
}
|
||||
}
|
||||
|
||||
public func disableVideo() {
|
||||
self.withContext { context in
|
||||
context.nativeDisableVideo()
|
||||
@ -837,9 +878,16 @@ public final class OngoingCallContext {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.aspect
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { [weak view] f in
|
||||
view?.setOnOrientationUpdated { value in
|
||||
f?(OngoingCallVideoOrientation(value))
|
||||
view?.setOnOrientationUpdated { value, aspect in
|
||||
f?(OngoingCallVideoOrientation(value), aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { [weak view] f in
|
||||
|
6
third-party/webrtc/BUILD
vendored
6
third-party/webrtc/BUILD
vendored
@ -1,3 +1,5 @@
|
||||
use_gn_build = True
|
||||
|
||||
webrtc_libs = [
|
||||
"libwebrtc.a",
|
||||
]
|
||||
@ -70,7 +72,7 @@ genrule(
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "webrtc_lib_gn",
|
||||
name = "webrtc_lib_gn" if not use_gn_build else "webrtc_lib",
|
||||
srcs = [":" + x for x in webrtc_libs],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@ -2182,7 +2184,7 @@ objc_library(
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "webrtc_lib",
|
||||
name = "webrtc_lib_custom" if use_gn_build else "webrtc_lib",
|
||||
enable_modules = True,
|
||||
module_name = "webrtc",
|
||||
srcs = combined_sources,
|
||||
|
Loading…
x
Reference in New Issue
Block a user