mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-27 18:42:25 +00:00
Fixed media thumbnail not appearing immediately after upload started
Fixed instant video automatic download Fixed Passport document recognition for uploaded scans
This commit is contained in:
parent
384cdf4a94
commit
0d41a372c9
@ -22,6 +22,7 @@
|
|||||||
0941A9A0210B057200EBE194 /* OpenInActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */; };
|
0941A9A0210B057200EBE194 /* OpenInActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */; };
|
||||||
0941A9A4210B0E2E00EBE194 /* OpenInAppIconResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */; };
|
0941A9A4210B0E2E00EBE194 /* OpenInAppIconResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */; };
|
||||||
0941A9A6210B822D00EBE194 /* OpenInOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */; };
|
0941A9A6210B822D00EBE194 /* OpenInOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */; };
|
||||||
|
0952D1752176DEB500194860 /* NotificationMuteSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0952D1742176DEB500194860 /* NotificationMuteSettingsController.swift */; };
|
||||||
09797873210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */; };
|
09797873210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */; };
|
||||||
0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */; };
|
0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */; };
|
||||||
0979787E210646C00077D77F /* YoutubeEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */; };
|
0979787E210646C00077D77F /* YoutubeEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */; };
|
||||||
@ -1042,6 +1043,7 @@
|
|||||||
0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInActionSheetController.swift; sourceTree = "<group>"; };
|
0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInActionSheetController.swift; sourceTree = "<group>"; };
|
||||||
0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInAppIconResources.swift; sourceTree = "<group>"; };
|
0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInAppIconResources.swift; sourceTree = "<group>"; };
|
||||||
0941A9A5210B822D00EBE194 /* OpenInOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInOptions.swift; sourceTree = "<group>"; };
|
0941A9A5210B822D00EBE194 /* OpenInOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInOptions.swift; sourceTree = "<group>"; };
|
||||||
|
0952D1742176DEB500194860 /* NotificationMuteSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationMuteSettingsController.swift; sourceTree = "<group>"; };
|
||||||
09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageSettingsButtonItemNode.swift; sourceTree = "<group>"; };
|
09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageSettingsButtonItemNode.swift; sourceTree = "<group>"; };
|
||||||
0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebEmbedPlayerNode.swift; sourceTree = "<group>"; };
|
0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebEmbedPlayerNode.swift; sourceTree = "<group>"; };
|
||||||
0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YoutubeEmbedImplementation.swift; sourceTree = "<group>"; };
|
0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YoutubeEmbedImplementation.swift; sourceTree = "<group>"; };
|
||||||
@ -3301,6 +3303,7 @@
|
|||||||
D0C50E3D1E93D09200F62E39 /* NotificationItemContainerNode.swift */,
|
D0C50E3D1E93D09200F62E39 /* NotificationItemContainerNode.swift */,
|
||||||
D0C50E3B1E93CC2600F62E39 /* NotificationItem.swift */,
|
D0C50E3B1E93CC2600F62E39 /* NotificationItem.swift */,
|
||||||
D0C50E3F1E93D3B000F62E39 /* ChatMessageNotificationItem.swift */,
|
D0C50E3F1E93D3B000F62E39 /* ChatMessageNotificationItem.swift */,
|
||||||
|
0952D1742176DEB500194860 /* NotificationMuteSettingsController.swift */,
|
||||||
);
|
);
|
||||||
name = Notifications;
|
name = Notifications;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -5537,6 +5540,7 @@
|
|||||||
D0EC6E811EB9F58900EBF1C3 /* NotificationContainerController.swift in Sources */,
|
D0EC6E811EB9F58900EBF1C3 /* NotificationContainerController.swift in Sources */,
|
||||||
D0754D271EEE10C800884F6E /* BotCheckoutController.swift in Sources */,
|
D0754D271EEE10C800884F6E /* BotCheckoutController.swift in Sources */,
|
||||||
D053DADA201A4C4400993D32 /* ChatTextInputAttributes.swift in Sources */,
|
D053DADA201A4C4400993D32 /* ChatTextInputAttributes.swift in Sources */,
|
||||||
|
0952D1752176DEB500194860 /* NotificationMuteSettingsController.swift in Sources */,
|
||||||
D0EC6E821EB9F58900EBF1C3 /* NotificationContainerControllerNode.swift in Sources */,
|
D0EC6E821EB9F58900EBF1C3 /* NotificationContainerControllerNode.swift in Sources */,
|
||||||
D0EC6E831EB9F58900EBF1C3 /* NotificationItemContainerNode.swift in Sources */,
|
D0EC6E831EB9F58900EBF1C3 /* NotificationItemContainerNode.swift in Sources */,
|
||||||
D0CB27D220C17A7F001ACF93 /* TermsOfServiceControllerNode.swift in Sources */,
|
D0CB27D220C17A7F001ACF93 /* TermsOfServiceControllerNode.swift in Sources */,
|
||||||
|
|||||||
@ -89,7 +89,7 @@ public final class CallController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = CallControllerNode(account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit)
|
self.displayNode = CallControllerNode(account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit)
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
self.controllerNode.toggleMute = { [weak self] in
|
self.controllerNode.toggleMute = { [weak self] in
|
||||||
|
|||||||
@ -14,6 +14,7 @@ final class CallControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var peer: Peer?
|
private var peer: Peer?
|
||||||
|
private let debugInfo: Signal<(String, String), NoError>
|
||||||
|
|
||||||
private let containerNode: ASDisplayNode
|
private let containerNode: ASDisplayNode
|
||||||
|
|
||||||
@ -51,10 +52,11 @@ final class CallControllerNode: ASDisplayNode {
|
|||||||
var back: (() -> Void)?
|
var back: (() -> Void)?
|
||||||
var dismissedInteractively: (() -> Void)?
|
var dismissedInteractively: (() -> Void)?
|
||||||
|
|
||||||
init(account: Account, presentationData: PresentationData, statusBar: StatusBar, shouldStayHiddenUntilConnection: Bool = false) {
|
init(account: Account, presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, shouldStayHiddenUntilConnection: Bool = false) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.statusBar = statusBar
|
self.statusBar = statusBar
|
||||||
|
self.debugInfo = debugInfo
|
||||||
self.shouldStayHiddenUntilConnection = shouldStayHiddenUntilConnection
|
self.shouldStayHiddenUntilConnection = shouldStayHiddenUntilConnection
|
||||||
|
|
||||||
self.containerNode = ASDisplayNode()
|
self.containerNode = ASDisplayNode()
|
||||||
@ -377,6 +379,10 @@ final class CallControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
let keyTextSize = self.keyButtonNode.frame.size
|
let keyTextSize = self.keyButtonNode.frame.size
|
||||||
transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: navigationOffset + 8.0), size: keyTextSize))
|
transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: navigationOffset + 8.0), size: keyTextSize))
|
||||||
|
|
||||||
|
if let debugNode = self.debugNode {
|
||||||
|
transition.updateFrame(node: debugNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func keyPressed() {
|
@objc func keyPressed() {
|
||||||
@ -410,14 +416,56 @@ final class CallControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var debugTapCounter: (Double, Int) = (0.0, 0)
|
||||||
|
|
||||||
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
if case .ended = recognizer.state {
|
if case .ended = recognizer.state {
|
||||||
if let _ = self.keyPreviewNode {
|
if let _ = self.keyPreviewNode {
|
||||||
self.backPressed()
|
self.backPressed()
|
||||||
|
} else {
|
||||||
|
let point = recognizer.location(in: recognizer.view)
|
||||||
|
if self.statusNode.frame.contains(point) {
|
||||||
|
let timestamp = CACurrentMediaTime()
|
||||||
|
if self.debugTapCounter.0 < timestamp - 0.4 {
|
||||||
|
self.debugTapCounter.0 = timestamp
|
||||||
|
self.debugTapCounter.1 = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.debugTapCounter.0 >= timestamp - 0.4 {
|
||||||
|
self.debugTapCounter.0 = timestamp
|
||||||
|
self.debugTapCounter.1 += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.debugTapCounter.1 >= 10 {
|
||||||
|
self.debugTapCounter.1 = 0
|
||||||
|
|
||||||
|
self.presentDebugNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentDebugNode() {
|
||||||
|
guard self.debugNode == nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let debugNode = CallDebugNode(signal: self.debugInfo)
|
||||||
|
debugNode.dismiss = { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.debugNode?.removeFromSupernode()
|
||||||
|
strongSelf.debugNode = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.addSubnode(debugNode)
|
||||||
|
self.debugNode = debugNode
|
||||||
|
|
||||||
|
if let (layout, navigationBarHeight) = self.validLayout {
|
||||||
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .changed:
|
case .changed:
|
||||||
@ -454,25 +502,96 @@ final class CallControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func attributedStringForDebugInfo(_ info: String, version: String) -> NSAttributedString {
|
||||||
|
guard !info.isEmpty else {
|
||||||
|
return NSAttributedString(string: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
var string = info
|
||||||
|
string = "libtgvoip v\(version)\n" + string
|
||||||
|
string = string.replacingOccurrences(of: "Remote endpoints: \n", with: "")
|
||||||
|
string = string.replacingOccurrences(of: "Jitter ", with: "\nJitter ")
|
||||||
|
string = string.replacingOccurrences(of: "Key fingerprint:\n", with: "Key fingerprint: ")
|
||||||
|
|
||||||
|
let attributedString = NSMutableAttributedString(string: string, attributes: [NSAttributedStringKey.font: Font.monospace(15), NSAttributedStringKey.foregroundColor: UIColor.white])
|
||||||
|
|
||||||
|
let titleStyle = NSMutableParagraphStyle()
|
||||||
|
titleStyle.alignment = .center
|
||||||
|
titleStyle.lineSpacing = 7.0
|
||||||
|
|
||||||
|
let style = NSMutableParagraphStyle()
|
||||||
|
style.lineHeightMultiple = 1.15
|
||||||
|
|
||||||
|
let secondaryColor = UIColor(rgb: 0xa6a9a8)
|
||||||
|
let activeColor = UIColor(rgb: 0xa0d875)
|
||||||
|
|
||||||
|
let titleAttributes = [NSAttributedStringKey.font: Font.semiboldMonospace(17), NSAttributedStringKey.paragraphStyle: titleStyle]
|
||||||
|
let nameAttributes = [NSAttributedStringKey.font: Font.semiboldMonospace(15), NSAttributedStringKey.foregroundColor: secondaryColor]
|
||||||
|
let styleAttributes = [NSAttributedStringKey.paragraphStyle: style]
|
||||||
|
let typeAttributes = [NSAttributedStringKey.foregroundColor: secondaryColor]
|
||||||
|
let activeAttributes = [NSAttributedStringKey.font: Font.semiboldMonospace(15), NSAttributedStringKey.foregroundColor: activeColor]
|
||||||
|
|
||||||
|
let range = string.startIndex ..< string.endIndex
|
||||||
|
string.enumerateSubstrings(in: range, options: NSString.EnumerationOptions.byLines) { (line, range, _, _) in
|
||||||
|
guard let line = line else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if range.lowerBound == string.startIndex {
|
||||||
|
attributedString.addAttributes(titleAttributes, range: NSRange(range, in: string))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let semicolonRange = line.range(of: ":") {
|
||||||
|
if let bracketRange = line.range(of: "[") {
|
||||||
|
if let _ = line.range(of: "IN_USE") {
|
||||||
|
attributedString.addAttributes(activeAttributes, range: NSRange(range, in: string))
|
||||||
|
} else {
|
||||||
|
let offset = line.distance(from: line.startIndex, to: bracketRange.lowerBound)
|
||||||
|
let distance = line.distance(from: line.startIndex, to: line.endIndex)
|
||||||
|
attributedString.addAttributes(typeAttributes, range: NSRange(string.index(range.lowerBound, offsetBy: offset) ..< string.index(range.lowerBound, offsetBy: distance), in: string))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attributedString.addAttributes(styleAttributes, range: NSRange(range, in: string))
|
||||||
|
|
||||||
|
let offset = line.distance(from: line.startIndex, to: semicolonRange.upperBound)
|
||||||
|
attributedString.addAttributes(nameAttributes, range: NSRange(range.lowerBound ..< string.index(range.lowerBound, offsetBy: offset), in: string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributedString
|
||||||
|
}
|
||||||
|
|
||||||
final private class CallDebugNode: ASDisplayNode {
|
final private class CallDebugNode: ASDisplayNode {
|
||||||
private let disposable = MetaDisposable()
|
private let disposable = MetaDisposable()
|
||||||
|
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
private let textNode: ASTextNode
|
private let textNode: ASTextNode
|
||||||
|
|
||||||
override init() {
|
public var dismiss: (() -> Void)?
|
||||||
|
|
||||||
|
init(signal: Signal<(String, String), NoError>) {
|
||||||
self.dimNode = ASDisplayNode()
|
self.dimNode = ASDisplayNode()
|
||||||
self.dimNode.isLayerBacked = true
|
self.dimNode.isLayerBacked = true
|
||||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
|
self.dimNode.backgroundColor = UIColor(rgb: 0x26282c, alpha: 0.95)
|
||||||
|
self.dimNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ASTextNode()
|
||||||
self.textNode.isUserInteractionEnabled = false
|
self.textNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.dimNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
|
||||||
|
self.disposable.set((signal
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] (version, info) in
|
||||||
|
self?.update(info, version: version)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
disposable.dispose()
|
self.disposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
@ -482,19 +601,22 @@ final private class CallDebugNode: ASDisplayNode {
|
|||||||
self.view.addGestureRecognizer(tapRecognizer)
|
self.view.addGestureRecognizer(tapRecognizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func update(_ info: String, version: String) {
|
||||||
|
self.textNode.attributedText = attributedStringForDebugInfo(info, version: version)
|
||||||
|
self.setNeedsLayout()
|
||||||
|
}
|
||||||
|
|
||||||
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
|
self.dismiss?()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layout() {
|
override func layout() {
|
||||||
super.layout()
|
super.layout()
|
||||||
|
|
||||||
let size = self.bounds.size
|
let size = self.bounds.size
|
||||||
|
self.dimNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height))
|
||||||
|
|
||||||
self.dimNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width))
|
let textSize = textNode.measure(CGSize(width: size.width - 20.0, height: size.height))
|
||||||
|
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||||
|
|
||||||
//let labelSize = labelNode.measure(CGSize(width: , height: 100.0))
|
|
||||||
//textNode.frame = CGRect(origin: CGPoint(x: floor(size.width, y: 81.0), size: labelSize)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -720,58 +720,9 @@ public func channelInfoController(account: Account, peerId: PeerId) -> ViewContr
|
|||||||
presentControllerImpl?(channelVisibilityController(account: account, peerId: peerId, mode: .generic), ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
presentControllerImpl?(channelVisibilityController(account: account, peerId: peerId, mode: .generic), ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||||
}, changeNotificationMuteSettings: {
|
}, changeNotificationMuteSettings: {
|
||||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
let controller = notificationMuteSettingsController(presentationData: presentationData, updateSettings: { value in
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: value).start())
|
||||||
controller?.dismissAnimated()
|
})
|
||||||
}
|
|
||||||
let notificationAction: (Int32?) -> Void = { muteUntil in
|
|
||||||
let muteInterval: Int32?
|
|
||||||
if let muteUntil = muteUntil {
|
|
||||||
if muteUntil <= 0 {
|
|
||||||
muteInterval = 0
|
|
||||||
} else if muteUntil == Int32.max {
|
|
||||||
muteInterval = Int32.max
|
|
||||||
} else {
|
|
||||||
muteInterval = muteUntil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
muteInterval = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: muteInterval).start())
|
|
||||||
}
|
|
||||||
var items: [ActionSheetItem] = []
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsEnable, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(0)
|
|
||||||
}))
|
|
||||||
let intervals: [Int32?] = [
|
|
||||||
nil,
|
|
||||||
1 * 60 * 60,
|
|
||||||
8 * 60 * 60,
|
|
||||||
2 * 24 * 60 * 60
|
|
||||||
]
|
|
||||||
for value in intervals {
|
|
||||||
let title: String
|
|
||||||
if let value = value {
|
|
||||||
title = muteForIntervalString(strings: presentationData.strings, value: value)
|
|
||||||
} else {
|
|
||||||
title = presentationData.strings.UserInfo_NotificationsDefault
|
|
||||||
}
|
|
||||||
items.append(ActionSheetButtonItem(title: title, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(value)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsDisable, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(Int32.max)
|
|
||||||
}))
|
|
||||||
|
|
||||||
controller.setItemGroups([
|
|
||||||
ActionSheetItemGroup(items: items),
|
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
||||||
])
|
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}, changeNotificationSoundSettings: {
|
}, changeNotificationSoundSettings: {
|
||||||
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
||||||
|
|||||||
@ -818,7 +818,7 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode:
|
|||||||
doneEnabled = false
|
doneEnabled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
doneEnabled = false
|
doneEnabled = !(peer.addressName?.isEmpty ?? true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4904,6 +4904,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV
|
|||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
})
|
})
|
||||||
])])
|
])])
|
||||||
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
strongSelf.present(actionSheet, in: .window(.root))
|
strongSelf.present(actionSheet, in: .window(.root))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -5122,12 +5123,12 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV
|
|||||||
let otherShortcuts: [KeyShortcut] = [
|
let otherShortcuts: [KeyShortcut] = [
|
||||||
KeyShortcut(title: strings.KeyCommand_ScrollUp, input: UIKeyInputUpArrow, modifiers: [.shift], action: { [weak self] in
|
KeyShortcut(title: strings.KeyCommand_ScrollUp, input: UIKeyInputUpArrow, modifiers: [.shift], action: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.chatDisplayNode.historyNode.scrollWithDeltaOffset(-75)
|
strongSelf.chatDisplayNode.historyNode.scrollWithDeltaOffset(75)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
KeyShortcut(title: strings.KeyCommand_ScrollDown, input: UIKeyInputDownArrow, modifiers: [.shift], action: { [weak self] in
|
KeyShortcut(title: strings.KeyCommand_ScrollDown, input: UIKeyInputDownArrow, modifiers: [.shift], action: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.chatDisplayNode.historyNode.scrollWithDeltaOffset(75)
|
strongSelf.chatDisplayNode.historyNode.scrollWithDeltaOffset(-75)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
KeyShortcut(title: strings.KeyCommand_ChatInfo, input: "I", modifiers: [.command, .control], action: { [weak self] in
|
KeyShortcut(title: strings.KeyCommand_ChatInfo, input: "I", modifiers: [.command, .control], action: { [weak self] in
|
||||||
@ -5144,51 +5145,12 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
KeyShortcut(input: "W", modifiers: [.command], action: {
|
KeyShortcut(input: "W", modifiers: [.command], action: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
return inputShortcuts + otherShortcuts
|
return inputShortcuts + otherShortcuts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NSMutableArray *commands = [[NSMutableArray alloc] init];
|
|
||||||
//
|
|
||||||
// if (!_inputTextPanel.maybeInputField.isFirstResponder)
|
|
||||||
// {
|
|
||||||
// TGKeyCommand *focusKeyCommand = [TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.FocusOnInputField") input:@"\r" modifierFlags:0];
|
|
||||||
//
|
|
||||||
// if ([self canEditLastMessage])
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputUpArrow modifierFlags:0]];
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.SendMessage") input:@"\r" modifierFlags:0]];
|
|
||||||
//
|
|
||||||
// if ([_inputTextPanel associatedPanel] != nil)
|
|
||||||
// {
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputUpArrow modifierFlags:0]];
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputDownArrow modifierFlags:0]];
|
|
||||||
// }
|
|
||||||
// else if ([self canEditLastMessage])
|
|
||||||
// {
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputUpArrow modifierFlags:0]];
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (_inputTextPanel.messageEditingContext != nil)
|
|
||||||
// {
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputEscape modifierFlags:0]];
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"\t" modifierFlags:0]];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"/" modifierFlags:UIKeyModifierCommand]];
|
|
||||||
//
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.ScrollUp") input:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift]];
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.ScrollDown") input:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift]];
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.ChatInfo") input:@"I" modifierFlags:UIKeyModifierControl | UIKeyModifierCommand]];
|
|
||||||
// [commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"W" modifierFlags:UIKeyModifierCommand]];
|
|
||||||
//
|
|
||||||
// return commands;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -140,7 +140,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
var checkProxy = false
|
var checkProxy = false
|
||||||
switch state {
|
switch state {
|
||||||
case .waitingForNetwork:
|
case .waitingForNetwork:
|
||||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isPasscodeSet, isManuallyLocked: isManuallyLocked)
|
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: isPasscodeSet, isManuallyLocked: isManuallyLocked)
|
||||||
case let .connecting(proxy):
|
case let .connecting(proxy):
|
||||||
var text = strongSelf.presentationData.strings.State_Connecting
|
var text = strongSelf.presentationData.strings.State_Connecting
|
||||||
if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 {
|
if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 {
|
||||||
|
|||||||
@ -419,6 +419,7 @@ final class ChatListNode: ListView {
|
|||||||
return chatListViewForLocation(groupId: groupId, location: location, account: account)
|
return chatListViewForLocation(groupId: groupId, location: location, account: account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let previousState = Atomic<ChatListNodeState>(value: self.currentState)
|
||||||
let previousView = Atomic<ChatListNodeView?>(value: nil)
|
let previousView = Atomic<ChatListNodeView?>(value: nil)
|
||||||
|
|
||||||
let savedMessagesPeer: Signal<Peer?, NoError>
|
let savedMessagesPeer: Signal<Peer?, NoError>
|
||||||
@ -430,13 +431,14 @@ final class ChatListNode: ListView {
|
|||||||
|
|
||||||
let chatListNodeViewTransition = combineLatest(savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) |> mapToQueue { (savedMessagesPeer, update, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
let chatListNodeViewTransition = combineLatest(savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) |> mapToQueue { (savedMessagesPeer, update, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||||
let processedView = ChatListNodeView(originalView: update.view, filteredEntries: chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode))
|
let processedView = ChatListNodeView(originalView: update.view, filteredEntries: chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode))
|
||||||
let previous = previousView.swap(processedView)
|
let previousView = previousView.swap(processedView)
|
||||||
|
let previousState = previousState.swap(state)
|
||||||
|
|
||||||
let reason: ChatListNodeViewTransitionReason
|
let reason: ChatListNodeViewTransitionReason
|
||||||
var prepareOnMainQueue = false
|
var prepareOnMainQueue = false
|
||||||
|
|
||||||
var previousWasEmptyOrSingleHole = false
|
var previousWasEmptyOrSingleHole = false
|
||||||
if let previous = previous {
|
if let previous = previousView {
|
||||||
if previous.filteredEntries.count == 1 {
|
if previous.filteredEntries.count == 1 {
|
||||||
if case .HoleEntry = previous.filteredEntries[0] {
|
if case .HoleEntry = previous.filteredEntries[0] {
|
||||||
previousWasEmptyOrSingleHole = true
|
previousWasEmptyOrSingleHole = true
|
||||||
@ -450,11 +452,11 @@ final class ChatListNode: ListView {
|
|||||||
|
|
||||||
if previousWasEmptyOrSingleHole {
|
if previousWasEmptyOrSingleHole {
|
||||||
reason = .initial
|
reason = .initial
|
||||||
if previous == nil {
|
if previousView == nil {
|
||||||
prepareOnMainQueue = true
|
prepareOnMainQueue = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if previous?.originalView === update.view {
|
if previousView?.originalView === update.view {
|
||||||
reason = .interactiveChanges
|
reason = .interactiveChanges
|
||||||
updatedScrollPosition = nil
|
updatedScrollPosition = nil
|
||||||
} else {
|
} else {
|
||||||
@ -472,7 +474,34 @@ final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return preparedChatListNodeViewTransition(from: previous, to: processedView, reason: reason, disableAnimations: state.presentationData.disableAnimations, account: account, scrollPosition: updatedScrollPosition)
|
var disableAnimations = state.presentationData.disableAnimations
|
||||||
|
if previousState.editing != state.editing {
|
||||||
|
disableAnimations = false
|
||||||
|
} else {
|
||||||
|
var previousPinnedCount = 0
|
||||||
|
var updatedPinnedCount = 0
|
||||||
|
if let previous = previousView {
|
||||||
|
for entry in previous.filteredEntries {
|
||||||
|
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||||
|
if index.pinningIndex != nil {
|
||||||
|
previousPinnedCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for entry in processedView.filteredEntries {
|
||||||
|
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||||
|
if index.pinningIndex != nil {
|
||||||
|
updatedPinnedCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if previousPinnedCount != updatedPinnedCount {
|
||||||
|
disableAnimations = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preparedChatListNodeViewTransition(from: previousView, to: processedView, reason: reason, disableAnimations: disableAnimations, account: account, scrollPosition: updatedScrollPosition)
|
||||||
|> map({ mappedChatListNodeViewListTransition(account: account, nodeInteraction: nodeInteraction, peerGroupId: groupId, mode: mode, transition: $0) })
|
|> map({ mappedChatListNodeViewListTransition(account: account, nodeInteraction: nodeInteraction, peerGroupId: groupId, mode: mode, transition: $0) })
|
||||||
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
|
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,27 +71,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
|||||||
case .initial:
|
case .initial:
|
||||||
let _ = options.insert(.LowLatency)
|
let _ = options.insert(.LowLatency)
|
||||||
let _ = options.insert(.Synchronous)
|
let _ = options.insert(.Synchronous)
|
||||||
case .interactiveChanges:
|
case .interactiveChanges:
|
||||||
var previousPinnedCount = 0
|
|
||||||
var updatedPinnedCount = 0
|
|
||||||
|
|
||||||
if let fromView = fromView {
|
|
||||||
for entry in fromView.filteredEntries {
|
|
||||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
|
||||||
if index.pinningIndex != nil {
|
|
||||||
previousPinnedCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for entry in toView.filteredEntries {
|
|
||||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
|
||||||
if index.pinningIndex != nil {
|
|
||||||
updatedPinnedCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index, _, _) in indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
|
for (index, _, _) in indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
|
||||||
let adjustedIndex = updatedCount - 1 - index
|
let adjustedIndex = updatedCount - 1 - index
|
||||||
if adjustedIndex == maxAnimatedInsertionIndex + 1 {
|
if adjustedIndex == maxAnimatedInsertionIndex + 1 {
|
||||||
@ -118,7 +98,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
|||||||
let _ = options.insert(.AnimateCrossfade)
|
let _ = options.insert(.AnimateCrossfade)
|
||||||
} else {
|
} else {
|
||||||
let _ = options.insert(.AnimateAlpha)
|
let _ = options.insert(.AnimateAlpha)
|
||||||
if !disableAnimations || previousPinnedCount != updatedPinnedCount {
|
if !disableAnimations {
|
||||||
let _ = options.insert(.AnimateInsertion)
|
let _ = options.insert(.AnimateInsertion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -375,12 +375,12 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
if let (media, flags) = mediaAndFlags {
|
if let (media, flags) = mediaAndFlags {
|
||||||
if let file = media as? TelegramMediaFile {
|
if let file = media as? TelegramMediaFile {
|
||||||
if file.isInstantVideo {
|
if file.isInstantVideo {
|
||||||
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(account: account, controllerInteraction: controllerInteraction, message: message, read: messageRead, presentationData: presentationData, associatedData: associatedData), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 180.0, height: 180.0), .bubble)
|
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
||||||
|
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(account: account, controllerInteraction: controllerInteraction, message: message, read: messageRead, presentationData: presentationData, associatedData: associatedData), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 180.0, height: 180.0), .bubble, automaticDownload)
|
||||||
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
|
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
|
||||||
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
||||||
} else if file.isVideo {
|
} else if file.isVideo {
|
||||||
var automaticDownload = false
|
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
||||||
automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
|
||||||
let (_, initialImageWidth, refineLayout) = contentImageLayout(account, presentationData.theme.theme, presentationData.strings, message, file, automaticDownload, automaticDownloadSettings.autoplayGifs, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants)
|
let (_, initialImageWidth, refineLayout) = contentImageLayout(account, presentationData.theme.theme, presentationData.strings, message, file, automaticDownload, automaticDownloadSettings.autoplayGifs, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants)
|
||||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||||
refineContentImageLayout = refineLayout
|
refineContentImageLayout = refineLayout
|
||||||
@ -390,8 +390,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||||
refineContentImageLayout = refineLayout
|
refineContentImageLayout = refineLayout
|
||||||
} else {
|
} else {
|
||||||
var automaticDownload = false
|
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
||||||
automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
|
||||||
|
|
||||||
let statusType: ChatMessageDateAndStatusType
|
let statusType: ChatMessageDateAndStatusType
|
||||||
if message.effectivelyIncoming(account.peerId) {
|
if message.effectivelyIncoming(account.peerId) {
|
||||||
|
|||||||
@ -121,7 +121,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
let displaySize = CGSize(width: 212.0, height: 212.0)
|
let displaySize = CGSize(width: 212.0, height: 212.0)
|
||||||
|
|
||||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(account: item.account, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, presentationData: item.presentationData, associatedData: item.associatedData), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free)
|
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(account: item.account, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, presentationData: item.presentationData, associatedData: item.associatedData), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, true)
|
||||||
|
|
||||||
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left)), y: 0.0), size: videoLayout.contentSize)
|
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left)), y: 0.0), size: videoLayout.contentSize)
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
private var videoFrame: CGRect?
|
private var videoFrame: CGRect?
|
||||||
|
|
||||||
private var item: ChatMessageBubbleContentItem?
|
private var item: ChatMessageBubbleContentItem?
|
||||||
|
private var automaticDownload: Bool?
|
||||||
var telegramFile: TelegramMediaFile?
|
var telegramFile: TelegramMediaFile?
|
||||||
private var secretProgressIcon: UIImage?
|
private var secretProgressIcon: UIImage?
|
||||||
|
|
||||||
@ -108,14 +109,15 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
self.view.addGestureRecognizer(recognizer)
|
self.view.addGestureRecognizer(recognizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void) {
|
func asyncLayout() -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType, _ automaticDownload: Bool) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void) {
|
||||||
let previousFile = self.telegramFile
|
let previousFile = self.telegramFile
|
||||||
|
|
||||||
let currentItem = self.item
|
let currentItem = self.item
|
||||||
|
let previousAutomaticDownload = self.automaticDownload
|
||||||
|
|
||||||
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
||||||
|
|
||||||
return { item, width, displaySize, statusDisplayType in
|
return { item, width, displaySize, statusDisplayType, automaticDownload in
|
||||||
var updatedTheme: ChatPresentationThemeData?
|
var updatedTheme: ChatPresentationThemeData?
|
||||||
|
|
||||||
var secretVideoPlaceholderBackgroundImage: UIImage?
|
var secretVideoPlaceholderBackgroundImage: UIImage?
|
||||||
@ -251,6 +253,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
strongSelf.videoFrame = videoFrame
|
strongSelf.videoFrame = videoFrame
|
||||||
strongSelf.secretProgressIcon = secretProgressIcon
|
strongSelf.secretProgressIcon = secretProgressIcon
|
||||||
|
strongSelf.automaticDownload = automaticDownload
|
||||||
|
|
||||||
if let updatedInfoBackgroundImage = updatedInfoBackgroundImage {
|
if let updatedInfoBackgroundImage = updatedInfoBackgroundImage {
|
||||||
strongSelf.infoBackgroundNode.image = updatedInfoBackgroundImage
|
strongSelf.infoBackgroundNode.image = updatedInfoBackgroundImage
|
||||||
@ -337,7 +340,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: false, enableSound: false), priority: .embedded, autoplay: true)
|
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: false, enableSound: false, fetchAutomatically: false), priority: .embedded, autoplay: true)
|
||||||
let previousVideoNode = strongSelf.videoNode
|
let previousVideoNode = strongSelf.videoNode
|
||||||
strongSelf.videoNode = videoNode
|
strongSelf.videoNode = videoNode
|
||||||
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
||||||
@ -375,6 +378,10 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
applySecretPlaceholder()
|
applySecretPlaceholder()
|
||||||
|
|
||||||
strongSelf.updateStatus()
|
strongSelf.updateStatus()
|
||||||
|
|
||||||
|
if let telegramFile = updatedFile, previousAutomaticDownload != automaticDownload, automaticDownload {
|
||||||
|
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(account: item.account, message: item.message, file: telegramFile, userInitiated: false).start())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -628,16 +635,16 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
static func asyncLayout(_ node: ChatMessageInteractiveInstantVideoNode?) -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> ChatMessageInteractiveInstantVideoNode) {
|
static func asyncLayout(_ node: ChatMessageInteractiveInstantVideoNode?) -> (_ item: ChatMessageBubbleContentItem, _ width: CGFloat, _ displaySize: CGSize, _ statusType: ChatMessageInteractiveInstantVideoNodeStatusType, _ automaticDownload: Bool) -> (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> ChatMessageInteractiveInstantVideoNode) {
|
||||||
let makeLayout = node?.asyncLayout()
|
let makeLayout = node?.asyncLayout()
|
||||||
return { item, width, displaySize, statusType in
|
return { item, width, displaySize, statusType, automaticDownload in
|
||||||
var createdNode: ChatMessageInteractiveInstantVideoNode?
|
var createdNode: ChatMessageInteractiveInstantVideoNode?
|
||||||
let sizeAndApplyLayout: (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void)
|
let sizeAndApplyLayout: (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void)
|
||||||
if let makeLayout = makeLayout {
|
if let makeLayout = makeLayout {
|
||||||
sizeAndApplyLayout = makeLayout(item, width, displaySize, statusType)
|
sizeAndApplyLayout = makeLayout(item, width, displaySize, statusType, automaticDownload)
|
||||||
} else {
|
} else {
|
||||||
let node = ChatMessageInteractiveInstantVideoNode()
|
let node = ChatMessageInteractiveInstantVideoNode()
|
||||||
sizeAndApplyLayout = node.asyncLayout()(item, width, displaySize, statusType)
|
sizeAndApplyLayout = node.asyncLayout()(item, width, displaySize, statusType, automaticDownload)
|
||||||
createdNode = node
|
createdNode = node
|
||||||
}
|
}
|
||||||
return (sizeAndApplyLayout.0, { [weak node] layoutData, transition in
|
return (sizeAndApplyLayout.0, { [weak node] layoutData, transition in
|
||||||
|
|||||||
@ -237,7 +237,11 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
mediaAndFlags = (image, flags)
|
mediaAndFlags = (image, flags)
|
||||||
} else if let _ = largestImageRepresentation(image.representations)?.dimensions {
|
} else if let _ = largestImageRepresentation(image.representations)?.dimensions {
|
||||||
mediaAndFlags = (image, [.preferMediaInline])
|
var flags = ChatMessageAttachedContentNodeMediaFlags()
|
||||||
|
if webpage.instantPage == nil {
|
||||||
|
flags.insert(.preferMediaInline)
|
||||||
|
}
|
||||||
|
mediaAndFlags = (image, flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1278,55 +1278,9 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl
|
|||||||
presentControllerImpl?(controller, presentationArguments)
|
presentControllerImpl?(controller, presentationArguments)
|
||||||
}, changeNotificationMuteSettings: {
|
}, changeNotificationMuteSettings: {
|
||||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
let controller = notificationMuteSettingsController(presentationData: presentationData, updateSettings: { value in
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: value).start())
|
||||||
controller?.dismissAnimated()
|
})
|
||||||
}
|
|
||||||
let notificationAction: (Int32?) -> Void = { muteUntil in
|
|
||||||
let muteInterval: Int32?
|
|
||||||
if let muteUntil = muteUntil {
|
|
||||||
if muteUntil <= 0 {
|
|
||||||
muteInterval = 0
|
|
||||||
} else if muteUntil == Int32.max {
|
|
||||||
muteInterval = Int32.max
|
|
||||||
} else {
|
|
||||||
muteInterval = muteUntil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
muteInterval = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: muteInterval).start())
|
|
||||||
}
|
|
||||||
controller.setItemGroups([
|
|
||||||
ActionSheetItemGroup(items: [
|
|
||||||
ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsDefault, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(nil)
|
|
||||||
}),
|
|
||||||
ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsEnable, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(0)
|
|
||||||
}),
|
|
||||||
ActionSheetButtonItem(title: muteForIntervalString(strings: presentationData.strings, value: 1 * 60 * 60), action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(1 * 60 * 60)
|
|
||||||
}),
|
|
||||||
ActionSheetButtonItem(title: muteForIntervalString(strings: presentationData.strings, value: 8 * 60 * 60), action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(8 * 60 * 60)
|
|
||||||
}),
|
|
||||||
ActionSheetButtonItem(title: muteForIntervalString(strings: presentationData.strings, value: 2 * 24 * 60 * 60), action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(2 * 24 * 60 * 60)
|
|
||||||
}),
|
|
||||||
ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsDisable, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(Int32.max)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
||||||
])
|
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}, changeNotificationSoundSettings: {
|
}, changeNotificationSoundSettings: {
|
||||||
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
||||||
|
|||||||
@ -86,7 +86,7 @@ final class InstantVideoController: LegacyController {
|
|||||||
|
|
||||||
func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, account: Account, peerId: PeerId, send: @escaping (EnqueueMessage) -> Void) -> InstantVideoController {
|
func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, account: Account, peerId: PeerId, send: @escaping (EnqueueMessage) -> Void) -> InstantVideoController {
|
||||||
let legacyController = InstantVideoController(presentation: .custom, theme: theme)
|
let legacyController = InstantVideoController(presentation: .custom, theme: theme)
|
||||||
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all)
|
||||||
legacyController.statusBar.statusBarStyle = .Hide
|
legacyController.statusBar.statusBarStyle = .Hide
|
||||||
let baseController = TGViewController(context: legacyController.context)!
|
let baseController = TGViewController(context: legacyController.context)!
|
||||||
legacyController.bind(controller: baseController)
|
legacyController.bind(controller: baseController)
|
||||||
|
|||||||
@ -81,9 +81,9 @@ private enum LegacyAssetVideoData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum LegacyAssetItem {
|
private enum LegacyAssetItem {
|
||||||
case image(data: LegacyAssetImageData, caption: String?)
|
case image(data: LegacyAssetImageData, thumbnail: UIImage?, caption: String?)
|
||||||
case file(data: LegacyAssetImageData, mimeType: String, name: String, caption: String?)
|
case file(data: LegacyAssetImageData, thumbnail: UIImage?, mimeType: String, name: String, caption: String?)
|
||||||
case video(data: LegacyAssetVideoData, previewImage: UIImage?, adjustments: TGVideoEditAdjustments?, caption: String?, asFile: Bool, asAnimation: Bool)
|
case video(data: LegacyAssetVideoData, thumbnail: UIImage?, adjustments: TGVideoEditAdjustments?, caption: String?, asFile: Bool, asAnimation: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class LegacyAssetItemWrapper: NSObject {
|
private final class LegacyAssetItemWrapper: NSObject {
|
||||||
@ -105,11 +105,13 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
|||||||
let dict = anyDict as! NSDictionary
|
let dict = anyDict as! NSDictionary
|
||||||
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
|
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
|
||||||
let image = dict["image"] as! UIImage
|
let image = dict["image"] as! UIImage
|
||||||
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
var result: [AnyHashable : Any] = [:]
|
var result: [AnyHashable : Any] = [:]
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), thumbnail: thumbnail, caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
return result
|
return result
|
||||||
} else if (dict["type"] as! NSString) == "cloudPhoto" {
|
} else if (dict["type"] as! NSString) == "cloudPhoto" {
|
||||||
let asset = dict["asset"] as! TGMediaAsset
|
let asset = dict["asset"] as! TGMediaAsset
|
||||||
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
var asFile = false
|
var asFile = false
|
||||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||||
asFile = true
|
asFile = true
|
||||||
@ -125,13 +127,14 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
|||||||
name = customName
|
name = customName
|
||||||
}
|
}
|
||||||
|
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .asset(asset.backingAsset), mimeType: mimeType, name: name, caption: caption), timer: nil, groupedId: nil)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .asset(asset.backingAsset), thumbnail: thumbnail, mimeType: mimeType, name: name, caption: caption), timer: nil, groupedId: nil)
|
||||||
} else {
|
} else {
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .asset(asset.backingAsset), caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .asset(asset.backingAsset), thumbnail: thumbnail, caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} else if (dict["type"] as! NSString) == "file" {
|
} else if (dict["type"] as! NSString) == "file" {
|
||||||
if let tempFileUrl = dict["tempFileUrl"] as? URL {
|
if let tempFileUrl = dict["tempFileUrl"] as? URL {
|
||||||
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
var mimeType = "application/binary"
|
var mimeType = "application/binary"
|
||||||
if let customMimeType = dict["mimeType"] as? String {
|
if let customMimeType = dict["mimeType"] as? String {
|
||||||
mimeType = customMimeType
|
mimeType = customMimeType
|
||||||
@ -147,15 +150,16 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
|||||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||||
|
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: tempFileUrl.path, dimensions: dimensions, duration: duration), previewImage: dict["previewImage"] as? UIImage, adjustments: nil, caption: caption, asFile: false, asAnimation: true), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: tempFileUrl.path, dimensions: dimensions, duration: duration), thumbnail: thumbnail, adjustments: nil, caption: caption, asFile: false, asAnimation: true), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
var result: [AnyHashable: Any] = [:]
|
var result: [AnyHashable: Any] = [:]
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .tempFile(tempFileUrl.path), mimeType: mimeType, name: name, caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .tempFile(tempFileUrl.path), thumbnail: thumbnail, mimeType: mimeType, name: name, caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
} else if (dict["type"] as! NSString) == "video" {
|
} else if (dict["type"] as! NSString) == "video" {
|
||||||
|
var thumbnail = dict["previewImage"] as? UIImage
|
||||||
var asFile = false
|
var asFile = false
|
||||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||||
asFile = true
|
asFile = true
|
||||||
@ -163,16 +167,17 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
|||||||
|
|
||||||
if let asset = dict["asset"] as? TGMediaAsset {
|
if let asset = dict["asset"] as? TGMediaAsset {
|
||||||
var result: [AnyHashable: Any] = [:]
|
var result: [AnyHashable: Any] = [:]
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .asset(asset), previewImage: dict["previewImage"] as? UIImage, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .asset(asset), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
return result
|
return result
|
||||||
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
||||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||||
var result: [AnyHashable: Any] = [:]
|
var result: [AnyHashable: Any] = [:]
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), previewImage: dict["previewImage"] as? UIImage, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
||||||
|
let thumbnail = dict["previewImage"] as? UIImage
|
||||||
var asFile = false
|
var asFile = false
|
||||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||||
asFile = true
|
asFile = true
|
||||||
@ -184,7 +189,7 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
|||||||
let dimensions = previewImage.pixelSize()
|
let dimensions = previewImage.pixelSize()
|
||||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||||
var result: [AnyHashable: Any] = [:]
|
var result: [AnyHashable: Any] = [:]
|
||||||
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), previewImage: dict["previewImage"] as? UIImage, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,7 +247,17 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
outer: for item in (anyValues as! NSArray) {
|
outer: for item in (anyValues as! NSArray) {
|
||||||
if let item = (item as? NSDictionary)?.object(forKey: "item") as? LegacyAssetItemWrapper {
|
if let item = (item as? NSDictionary)?.object(forKey: "item") as? LegacyAssetItemWrapper {
|
||||||
switch item.item {
|
switch item.item {
|
||||||
case let .image(data, caption):
|
case let .image(data, thumbnail, caption):
|
||||||
|
var representations: [TelegramMediaImageRepresentation] = []
|
||||||
|
if let thumbnail = thumbnail {
|
||||||
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
|
let thumbnailSize = thumbnail.size.aspectFitted(CGSize(width: 90.0, height: 90.0))
|
||||||
|
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
|
||||||
|
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
|
||||||
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
||||||
|
representations.append(TelegramMediaImageRepresentation(dimensions: thumbnailSize, resource: resource))
|
||||||
|
}
|
||||||
|
}
|
||||||
switch data {
|
switch data {
|
||||||
case let .image(image):
|
case let .image(image):
|
||||||
var randomId: Int64 = 0
|
var randomId: Int64 = 0
|
||||||
@ -268,9 +283,11 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
|
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
|
||||||
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource)], reference: nil, partialReference: nil)
|
representations.append(TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource))
|
||||||
|
|
||||||
|
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, reference: nil, partialReference: nil)
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||||
@ -284,8 +301,9 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
||||||
let scaledSize = size.aspectFitted(CGSize(width: 1280.0, height: 1280.0))
|
let scaledSize = size.aspectFitted(CGSize(width: 1280.0, height: 1280.0))
|
||||||
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())
|
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())
|
||||||
|
representations.append(TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource))
|
||||||
|
|
||||||
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource)], reference: nil, partialReference: nil)
|
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, reference: nil, partialReference: nil)
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||||
@ -294,7 +312,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
case .tempFile:
|
case .tempFile:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case let .file(data, mimeType, name, caption):
|
case let .file(data, thumbnail, mimeType, name, caption):
|
||||||
switch data {
|
switch data {
|
||||||
case let .tempFile(path):
|
case let .tempFile(path):
|
||||||
var randomId: Int64 = 0
|
var randomId: Int64 = 0
|
||||||
@ -311,7 +329,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case let .video(data, previewImage, adjustments, caption, asFile, asAnimation):
|
case let .video(data, thumbnail, adjustments, caption, asFile, asAnimation):
|
||||||
var finalDimensions: CGSize
|
var finalDimensions: CGSize
|
||||||
var finalDuration: Double
|
var finalDuration: Double
|
||||||
switch data {
|
switch data {
|
||||||
@ -328,10 +346,10 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
}
|
}
|
||||||
|
|
||||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||||
if let previewImage = previewImage {
|
if let thumbnail = thumbnail {
|
||||||
let resource = LocalFileMediaResource(fileId: arc4random64())
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 90.0, height: 90.0))
|
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 90.0, height: 90.0))
|
||||||
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
|
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
|
||||||
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
|
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
|
||||||
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
||||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnailSize, resource: resource))
|
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnailSize, resource: resource))
|
||||||
@ -409,59 +427,3 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func legacyAssetPickerDataSignals(account: Account, signals: [Any]) -> Signal<[TelegramMediaResource], Void> {
|
|
||||||
return Signal { subscriber in
|
|
||||||
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
|
|
||||||
var datas: [TelegramMediaResource] = []
|
|
||||||
|
|
||||||
outer: for item in (anyValues as! NSArray) {
|
|
||||||
if let item = (item as? NSDictionary)?.object(forKey: "item") as? LegacyAssetItemWrapper {
|
|
||||||
switch item.item {
|
|
||||||
case let .image(data, _):
|
|
||||||
switch data {
|
|
||||||
case let .image(image):
|
|
||||||
var randomId: Int64 = 0
|
|
||||||
arc4random_buf(&randomId, 8)
|
|
||||||
let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpeg"
|
|
||||||
let scaledSize = image.size.aspectFitted(CGSize(width: 2048.0, height: 2048.0))
|
|
||||||
if let scaledImage = TGScaleImageToPixelSize(image, scaledSize) {
|
|
||||||
if let scaledImageData = compressImageToJPEG(scaledImage, quality: 0.84) {
|
|
||||||
let _ = try? scaledImageData.write(to: URL(fileURLWithPath: tempFilePath))
|
|
||||||
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
|
|
||||||
datas.append(resource)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .asset(asset):
|
|
||||||
break
|
|
||||||
case .tempFile:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case let .file(data, mimeType, name, caption):
|
|
||||||
switch data {
|
|
||||||
case let .tempFile(path):
|
|
||||||
var randomId: Int64 = 0
|
|
||||||
arc4random_buf(&randomId, 8)
|
|
||||||
let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId)
|
|
||||||
datas.append(resource)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case .video:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriber.putNext(datas)
|
|
||||||
subscriber.putCompletion()
|
|
||||||
}, error: { _ in
|
|
||||||
subscriber.putError(Void())
|
|
||||||
}, completed: nil)
|
|
||||||
|
|
||||||
return ActionDisposable {
|
|
||||||
disposable?.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@ -55,14 +55,18 @@ func presentLegacySecureIdAttachmentMenu(account: Account, present: @escaping (V
|
|||||||
if let signal = signal {
|
if let signal = signal {
|
||||||
let _ = (processedLegacySecureIdAttachmentItems(postbox: account.postbox, signal: signal)
|
let _ = (processedLegacySecureIdAttachmentItems(postbox: account.postbox, signal: signal)
|
||||||
|> mapToSignal { resources -> Signal<([TelegramMediaResource], SecureIdRecognizedDocumentData?), NoError> in
|
|> mapToSignal { resources -> Signal<([TelegramMediaResource], SecureIdRecognizedDocumentData?), NoError> in
|
||||||
if case .generic = type, recognizeDocumentData {
|
switch type {
|
||||||
return recognizedResources(postbox: account.postbox, resources: resources)
|
case .generic, .idCard:
|
||||||
|> map { data -> ([TelegramMediaResource], SecureIdRecognizedDocumentData?) in
|
if recognizeDocumentData {
|
||||||
return (resources, data)
|
return recognizedResources(postbox: account.postbox, resources: resources, shouldBeDriversLicense: false)
|
||||||
}
|
|> map { data -> ([TelegramMediaResource], SecureIdRecognizedDocumentData?) in
|
||||||
} else {
|
return (resources, data)
|
||||||
return .single((resources, nil))
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
return .single((resources, nil))
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { resourcesAndData in
|
|> deliverOnMainQueue).start(next: { resourcesAndData in
|
||||||
completion(resourcesAndData.0, resourcesAndData.1)
|
completion(resourcesAndData.0, resourcesAndData.1)
|
||||||
@ -152,7 +156,7 @@ private func processedLegacySecureIdAttachmentItems(postbox: Postbox, signal: SS
|
|||||||
return collectedItems
|
return collectedItems
|
||||||
}
|
}
|
||||||
|
|
||||||
private func recognizedResources(postbox: Postbox, resources: [TelegramMediaResource]) -> Signal<SecureIdRecognizedDocumentData?, NoError> {
|
private func recognizedResources(postbox: Postbox, resources: [TelegramMediaResource], shouldBeDriversLicense: Bool) -> Signal<SecureIdRecognizedDocumentData?, NoError> {
|
||||||
var signals: [Signal<SecureIdRecognizedDocumentData?, NoError>] = []
|
var signals: [Signal<SecureIdRecognizedDocumentData?, NoError>] = []
|
||||||
for resource in resources {
|
for resource in resources {
|
||||||
let image = Signal<UIImage?, NoError> { subscriber in
|
let image = Signal<UIImage?, NoError> { subscriber in
|
||||||
@ -178,7 +182,7 @@ private func recognizedResources(postbox: Postbox, resources: [TelegramMediaReso
|
|||||||
|> mapToSignal { image -> Signal<SecureIdRecognizedDocumentData?, NoError> in
|
|> mapToSignal { image -> Signal<SecureIdRecognizedDocumentData?, NoError> in
|
||||||
if let image = image {
|
if let image = image {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = TGPassportOCR.recognizeMRZ(in: image)?.start(next: { value in
|
let disposable = TGPassportOCR.recognizeData(in: image, shouldBeDriversLicense: shouldBeDriversLicense)?.start(next: { value in
|
||||||
if let value = value as? TGPassportMRZ {
|
if let value = value as? TGPassportMRZ {
|
||||||
var issuingCountry: String? = nil
|
var issuingCountry: String? = nil
|
||||||
if let issuingCountryValue = value.issuingCountry {
|
if let issuingCountryValue = value.issuingCountry {
|
||||||
|
|||||||
75
TelegramUI/NotificationMuteSettingsController.swift
Normal file
75
TelegramUI/NotificationMuteSettingsController.swift
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import Foundation
|
||||||
|
import Display
|
||||||
|
|
||||||
|
private enum NotificationMuteOption {
|
||||||
|
case `default`
|
||||||
|
case enable
|
||||||
|
case interval(Int32)
|
||||||
|
case disable
|
||||||
|
}
|
||||||
|
|
||||||
|
func notificationMuteSettingsController(presentationData: PresentationData, updateSettings: @escaping (Int32?) -> Void) -> ViewController {
|
||||||
|
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
||||||
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
|
controller?.dismissAnimated()
|
||||||
|
}
|
||||||
|
let notificationAction: (Int32?) -> Void = { muteUntil in
|
||||||
|
let muteInterval: Int32?
|
||||||
|
if let muteUntil = muteUntil {
|
||||||
|
if muteUntil <= 0 {
|
||||||
|
muteInterval = 0
|
||||||
|
} else if muteUntil == Int32.max {
|
||||||
|
muteInterval = Int32.max
|
||||||
|
} else {
|
||||||
|
muteInterval = muteUntil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
muteInterval = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSettings(muteInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
let options: [NotificationMuteOption] = [
|
||||||
|
.default,
|
||||||
|
.enable,
|
||||||
|
.interval(1 * 60 * 60),
|
||||||
|
.interval(8 * 60 * 60),
|
||||||
|
.interval(2 * 24 * 60 * 60),
|
||||||
|
.disable
|
||||||
|
]
|
||||||
|
var items: [ActionSheetItem] = []
|
||||||
|
for option in options {
|
||||||
|
let item: ActionSheetButtonItem
|
||||||
|
switch option {
|
||||||
|
case .default:
|
||||||
|
item = ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsDefault, action: {
|
||||||
|
dismissAction()
|
||||||
|
notificationAction(nil)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case .enable:
|
||||||
|
item = ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsEnable, action: {
|
||||||
|
dismissAction()
|
||||||
|
notificationAction(0)
|
||||||
|
})
|
||||||
|
case let .interval(value):
|
||||||
|
item = ActionSheetButtonItem(title: muteForIntervalString(strings: presentationData.strings, value: value), action: {
|
||||||
|
dismissAction()
|
||||||
|
notificationAction(value)
|
||||||
|
})
|
||||||
|
case .disable:
|
||||||
|
item = ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsDisable, action: {
|
||||||
|
dismissAction()
|
||||||
|
notificationAction(Int32.max)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
items.append(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.setItemGroups([
|
||||||
|
ActionSheetItemGroup(items: items),
|
||||||
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
|
])
|
||||||
|
return controller
|
||||||
|
}
|
||||||
@ -9,6 +9,39 @@ private func callConnectionDescription(_ connection: CallSessionConnection) -> O
|
|||||||
return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
|
return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let callLogsLimit = 20
|
||||||
|
|
||||||
|
private func callLogsPath(account: Account) -> String {
|
||||||
|
let path = account.basePath + "/calls"
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
if !fileManager.fileExists(atPath: path, isDirectory: nil) {
|
||||||
|
try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldest: (URL, Date)? = nil
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count > callLogsLimit, let oldest = oldest {
|
||||||
|
try? fileManager.removeItem(atPath: oldest.0.path)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
private let setupLogs: Bool = {
|
private let setupLogs: Bool = {
|
||||||
OngoingCallThreadLocalContext.setupLoggingFunction({ value in
|
OngoingCallThreadLocalContext.setupLoggingFunction({ value in
|
||||||
if let value = value {
|
if let value = value {
|
||||||
@ -64,25 +97,6 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum UsageCalculationConnection: Int32 {
|
|
||||||
case cellular = 0
|
|
||||||
case wifi = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum UsageCalculationDirection: Int32 {
|
|
||||||
case incoming = 0
|
|
||||||
case outgoing = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct UsageCalculationTag {
|
|
||||||
let connection: UsageCalculationConnection
|
|
||||||
let direction: UsageCalculationDirection
|
|
||||||
|
|
||||||
var key: Int32 {
|
|
||||||
return 5 * 4 + self.connection.rawValue * 2 + self.direction.rawValue * 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class OngoingCallContext {
|
final class OngoingCallContext {
|
||||||
let internalId: CallSessionInternalId
|
let internalId: CallSessionInternalId
|
||||||
|
|
||||||
@ -133,24 +147,15 @@ final class OngoingCallContext {
|
|||||||
context.stateChanged = { [weak self] state in
|
context.stateChanged = { [weak self] state in
|
||||||
self?.contextState.set(.single(state))
|
self?.contextState.set(.single(state))
|
||||||
}
|
}
|
||||||
context.callEnded = { [weak self] debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
context.callEnded = { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||||
if let strongSelf = self {
|
let delta = NetworkUsageStatsConnectionsEntry(
|
||||||
var update: [Int32 : Int64] = [:]
|
cellular: NetworkUsageStatsDirectionsEntry(
|
||||||
update[UsageCalculationTag(connection: .cellular, direction: .incoming).key] = bytesReceivedMobile
|
incoming: bytesReceivedMobile,
|
||||||
update[UsageCalculationTag(connection: .cellular, direction: .outgoing).key] = bytesSentMobile
|
outgoing: bytesSentMobile),
|
||||||
|
wifi: NetworkUsageStatsDirectionsEntry(
|
||||||
update[UsageCalculationTag(connection: .wifi, direction: .incoming).key] = bytesReceivedWifi
|
incoming: bytesReceivedWifi,
|
||||||
update[UsageCalculationTag(connection: .wifi, direction: .outgoing).key] = bytesSentWifi
|
outgoing: bytesSentWifi))
|
||||||
|
let _ = updateAccountNetworkUsageStats(account: account, category: .call, delta: delta)
|
||||||
let delta = NetworkUsageStatsConnectionsEntry(
|
|
||||||
cellular: NetworkUsageStatsDirectionsEntry(
|
|
||||||
incoming: bytesReceivedMobile,
|
|
||||||
outgoing: bytesSentMobile),
|
|
||||||
wifi: NetworkUsageStatsDirectionsEntry(
|
|
||||||
incoming: bytesReceivedWifi,
|
|
||||||
outgoing: bytesSentWifi))
|
|
||||||
let _ = updateAccountNetworkUsageStats(account: account, category: .call, delta: delta)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,4 +209,20 @@ final class OngoingCallContext {
|
|||||||
context.setIsMuted(value)
|
context.setIsMuted(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func debugInfo() -> Signal<(String, String), NoError> {
|
||||||
|
let poll = Signal<(String, String), NoError> { subscriber in
|
||||||
|
self.withContext { context in
|
||||||
|
let version = context.version()
|
||||||
|
let debugInfo = context.debugInfo()
|
||||||
|
if let version = version, let debugInfo = debugInfo {
|
||||||
|
subscriber.putNext((version, debugInfo))
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
return (poll |> then(.complete() |> delay(0.5, queue: Queue.concurrentDefaultQueue()))) |> restart
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,6 +58,9 @@ typedef NS_ENUM(int32_t, OngoingCallNetworkType) {
|
|||||||
- (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer;
|
- (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer;
|
||||||
- (void)stop;
|
- (void)stop;
|
||||||
|
|
||||||
|
- (NSString * _Nullable)debugInfo;
|
||||||
|
- (NSString * _Nullable)version;
|
||||||
|
|
||||||
- (void)setIsMuted:(bool)isMuted;
|
- (void)setIsMuted:(bool)isMuted;
|
||||||
- (void)setNetworkType:(OngoingCallNetworkType)networkType;
|
- (void)setNetworkType:(OngoingCallNetworkType)networkType;
|
||||||
|
|
||||||
|
|||||||
@ -287,6 +287,23 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *)debugInfo {
|
||||||
|
if (_controller != nil) {
|
||||||
|
auto rawDebugString = _controller->GetDebugString();
|
||||||
|
return [NSString stringWithUTF8String:rawDebugString.c_str()];
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)version {
|
||||||
|
if (_controller != nil) {
|
||||||
|
return [NSString stringWithUTF8String:_controller->GetVersion()];
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)controllerStateChanged:(int)state {
|
- (void)controllerStateChanged:(int)state {
|
||||||
OngoingCallState callState = OngoingCallStateInitializing;
|
OngoingCallState callState = OngoingCallStateInitializing;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
|||||||
@ -586,4 +586,8 @@ public final class PresentationCall {
|
|||||||
audioSessionControl.setOutputMode(.custom(output))
|
audioSessionControl.setOutputMode(.custom(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func debugInfo() -> Signal<(String, String), NoError> {
|
||||||
|
return self.ongoingContext.debugInfo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -860,58 +860,9 @@ public func userInfoController(account: Account, peerId: PeerId, mode: UserInfoC
|
|||||||
startSecretChatImpl?()
|
startSecretChatImpl?()
|
||||||
}, changeNotificationMuteSettings: {
|
}, changeNotificationMuteSettings: {
|
||||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
let controller = notificationMuteSettingsController(presentationData: presentationData, updateSettings: { value in
|
||||||
let dismissAction: () -> Void = { [weak controller] in
|
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: value).start())
|
||||||
controller?.dismissAnimated()
|
})
|
||||||
}
|
|
||||||
let notificationAction: (Int32?) -> Void = { muteUntil in
|
|
||||||
let muteInterval: Int32?
|
|
||||||
if let muteUntil = muteUntil {
|
|
||||||
if muteUntil <= 0 {
|
|
||||||
muteInterval = 0
|
|
||||||
} else if muteUntil == Int32.max {
|
|
||||||
muteInterval = Int32.max
|
|
||||||
} else {
|
|
||||||
muteInterval = muteUntil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
muteInterval = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: muteInterval).start())
|
|
||||||
}
|
|
||||||
var items: [ActionSheetItem] = []
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsEnable, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(0)
|
|
||||||
}))
|
|
||||||
let intervals: [Int32?] = [
|
|
||||||
nil,
|
|
||||||
1 * 60 * 60,
|
|
||||||
8 * 60 * 60,
|
|
||||||
2 * 24 * 60 * 60
|
|
||||||
]
|
|
||||||
for value in intervals {
|
|
||||||
let title: String
|
|
||||||
if let value = value {
|
|
||||||
title = muteForIntervalString(strings: presentationData.strings, value: value)
|
|
||||||
} else {
|
|
||||||
title = presentationData.strings.UserInfo_NotificationsDefault
|
|
||||||
}
|
|
||||||
items.append(ActionSheetButtonItem(title: title, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(value)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.UserInfo_NotificationsDisable, action: {
|
|
||||||
dismissAction()
|
|
||||||
notificationAction(Int32.max)
|
|
||||||
}))
|
|
||||||
|
|
||||||
controller.setItemGroups([
|
|
||||||
ActionSheetItemGroup(items: items),
|
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
||||||
])
|
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}, changeNotificationSoundSettings: {
|
}, changeNotificationSoundSettings: {
|
||||||
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user