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:
Ilya Laktyushin 2018-10-17 16:03:43 +03:00
parent 384cdf4a94
commit 0d41a372c9
23 changed files with 422 additions and 373 deletions

View File

@ -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 */,

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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)
} }
} }
} }

View File

@ -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;
} }

View File

@ -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 {

View File

@ -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)
} }

View File

@ -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)
} }
} }

View File

@ -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) {

View File

@ -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)

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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)

View File

@ -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()
}
}
}

View File

@ -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 {

View 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
}

View File

@ -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
}
} }

View File

@ -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;

View File

@ -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) {

View File

@ -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()
}
} }

View File

@ -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