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 */; };
|
||||
0941A9A4210B0E2E00EBE194 /* OpenInAppIconResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.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 */; };
|
||||
0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -3301,6 +3303,7 @@
|
||||
D0C50E3D1E93D09200F62E39 /* NotificationItemContainerNode.swift */,
|
||||
D0C50E3B1E93CC2600F62E39 /* NotificationItem.swift */,
|
||||
D0C50E3F1E93D3B000F62E39 /* ChatMessageNotificationItem.swift */,
|
||||
0952D1742176DEB500194860 /* NotificationMuteSettingsController.swift */,
|
||||
);
|
||||
name = Notifications;
|
||||
sourceTree = "<group>";
|
||||
@ -5537,6 +5540,7 @@
|
||||
D0EC6E811EB9F58900EBF1C3 /* NotificationContainerController.swift in Sources */,
|
||||
D0754D271EEE10C800884F6E /* BotCheckoutController.swift in Sources */,
|
||||
D053DADA201A4C4400993D32 /* ChatTextInputAttributes.swift in Sources */,
|
||||
0952D1752176DEB500194860 /* NotificationMuteSettingsController.swift in Sources */,
|
||||
D0EC6E821EB9F58900EBF1C3 /* NotificationContainerControllerNode.swift in Sources */,
|
||||
D0EC6E831EB9F58900EBF1C3 /* NotificationItemContainerNode.swift in Sources */,
|
||||
D0CB27D220C17A7F001ACF93 /* TermsOfServiceControllerNode.swift in Sources */,
|
||||
|
||||
@ -89,7 +89,7 @@ public final class CallController: ViewController {
|
||||
}
|
||||
|
||||
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.controllerNode.toggleMute = { [weak self] in
|
||||
|
||||
@ -14,6 +14,7 @@ final class CallControllerNode: ASDisplayNode {
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var peer: Peer?
|
||||
private let debugInfo: Signal<(String, String), NoError>
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
|
||||
@ -51,10 +52,11 @@ final class CallControllerNode: ASDisplayNode {
|
||||
var back: (() -> 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.presentationData = presentationData
|
||||
self.statusBar = statusBar
|
||||
self.debugInfo = debugInfo
|
||||
self.shouldStayHiddenUntilConnection = shouldStayHiddenUntilConnection
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
@ -377,6 +379,10 @@ final class CallControllerNode: ASDisplayNode {
|
||||
|
||||
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))
|
||||
|
||||
if let debugNode = self.debugNode {
|
||||
transition.updateFrame(node: debugNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
}
|
||||
}
|
||||
|
||||
@objc func keyPressed() {
|
||||
@ -410,14 +416,56 @@ final class CallControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private var debugTapCounter: (Double, Int) = (0.0, 0)
|
||||
|
||||
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
if let _ = self.keyPreviewNode {
|
||||
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) {
|
||||
switch recognizer.state {
|
||||
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 {
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
private let textNode: ASTextNode
|
||||
|
||||
override init() {
|
||||
public var dismiss: (() -> Void)?
|
||||
|
||||
init(signal: Signal<(String, String), NoError>) {
|
||||
self.dimNode = ASDisplayNode()
|
||||
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.isUserInteractionEnabled = false
|
||||
|
||||
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 {
|
||||
disposable.dispose()
|
||||
self.disposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -482,19 +601,22 @@ final private class CallDebugNode: ASDisplayNode {
|
||||
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) {
|
||||
|
||||
self.dismiss?()
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
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 labelSize = labelNode.measure(CGSize(width: , height: 100.0))
|
||||
//textNode.frame = CGRect(origin: CGPoint(x: floor(size.width, y: 81.0), size: labelSize)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,58 +720,9 @@ public func channelInfoController(account: Account, peerId: PeerId) -> ViewContr
|
||||
presentControllerImpl?(channelVisibilityController(account: account, peerId: peerId, mode: .generic), ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
||||
}, changeNotificationMuteSettings: {
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
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
|
||||
}
|
||||
|
||||
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() })])
|
||||
])
|
||||
let controller = notificationMuteSettingsController(presentationData: presentationData, updateSettings: { value in
|
||||
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: value).start())
|
||||
})
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, changeNotificationSoundSettings: {
|
||||
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
||||
|
||||
@ -818,7 +818,7 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode:
|
||||
doneEnabled = false
|
||||
}
|
||||
} else {
|
||||
doneEnabled = false
|
||||
doneEnabled = !(peer.addressName?.isEmpty ?? true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4904,6 +4904,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
}))
|
||||
@ -5122,12 +5123,12 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UIV
|
||||
let otherShortcuts: [KeyShortcut] = [
|
||||
KeyShortcut(title: strings.KeyCommand_ScrollUp, input: UIKeyInputUpArrow, modifiers: [.shift], action: { [weak self] in
|
||||
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
|
||||
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
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
switch state {
|
||||
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):
|
||||
var text = strongSelf.presentationData.strings.State_Connecting
|
||||
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)
|
||||
}
|
||||
|
||||
let previousState = Atomic<ChatListNodeState>(value: self.currentState)
|
||||
let previousView = Atomic<ChatListNodeView?>(value: nil)
|
||||
|
||||
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 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
|
||||
var prepareOnMainQueue = false
|
||||
|
||||
var previousWasEmptyOrSingleHole = false
|
||||
if let previous = previous {
|
||||
if let previous = previousView {
|
||||
if previous.filteredEntries.count == 1 {
|
||||
if case .HoleEntry = previous.filteredEntries[0] {
|
||||
previousWasEmptyOrSingleHole = true
|
||||
@ -450,11 +452,11 @@ final class ChatListNode: ListView {
|
||||
|
||||
if previousWasEmptyOrSingleHole {
|
||||
reason = .initial
|
||||
if previous == nil {
|
||||
if previousView == nil {
|
||||
prepareOnMainQueue = true
|
||||
}
|
||||
} else {
|
||||
if previous?.originalView === update.view {
|
||||
if previousView?.originalView === update.view {
|
||||
reason = .interactiveChanges
|
||||
updatedScrollPosition = nil
|
||||
} 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) })
|
||||
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
|
||||
}
|
||||
|
||||
@ -71,27 +71,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
||||
case .initial:
|
||||
let _ = options.insert(.LowLatency)
|
||||
let _ = options.insert(.Synchronous)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case .interactiveChanges:
|
||||
for (index, _, _) in indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
|
||||
let adjustedIndex = updatedCount - 1 - index
|
||||
if adjustedIndex == maxAnimatedInsertionIndex + 1 {
|
||||
@ -118,7 +98,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
||||
let _ = options.insert(.AnimateCrossfade)
|
||||
} else {
|
||||
let _ = options.insert(.AnimateAlpha)
|
||||
if !disableAnimations || previousPinnedCount != updatedPinnedCount {
|
||||
if !disableAnimations {
|
||||
let _ = options.insert(.AnimateInsertion)
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,12 +375,12 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
if let (media, flags) = mediaAndFlags {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
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
|
||||
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
||||
} else if file.isVideo {
|
||||
var automaticDownload = false
|
||||
automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
||||
let 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)
|
||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||
refineContentImageLayout = refineLayout
|
||||
@ -390,8 +390,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||
refineContentImageLayout = refineLayout
|
||||
} else {
|
||||
var automaticDownload = false
|
||||
automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
||||
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType
|
||||
if message.effectivelyIncoming(account.peerId) {
|
||||
|
||||
@ -121,7 +121,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
private var videoFrame: CGRect?
|
||||
|
||||
private var item: ChatMessageBubbleContentItem?
|
||||
private var automaticDownload: Bool?
|
||||
var telegramFile: TelegramMediaFile?
|
||||
private var secretProgressIcon: UIImage?
|
||||
|
||||
@ -108,14 +109,15 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
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 currentItem = self.item
|
||||
let previousAutomaticDownload = self.automaticDownload
|
||||
|
||||
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
|
||||
|
||||
return { item, width, displaySize, statusDisplayType in
|
||||
return { item, width, displaySize, statusDisplayType, automaticDownload in
|
||||
var updatedTheme: ChatPresentationThemeData?
|
||||
|
||||
var secretVideoPlaceholderBackgroundImage: UIImage?
|
||||
@ -251,6 +253,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
strongSelf.item = item
|
||||
strongSelf.videoFrame = videoFrame
|
||||
strongSelf.secretProgressIcon = secretProgressIcon
|
||||
strongSelf.automaticDownload = automaticDownload
|
||||
|
||||
if let updatedInfoBackgroundImage = 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
|
||||
strongSelf.videoNode = videoNode
|
||||
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
||||
@ -375,6 +378,10 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
applySecretPlaceholder()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
return { item, width, displaySize, statusType in
|
||||
return { item, width, displaySize, statusType, automaticDownload in
|
||||
var createdNode: ChatMessageInteractiveInstantVideoNode?
|
||||
let sizeAndApplyLayout: (ChatMessageInstantVideoItemLayoutResult, (ChatMessageInstantVideoItemLayoutData, ContainedViewLayoutTransition) -> Void)
|
||||
if let makeLayout = makeLayout {
|
||||
sizeAndApplyLayout = makeLayout(item, width, displaySize, statusType)
|
||||
sizeAndApplyLayout = makeLayout(item, width, displaySize, statusType, automaticDownload)
|
||||
} else {
|
||||
let node = ChatMessageInteractiveInstantVideoNode()
|
||||
sizeAndApplyLayout = node.asyncLayout()(item, width, displaySize, statusType)
|
||||
sizeAndApplyLayout = node.asyncLayout()(item, width, displaySize, statusType, automaticDownload)
|
||||
createdNode = node
|
||||
}
|
||||
return (sizeAndApplyLayout.0, { [weak node] layoutData, transition in
|
||||
|
||||
@ -237,7 +237,11 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
mediaAndFlags = (image, flags)
|
||||
} 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)
|
||||
}, changeNotificationMuteSettings: {
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
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
|
||||
}
|
||||
|
||||
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() })])
|
||||
])
|
||||
let controller = notificationMuteSettingsController(presentationData: presentationData, updateSettings: { value in
|
||||
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: value).start())
|
||||
})
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, changeNotificationSoundSettings: {
|
||||
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 {
|
||||
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
|
||||
let baseController = TGViewController(context: legacyController.context)!
|
||||
legacyController.bind(controller: baseController)
|
||||
|
||||
@ -81,9 +81,9 @@ private enum LegacyAssetVideoData {
|
||||
}
|
||||
|
||||
private enum LegacyAssetItem {
|
||||
case image(data: LegacyAssetImageData, caption: String?)
|
||||
case file(data: LegacyAssetImageData, mimeType: String, name: String, caption: String?)
|
||||
case video(data: LegacyAssetVideoData, previewImage: UIImage?, adjustments: TGVideoEditAdjustments?, caption: String?, asFile: Bool, asAnimation: Bool)
|
||||
case image(data: LegacyAssetImageData, thumbnail: UIImage?, caption: String?)
|
||||
case file(data: LegacyAssetImageData, thumbnail: UIImage?, mimeType: String, name: String, caption: String?)
|
||||
case video(data: LegacyAssetVideoData, thumbnail: UIImage?, adjustments: TGVideoEditAdjustments?, caption: String?, asFile: Bool, asAnimation: Bool)
|
||||
}
|
||||
|
||||
private final class LegacyAssetItemWrapper: NSObject {
|
||||
@ -105,11 +105,13 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
||||
let dict = anyDict as! NSDictionary
|
||||
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
|
||||
let image = dict["image"] as! UIImage
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
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
|
||||
} else if (dict["type"] as! NSString) == "cloudPhoto" {
|
||||
let asset = dict["asset"] as! TGMediaAsset
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
var asFile = false
|
||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||
asFile = true
|
||||
@ -125,13 +127,14 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
||||
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 {
|
||||
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
|
||||
} else if (dict["type"] as! NSString) == "file" {
|
||||
if let tempFileUrl = dict["tempFileUrl"] as? URL {
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
var mimeType = "application/binary"
|
||||
if let customMimeType = dict["mimeType"] as? String {
|
||||
mimeType = customMimeType
|
||||
@ -147,15 +150,16 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
} else if (dict["type"] as! NSString) == "video" {
|
||||
var thumbnail = dict["previewImage"] as? UIImage
|
||||
var asFile = false
|
||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||
asFile = true
|
||||
@ -163,16 +167,17 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
||||
|
||||
if let asset = dict["asset"] as? TGMediaAsset {
|
||||
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
|
||||
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
||||
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||
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
|
||||
}
|
||||
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
||||
let thumbnail = dict["previewImage"] as? UIImage
|
||||
var asFile = false
|
||||
if let document = dict["document"] as? NSNumber, document.boolValue {
|
||||
asFile = true
|
||||
@ -184,7 +189,7 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [A
|
||||
let dimensions = previewImage.pixelSize()
|
||||
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -242,7 +247,17 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
||||
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, 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 {
|
||||
case let .image(image):
|
||||
var randomId: Int64 = 0
|
||||
@ -268,9 +283,11 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
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] = []
|
||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||
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 scaledSize = size.aspectFitted(CGSize(width: 1280.0, height: 1280.0))
|
||||
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] = []
|
||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||
@ -294,7 +312,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
||||
case .tempFile:
|
||||
break
|
||||
}
|
||||
case let .file(data, mimeType, name, caption):
|
||||
case let .file(data, thumbnail, mimeType, name, caption):
|
||||
switch data {
|
||||
case let .tempFile(path):
|
||||
var randomId: Int64 = 0
|
||||
@ -311,7 +329,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
||||
default:
|
||||
break
|
||||
}
|
||||
case let .video(data, previewImage, adjustments, caption, asFile, asAnimation):
|
||||
case let .video(data, thumbnail, adjustments, caption, asFile, asAnimation):
|
||||
var finalDimensions: CGSize
|
||||
var finalDuration: Double
|
||||
switch data {
|
||||
@ -328,10 +346,10 @@ func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signa
|
||||
}
|
||||
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if let previewImage = previewImage {
|
||||
if let thumbnail = thumbnail {
|
||||
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||
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) {
|
||||
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
||||
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 {
|
||||
let _ = (processedLegacySecureIdAttachmentItems(postbox: account.postbox, signal: signal)
|
||||
|> mapToSignal { resources -> Signal<([TelegramMediaResource], SecureIdRecognizedDocumentData?), NoError> in
|
||||
if case .generic = type, recognizeDocumentData {
|
||||
return recognizedResources(postbox: account.postbox, resources: resources)
|
||||
|> map { data -> ([TelegramMediaResource], SecureIdRecognizedDocumentData?) in
|
||||
return (resources, data)
|
||||
}
|
||||
} else {
|
||||
return .single((resources, nil))
|
||||
switch type {
|
||||
case .generic, .idCard:
|
||||
if recognizeDocumentData {
|
||||
return recognizedResources(postbox: account.postbox, resources: resources, shouldBeDriversLicense: false)
|
||||
|> map { data -> ([TelegramMediaResource], SecureIdRecognizedDocumentData?) in
|
||||
return (resources, data)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return .single((resources, nil))
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { resourcesAndData in
|
||||
completion(resourcesAndData.0, resourcesAndData.1)
|
||||
@ -152,7 +156,7 @@ private func processedLegacySecureIdAttachmentItems(postbox: Postbox, signal: SS
|
||||
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>] = []
|
||||
for resource in resources {
|
||||
let image = Signal<UIImage?, NoError> { subscriber in
|
||||
@ -178,7 +182,7 @@ private func recognizedResources(postbox: Postbox, resources: [TelegramMediaReso
|
||||
|> mapToSignal { image -> Signal<SecureIdRecognizedDocumentData?, NoError> in
|
||||
if let image = image {
|
||||
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 {
|
||||
var issuingCountry: String? = nil
|
||||
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)
|
||||
}
|
||||
|
||||
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 = {
|
||||
OngoingCallThreadLocalContext.setupLoggingFunction({ value in
|
||||
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 {
|
||||
let internalId: CallSessionInternalId
|
||||
|
||||
@ -133,24 +147,15 @@ final class OngoingCallContext {
|
||||
context.stateChanged = { [weak self] state in
|
||||
self?.contextState.set(.single(state))
|
||||
}
|
||||
context.callEnded = { [weak self] debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||
if let strongSelf = self {
|
||||
var update: [Int32 : Int64] = [:]
|
||||
update[UsageCalculationTag(connection: .cellular, direction: .incoming).key] = bytesReceivedMobile
|
||||
update[UsageCalculationTag(connection: .cellular, direction: .outgoing).key] = bytesSentMobile
|
||||
|
||||
update[UsageCalculationTag(connection: .wifi, direction: .incoming).key] = bytesReceivedWifi
|
||||
update[UsageCalculationTag(connection: .wifi, direction: .outgoing).key] = bytesSentWifi
|
||||
|
||||
let delta = NetworkUsageStatsConnectionsEntry(
|
||||
cellular: NetworkUsageStatsDirectionsEntry(
|
||||
incoming: bytesReceivedMobile,
|
||||
outgoing: bytesSentMobile),
|
||||
wifi: NetworkUsageStatsDirectionsEntry(
|
||||
incoming: bytesReceivedWifi,
|
||||
outgoing: bytesSentWifi))
|
||||
let _ = updateAccountNetworkUsageStats(account: account, category: .call, delta: delta)
|
||||
}
|
||||
context.callEnded = { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)stop;
|
||||
|
||||
- (NSString * _Nullable)debugInfo;
|
||||
- (NSString * _Nullable)version;
|
||||
|
||||
- (void)setIsMuted:(bool)isMuted;
|
||||
- (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 {
|
||||
OngoingCallState callState = OngoingCallStateInitializing;
|
||||
switch (state) {
|
||||
|
||||
@ -586,4 +586,8 @@ public final class PresentationCall {
|
||||
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?()
|
||||
}, changeNotificationMuteSettings: {
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
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
|
||||
}
|
||||
|
||||
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() })])
|
||||
])
|
||||
let controller = notificationMuteSettingsController(presentationData: presentationData, updateSettings: { value in
|
||||
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: account, peerId: peerId, muteInterval: value).start())
|
||||
})
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, changeNotificationSoundSettings: {
|
||||
let _ = (account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user