mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
1843d3c824
commit
191c1b31ef
@ -2494,6 +2494,13 @@ Unused sets are archived when you add more.";
|
||||
|
||||
"Message.PaymentSent" = "Payment: %@";
|
||||
"Notification.PaymentSent" = "You have just successfully transferred {amount} to {name} for {title}";
|
||||
"Notification.PaymentSentNoTitle" = "You have just successfully transferred {amount} to {name}";
|
||||
|
||||
"Notification.PaymentSentRecurringInit" = "You have just successfully transferred {amount} to {name} for {title} and allowed future reccurrent payments";
|
||||
"Notification.PaymentSentRecurringInitNoTitle" = "You have just successfully transferred {amount} to {name} and allowed future reccurrent payments";
|
||||
|
||||
"Notification.PaymentSentRecurringUsed" = "You have just successfully transferred {amount} to {name} for {title} via recurrent payments";
|
||||
"Notification.PaymentSentRecurringUsedNoTitle" = "You have just successfully transferred {amount} to {name} via recurrent payments";
|
||||
|
||||
"Common.NotNow" = "Not Now";
|
||||
|
||||
@ -7679,3 +7686,8 @@ Sorry for the inconvenience.";
|
||||
"WebApp.Settings" = "Settings";
|
||||
|
||||
"Bot.AccepRecurrentInfo" = "I accept [Terms of Service]() of **%1$@**";
|
||||
|
||||
"Chat.AudioTranscriptionRateAction" = "Rate Transcription";
|
||||
"Chat.AudioTranscriptionFeedbackTip" = "Thank you for your feedback.";
|
||||
"Message.AudioTranscription.ErrorEmpty" = "No speech detected";
|
||||
"Message.AudioTranscription.ErrorTooLong" = "The audio is too long";
|
||||
|
@ -1018,31 +1018,36 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let selectionPromise = self.selectedMessagesPromise
|
||||
|
||||
let previousRecentlySearchedPeerOrder = Atomic<[EnginePeer.Id]>(value: [])
|
||||
let fixedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers()
|
||||
|> map { peers -> [RecentlySearchedPeer] in
|
||||
var result: [RecentlySearchedPeer] = []
|
||||
let _ = previousRecentlySearchedPeerOrder.modify { current in
|
||||
var updated: [EnginePeer.Id] = []
|
||||
for id in current {
|
||||
inner: for peer in peers {
|
||||
if peer.peer.peerId == id {
|
||||
updated.append(id)
|
||||
result.append(peer)
|
||||
break inner
|
||||
let fixedRecentlySearchedPeers: Signal<[RecentlySearchedPeer], NoError>
|
||||
if case .chats = key {
|
||||
fixedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers()
|
||||
|> map { peers -> [RecentlySearchedPeer] in
|
||||
var result: [RecentlySearchedPeer] = []
|
||||
let _ = previousRecentlySearchedPeerOrder.modify { current in
|
||||
var updated: [EnginePeer.Id] = []
|
||||
for id in current {
|
||||
inner: for peer in peers {
|
||||
if peer.peer.peerId == id {
|
||||
updated.append(id)
|
||||
result.append(peer)
|
||||
break inner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for peer in peers.reversed() {
|
||||
if !updated.contains(peer.peer.peerId) {
|
||||
updated.insert(peer.peer.peerId, at: 0)
|
||||
result.insert(peer, at: 0)
|
||||
for peer in peers.reversed() {
|
||||
if !updated.contains(peer.peer.peerId) {
|
||||
updated.insert(peer.peer.peerId, at: 0)
|
||||
result.insert(peer, at: 0)
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
return updated
|
||||
return result
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
fixedRecentlySearchedPeers = .single([])
|
||||
}
|
||||
|
||||
|
||||
let downloadItems: Signal<(inProgressItems: [DownloadItem], doneItems: [RenderedRecentDownloadItem]), NoError>
|
||||
if key == .downloads {
|
||||
var firstTime = true
|
||||
@ -1191,7 +1196,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1)
|
||||
let foundLocalPeers: Signal<(peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set<EnginePeer.Id>), NoError>
|
||||
if let query = query {
|
||||
if let query = query, case .chats = key {
|
||||
let fixedOrRemovedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers()
|
||||
|> map { peers -> [RecentlySearchedPeer] in
|
||||
let allIds = peers.map(\.peer.peerId)
|
||||
|
@ -1,12 +1,15 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Markdown
|
||||
|
||||
public class ActionSheetTextItem: ActionSheetItem {
|
||||
public let title: String
|
||||
public let parseMarkdown: Bool
|
||||
|
||||
public init(title: String) {
|
||||
public init(title: String, parseMarkdown: Bool = true) {
|
||||
self.title = title
|
||||
self.parseMarkdown = parseMarkdown
|
||||
}
|
||||
|
||||
public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode {
|
||||
@ -63,8 +66,20 @@ public class ActionSheetTextNode: ActionSheetItemNode {
|
||||
self.item = item
|
||||
|
||||
let defaultFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0))
|
||||
let boldFont = Font.semibold(floor(theme.baseFontSize * 13.0 / 17.0))
|
||||
|
||||
if item.parseMarkdown {
|
||||
let body = MarkdownAttributeSet(font: defaultFont, textColor: self.theme.secondaryTextColor)
|
||||
let bold = MarkdownAttributeSet(font: boldFont, textColor: self.theme.secondaryTextColor)
|
||||
let link = body
|
||||
|
||||
self.label.attributedText = parseMarkdownIntoAttributedString(item.title, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in
|
||||
return nil
|
||||
}))
|
||||
} else {
|
||||
self.label.attributedText = NSAttributedString(string: item.title, font: defaultFont, textColor: self.theme.secondaryTextColor, paragraphAlignment: .center)
|
||||
}
|
||||
|
||||
self.label.attributedText = NSAttributedString(string: item.title, font: defaultFont, textColor: self.theme.secondaryTextColor, paragraphAlignment: .center)
|
||||
self.accessibilityArea.accessibilityLabel = item.title
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,15 @@ public extension CAAnimation {
|
||||
}
|
||||
}
|
||||
|
||||
private func adjustFrameRate(animation: CAAnimation) {
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension CALayer {
|
||||
func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
|
||||
if timingFunction.hasPrefix(kCAMediaTimingFunctionCustomSpringPrefix) {
|
||||
@ -84,9 +93,8 @@ public extension CALayer {
|
||||
animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor()
|
||||
animation.fillMode = .both
|
||||
}
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
return animation
|
||||
} else if timingFunction == kCAMediaTimingFunctionSpring {
|
||||
let animation = makeSpringAnimation(keyPath)
|
||||
@ -112,9 +120,7 @@ public extension CALayer {
|
||||
animation.fillMode = .both
|
||||
}
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
return animation
|
||||
} else {
|
||||
@ -146,9 +152,7 @@ public extension CALayer {
|
||||
animation.fillMode = .both
|
||||
}
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
return animation
|
||||
}
|
||||
@ -208,9 +212,7 @@ public extension CALayer {
|
||||
animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion)
|
||||
}
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
self.add(animation, forKey: keyPath)
|
||||
}
|
||||
@ -241,9 +243,7 @@ public extension CALayer {
|
||||
animation.speed = speed * Float(animation.duration / duration)
|
||||
animation.isAdditive = additive
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
return animation
|
||||
}
|
||||
@ -277,9 +277,7 @@ public extension CALayer {
|
||||
animation.speed = speed * Float(animation.duration / duration)
|
||||
animation.isAdditive = additive
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
self.add(animation, forKey: keyPath)
|
||||
}
|
||||
@ -308,9 +306,7 @@ public extension CALayer {
|
||||
animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion)
|
||||
}
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
|
||||
}
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
self.add(animation, forKey: key)
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ final class MessageHistoryReadStateTable: Table {
|
||||
return (nil, false)
|
||||
}
|
||||
|
||||
func applyIncomingMaxReadIndex(_ messageIndex: MessageIndex, topMessageIndex: MessageIndex?, incomingStatsInRange: (MessageIndex, MessageIndex) -> (count: Int, holes: Bool, readMesageIds: [MessageId])) -> (CombinedPeerReadState?, Bool, [MessageId]) {
|
||||
func applyIncomingMaxReadIndex(_ messageIndex: MessageIndex, topMessageIndex: MessageIndex?, incomingStatsInRange: (MessageIndex, MessageIndex) -> (count: Int, holes: Bool, readMessageIds: [MessageId])) -> (CombinedPeerReadState?, Bool, [MessageId]) {
|
||||
if let states = self.get(messageIndex.id.peerId), let state = states.namespaces[messageIndex.id.namespace] {
|
||||
if traceReadStates {
|
||||
print("[ReadStateTable] applyIncomingMaxReadIndex peerId: \(messageIndex.id.peerId), maxReadIndex: \(messageIndex) (before: \(states.namespaces))")
|
||||
@ -380,7 +380,7 @@ final class MessageHistoryReadStateTable: Table {
|
||||
return (nil, false, [])
|
||||
}
|
||||
|
||||
func applyInteractiveMaxReadIndex(postbox: PostboxImpl, messageIndex: MessageIndex, incomingStatsInRange: (MessageId.Namespace, MessageId.Id, MessageId.Id) -> (count: Int, holes: Bool), incomingIndexStatsInRange: (MessageIndex, MessageIndex) -> (count: Int, holes: Bool, readMesageIds: [MessageId]), topMessageId: (MessageId.Id, Bool)?, topMessageIndexByNamespace: (MessageId.Namespace) -> MessageIndex?) -> (combinedState: CombinedPeerReadState?, ApplyInteractiveMaxReadIdResult, readMesageIds: [MessageId]) {
|
||||
func applyInteractiveMaxReadIndex(postbox: PostboxImpl, messageIndex: MessageIndex, incomingStatsInRange: (MessageId.Namespace, MessageId.Id, MessageId.Id) -> (count: Int, holes: Bool), incomingIndexStatsInRange: (MessageIndex, MessageIndex) -> (count: Int, holes: Bool, readMessageIds: [MessageId]), topMessageId: (MessageId.Id, Bool)?, topMessageIndexByNamespace: (MessageId.Namespace) -> MessageIndex?) -> (combinedState: CombinedPeerReadState?, ApplyInteractiveMaxReadIdResult, readMessageIds: [MessageId]) {
|
||||
if let states = self.get(messageIndex.id.peerId) {
|
||||
if let state = states.namespaces[messageIndex.id.namespace] {
|
||||
switch state {
|
||||
|
@ -1118,7 +1118,7 @@ public final class MessageHistoryView {
|
||||
self.maxReadIndex = nil
|
||||
}
|
||||
case let .external(input):
|
||||
if let maxReadMesageId = input.maxReadIncomingMessageId {
|
||||
if let maxReadMessageId = input.maxReadIncomingMessageId {
|
||||
var maxIndex: MessageIndex?
|
||||
|
||||
let hasUnread = true
|
||||
@ -1128,15 +1128,15 @@ public final class MessageHistoryView {
|
||||
peerIds.insert(entry.index.id.peerId)
|
||||
}
|
||||
for peerId in peerIds {
|
||||
if peerId != maxReadMesageId.peerId {
|
||||
if peerId != maxReadMessageId.peerId {
|
||||
continue
|
||||
}
|
||||
let namespace = maxReadMesageId.namespace
|
||||
let namespace = maxReadMessageId.namespace
|
||||
|
||||
var maxNamespaceIndex: MessageIndex?
|
||||
var index = entries.count - 1
|
||||
for entry in entries.reversed() {
|
||||
if entry.index.id.peerId == peerId && entry.index.id.namespace == namespace && entry.index.id <= maxReadMesageId {
|
||||
if entry.index.id.peerId == peerId && entry.index.id.namespace == namespace && entry.index.id <= maxReadMessageId {
|
||||
maxNamespaceIndex = entry.index
|
||||
break
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ swift_library(
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -420,8 +420,21 @@ public final class ShareController: ViewController {
|
||||
if case .saveToCameraRoll = preferredAction {
|
||||
self.actionIsMediaSaving = true
|
||||
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in
|
||||
self?.saveToCameraRoll(messages: messages)
|
||||
self?.actionCompleted?()
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let actionCompleted = strongSelf.actionCompleted
|
||||
strongSelf.saveToCameraRoll(messages: messages, completion: {
|
||||
actionCompleted?()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controllerNode.animateOut(shared: false, completion: {
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
})
|
||||
})
|
||||
})
|
||||
} else if let message = messages.first {
|
||||
let groupingKey: Int64? = message.groupingKey
|
||||
@ -907,7 +920,7 @@ public final class ShareController: ViewController {
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
}
|
||||
|
||||
private func saveToCameraRoll(messages: [Message]) {
|
||||
private func saveToCameraRoll(messages: [Message], completion: @escaping () -> Void) {
|
||||
let postbox = self.currentAccount.postbox
|
||||
let signals: [Signal<Float, NoError>] = messages.compactMap { message -> Signal<Float, NoError>? in
|
||||
if let media = message.media.first {
|
||||
@ -932,7 +945,7 @@ public final class ShareController: ViewController {
|
||||
total /= Float(values.count)
|
||||
return total
|
||||
}
|
||||
self.controllerNode.transitionToProgressWithValue(signal: total)
|
||||
self.controllerNode.transitionToProgressWithValue(signal: total, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,7 +957,7 @@ public final class ShareController: ViewController {
|
||||
} else {
|
||||
context = self.sharedContext.makeTempAccountContext(account: self.currentAccount)
|
||||
}
|
||||
self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, mediaReference: .standalone(media: media)) |> map(Optional.init), dismissImmediately: true)
|
||||
self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, mediaReference: .standalone(media: media)) |> map(Optional.init), dismissImmediately: true, completion: {})
|
||||
}
|
||||
|
||||
private func saveToCameraRoll(mediaReference: AnyMediaReference) {
|
||||
@ -954,7 +967,7 @@ public final class ShareController: ViewController {
|
||||
} else {
|
||||
context = self.sharedContext.makeTempAccountContext(account: self.currentAccount)
|
||||
}
|
||||
self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, mediaReference: mediaReference) |> map(Optional.init), dismissImmediately: true)
|
||||
self.controllerNode.transitionToProgressWithValue(signal: SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, mediaReference: mediaReference) |> map(Optional.init), dismissImmediately: true, completion: {})
|
||||
}
|
||||
|
||||
private func switchToAccount(account: Account, animateIn: Bool) {
|
||||
|
@ -697,7 +697,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
if fromForeignApp, case let .preparing(long) = status, !transitioned {
|
||||
transitioned = true
|
||||
if long {
|
||||
strongSelf.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, forceNativeAppearance: true), fastOut: true)
|
||||
strongSelf.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, forceNativeAppearance: true, account: strongSelf.context?.account, sharedContext: strongSelf.sharedContext), fastOut: true)
|
||||
} else {
|
||||
strongSelf.transitionToContentNode(ShareLoadingContainerNode(theme: strongSelf.presentationData.theme, forceNativeAppearance: true), fastOut: true)
|
||||
}
|
||||
@ -1007,7 +1007,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
transition.updateAlpha(node: self.actionSeparatorNode, alpha: 0.0)
|
||||
transition.updateAlpha(node: self.actionsBackgroundNode, alpha: 0.0)
|
||||
|
||||
self.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, forceNativeAppearance: true), fastOut: true)
|
||||
self.transitionToContentNode(ShareProlongedLoadingContainerNode(theme: self.presentationData.theme, strings: self.presentationData.strings, forceNativeAppearance: true, account: self.context?.account, sharedContext: self.sharedContext), fastOut: true)
|
||||
let timestamp = CACurrentMediaTime()
|
||||
self.shareDisposable.set(signal.start(completed: { [weak self] in
|
||||
let minDelay = 0.6
|
||||
@ -1022,7 +1022,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}))
|
||||
}
|
||||
|
||||
func transitionToProgressWithValue(signal: Signal<Float?, NoError>, dismissImmediately: Bool = false) {
|
||||
func transitionToProgressWithValue(signal: Signal<Float?, NoError>, dismissImmediately: Bool = false, completion: @escaping () -> Void) {
|
||||
self.inputFieldNode.deactivateInput()
|
||||
|
||||
if dismissImmediately {
|
||||
@ -1035,6 +1035,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
if let strongSelf = self {
|
||||
strongSelf.dismiss?(true)
|
||||
}
|
||||
|
||||
completion()
|
||||
}))
|
||||
} else {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.12, curve: .easeInOut)
|
||||
@ -1067,6 +1069,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
contentNode.state = .progress(status)
|
||||
}
|
||||
}, completed: { [weak self] in
|
||||
completion()
|
||||
|
||||
guard let strongSelf = self, let contentNode = strongSelf.contentNode as? ShareLoadingContainer else {
|
||||
return
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ final class ShareControllerRecentPeersGridItemNode: GridItemNode {
|
||||
|
||||
let bounds = self.bounds
|
||||
|
||||
self.peersNode?.frame = CGRect(origin: CGPoint(), size: bounds.size)
|
||||
self.peersNode?.frame = CGRect(origin: CGPoint(x: -8.0, y: 0.0), size: CGSize(width: bounds.width + 8.0, height: bounds.height))
|
||||
self.peersNode?.updateLayout(size: bounds.size, leftInset: 0.0, rightInset: 0.0)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ import ActivityIndicator
|
||||
import RadialStatusNode
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import AppBundle
|
||||
import TelegramUniversalVideoContent
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
|
||||
public enum ShareLoadingState {
|
||||
case preparing
|
||||
@ -115,6 +119,8 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
|
||||
|
||||
private var startTimestamp: Double?
|
||||
|
||||
private var videoNode: UniversalVideoNode?
|
||||
|
||||
public var state: ShareLoadingState = .preparing {
|
||||
didSet {
|
||||
switch self.state {
|
||||
@ -202,7 +208,7 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
|
||||
return self.elapsedTime + 3.0 + 0.15
|
||||
}
|
||||
|
||||
public init(theme: PresentationTheme, strings: PresentationStrings, forceNativeAppearance: Bool) {
|
||||
public init(theme: PresentationTheme, strings: PresentationStrings, forceNativeAppearance: Bool, account: Account?, sharedContext: SharedAccountContext) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
@ -242,6 +248,23 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
|
||||
strongSelf.elapsedTime = status.duration - status.timestamp
|
||||
}
|
||||
}))
|
||||
|
||||
if let account = account, let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) {
|
||||
let decoration = ChatBubbleVideoDecoration(corners: ImageCorners(), nativeSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, backgroundColor: .black)
|
||||
|
||||
let dummyFile = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [])])
|
||||
|
||||
let videoContent = NativeVideoContent(id: .message(1, MediaId(namespace: 0, id: 1)), fileReference: .standalone(media: dummyFile), streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black)
|
||||
|
||||
let videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: sharedContext.mediaManager.audioSession, manager: sharedContext.mediaManager.universalVideoManager, decoration: decoration, content: videoContent, priority: .embedded)
|
||||
videoNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: 2.0))
|
||||
videoNode.alpha = 0.01
|
||||
self.videoNode = videoNode
|
||||
|
||||
self.addSubnode(videoNode)
|
||||
videoNode.canAttachContent = true
|
||||
videoNode.play()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
@ -808,7 +808,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1135492588] = { return Api.Update.parse_updateStickerSets($0) }
|
||||
dict[196268545] = { return Api.Update.parse_updateStickerSetsOrder($0) }
|
||||
dict[-2112423005] = { return Api.Update.parse_updateTheme($0) }
|
||||
dict[-2006880112] = { return Api.Update.parse_updateTranscribeAudio($0) }
|
||||
dict[8703322] = { return Api.Update.parse_updateTranscribedAudio($0) }
|
||||
dict[-1007549728] = { return Api.Update.parse_updateUserName($0) }
|
||||
dict[88680979] = { return Api.Update.parse_updateUserPhone($0) }
|
||||
dict[-232290676] = { return Api.Update.parse_updateUserPhoto($0) }
|
||||
|
@ -599,7 +599,7 @@ public extension Api {
|
||||
case updateStickerSets
|
||||
case updateStickerSetsOrder(flags: Int32, order: [Int64])
|
||||
case updateTheme(theme: Api.Theme)
|
||||
case updateTranscribeAudio(flags: Int32, transcriptionId: Int64, text: String)
|
||||
case updateTranscribedAudio(flags: Int32, peer: Api.Peer, msgId: Int32, transcriptionId: Int64, text: String)
|
||||
case updateUserName(userId: Int64, firstName: String, lastName: String, username: String)
|
||||
case updateUserPhone(userId: Int64, phone: String)
|
||||
case updateUserPhoto(userId: Int64, date: Int32, photo: Api.UserProfilePhoto, previous: Api.Bool)
|
||||
@ -1424,11 +1424,13 @@ public extension Api {
|
||||
}
|
||||
theme.serialize(buffer, true)
|
||||
break
|
||||
case .updateTranscribeAudio(let flags, let transcriptionId, let text):
|
||||
case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2006880112)
|
||||
buffer.appendInt32(8703322)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(transcriptionId, buffer: buffer, boxed: false)
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
break
|
||||
@ -1676,8 +1678,8 @@ public extension Api {
|
||||
return ("updateStickerSetsOrder", [("flags", String(describing: flags)), ("order", String(describing: order))])
|
||||
case .updateTheme(let theme):
|
||||
return ("updateTheme", [("theme", String(describing: theme))])
|
||||
case .updateTranscribeAudio(let flags, let transcriptionId, let text):
|
||||
return ("updateTranscribeAudio", [("flags", String(describing: flags)), ("transcriptionId", String(describing: transcriptionId)), ("text", String(describing: text))])
|
||||
case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text):
|
||||
return ("updateTranscribedAudio", [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("transcriptionId", String(describing: transcriptionId)), ("text", String(describing: text))])
|
||||
case .updateUserName(let userId, let firstName, let lastName, let username):
|
||||
return ("updateUserName", [("userId", String(describing: userId)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("username", String(describing: username))])
|
||||
case .updateUserPhone(let userId, let phone):
|
||||
@ -3336,18 +3338,26 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateTranscribeAudio(_ reader: BufferReader) -> Update? {
|
||||
public static func parse_updateTranscribedAudio(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateTranscribeAudio(flags: _1!, transcriptionId: _2!, text: _3!)
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.Update.updateTranscribedAudio(flags: _1!, peer: _2!, msgId: _3!, transcriptionId: _4!, text: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -113,7 +113,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateGroupCall(peerId: PeerId, call: Api.GroupCall)
|
||||
case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?)
|
||||
case UpdateAttachMenuBots
|
||||
case UpdateAudioTranscription(id: Int64, isPending: Bool, text: String)
|
||||
case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String)
|
||||
}
|
||||
|
||||
struct HoleFromPreviousState {
|
||||
@ -509,8 +509,8 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateAttachMenuBots)
|
||||
}
|
||||
|
||||
mutating func updateAudioTranscription(id: Int64, isPending: Bool, text: String) {
|
||||
self.addOperation(.UpdateAudioTranscription(id: id, isPending: isPending, text: text))
|
||||
mutating func updateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String) {
|
||||
self.addOperation(.UpdateAudioTranscription(messageId: messageId, id: id, isPending: isPending, text: text))
|
||||
}
|
||||
|
||||
mutating func addDismissedWebView(queryId: Int64) {
|
||||
|
@ -40,8 +40,10 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
return TelegramMediaAction(action: .phoneCall(callId: callId, discardReason: discardReason, duration: duration, isVideo: isVideo))
|
||||
case .messageActionEmpty:
|
||||
return nil
|
||||
case let .messageActionPaymentSent(_, currency, totalAmount, invoiceSlug):
|
||||
return TelegramMediaAction(action: .paymentSent(currency: currency, totalAmount: totalAmount, invoiceSlug: invoiceSlug))
|
||||
case let .messageActionPaymentSent(flags, currency, totalAmount, invoiceSlug):
|
||||
let isRecurringInit = (flags & (1 << 2)) != 0
|
||||
let isRecurringUsed = (flags & (1 << 3)) != 0
|
||||
return TelegramMediaAction(action: .paymentSent(currency: currency, totalAmount: totalAmount, invoiceSlug: invoiceSlug, isRecurringInit: isRecurringInit, isRecurringUsed: isRecurringUsed))
|
||||
case .messageActionPaymentSentMe:
|
||||
return nil
|
||||
case .messageActionScreenshotTaken:
|
||||
|
@ -1101,9 +1101,9 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
updatedState.updateMedia(webpage.webpageId, media: webpage)
|
||||
}
|
||||
}
|
||||
case let .updateTranscribeAudio(flags, transcriptionId, text):
|
||||
let isFinal = (flags & (1 << 0)) != 0
|
||||
updatedState.updateAudioTranscription(id: transcriptionId, isPending: !isFinal, text: text)
|
||||
case let .updateTranscribedAudio(flags, peer, msgId, transcriptionId, text):
|
||||
let isPending = (flags & (1 << 0)) != 0
|
||||
updatedState.updateAudioTranscription(messageId: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), id: transcriptionId, isPending: isPending, text: text)
|
||||
case let .updateNotifySettings(apiPeer, apiNotificationSettings):
|
||||
switch apiPeer {
|
||||
case let .notifyPeer(peer):
|
||||
@ -2403,8 +2403,7 @@ func replayFinalState(
|
||||
auxiliaryMethods: AccountAuxiliaryMethods,
|
||||
finalState: AccountFinalState,
|
||||
removePossiblyDeliveredMessagesUniqueIds: [Int64: PeerId],
|
||||
ignoreDate: Bool,
|
||||
audioTranscriptionManager: Atomic<AudioTranscriptionManager>?
|
||||
ignoreDate: Bool
|
||||
) -> AccountReplayedFinalState? {
|
||||
let verified = verifyTransaction(transaction, finalState: finalState.state)
|
||||
if !verified {
|
||||
@ -3344,48 +3343,42 @@ func replayFinalState(
|
||||
})
|
||||
case .UpdateAttachMenuBots:
|
||||
syncAttachMenuBots = true
|
||||
case let .UpdateAudioTranscription(id, isPending, text):
|
||||
if let audioTranscriptionManager = audioTranscriptionManager {
|
||||
if let messageId = audioTranscriptionManager.with({ audioTranscriptionManager in
|
||||
return audioTranscriptionManager.getPendingMapping(transcriptionId: id)
|
||||
}) {
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
||||
}
|
||||
var attributes = currentMessage.attributes
|
||||
var found = false
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? AudioTranscriptionMessageAttribute {
|
||||
attributes[j] = AudioTranscriptionMessageAttribute(id: id, text: text, isPending: isPending, didRate: attribute.didRate)
|
||||
found = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
attributes.append(AudioTranscriptionMessageAttribute(id: id, text: text, isPending: isPending, didRate: false))
|
||||
}
|
||||
|
||||
return .update(StoreMessage(
|
||||
id: currentMessage.id,
|
||||
globallyUniqueId: currentMessage.globallyUniqueId,
|
||||
groupingKey: currentMessage.groupingKey,
|
||||
threadId: currentMessage.threadId,
|
||||
timestamp: currentMessage.timestamp,
|
||||
flags: StoreMessageFlags(currentMessage.flags),
|
||||
tags: currentMessage.tags,
|
||||
globalTags: currentMessage.globalTags,
|
||||
localTags: currentMessage.localTags,
|
||||
forwardInfo: storeForwardInfo,
|
||||
authorId: currentMessage.author?.id,
|
||||
text: currentMessage.text,
|
||||
attributes: attributes,
|
||||
media: currentMessage.media
|
||||
))
|
||||
})
|
||||
case let .UpdateAudioTranscription(messageId, id, isPending, text):
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
||||
}
|
||||
}
|
||||
var attributes = currentMessage.attributes
|
||||
var found = false
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? AudioTranscriptionMessageAttribute {
|
||||
attributes[j] = AudioTranscriptionMessageAttribute(id: id, text: text, isPending: isPending, didRate: attribute.didRate, error: nil)
|
||||
found = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
attributes.append(AudioTranscriptionMessageAttribute(id: id, text: text, isPending: isPending, didRate: false, error: nil))
|
||||
}
|
||||
|
||||
return .update(StoreMessage(
|
||||
id: currentMessage.id,
|
||||
globallyUniqueId: currentMessage.globallyUniqueId,
|
||||
groupingKey: currentMessage.groupingKey,
|
||||
threadId: currentMessage.threadId,
|
||||
timestamp: currentMessage.timestamp,
|
||||
flags: StoreMessageFlags(currentMessage.flags),
|
||||
tags: currentMessage.tags,
|
||||
globalTags: currentMessage.globalTags,
|
||||
localTags: currentMessage.localTags,
|
||||
forwardInfo: storeForwardInfo,
|
||||
authorId: currentMessage.author?.id,
|
||||
text: currentMessage.text,
|
||||
attributes: attributes,
|
||||
media: currentMessage.media
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,6 @@ public final class AccountStateManager {
|
||||
let auxiliaryMethods: AccountAuxiliaryMethods
|
||||
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
|
||||
|
||||
let audioTranscriptionManager = Atomic<AudioTranscriptionManager>(value: AudioTranscriptionManager())
|
||||
|
||||
private var updateService: UpdateMessageService?
|
||||
private let updateServiceDisposable = MetaDisposable()
|
||||
|
||||
@ -427,7 +425,6 @@ public final class AccountStateManager {
|
||||
let mediaBox = postbox.mediaBox
|
||||
let accountPeerId = self.accountPeerId
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let audioTranscriptionManager = self.audioTranscriptionManager
|
||||
let signal = postbox.stateView()
|
||||
|> mapToSignal { view -> Signal<AuthorizedAccountState, NoError> in
|
||||
if let state = view.state as? AuthorizedAccountState {
|
||||
@ -479,7 +476,7 @@ public final class AccountStateManager {
|
||||
let removePossiblyDeliveredMessagesUniqueIds = self?.removePossiblyDeliveredMessagesUniqueIds ?? Dictionary()
|
||||
return postbox.transaction { transaction -> (difference: Api.updates.Difference?, finalStatte: AccountReplayedFinalState?, skipBecauseOfError: Bool) in
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let replayedState = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false, audioTranscriptionManager: audioTranscriptionManager)
|
||||
let replayedState = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
@ -578,7 +575,6 @@ public final class AccountStateManager {
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let accountPeerId = self.accountPeerId
|
||||
let mediaBox = postbox.mediaBox
|
||||
let audioTranscriptionManager = self.audioTranscriptionManager
|
||||
let queue = self.queue
|
||||
let signal = initialStateWithUpdateGroups(postbox: postbox, groups: groups)
|
||||
|> mapToSignal { [weak self] state -> Signal<(AccountReplayedFinalState?, AccountFinalState), NoError> in
|
||||
@ -598,7 +594,7 @@ public final class AccountStateManager {
|
||||
return nil
|
||||
} else {
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false, audioTranscriptionManager: audioTranscriptionManager)
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
@ -831,11 +827,10 @@ public final class AccountStateManager {
|
||||
let mediaBox = self.postbox.mediaBox
|
||||
let network = self.network
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let audioTranscriptionManager = self.audioTranscriptionManager
|
||||
let removePossiblyDeliveredMessagesUniqueIds = self.removePossiblyDeliveredMessagesUniqueIds
|
||||
let signal = self.postbox.transaction { transaction -> AccountReplayedFinalState? in
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false, audioTranscriptionManager: audioTranscriptionManager)
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
@ -877,11 +872,10 @@ public final class AccountStateManager {
|
||||
let mediaBox = self.postbox.mediaBox
|
||||
let network = self.network
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let audioTranscriptionManager = self.audioTranscriptionManager
|
||||
let removePossiblyDeliveredMessagesUniqueIds = self.removePossiblyDeliveredMessagesUniqueIds
|
||||
let signal = self.postbox.transaction { transaction -> AccountReplayedFinalState? in
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false, audioTranscriptionManager: audioTranscriptionManager)
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState, removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds, ignoreDate: false)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
@ -903,7 +897,6 @@ public final class AccountStateManager {
|
||||
let mediaBox = postbox.mediaBox
|
||||
let accountPeerId = self.accountPeerId
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let audioTranscriptionManager = self.audioTranscriptionManager
|
||||
|
||||
let signal = postbox.stateView()
|
||||
|> mapToSignal { view -> Signal<AuthorizedAccountState, NoError> in
|
||||
@ -966,8 +959,7 @@ public final class AccountStateManager {
|
||||
auxiliaryMethods: auxiliaryMethods,
|
||||
finalState: finalState,
|
||||
removePossiblyDeliveredMessagesUniqueIds: removePossiblyDeliveredMessagesUniqueIds,
|
||||
ignoreDate: true,
|
||||
audioTranscriptionManager: audioTranscriptionManager
|
||||
ignoreDate: true
|
||||
)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
|
@ -1,20 +1,27 @@
|
||||
import Postbox
|
||||
|
||||
public class AudioTranscriptionMessageAttribute: MessageAttribute, Equatable {
|
||||
public enum TranscriptionError: Int32, Error {
|
||||
case generic = 0
|
||||
case tooLong = 1
|
||||
}
|
||||
|
||||
public let id: Int64
|
||||
public let text: String
|
||||
public let isPending: Bool
|
||||
public let didRate: Bool
|
||||
public let error: TranscriptionError?
|
||||
|
||||
public var associatedPeerIds: [PeerId] {
|
||||
return []
|
||||
}
|
||||
|
||||
public init(id: Int64, text: String, isPending: Bool, didRate: Bool) {
|
||||
public init(id: Int64, text: String, isPending: Bool, didRate: Bool, error: TranscriptionError?) {
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.isPending = isPending
|
||||
self.didRate = didRate
|
||||
self.error = error
|
||||
}
|
||||
|
||||
required public init(decoder: PostboxDecoder) {
|
||||
@ -22,6 +29,11 @@ public class AudioTranscriptionMessageAttribute: MessageAttribute, Equatable {
|
||||
self.text = decoder.decodeStringForKey("text", orElse: "")
|
||||
self.isPending = decoder.decodeBoolForKey("isPending", orElse: false)
|
||||
self.didRate = decoder.decodeBoolForKey("didRate", orElse: false)
|
||||
if let errorValue = decoder.decodeOptionalInt32ForKey("error") {
|
||||
self.error = TranscriptionError(rawValue: errorValue)
|
||||
} else {
|
||||
self.error = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -29,6 +41,11 @@ public class AudioTranscriptionMessageAttribute: MessageAttribute, Equatable {
|
||||
encoder.encodeString(self.text, forKey: "text")
|
||||
encoder.encodeBool(self.isPending, forKey: "isPending")
|
||||
encoder.encodeBool(self.didRate, forKey: "didRate")
|
||||
if let error = self.error {
|
||||
encoder.encodeInt32(error.rawValue, forKey: "error")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "error")
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: AudioTranscriptionMessageAttribute, rhs: AudioTranscriptionMessageAttribute) -> Bool {
|
||||
@ -44,14 +61,17 @@ public class AudioTranscriptionMessageAttribute: MessageAttribute, Equatable {
|
||||
if lhs.didRate != rhs.didRate {
|
||||
return false
|
||||
}
|
||||
if lhs.error != rhs.error {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func merge(withPrevious other: AudioTranscriptionMessageAttribute) -> AudioTranscriptionMessageAttribute {
|
||||
return AudioTranscriptionMessageAttribute(id: self.id, text: self.text, isPending: self.isPending, didRate: self.didRate || other.didRate)
|
||||
return AudioTranscriptionMessageAttribute(id: self.id, text: self.text, isPending: self.isPending, didRate: self.didRate || other.didRate, error: self.error)
|
||||
}
|
||||
|
||||
func withDidRate() -> AudioTranscriptionMessageAttribute {
|
||||
return AudioTranscriptionMessageAttribute(id: self.id, text: self.text, isPending: self.isPending, didRate: true)
|
||||
return AudioTranscriptionMessageAttribute(id: self.id, text: self.text, isPending: self.isPending, didRate: true, error: self.error)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
case messageAutoremoveTimeoutUpdated(Int32)
|
||||
case gameScore(gameId: Int64, score: Int32)
|
||||
case phoneCall(callId: Int64, discardReason: PhoneCallDiscardReason?, duration: Int32?, isVideo: Bool)
|
||||
case paymentSent(currency: String, totalAmount: Int64, invoiceSlug: String?)
|
||||
case paymentSent(currency: String, totalAmount: Int64, invoiceSlug: String?, isRecurringInit: Bool, isRecurringUsed: Bool)
|
||||
case customText(text: String, entities: [MessageTextEntity])
|
||||
case botDomainAccessGranted(domain: String)
|
||||
case botSentSecureValues(types: [SentSecureValueType])
|
||||
@ -88,7 +88,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
}
|
||||
self = .phoneCall(callId: decoder.decodeInt64ForKey("i", orElse: 0), discardReason: discardReason, duration: decoder.decodeInt32ForKey("d", orElse: 0), isVideo: decoder.decodeInt32ForKey("vc", orElse: 0) != 0)
|
||||
case 15:
|
||||
self = .paymentSent(currency: decoder.decodeStringForKey("currency", orElse: ""), totalAmount: decoder.decodeInt64ForKey("ta", orElse: 0), invoiceSlug: decoder.decodeOptionalStringForKey("invoiceSlug"))
|
||||
self = .paymentSent(currency: decoder.decodeStringForKey("currency", orElse: ""), totalAmount: decoder.decodeInt64ForKey("ta", orElse: 0), invoiceSlug: decoder.decodeOptionalStringForKey("invoiceSlug"), isRecurringInit: decoder.decodeBoolForKey("isRecurringInit", orElse: false), isRecurringUsed: decoder.decodeBoolForKey("isRecurringUsed", orElse: false))
|
||||
case 16:
|
||||
self = .customText(text: decoder.decodeStringForKey("text", orElse: ""), entities: decoder.decodeObjectArrayWithDecoderForKey("ent"))
|
||||
case 17:
|
||||
@ -172,7 +172,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
encoder.encodeInt32(13, forKey: "_rawValue")
|
||||
encoder.encodeInt64(gameId, forKey: "i")
|
||||
encoder.encodeInt32(score, forKey: "s")
|
||||
case let .paymentSent(currency, totalAmount, invoiceSlug):
|
||||
case let .paymentSent(currency, totalAmount, invoiceSlug, isRecurringInit, isRecurringUsed):
|
||||
encoder.encodeInt32(15, forKey: "_rawValue")
|
||||
encoder.encodeString(currency, forKey: "currency")
|
||||
encoder.encodeInt64(totalAmount, forKey: "ta")
|
||||
@ -181,6 +181,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "invoiceSlug")
|
||||
}
|
||||
encoder.encodeBool(isRecurringInit, forKey: "isRecurringInit")
|
||||
encoder.encodeBool(isRecurringUsed, forKey: "isRecurringUsed")
|
||||
case let .phoneCall(callId, discardReason, duration, isVideo):
|
||||
encoder.encodeInt32(14, forKey: "_rawValue")
|
||||
encoder.encodeInt64(callId, forKey: "i")
|
||||
|
@ -434,14 +434,14 @@ private class ReplyThreadHistoryContextImpl {
|
||||
return .single(nil)
|
||||
}
|
||||
|> afterNext { result in
|
||||
guard let (incomingMesageId, count) = result else {
|
||||
guard let (incomingMessageId, count) = result else {
|
||||
return
|
||||
}
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.maxReadIncomingMessageIdValue = incomingMesageId
|
||||
strongSelf.maxReadIncomingMessageIdValue = incomingMessageId
|
||||
strongSelf.unreadCountValue = count
|
||||
}
|
||||
}
|
||||
|
@ -329,16 +329,16 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
public func transcribeAudio(messageId: MessageId) -> Signal<EngineAudioTranscriptionResult, NoError> {
|
||||
return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, audioTranscriptionManager: self.account.stateManager.audioTranscriptionManager, messageId: messageId)
|
||||
return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, messageId: messageId)
|
||||
}
|
||||
|
||||
public func storeLocallyTranscribedAudio(messageId: MessageId, text: String, isFinal: Bool) -> Signal<Never, NoError> {
|
||||
public func storeLocallyTranscribedAudio(messageId: MessageId, text: String, isFinal: Bool, error: AudioTranscriptionMessageAttribute.TranscriptionError?) -> Signal<Never, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> Void in
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
|
||||
var attributes = currentMessage.attributes.filter { !($0 is AudioTranscriptionMessageAttribute) }
|
||||
|
||||
attributes.append(AudioTranscriptionMessageAttribute(id: 0, text: text, isPending: !isFinal, didRate: false))
|
||||
attributes.append(AudioTranscriptionMessageAttribute(id: 0, text: text, isPending: !isFinal, didRate: false, error: error))
|
||||
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
|
@ -34,22 +34,7 @@ public enum EngineAudioTranscriptionResult {
|
||||
case error
|
||||
}
|
||||
|
||||
class AudioTranscriptionManager {
|
||||
private var pendingMapping: [Int64: MessageId] = [:]
|
||||
|
||||
init() {
|
||||
}
|
||||
|
||||
func addPendingMapping(transcriptionId: Int64, messageId: MessageId) {
|
||||
self.pendingMapping[transcriptionId] = messageId
|
||||
}
|
||||
|
||||
func getPendingMapping(transcriptionId: Int64) -> MessageId? {
|
||||
return self.pendingMapping[transcriptionId]
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_transcribeAudio(postbox: Postbox, network: Network, audioTranscriptionManager: Atomic<AudioTranscriptionManager>, messageId: MessageId) -> Signal<EngineAudioTranscriptionResult, NoError> {
|
||||
func _internal_transcribeAudio(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<EngineAudioTranscriptionResult, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
@ -58,28 +43,31 @@ func _internal_transcribeAudio(postbox: Postbox, network: Network, audioTranscri
|
||||
return .single(.error)
|
||||
}
|
||||
return network.request(Api.functions.messages.transcribeAudio(peer: inputPeer, msgId: messageId.id))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.TranscribedAudio?, NoError> in
|
||||
return .single(nil)
|
||||
|> map { result -> Result<Api.messages.TranscribedAudio, AudioTranscriptionMessageAttribute.TranscriptionError> in
|
||||
return .success(result)
|
||||
}
|
||||
|> `catch` { error -> Signal<Result<Api.messages.TranscribedAudio, AudioTranscriptionMessageAttribute.TranscriptionError>, NoError> in
|
||||
let mappedError: AudioTranscriptionMessageAttribute.TranscriptionError
|
||||
if error.errorDescription == "MSG_VOICE_TOO_LONG" {
|
||||
mappedError = .tooLong
|
||||
} else {
|
||||
mappedError = .generic
|
||||
}
|
||||
return .single(.failure(mappedError))
|
||||
}
|
||||
|> mapToSignal { result -> Signal<EngineAudioTranscriptionResult, NoError> in
|
||||
return postbox.transaction { transaction -> EngineAudioTranscriptionResult in
|
||||
let updatedAttribute: AudioTranscriptionMessageAttribute
|
||||
if let result = result {
|
||||
switch result {
|
||||
switch result {
|
||||
case let .success(transcribedAudio):
|
||||
switch transcribedAudio {
|
||||
case let .transcribedAudio(flags, transcriptionId, text):
|
||||
let isPending = (flags & (1 << 0)) != 0
|
||||
|
||||
if isPending {
|
||||
audioTranscriptionManager.with { audioTranscriptionManager in
|
||||
audioTranscriptionManager.addPendingMapping(transcriptionId: transcriptionId, messageId: messageId)
|
||||
}
|
||||
}
|
||||
|
||||
updatedAttribute = AudioTranscriptionMessageAttribute(id: transcriptionId, text: text, isPending: isPending, didRate: false)
|
||||
updatedAttribute = AudioTranscriptionMessageAttribute(id: transcriptionId, text: text, isPending: isPending, didRate: false, error: nil)
|
||||
}
|
||||
} else {
|
||||
updatedAttribute = AudioTranscriptionMessageAttribute(id: 0, text: "", isPending: false, didRate: false)
|
||||
case let .failure(error):
|
||||
updatedAttribute = AudioTranscriptionMessageAttribute(id: 0, text: "", isPending: false, didRate: false, error: error)
|
||||
}
|
||||
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
@ -91,7 +79,7 @@ func _internal_transcribeAudio(postbox: Postbox, network: Network, audioTranscri
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
|
||||
if let _ = result {
|
||||
if updatedAttribute.error == nil {
|
||||
return .success
|
||||
} else {
|
||||
return .error
|
||||
|
@ -482,7 +482,7 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa
|
||||
switch source {
|
||||
case let .slug(slug):
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?) = action.action, invoiceSlug == slug {
|
||||
if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?, _, _) = action.action, invoiceSlug == slug {
|
||||
if case let .Id(id) = message.id {
|
||||
receiptMessageId = id
|
||||
}
|
||||
|
@ -71,6 +71,12 @@ func _internal_joinChannel(account: Account, peerId: PeerId, hash: String?) -> S
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let channel = transaction.getPeer(peerId) as? TelegramChannel, case .broadcast = channel.info {
|
||||
let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings ?? TelegramPeerNotificationSettings.defaultSettings
|
||||
transaction.updateCurrentPeerNotificationSettings([peerId: notificationSettings.withUpdatedMuteState(.muted(until: Int32.max))])
|
||||
}
|
||||
|
||||
return RenderedChannelParticipant(participant: updatedParticipant, peer: peer, peers: peers, presences: presences)
|
||||
}
|
||||
|> castError(JoinChannelError.self)
|
||||
|
@ -437,7 +437,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
var argumentAttributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])
|
||||
argumentAttributes[1] = MarkdownAttributeSet(font: titleBoldFont, textColor: primaryTextColor, additionalAttributes: [:])
|
||||
attributedString = addAttributesToStringWithRanges(formatWithArgumentRanges(baseString, ranges, [authorName, gameTitle ?? ""]), body: bodyAttributes, argumentAttributes: argumentAttributes)
|
||||
case let .paymentSent(currency, totalAmount, _):
|
||||
case let .paymentSent(currency, totalAmount, _, isRecurringInit, isRecurringUsed):
|
||||
var invoiceMessage: EngineMessage?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute, let message = message.associatedMessages[attribute.messageId] {
|
||||
@ -454,34 +454,53 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
}
|
||||
}
|
||||
|
||||
if let invoiceTitle = invoiceTitle {
|
||||
let botString: String
|
||||
if let peer = messageMainPeer(message) {
|
||||
botString = peer.compactDisplayTitle
|
||||
let patternString: String
|
||||
if isRecurringInit {
|
||||
if let _ = invoiceTitle {
|
||||
patternString = strings.Notification_PaymentSentRecurringInit
|
||||
} else {
|
||||
botString = ""
|
||||
patternString = strings.Notification_PaymentSentRecurringInitNoTitle
|
||||
}
|
||||
let mutableString = NSMutableAttributedString()
|
||||
mutableString.append(NSAttributedString(string: strings.Notification_PaymentSent, font: titleFont, textColor: primaryTextColor))
|
||||
|
||||
var range = NSRange(location: NSNotFound, length: 0)
|
||||
|
||||
range = (mutableString.string as NSString).range(of: "{amount}")
|
||||
if range.location != NSNotFound {
|
||||
mutableString.replaceCharacters(in: range, with: NSAttributedString(string: formatCurrencyAmount(totalAmount, currency: currency), font: titleBoldFont, textColor: primaryTextColor))
|
||||
} else if isRecurringUsed {
|
||||
if let _ = invoiceTitle {
|
||||
patternString = strings.Notification_PaymentSentRecurringUsed
|
||||
} else {
|
||||
patternString = strings.Notification_PaymentSentRecurringUsedNoTitle
|
||||
}
|
||||
range = (mutableString.string as NSString).range(of: "{name}")
|
||||
if range.location != NSNotFound {
|
||||
mutableString.replaceCharacters(in: range, with: NSAttributedString(string: botString, font: titleBoldFont, textColor: primaryTextColor))
|
||||
} else {
|
||||
if let _ = invoiceTitle {
|
||||
patternString = strings.Notification_PaymentSent
|
||||
} else {
|
||||
patternString = strings.Notification_PaymentSentNoTitle
|
||||
}
|
||||
}
|
||||
|
||||
let botString: String
|
||||
if let peer = messageMainPeer(message) {
|
||||
botString = peer.compactDisplayTitle
|
||||
} else {
|
||||
botString = ""
|
||||
}
|
||||
let mutableString = NSMutableAttributedString()
|
||||
mutableString.append(NSAttributedString(string: patternString, font: titleFont, textColor: primaryTextColor))
|
||||
|
||||
var range = NSRange(location: NSNotFound, length: 0)
|
||||
|
||||
range = (mutableString.string as NSString).range(of: "{amount}")
|
||||
if range.location != NSNotFound {
|
||||
mutableString.replaceCharacters(in: range, with: NSAttributedString(string: formatCurrencyAmount(totalAmount, currency: currency), font: titleBoldFont, textColor: primaryTextColor))
|
||||
}
|
||||
range = (mutableString.string as NSString).range(of: "{name}")
|
||||
if range.location != NSNotFound {
|
||||
mutableString.replaceCharacters(in: range, with: NSAttributedString(string: botString, font: titleBoldFont, textColor: primaryTextColor))
|
||||
}
|
||||
if let invoiceTitle = invoiceTitle {
|
||||
range = (mutableString.string as NSString).range(of: "{title}")
|
||||
if range.location != NSNotFound {
|
||||
mutableString.replaceCharacters(in: range, with: NSAttributedString(string: invoiceTitle, font: titleFont, textColor: primaryTextColor))
|
||||
}
|
||||
attributedString = mutableString
|
||||
} else {
|
||||
attributedString = NSAttributedString(string: strings.Message_PaymentSent(formatCurrencyAmount(totalAmount, currency: currency)).string, font: titleFont, textColor: primaryTextColor)
|
||||
}
|
||||
attributedString = mutableString
|
||||
case let .phoneCall(_, discardReason, _, _):
|
||||
var titleString: String
|
||||
let incoming: Bool
|
||||
|
@ -715,8 +715,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
|
||||
let _ = context.engine.messages.rateAudioTranscription(messageId: message.id, id: audioTranscription.id, isGood: value).start()
|
||||
|
||||
//TODO:localize
|
||||
let content: UndoOverlayContent = .info(title: nil, text: "Thank you for your feedback.")
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let content: UndoOverlayContent = .info(title: nil, text: presentationData.strings.Chat_AudioTranscriptionFeedbackTip)
|
||||
controllerInteraction.displayUndo(content)
|
||||
}), false), at: 0)
|
||||
actions.insert(.separator, at: 1)
|
||||
@ -2423,8 +2423,7 @@ private final class ChatRateTranscriptionContextItemNode: ASDisplayNode, Context
|
||||
self.textNode.isAccessibilityElement = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = false
|
||||
//TODO:localizable
|
||||
self.textNode.attributedText = NSAttributedString(string: "Rate Transcription", font: textFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
||||
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Chat_AudioTranscriptionRateAction, font: textFont, textColor: presentationData.theme.contextMenu.secondaryColor)
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
|
||||
self.upButtonImageNode = ASImageNode()
|
||||
|
@ -33,7 +33,7 @@ private struct FetchControls {
|
||||
|
||||
private enum TranscribedText {
|
||||
case success(text: String, isPending: Bool)
|
||||
case error
|
||||
case error(AudioTranscriptionMessageAttribute.TranscriptionError)
|
||||
}
|
||||
|
||||
private func transcribedText(message: Message) -> TranscribedText? {
|
||||
@ -45,7 +45,7 @@ private func transcribedText(message: Message) -> TranscribedText? {
|
||||
if attribute.isPending {
|
||||
return nil
|
||||
} else {
|
||||
return .error
|
||||
return .error(attribute.error ?? .generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,7 +413,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let result = result {
|
||||
let _ = arguments.context.engine.messages.storeLocallyTranscribedAudio(messageId: arguments.message.id, text: result.text, isFinal: result.isFinal).start()
|
||||
let _ = arguments.context.engine.messages.storeLocallyTranscribedAudio(messageId: arguments.message.id, text: result.text, isFinal: result.isFinal, error: nil).start()
|
||||
} else {
|
||||
strongSelf.audioTranscriptionState = .collapsed
|
||||
strongSelf.requestUpdateLayout(true)
|
||||
@ -658,10 +658,16 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
resultText += " [...]"
|
||||
}
|
||||
textString = NSAttributedString(string: resultText, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
case .error:
|
||||
case let .error(error):
|
||||
let errorTextFont = Font.regular(floor(arguments.presentationData.fontSize.baseDisplaySize * 15.0 / 17.0))
|
||||
//TODO:localize
|
||||
textString = NSAttributedString(string: "No speech detected", font: errorTextFont, textColor: messageTheme.secondaryTextColor)
|
||||
let errorText: String
|
||||
switch error {
|
||||
case .generic:
|
||||
errorText = arguments.presentationData.strings.Message_AudioTranscription_ErrorEmpty
|
||||
case .tooLong:
|
||||
errorText = arguments.presentationData.strings.Message_AudioTranscription_ErrorTooLong
|
||||
}
|
||||
textString = NSAttributedString(string: errorText, font: errorTextFont, textColor: messageTheme.secondaryTextColor)
|
||||
}
|
||||
} else {
|
||||
textString = nil
|
||||
|
@ -102,7 +102,9 @@ static bool notyfyingShiftState = false;
|
||||
- (void)_65087dc8_setPreferredFrameRateRange:(CAFrameRateRange)range API_AVAILABLE(ios(15.0)) {
|
||||
if ([self associatedObjectForKey:forceFullRefreshRateKey] != nil) {
|
||||
float maxFps = [UIScreen mainScreen].maximumFramesPerSecond;
|
||||
range = CAFrameRateRangeMake(maxFps, maxFps, maxFps);
|
||||
if (maxFps > 61.0f) {
|
||||
range = CAFrameRateRangeMake(maxFps, maxFps, maxFps);
|
||||
}
|
||||
}
|
||||
|
||||
[self _65087dc8_setPreferredFrameRateRange:range];
|
||||
@ -127,7 +129,9 @@ static bool notyfyingShiftState = false;
|
||||
|
||||
if (@available(iOS 15.0, *)) {
|
||||
float maxFps = [UIScreen mainScreen].maximumFramesPerSecond;
|
||||
[displayLink setPreferredFrameRateRange:CAFrameRateRangeMake(maxFps, maxFps, maxFps)];
|
||||
if (maxFps > 61.0f) {
|
||||
[displayLink setPreferredFrameRateRange:CAFrameRateRangeMake(maxFps, maxFps, maxFps)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user