diff --git a/Images.xcassets/Chat List/LockLockedBottom.imageset/ChatListLock_LockedBottom@2x.png b/Images.xcassets/Chat List/LockLockedBottom.imageset/ChatListLock_LockedBottom@2x.png deleted file mode 100644 index 6c6bbe3e6b..0000000000 Binary files a/Images.xcassets/Chat List/LockLockedBottom.imageset/ChatListLock_LockedBottom@2x.png and /dev/null differ diff --git a/Images.xcassets/Chat List/LockLockedBottom.imageset/Contents.json b/Images.xcassets/Chat List/LockLockedBottom.imageset/Contents.json deleted file mode 100644 index d55ce1215a..0000000000 --- a/Images.xcassets/Chat List/LockLockedBottom.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ChatListLock_LockedBottom@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Images.xcassets/Chat List/LockLockedTop.imageset/ChatListLock_LockedTop@2x.png b/Images.xcassets/Chat List/LockLockedTop.imageset/ChatListLock_LockedTop@2x.png deleted file mode 100644 index 9bdcc22b16..0000000000 Binary files a/Images.xcassets/Chat List/LockLockedTop.imageset/ChatListLock_LockedTop@2x.png and /dev/null differ diff --git a/Images.xcassets/Chat List/LockLockedTop.imageset/Contents.json b/Images.xcassets/Chat List/LockLockedTop.imageset/Contents.json deleted file mode 100644 index eb6ec5fb8d..0000000000 --- a/Images.xcassets/Chat List/LockLockedTop.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ChatListLock_LockedTop@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Images.xcassets/Chat List/LockUnlockedBottom.imageset/ChatListLock_UnlockedBottom@2x.png b/Images.xcassets/Chat List/LockUnlockedBottom.imageset/ChatListLock_UnlockedBottom@2x.png deleted file mode 100644 index 16c40761e0..0000000000 Binary files a/Images.xcassets/Chat List/LockUnlockedBottom.imageset/ChatListLock_UnlockedBottom@2x.png and /dev/null differ diff --git a/Images.xcassets/Chat List/LockUnlockedBottom.imageset/Contents.json b/Images.xcassets/Chat List/LockUnlockedBottom.imageset/Contents.json deleted file mode 100644 index c540009597..0000000000 --- a/Images.xcassets/Chat List/LockUnlockedBottom.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ChatListLock_UnlockedBottom@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Images.xcassets/Chat List/LockUnlockedTop.imageset/ChatListLock_UnlockedTop@2x.png b/Images.xcassets/Chat List/LockUnlockedTop.imageset/ChatListLock_UnlockedTop@2x.png deleted file mode 100644 index e7b1c74033..0000000000 Binary files a/Images.xcassets/Chat List/LockUnlockedTop.imageset/ChatListLock_UnlockedTop@2x.png and /dev/null differ diff --git a/Images.xcassets/Chat List/LockUnlockedTop.imageset/Contents.json b/Images.xcassets/Chat List/LockUnlockedTop.imageset/Contents.json deleted file mode 100644 index 4ff779de7e..0000000000 --- a/Images.xcassets/Chat List/LockUnlockedTop.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "ChatListLock_UnlockedTop@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index 9ba11c024b..da8ab078dd 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 091EB4EB213F48B4005284DE /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 094981C52138D73B00A10660 /* Vision.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 091EB4F0213F4C4C005284DE /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091EB4EF213F4C4C005284DE /* Lottie.framework */; }; 09310D2C213ED5FB0020033A /* anim_read.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D14213BC5DE0020033A /* anim_read.json */; }; 09310D2D213ED5FB0020033A /* anim_pin.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D15213BC5DE0020033A /* anim_pin.json */; }; 09310D2E213ED5FB0020033A /* anim_unmute.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D16213BC5DE0020033A /* anim_unmute.json */; }; @@ -347,6 +346,7 @@ D0C27B3B1F4B453700A4E170 /* InstantPagePlayableVideoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B3A1F4B453700A4E170 /* InstantPagePlayableVideoItem.swift */; }; D0C27B3D1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B3C1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift */; }; D0C44B641FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C44B631FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift */; }; + D0C45E9F213FFAFD00988156 /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C45E9E213FFAFD00988156 /* Lottie.framework */; }; D0CAD8FB20AE1D1B00ACD96E /* ChannelMemberCategoryListContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD8FA20AE1D1B00ACD96E /* ChannelMemberCategoryListContext.swift */; }; D0CAD8FD20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD8FC20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift */; }; D0CAD90120AEECAC00ACD96E /* ChatEditInterfaceMessageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CAD90020AEECAC00ACD96E /* ChatEditInterfaceMessageState.swift */; }; @@ -1016,9 +1016,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 091EB4E9213F475A005284DE /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 091EB4EC213F48BE005284DE /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 091EB4EF213F4C4C005284DE /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 09310D14213BC5DE0020033A /* anim_read.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_read.json; sourceTree = ""; }; 09310D15213BC5DE0020033A /* anim_pin.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_pin.json; sourceTree = ""; }; 09310D16213BC5DE0020033A /* anim_unmute.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_unmute.json; sourceTree = ""; }; @@ -1424,8 +1421,6 @@ D0575AF61EA0ED4F006F2541 /* ChatMessageInstantVideoItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatMessageInstantVideoItemNode.swift; sourceTree = ""; }; D0575AF91EA0FDA7006F2541 /* AvatarGalleryController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarGalleryController.swift; sourceTree = ""; }; D0575AFB1EA104A6006F2541 /* PeerAvatarImageGalleryItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerAvatarImageGalleryItem.swift; sourceTree = ""; }; - D057C52B2004202900990762 /* libLottie.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libLottie.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D057C5412004215B00990762 /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D05811931DD5F9380057C769 /* TelegramApplicationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramApplicationContext.swift; sourceTree = ""; }; D058E0CE1E8AD57300A442DE /* VideoPlayerProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerProxy.swift; sourceTree = ""; }; D05A32DB1E6EFCC2002760B4 /* NumericFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumericFormat.swift; sourceTree = ""; }; @@ -1686,6 +1681,7 @@ D0C27B3A1F4B453700A4E170 /* InstantPagePlayableVideoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantPagePlayableVideoItem.swift; sourceTree = ""; }; D0C27B3C1F4B454800A4E170 /* InstantPagePlayableVideoNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantPagePlayableVideoNode.swift; sourceTree = ""; }; D0C44B631FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismissGestureRecognizer.swift; sourceTree = ""; }; + D0C45E9E213FFAFD00988156 /* Lottie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Lottie.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0C48F431E81D5110075317D /* ChatEmptyItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatEmptyItem.swift; sourceTree = ""; }; D0C50DE81E93A07900F62E39 /* libtgvoip.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libtgvoip.framework; path = "../libtgvoip/build/Debug-iphoneos/libtgvoip.framework"; sourceTree = ""; }; D0C50E281E93A33700F62E39 /* VoipDynamic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VoipDynamic.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphoneos/VoipDynamic.framework"; sourceTree = ""; }; @@ -2119,7 +2115,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 091EB4F0213F4C4C005284DE /* Lottie.framework in Frameworks */, + D0C45E9F213FFAFD00988156 /* Lottie.framework in Frameworks */, 091EB4EB213F48B4005284DE /* Vision.framework in Frameworks */, D00ACA4B20222C280045D427 /* libtgvoip.framework in Frameworks */, D07BCBFE1F2B792300ED97AA /* LegacyComponents.framework in Frameworks */, @@ -3010,13 +3006,9 @@ D08D45281D5E340200A7428A /* Frameworks */ = { isa = PBXGroup; children = ( - 091EB4EF213F4C4C005284DE /* Lottie.framework */, - 091EB4EC213F48BE005284DE /* Lottie.framework */, - 091EB4E9213F475A005284DE /* Lottie.framework */, + D0C45E9E213FFAFD00988156 /* Lottie.framework */, D02DADBE2138D76F00116225 /* Vision.framework */, D00ACA4C20222C280045D427 /* libtgvoip.framework */, - D057C5412004215B00990762 /* Lottie.framework */, - D057C52B2004202900990762 /* libLottie.a */, D07BCBFD1F2B792300ED97AA /* LegacyComponents.framework */, D053B4361F1A9CA000E2D58A /* WebKit.framework */, D09E63B11F11289A003444CD /* PassKit.framework */, diff --git a/TelegramUI/AuthorizationSequenceController.swift b/TelegramUI/AuthorizationSequenceController.swift index fac23a3398..5dbbbb5bfe 100644 --- a/TelegramUI/AuthorizationSequenceController.swift +++ b/TelegramUI/AuthorizationSequenceController.swift @@ -48,6 +48,11 @@ public final class AuthorizationSequenceController: NavigationController { self.actionDisposable.dispose() } + override public func loadView() { + super.loadView() + self.view.backgroundColor = self.theme.backgroundColor + } + private func splashController() -> AuthorizationSequenceSplashController { var currentController: AuthorizationSequenceSplashController? for c in self.viewControllers { @@ -527,4 +532,12 @@ public final class AuthorizationSequenceController: NavigationController { } else if let _ = state as? AuthorizedAccountState { } } + + override public func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) { + let wasEmpty = self.viewControllers.isEmpty + super.setViewControllers(viewControllers, animated: animated) + if wasEmpty { + self.topViewController?.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + } + } } diff --git a/TelegramUI/AuthorizationSequenceSplashController.swift b/TelegramUI/AuthorizationSequenceSplashController.swift index f2ac9cbe75..21f5ce2e3c 100644 --- a/TelegramUI/AuthorizationSequenceSplashController.swift +++ b/TelegramUI/AuthorizationSequenceSplashController.swift @@ -59,7 +59,7 @@ final class AuthorizationSequenceSplashController: ViewController { }) }) - self.controller = RMIntroViewController(backroundColor: theme.backgroundColor, primaryColor: theme.primaryColor, buttonColor: theme.startButtonColor, accentColor: theme.accentColor, regularDotColor: theme.disclosureControlColor, highlightedDotColor: theme.primaryColor, suggestedLocalizationSignal: localizationSignal) + self.controller = RMIntroViewController(backroundColor: theme.backgroundColor, primaryColor: theme.primaryColor, buttonColor: theme.startButtonColor, accentColor: theme.accentColor, regularDotColor: theme.dotColor, highlightedDotColor: theme.primaryColor, suggestedLocalizationSignal: localizationSignal) super.init(navigationBarPresentationData: nil) diff --git a/TelegramUI/AuthorizationTheme.swift b/TelegramUI/AuthorizationTheme.swift index b8d86d3f79..43cf7df62b 100644 --- a/TelegramUI/AuthorizationTheme.swift +++ b/TelegramUI/AuthorizationTheme.swift @@ -16,6 +16,7 @@ public final class AuthorizationTheme { let primaryColor: UIColor let separatorColor: UIColor let itemHighlightedBackgroundColor: UIColor + let dotColor: UIColor let startButtonColor: UIColor let accentColor: UIColor let destructiveColor: UIColor @@ -24,7 +25,7 @@ public final class AuthorizationTheme { let alertBackgroundColor: UIColor let listBackgroundColor: UIColor - init(statusBarStyle: StatusBarStyle, navigationBarBackgroundColor: UIColor, navigationBarTextColor: UIColor, navigationBarSeparatorColor: UIColor, searchBarBackgroundColor: UIColor, searchBarFillColor: UIColor, searchBarPlaceholderColor: UIColor, searchBarTextColor: UIColor, keyboardAppearance: UIKeyboardAppearance, backgroundColor: UIColor, primaryColor: UIColor, separatorColor: UIColor, itemHighlightedBackgroundColor: UIColor, startButtonColor: UIColor, accentColor: UIColor, destructiveColor: UIColor, disclosureControlColor: UIColor, textPlaceholderColor: UIColor, alertBackgroundColor: UIColor, listBackgroundColor: UIColor) { + init(statusBarStyle: StatusBarStyle, navigationBarBackgroundColor: UIColor, navigationBarTextColor: UIColor, navigationBarSeparatorColor: UIColor, searchBarBackgroundColor: UIColor, searchBarFillColor: UIColor, searchBarPlaceholderColor: UIColor, searchBarTextColor: UIColor, keyboardAppearance: UIKeyboardAppearance, backgroundColor: UIColor, primaryColor: UIColor, separatorColor: UIColor, itemHighlightedBackgroundColor: UIColor, dotColor: UIColor, startButtonColor: UIColor, accentColor: UIColor, destructiveColor: UIColor, disclosureControlColor: UIColor, textPlaceholderColor: UIColor, alertBackgroundColor: UIColor, listBackgroundColor: UIColor) { self.statusBarStyle = statusBarStyle self.navigationBarBackgroundColor = navigationBarBackgroundColor self.navigationBarTextColor = navigationBarTextColor @@ -38,6 +39,7 @@ public final class AuthorizationTheme { self.primaryColor = primaryColor self.separatorColor = separatorColor self.itemHighlightedBackgroundColor = itemHighlightedBackgroundColor + self.dotColor = dotColor self.startButtonColor = startButtonColor self.accentColor = accentColor self.destructiveColor = destructiveColor @@ -62,6 +64,7 @@ let defaultLightAuthorizationTheme = AuthorizationTheme( primaryColor: .black, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), itemHighlightedBackgroundColor: UIColor(rgb: 0xd9d9d9), + dotColor: UIColor(rgb: 0xd9d9d9), startButtonColor: UIColor(rgb: 0x2ca5e0), accentColor: UIColor(rgb: 0x007ee5), destructiveColor: UIColor(rgb: 0xff3b30), @@ -85,6 +88,7 @@ let defaultAuthorizationTheme = AuthorizationTheme( primaryColor: .white, separatorColor: UIColor(rgb: 0x252525), itemHighlightedBackgroundColor: UIColor(rgb: 0x1b1b1b), + dotColor: UIColor(rgb: 0x717171), startButtonColor: .white, accentColor: .white, destructiveColor: UIColor(rgb: 0xFF736B), diff --git a/TelegramUI/ChatBotInfoItem.swift b/TelegramUI/ChatBotInfoItem.swift index 5c51bcebe1..f94afdaa80 100644 --- a/TelegramUI/ChatBotInfoItem.swift +++ b/TelegramUI/ChatBotInfoItem.swift @@ -209,7 +209,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { case let .url(url, concealed): foundTapAction = true if let controllerInteraction = self.controllerInteraction { - controllerInteraction.openUrl(url, concealed) + controllerInteraction.openUrl(url, concealed, nil) } case let .peerMention(peerId, _): foundTapAction = true diff --git a/TelegramUI/ChatButtonKeyboardInputNode.swift b/TelegramUI/ChatButtonKeyboardInputNode.swift index 5e59b8d1b6..4a2714fb60 100644 --- a/TelegramUI/ChatButtonKeyboardInputNode.swift +++ b/TelegramUI/ChatButtonKeyboardInputNode.swift @@ -158,7 +158,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { case .text: controllerInteraction.sendMessage(markupButton.title) case let .url(url): - controllerInteraction.openUrl(url, true) + controllerInteraction.openUrl(url, true, nil) case .requestMap: controllerInteraction.shareCurrentLocation() case .requestPhone: diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index a01d451c00..9b59fdb1db 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -170,6 +170,8 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin private var screenCaptureEventsDisposable: Disposable? private let chatAdditionalDataDisposable = MetaDisposable() + private var beginMediaRecordingRequestId: Int = 0 + var purposefulAction: (() -> Void)? public init(account: Account, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false)) { @@ -349,8 +351,16 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin } } + var hasActions = false + for media in updatedMessages[0].media { + if media is TelegramMediaAction { + hasActions = true + break + } + } + if !contextActions.isEmpty { - contextMenuController = ContextMenuController(actions: contextActions, catchTapsOutside: true) + contextMenuController = ContextMenuController(actions: contextActions, catchTapsOutside: true, hasHapticFeedback: hasActions) } contextMenuController?.dismissed = { @@ -359,13 +369,6 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin } } - var hasActions = false - for media in updatedMessages[0].media { - if media is TelegramMediaAction { - hasActions = true - break - } - } if hasActions { if let contextMenuController = contextMenuController { strongSelf.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { @@ -516,7 +519,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin } else { strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), messageId: nil), fromMessage: nil) } - }, openUrl: { [weak self] url, concealed in + }, openUrl: { [weak self] url, concealed, _ in if let strongSelf = self { strongSelf.openUrl(url, concealed: concealed) } @@ -2120,57 +2123,70 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId))), fromMessage: nil) } }, beginMediaRecording: { [weak self] isVideo in - let begin: () -> Void = { - if let strongSelf = self { - let hasOngoingCall: Signal - if let signal = strongSelf.account.telegramApplicationContext.hasOngoingCall { - hasOngoingCall = signal - } else { - hasOngoingCall = .single(false) - } - let _ = (hasOngoingCall - |> deliverOnMainQueue).start(next: { hasOngoingCall in - if let strongSelf = self { - if hasOngoingCall { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Call_CallInProgressTitle, text: strongSelf.presentationData.strings.Call_RecordingDisabledMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { - })]), in: .window(.root)) - } else { - if isVideo { - strongSelf.requestVideoRecorder() - } else { - strongSelf.requestAudioRecorder(beginWithTone: false) - } - } - } - }) - } + guard let strongSelf = self else { + return } - if let strongSelf = self { - DeviceAccess.authorizeAccess(to: .microphone(isVideo ? .video : .audio), presentationData: strongSelf.presentationData, present: { c, a in - self?.present(c, in: .window(.root), with: a) - }, openSettings: { - self?.account.telegramApplicationContext.applicationBindings.openSettings() - }, { granted in - if granted { - if isVideo, let strongSelf = self { - DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in - self?.present(c, in: .window(.root), with: a) - }, openSettings: { - self?.account.telegramApplicationContext.applicationBindings.openSettings() - }, { granted in - if granted { - begin() - } - }) + let requestId = strongSelf.beginMediaRecordingRequestId + let begin: () -> Void = { + guard let strongSelf = self, strongSelf.beginMediaRecordingRequestId == requestId else { + return + } + let hasOngoingCall: Signal + if let signal = strongSelf.account.telegramApplicationContext.hasOngoingCall { + hasOngoingCall = signal + } else { + hasOngoingCall = .single(false) + } + let _ = (hasOngoingCall + |> deliverOnMainQueue).start(next: { hasOngoingCall in + guard let strongSelf = self, strongSelf.beginMediaRecordingRequestId == requestId else { + return + } + if hasOngoingCall { + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Call_CallInProgressTitle, text: strongSelf.presentationData.strings.Call_RecordingDisabledMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + })]), in: .window(.root)) + } else { + if isVideo { + strongSelf.requestVideoRecorder() } else { - begin() + strongSelf.requestAudioRecorder(beginWithTone: false) } } }) } + DeviceAccess.authorizeAccess(to: .microphone(isVideo ? .video : .audio), presentationData: strongSelf.presentationData, present: { c, a in + self?.present(c, in: .window(.root), with: a) + }, openSettings: { + self?.account.telegramApplicationContext.applicationBindings.openSettings() + }, { granted in + guard let strongSelf = self, granted else { + return + } + if isVideo { + DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in + self?.present(c, in: .window(.root), with: a) + }, openSettings: { + self?.account.telegramApplicationContext.applicationBindings.openSettings() + }, { granted in + if granted { + begin() + } + }) + } else { + begin() + } + }) }, finishMediaRecording: { [weak self] action in - self?.dismissMediaRecorder(action) + guard let strongSelf = self else { + return + } + strongSelf.beginMediaRecordingRequestId += 1 + strongSelf.dismissMediaRecorder(action) }, stopMediaRecording: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.beginMediaRecordingRequestId += 1 self?.stopMediaRecorder() }, lockMediaRecording: { [weak self] in self?.lockMediaRecorder() @@ -4365,7 +4381,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin private func openUrlIn(_ url: String) { if let applicationContext = self.account.applicationContext as? TelegramApplicationContext { - let actionSheet = OpenInActionSheetController(postbox: self.account.postbox, applicationContext: applicationContext, theme: self.presentationData.theme, strings: self.presentationData.strings, item: .url(url), openUrl: { [weak self] url in + let actionSheet = OpenInActionSheetController(postbox: self.account.postbox, applicationContext: applicationContext, theme: self.presentationData.theme, strings: self.presentationData.strings, item: .url(url: url), openUrl: { [weak self] url in if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext, let navigationController = strongSelf.navigationController as? NavigationController { openExternalUrl(account: strongSelf.account, url: url, presentationData: strongSelf.presentationData, applicationContext: applicationContext, navigationController: navigationController, dismissInput: { self?.chatDisplayNode.dismissInput() diff --git a/TelegramUI/ChatControllerInteraction.swift b/TelegramUI/ChatControllerInteraction.swift index e3a311a573..4cad1d9d2a 100644 --- a/TelegramUI/ChatControllerInteraction.swift +++ b/TelegramUI/ChatControllerInteraction.swift @@ -49,7 +49,7 @@ public final class ChatControllerInteraction { let sendGif: (FileMediaReference) -> Void let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool) -> Void let activateSwitchInline: (PeerId?, String) -> Void - let openUrl: (String, Bool) -> Void + let openUrl: (String, Bool, Bool?) -> Void let shareCurrentLocation: () -> Void let shareAccountContact: () -> Void let sendBotCommand: (MessageId?, String) -> Void @@ -78,7 +78,7 @@ public final class ChatControllerInteraction { var contextHighlightedState: ChatInterfaceHighlightedState? var automaticMediaDownloadSettings: AutomaticMediaDownloadSettings - init(openMessage: @escaping (Message) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32)->Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings) { + init(openMessage: @escaping (Message) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32)->Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings) { self.openMessage = openMessage self.openPeer = openPeer self.openPeerMention = openPeerMention diff --git a/TelegramUI/ChatExternalFileGalleryItem.swift b/TelegramUI/ChatExternalFileGalleryItem.swift index 82d39129be..13eba6da8c 100644 --- a/TelegramUI/ChatExternalFileGalleryItem.swift +++ b/TelegramUI/ChatExternalFileGalleryItem.swift @@ -326,7 +326,7 @@ class ChatExternalFileGalleryItemNode: GalleryItemNode { @objc func actionButtonPressed() { if let (account, _) = self.accountAndFile, let message = self.message, let status = self.status, case .Local = status { let baseNavigationController = self.baseNavigationController() - (baseNavigationController?.topViewController as? ViewController)?.present(ShareController(account: account, subject: .messages([message]), saveToCameraRoll: false, showInChat: nil, externalShare: true, immediateExternalShare: true), in: .window(.root)) + (baseNavigationController?.topViewController as? ViewController)?.present(ShareController(account: account, subject: .messages([message]), showInChat: nil, externalShare: true, immediateExternalShare: true), in: .window(.root)) } } } diff --git a/TelegramUI/ChatItemGalleryFooterContentNode.swift b/TelegramUI/ChatItemGalleryFooterContentNode.swift index 9497571774..acf0ffc64f 100644 --- a/TelegramUI/ChatItemGalleryFooterContentNode.swift +++ b/TelegramUI/ChatItemGalleryFooterContentNode.swift @@ -113,6 +113,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { } var scrubberView: ChatVideoGalleryItemScrubberView? = nil { + willSet { + if let scrubberView = self.scrubberView, scrubberView.superview == self.view { + scrubberView.removeFromSuperview() + } + } didSet { if let scrubberView = self.scrubberView { self.view.addSubview(scrubberView) @@ -283,24 +288,28 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { var panelHeight: CGFloat = 44.0 + bottomInset panelHeight += contentInset + var textFrame = CGRect() if !self.textNode.isHidden { let sideInset: CGFloat = 8.0 + leftInset let topInset: CGFloat = 8.0 let textBottomInset: CGFloat = 8.0 let textSize = self.textNode.measure(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude)) panelHeight += textSize.height + topInset + textBottomInset - self.textNode.frame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize) + textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize) } - if let scrubberView = self.scrubberView { + if let scrubberView = self.scrubberView, scrubberView.superview == self.view { let sideInset: CGFloat = 8.0 + leftInset let topInset: CGFloat = 8.0 let bottomInset: CGFloat = 8.0 panelHeight += 34.0 + topInset + bottomInset + textFrame.origin.y += 34.0 + topInset + bottomInset scrubberView.frame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: width - sideInset * 2.0, height: 34.0)) } + self.textNode.frame = textFrame + self.actionButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) self.deleteButton.frame = CGRect(origin: CGPoint(x: width - 44.0 - rightInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) @@ -422,6 +431,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { var items: [ActionSheetItem] = [] var personalPeerName: String? var isChannel = false + var peerId: PeerId = messages[0].id.peerId if let user = messages[0].peers[messages[0].id.peerId] as? TelegramUser { personalPeerName = user.compactDisplayTitle } else if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel, case .broadcast = channel.info { @@ -446,7 +456,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { })) } if actions.options.contains(.deleteLocally) { - items.append(ActionSheetButtonItem(title: strongSelf.strings.Conversation_DeleteMessagesForMe, color: .destructive, action: { [weak actionSheet] in + var localOptionText = strongSelf.strings.Conversation_DeleteMessagesForMe + if strongSelf.account.peerId == peerId { + localOptionText = strongSelf.strings.Conversation_Moderate_Delete + } + items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { let _ = deleteMessagesInteractively(postbox: strongSelf.account.postbox, messageIds: messages.map { $0.id }, type: .forLocalPeer).start() @@ -486,13 +500,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { break } } - var saveToCameraRoll = false + var preferredAction = ShareControllerPreferredAction.default if let generalMessageContentKind = generalMessageContentKind { switch generalMessageContentKind { - case .image, .video: - saveToCameraRoll = true - default: - break + case .image, .video: + preferredAction = .saveToCameraRoll + default: + break } } @@ -501,9 +515,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { for m in messages[0].media { if let image = m as? TelegramMediaImage { subject = .image(image.representations) + } else if let webpage = m as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, let _ = content.image { + preferredAction = .saveToCameraRoll } } - let shareController = ShareController(account: strongSelf.account, subject: subject, saveToCameraRoll: saveToCameraRoll) + let shareController = ShareController(account: strongSelf.account, subject: subject, preferredAction: preferredAction) strongSelf.controllerInteraction?.presentController(shareController, nil) } else { var singleText = presentationData.strings.Media_ShareItem(1) @@ -524,7 +540,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { let shareAction: ([Message]) -> Void = { messages in if let strongSelf = self { - let shareController = ShareController(account: strongSelf.account, subject: .messages(messages), saveToCameraRoll: saveToCameraRoll) + let shareController = ShareController(account: strongSelf.account, subject: .messages(messages), preferredAction: preferredAction) strongSelf.controllerInteraction?.presentController(shareController, nil) } } diff --git a/TelegramUI/ChatListController.swift b/TelegramUI/ChatListController.swift index f6e11206b6..a2c5dfd239 100644 --- a/TelegramUI/ChatListController.swift +++ b/TelegramUI/ChatListController.swift @@ -51,6 +51,8 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD private var didSetup3dTouch = false private let passcodeDisposable = MetaDisposable() + private var passcodeLockTooltipDisposable = MetaDisposable() + private var didShowPasscodeLockTooltipController = false private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -136,7 +138,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD } } strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: { - if let strongSelf = self, let rect = strongSelf.titleView.proxyButtonRect() { + if let strongSelf = self, let rect = strongSelf.titleView.proxyButtonFrame { return (strongSelf.titleView, rect.insetBy(dx: 0.0, dy: -4.0)) } return nil @@ -219,6 +221,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD self.titleDisposable?.dispose() self.badgeDisposable?.dispose() self.badgeIconDisposable?.dispose() + self.passcodeLockTooltipDisposable.dispose() self.passcodeDisposable.dispose() self.presentationDataDisposable?.dispose() } @@ -464,6 +467,31 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD self.registerForPreviewing(with: self, sourceView: self.view, theme: PeekControllerTheme(presentationTheme: self.presentationData.theme), onlyNative: false) } } + + if let lockViewFrame = self.titleView.lockViewFrame, !self.didShowPasscodeLockTooltipController { + self.passcodeLockTooltipDisposable.set((combineLatest(ApplicationSpecificNotice.getPasscodeLockTips(postbox: self.account.postbox), account.postbox.combinedView(keys: [.accessChallengeData]) |> take(1)) + |> deliverOnMainQueue).start(next: { [weak self] tooltipValue, passcodeView in + if let strongSelf = self { + if !tooltipValue { + let hasPasscode = (passcodeView.views[.accessChallengeData] as! AccessChallengeDataView).data.isLockable + if hasPasscode { + let _ = ApplicationSpecificNotice.setPasscodeLockTips(postbox: strongSelf.account.postbox).start() + + let tooltipController = TooltipController(text: strongSelf.presentationData.strings.DialogList_PasscodeLockHelp, dismissByTapOutside: true) + strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: { [weak self] in + if let strongSelf = self { + return (strongSelf.titleView, lockViewFrame.offsetBy(dx: 4.0, dy: 14.0)) + } + return nil + })) + strongSelf.didShowPasscodeLockTooltipController = true + } + } else { + strongSelf.didShowPasscodeLockTooltipController = true + } + } + })) + } } override public func viewDidDisappear(_ animated: Bool) { diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index 999da00eba..c3c0b60de5 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -139,15 +139,15 @@ private let textFont = Font.regular(15.0) private let dateFont = Font.regular(14.0) private let badgeFont = Font.regular(14.0) -private let pinIcon = ItemListRevealOptionIcon.animation("anim_pin", keysToColor: nil) -private let unpinIcon = ItemListRevealOptionIcon.animation("anim_unpin", keysToColor: ["un Outlines.Group 1.Stroke 1"]) -private let muteIcon = ItemListRevealOptionIcon.animation("anim_mute", keysToColor: ["un Outlines.Group 1.Stroke 1"]) -private let unmuteIcon = ItemListRevealOptionIcon.animation("anim_unmute", keysToColor: nil) -private let deleteIcon = ItemListRevealOptionIcon.animation("anim_delete", keysToColor: nil) -private let groupIcon = ItemListRevealOptionIcon.animation("anim_group", keysToColor: nil) -private let ungroupIcon = ItemListRevealOptionIcon.animation("anim_ungroup", keysToColor: ["un Outlines.Group 1.Stroke 1"]) -private let readIcon = ItemListRevealOptionIcon.animation("anim_read", keysToColor: nil) -private let unreadIcon = ItemListRevealOptionIcon.animation("anim_unread", keysToColor: ["Oval.Oval.Stroke 1"]) +private let pinIcon = ItemListRevealOptionIcon.animation(animation: "anim_pin", keysToColor: nil) +private let unpinIcon = ItemListRevealOptionIcon.animation(animation: "anim_unpin", keysToColor: ["un Outlines.Group 1.Stroke 1"]) +private let muteIcon = ItemListRevealOptionIcon.animation(animation: "anim_mute", keysToColor: ["un Outlines.Group 1.Stroke 1"]) +private let unmuteIcon = ItemListRevealOptionIcon.animation(animation: "anim_unmute", keysToColor: nil) +private let deleteIcon = ItemListRevealOptionIcon.animation(animation: "anim_delete", keysToColor: nil) +private let groupIcon = ItemListRevealOptionIcon.animation(animation: "anim_group", keysToColor: nil) +private let ungroupIcon = ItemListRevealOptionIcon.animation(animation: "anim_ungroup", keysToColor: ["un Outlines.Group 1.Stroke 1"]) +private let readIcon = ItemListRevealOptionIcon.animation(animation: "anim_read", keysToColor: nil) +private let unreadIcon = ItemListRevealOptionIcon.animation(animation: "anim_unread", keysToColor: ["Oval.Oval.Stroke 1"]) private enum RevealOptionKey: Int32 { case pin diff --git a/TelegramUI/ChatListItemStrings.swift b/TelegramUI/ChatListItemStrings.swift index e991b54554..f81e5fc8ae 100644 --- a/TelegramUI/ChatListItemStrings.swift +++ b/TelegramUI/ChatListItemStrings.swift @@ -33,6 +33,7 @@ public func chatListItemStrings(strings: PresentationStrings, message: Message?, } } var isAnimated = false + var isVideo = false inner: for attribute in fileMedia.attributes { switch attribute { case .Animated: @@ -69,22 +70,27 @@ public func chatListItemStrings(strings: PresentationStrings, message: Message?, case let .Video(_, _, flags): if flags.contains(.instantRoundVideo) { messageText = strings.Message_VideoMessage + break inner } else { if message.text.isEmpty { - messageText = strings.Message_Video + isVideo = true } else if #available(iOSApplicationExtension 9.0, *) { messageText = "📹 \(messageText)" + break inner } } default: if !message.text.isEmpty { messageText = "📎 \(messageText)" + break inner } break } } if isAnimated { messageText = strings.Message_Animation + } else if isVideo { + messageText = strings.Message_Video } case let location as TelegramMediaMap: if location.liveBroadcastingTimeout != nil { diff --git a/TelegramUI/ChatListTitleLockView.swift b/TelegramUI/ChatListTitleLockView.swift index 28a5f182cb..bb06f2be35 100644 --- a/TelegramUI/ChatListTitleLockView.swift +++ b/TelegramUI/ChatListTitleLockView.swift @@ -67,11 +67,11 @@ final class ChatListTitleLockView: UIView { private func layoutItems() { if self.isLocked { - self.topView.frame = CGRect(x: (10.0 - 7.0) / 2.0, y: 0.0, width: 7.0, height: 5.0) - self.bottomView.frame = CGRect(x: 0.0, y: 5.0, width: 10.0, height: 7.0) + self.topView.frame = CGRect(x: floorToScreenPixels((10.0 - 7.0) / 2.0), y: 0.0, width: 7.0, height: 6.0) + self.bottomView.frame = CGRect(x: 0.0, y: 6.0, width: 10.0, height: 7.0) } else { - self.topView.frame = CGRect(x: 6.0, y: 0.0, width: 7.0, height: 5.0) - self.bottomView.frame = CGRect(x: 0.0, y: 5.0, width: 10.0, height: 7.0) + self.topView.frame = CGRect(x: 6.0, y: 0.0, width: 7.0, height: 6.0) + self.bottomView.frame = CGRect(x: 0.0, y: 6.0, width: 10.0, height: 7.0) } } diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 489656b161..ce8aad691c 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -1519,7 +1519,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { break case let .url(url, concealed): foundTapAction = true - self.item?.controllerInteraction.openUrl(url, concealed) + self.item?.controllerInteraction.openUrl(url, concealed, nil) break loop case let .peerMention(peerId, _): foundTapAction = true @@ -1873,7 +1873,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { case .text: item.controllerInteraction.sendMessage(button.title) case let .url(url): - item.controllerInteraction.openUrl(url, true) + item.controllerInteraction.openUrl(url, true, nil) case .requestMap: item.controllerInteraction.shareCurrentLocation() case .requestPhone: diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index eaf9ced7c5..8fe1c0bcab 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -574,7 +574,12 @@ final class ChatMessageInteractiveMediaNode: ASTransformNode { } case .Local: state = .none - let secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaIcon(theme) + let secretProgressIcon: UIImage? + if case .constrained = sizeCalculation { + secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaIcon(theme) + } else { + secretProgressIcon = PresentationResourcesChat.chatBubbleSecretMediaCompactIcon(theme) + } if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout, let beginTime = maybeBeginTime { state = .secretTimeout(color: bubbleTheme.mediaOverlayControlForegroundColor, icon: secretProgressIcon, beginTime: beginTime, timeout: timeout) } else if isSecretMedia, let secretProgressIcon = secretProgressIcon { @@ -607,7 +612,7 @@ final class ChatMessageInteractiveMediaNode: ASTransformNode { } } - if badgeContent == nil, isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout { + if isSecretMedia, let (maybeBeginTime, timeout) = secretBeginTimeAndTimeout { let remainingTime: Int32 if let beginTime = maybeBeginTime { let elapsedTime = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 - beginTime @@ -615,6 +620,7 @@ final class ChatMessageInteractiveMediaNode: ASTransformNode { } else { remainingTime = Int32(timeout) } + badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: strings.MessageTimer_ShortSeconds(Int32(remainingTime)))) } diff --git a/TelegramUI/ChatMessageWebpageBubbleContentNode.swift b/TelegramUI/ChatMessageWebpageBubbleContentNode.swift index 8a22bc56ba..499b53104f 100644 --- a/TelegramUI/ChatMessageWebpageBubbleContentNode.swift +++ b/TelegramUI/ChatMessageWebpageBubbleContentNode.swift @@ -153,7 +153,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { } } if let webpage = webPageContent { - item.controllerInteraction.openUrl(webpage.url, false) + item.controllerInteraction.openUrl(webpage.url, false, nil) } } } diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index cb2ec74e65..1fc3e532b9 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -198,7 +198,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self?.openPeerMention(name) }, openMessageContextMenu: { [weak self] message, node, frame in self?.openMessageContextMenu(message: message, node: node, frame: frame) - }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _ in + }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _ in self?.openUrl(url) }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message in if let strongSelf = self, let navigationController = strongSelf.getNavigationController() { diff --git a/TelegramUI/ChatTextInputPanelNode.swift b/TelegramUI/ChatTextInputPanelNode.swift index 171c85b748..9bf0dbd26f 100644 --- a/TelegramUI/ChatTextInputPanelNode.swift +++ b/TelegramUI/ChatTextInputPanelNode.swift @@ -304,9 +304,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } } self.actionButtons.micButton.endRecording = { [weak self] sendMedia in - if let strongSelf = self, let interfaceState = strongSelf.presentationInterfaceState, let interfaceInteraction = strongSelf.interfaceInteraction, let _ = interfaceState.inputTextPanelState.mediaRecordingState { - if sendMedia { - interfaceInteraction.finishMediaRecording(.send) + if let strongSelf = self, let interfaceState = strongSelf.presentationInterfaceState, let interfaceInteraction = strongSelf.interfaceInteraction { + if let _ = interfaceState.inputTextPanelState.mediaRecordingState { + if sendMedia { + interfaceInteraction.finishMediaRecording(.send) + } else { + interfaceInteraction.finishMediaRecording(.dismiss) + } } else { interfaceInteraction.finishMediaRecording(.dismiss) } diff --git a/TelegramUI/ChatVideoGalleryItemScrubberView.swift b/TelegramUI/ChatVideoGalleryItemScrubberView.swift index 3c93a8a0d9..187f80336b 100644 --- a/TelegramUI/ChatVideoGalleryItemScrubberView.swift +++ b/TelegramUI/ChatVideoGalleryItemScrubberView.swift @@ -89,6 +89,9 @@ final class ChatVideoGalleryItemScrubberView: UIView { super.layoutSubviews() let size = self.bounds.size + guard !size.equalTo(CGSize.zero) else { + return + } let scrubberHeight: CGFloat = 14.0 diff --git a/TelegramUI/CheckDeviceAccess.swift b/TelegramUI/CheckDeviceAccess.swift index 965b16adfe..c016ea3046 100644 --- a/TelegramUI/CheckDeviceAccess.swift +++ b/TelegramUI/CheckDeviceAccess.swift @@ -53,7 +53,17 @@ public final class DeviceAccess { case .camera: let status = PGCamera.cameraAuthorizationStatus() if status == PGCameraAuthorizationStatusNotDetermined { - completion(true) + AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in + Queue.mainQueue().async { + completion(response) + if !response { + let text = presentationData.strings.AccessDenied_Camera + present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { + openSettings() + })]), nil) + } + } + } } else if status == PGCameraAuthorizationStatusRestricted || status == PGCameraAuthorizationStatusDenied { let text: String if status == PGCameraAuthorizationStatusRestricted { diff --git a/TelegramUI/GalleryThumbnailContainerNode.swift b/TelegramUI/GalleryThumbnailContainerNode.swift index fcbac4e3e2..59ac12f7c8 100644 --- a/TelegramUI/GalleryThumbnailContainerNode.swift +++ b/TelegramUI/GalleryThumbnailContainerNode.swift @@ -44,9 +44,9 @@ private final class GalleryThumbnailItemNode: ASDisplayNode { } } -final class GalleryThumbnailContainerNode: ASDisplayNode { +final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDelegate { let groupId: Int64 - private let contentNode: ASDisplayNode + private let scrollNode: ASScrollNode private(set) var items: [GalleryThumbnailItem] = [] private var itemNodes: [GalleryThumbnailItemNode] = [] @@ -55,11 +55,13 @@ final class GalleryThumbnailContainerNode: ASDisplayNode { init(groupId: Int64) { self.groupId = groupId - self.contentNode = ASDisplayNode() + self.scrollNode = ASScrollNode() super.init() - self.addSubnode(self.contentNode) + self.scrollNode.view.delegate = self + + self.addSubnode(self.scrollNode) } func updateItems(_ items: [GalleryThumbnailItem], centralIndex: Int, progress: CGFloat) { @@ -86,7 +88,7 @@ final class GalleryThumbnailContainerNode: ASDisplayNode { for itemNode in itemNodes { if itemNode.supernode == nil { - self.contentNode.addSubnode(itemNode) + self.scrollNode.addSubnode(itemNode) } } for itemNode in self.itemNodes { @@ -119,7 +121,7 @@ final class GalleryThumbnailContainerNode: ASDisplayNode { func updateLayout(size: CGSize, centralIndex: Int, progress: CGFloat, transition: ContainedViewLayoutTransition) { self.currentLayout = size - self.contentNode.frame = CGRect(origin: CGPoint(), size: size) + self.scrollNode.frame = CGRect(origin: CGPoint(), size: size) let spacing: CGFloat = 2.0 let centralSpacing: CGFloat = 8.0 let itemHeight: CGFloat = 42.0 @@ -203,4 +205,16 @@ final class GalleryThumbnailContainerNode: ASDisplayNode { delay += 0.01 } } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + + } } diff --git a/TelegramUI/GameControllerNode.swift b/TelegramUI/GameControllerNode.swift index 2389a10d50..e70c4a08e1 100644 --- a/TelegramUI/GameControllerNode.swift +++ b/TelegramUI/GameControllerNode.swift @@ -134,7 +134,7 @@ final class GameControllerNode: ViewControllerTracingNode { } else { return .single(.done) } - }), saveToCameraRoll: false, showInChat: nil, externalShare: false, immediateExternalShare: false), nil) + }), showInChat: nil, externalShare: false, immediateExternalShare: false), nil) } else { self.shareWithoutScore() } @@ -145,7 +145,7 @@ final class GameControllerNode: ViewControllerTracingNode { func shareWithoutScore() { if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty { let url = "https://t.me/\(addressName)?game=\(gameName)" - self.present(ShareController(account: self.account, subject: .url(url), saveToCameraRoll: false, showInChat: nil, externalShare: true), nil) + self.present(ShareController(account: self.account, subject: .url(url), showInChat: nil, externalShare: true), nil) } } } diff --git a/TelegramUI/InstantPageGalleryFooterContentNode.swift b/TelegramUI/InstantPageGalleryFooterContentNode.swift index 1ce1dc1197..419330dc1a 100644 --- a/TelegramUI/InstantPageGalleryFooterContentNode.swift +++ b/TelegramUI/InstantPageGalleryFooterContentNode.swift @@ -79,7 +79,7 @@ final class InstantPageGalleryFooterContentNode: GalleryFooterContentNode { @objc func actionButtonPressed() { if let shareMedia = self.shareMedia { - self.controllerInteraction?.presentController(ShareController(account: self.account, subject: .media(shareMedia), saveToCameraRoll: true, showInChat: nil, externalShare: true, immediateExternalShare: false), nil) + self.controllerInteraction?.presentController(ShareController(account: self.account, subject: .media(shareMedia), preferredAction: .saveToCameraRoll, showInChat: nil, externalShare: true, immediateExternalShare: false), nil) } } } diff --git a/TelegramUI/ItemListRevealOptionsNode.swift b/TelegramUI/ItemListRevealOptionsNode.swift index ee99a18519..e093af40a5 100644 --- a/TelegramUI/ItemListRevealOptionsNode.swift +++ b/TelegramUI/ItemListRevealOptionsNode.swift @@ -5,8 +5,8 @@ import Lottie enum ItemListRevealOptionIcon: Equatable { case none - case image(_ image: UIImage) - case animation(_ animation: String, keysToColor: [String]?) + case image(image: UIImage) + case animation(animation: String, keysToColor: [String]?) public static func ==(lhs: ItemListRevealOptionIcon, rhs: ItemListRevealOptionIcon) -> Bool { switch lhs { diff --git a/TelegramUI/LegacyCamera.swift b/TelegramUI/LegacyCamera.swift index 62fdcf2644..8b437294ab 100644 --- a/TelegramUI/LegacyCamera.swift +++ b/TelegramUI/LegacyCamera.swift @@ -177,7 +177,7 @@ func presentedLegacyShortcutCamera(account: Account, saveCapturedMedia: Bool, sa } |> then(.single(ShareControllerExternalStatus.done)) } - }), saveToCameraRoll: false, showInChat: nil, externalShare: false), in: .window(.root)) + }), showInChat: nil, externalShare: false), in: .window(.root)) } } diff --git a/TelegramUI/LegacyLocationController.swift b/TelegramUI/LegacyLocationController.swift index 2e06cf09e2..1a5d00f217 100644 --- a/TelegramUI/LegacyLocationController.swift +++ b/TelegramUI/LegacyLocationController.swift @@ -229,7 +229,7 @@ func legacyLocationController(message: Message, mapMedia: TelegramMediaMap, acco strongLegacyController.present(ShareController(account: account, subject: .mapMedia(map), externalShare: true), in: .window(.root), with: nil) }) - strongLegacyController.present(OpenInActionSheetController(postbox: account.postbox, applicationContext: account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: .location(map, withDirections: directions), additionalAction: !directions ? shareAction : nil, openUrl: openUrl), in: .window(.root), with: nil) + strongLegacyController.present(OpenInActionSheetController(postbox: account.postbox, applicationContext: account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: .location(location: map, withDirections: directions), additionalAction: !directions ? shareAction : nil, openUrl: openUrl), in: .window(.root), with: nil) } } diff --git a/TelegramUI/ListMessageSnippetItemNode.swift b/TelegramUI/ListMessageSnippetItemNode.swift index ae40731e9d..8eed0d260a 100644 --- a/TelegramUI/ListMessageSnippetItemNode.swift +++ b/TelegramUI/ListMessageSnippetItemNode.swift @@ -30,6 +30,7 @@ final class ListMessageSnippetItemNode: ListMessageNode { private var currentIconImageRepresentation: TelegramMediaImageRepresentation? private var currentMedia: Media? private var currentPrimaryUrl: String? + private var currentIsInstantView: Bool? private var appliedItem: ListMessageItem? @@ -285,7 +286,7 @@ final class ListMessageSnippetItemNode: ListMessageNode { let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(TextNodeLayoutArguments(attributedString: descriptionText, backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0))) - let (linkNodeLayout, linkNodeApply) = linkNodeMakeLayout(TextNodeLayoutArguments(attributedString: linkText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: isInstantView ? TextNodeCutout(position: .TopLeft, size: CGSize(width: 10.0, height: 8.0)) : nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0))) + let (linkNodeLayout, linkNodeApply) = linkNodeMakeLayout(TextNodeLayoutArguments(attributedString: linkText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: isInstantView ? TextNodeCutout(position: .TopLeft, size: CGSize(width: 14.0, height: 8.0)) : nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0))) var instantViewImage: UIImage? if isInstantView { instantViewImage = PresentationResourcesChat.sharedMediaInstantViewIcon(item.theme) @@ -331,8 +332,8 @@ final class ListMessageSnippetItemNode: ListMessageNode { strongSelf.appliedItem = item strongSelf.currentMedia = selectedMedia - strongSelf.currentPrimaryUrl = primaryUrl + strongSelf.currentIsInstantView = isInstantView if let _ = updatedTheme { strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor @@ -485,7 +486,7 @@ final class ListMessageSnippetItemNode: ListMessageNode { } } else { if !item.controllerInteraction.openMessage(item.message) { - item.controllerInteraction.openUrl(currentPrimaryUrl, false) + item.controllerInteraction.openUrl(currentPrimaryUrl, false, false) } } } @@ -535,10 +536,10 @@ final class ListMessageSnippetItemNode: ListMessageNode { item.controllerInteraction.longTap(ChatControllerInteractionLongTapAction.url(url)) } else if url == self.currentPrimaryUrl { if !item.controllerInteraction.openMessage(item.message) { - item.controllerInteraction.openUrl(url, false) + item.controllerInteraction.openUrl(url, false, false) } } else { - item.controllerInteraction.openUrl(url, false) + item.controllerInteraction.openUrl(url, false, true) } } case .hold, .doubleTap: diff --git a/TelegramUI/LocationBroadcastActionSheetItem.swift b/TelegramUI/LocationBroadcastActionSheetItem.swift index 903f8804d4..9fc27dd0e1 100644 --- a/TelegramUI/LocationBroadcastActionSheetItem.swift +++ b/TelegramUI/LocationBroadcastActionSheetItem.swift @@ -93,7 +93,7 @@ public class LocationBroadcastActionSheetItemNode: ActionSheetItemNode { func setItem(_ item: LocationBroadcastActionSheetItem) { self.item = item - let textColor: UIColor = self.theme.standardActionTextColor + let textColor: UIColor = self.theme.primaryTextColor self.label.attributedText = NSAttributedString(string: item.title, font: ActionSheetButtonNode.defaultFont, textColor: textColor) self.avatarNode.setPeer(account: item.account, peer: item.peer) diff --git a/TelegramUI/MediaPlayer.swift b/TelegramUI/MediaPlayer.swift index 138136b769..ed4fae85d5 100644 --- a/TelegramUI/MediaPlayer.swift +++ b/TelegramUI/MediaPlayer.swift @@ -727,11 +727,9 @@ private final class MediaPlayerContext { f?() case .stop: self.stoppedAtEnd = true - self.seek(timestamp: 0.0, action: .pause) self.pause(lostAudioSession: false) case let .action(f): self.stoppedAtEnd = true - self.seek(timestamp: 0.0, action: .pause) self.pause(lostAudioSession: false) f() case let .loopDisablingSound(f): diff --git a/TelegramUI/NetworkStatusTitleView.swift b/TelegramUI/NetworkStatusTitleView.swift index 6c5cd06bb3..05c64f6dbc 100644 --- a/TelegramUI/NetworkStatusTitleView.swift +++ b/TelegramUI/NetworkStatusTitleView.swift @@ -179,7 +179,7 @@ final class NetworkStatusTitleView: UIView, NavigationBarTitleView, NavigationBa let buttonX = max(0.0, titleFrame.minX - 10.0) self.buttonView.frame = CGRect(origin: CGPoint(x: buttonX, y: 0.0), size: CGSize(width: min(titleFrame.maxX + 28.0, size.width) - buttonX, height: titleFrame.maxY)) - self.lockView.frame = CGRect(x: titleFrame.maxX + 6.0, y: titleFrame.minY + 4.0, width: 2.0, height: 2.0) + self.lockView.frame = CGRect(x: titleFrame.maxX + 6.0, y: titleFrame.minY + 3.0, width: 2.0, height: 2.0) self.activityIndicator.frame = CGRect(origin: CGPoint(x: titleFrame.minX - indicatorSize.width - 6.0, y: titleFrame.minY - 1.0), size: indicatorSize) } @@ -223,13 +223,21 @@ final class NetworkStatusTitleView: UIView, NavigationBarTitleView, NavigationBa func animateLayoutTransition() { } - func proxyButtonRect() -> CGRect? { + var proxyButtonFrame: CGRect? { if !self.proxyNode.isHidden { return proxyNode.frame } return nil } + var lockViewFrame: CGRect? { + if !self.lockView.isHidden { + return self.lockView.frame + } else { + return nil + } + } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if !self.proxyButton.isHidden { return self.proxyButton.hitTest(point.offsetBy(dx: -self.proxyButton.frame.minX, dy: -self.proxyButton.frame.minY), with: event) diff --git a/TelegramUI/Notices.swift b/TelegramUI/Notices.swift index dcdd68c394..535c528edf 100644 --- a/TelegramUI/Notices.swift +++ b/TelegramUI/Notices.swift @@ -95,6 +95,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case chatMediaMediaRecordingTips = 3 case profileCallTips = 4 case setPublicChannelLink = 5 + case passcodeLockTips = 6 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) v.setInt32(0, value: self.rawValue) @@ -133,6 +134,10 @@ private struct ApplicationSpecificNoticeKeys { static func setPublicChannelLink() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.setPublicChannelLink.key) } + + static func passcodeLockTips() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.passcodeLockTips.key) + } } struct ApplicationSpecificNotice { @@ -272,6 +277,22 @@ struct ApplicationSpecificNotice { } } + static func getPasscodeLockTips(postbox: Postbox) -> Signal { + return postbox.transaction { transaction -> Bool in + if let _ = transaction.getNoticeEntry(key: ApplicationSpecificNoticeKeys.passcodeLockTips()) as? ApplicationSpecificBoolNotice { + return true + } else { + return false + } + } + } + + static func setPasscodeLockTips(postbox: Postbox) -> Signal { + return postbox.transaction { transaction -> Void in + transaction.setNoticeEntry(key: ApplicationSpecificNoticeKeys.passcodeLockTips(), value: ApplicationSpecificBoolNotice()) + } + } + static func reset(postbox: Postbox) -> Signal { return postbox.transaction { transaction -> Void in diff --git a/TelegramUI/NotificationSoundSelection.swift b/TelegramUI/NotificationSoundSelection.swift index 4f072d43ec..6553e52dea 100644 --- a/TelegramUI/NotificationSoundSelection.swift +++ b/TelegramUI/NotificationSoundSelection.swift @@ -57,14 +57,14 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry { case let .none(section, _, _, _): switch section { case .modern: - return 1 + return 2 case .classic: return 1001 } case let .default(section, _, _, _): switch section { case .modern: - return 2 + return 1 case .classic: return 1002 } @@ -143,10 +143,10 @@ private func notificationsAndSoundsEntries(presentationData: PresentationData, d var entries: [NotificationSoundSelectionEntry] = [] entries.append(.modernHeader(presentationData.theme, presentationData.strings.Notifications_AlertTones)) - entries.append(.none(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: .none), selected: state.selectedSound == .none)) if let defaultSound = defaultSound { entries.append(.default(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: .default, default: defaultSound), selected: state.selectedSound == .default)) } + entries.append(.none(section: .modern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: .none), selected: state.selectedSound == .none)) for i in 0 ..< 12 { let sound: PeerMessageSound = .bundledModern(id: Int32(i)) entries.append(.sound(section: .modern, index: Int32(i), theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: sound), sound: sound, selected: sound == state.selectedSound)) diff --git a/TelegramUI/OpenChatMessage.swift b/TelegramUI/OpenChatMessage.swift index add38dbad3..e9135367b3 100644 --- a/TelegramUI/OpenChatMessage.swift +++ b/TelegramUI/OpenChatMessage.swift @@ -218,7 +218,7 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever present(controller, nil) return true case .document: - present(ShareController(account: account, subject: .messages([message]), saveToCameraRoll: false, showInChat: nil, externalShare: true, immediateExternalShare: true), nil) + present(ShareController(account: account, subject: .messages([message]), showInChat: nil, externalShare: true, immediateExternalShare: true), nil) return true case let .audio(file): let location: PeerMessagesPlaylistLocation diff --git a/TelegramUI/OpenInActionSheetController.swift b/TelegramUI/OpenInActionSheetController.swift index 48f4de6b50..5ebc7ca345 100644 --- a/TelegramUI/OpenInActionSheetController.swift +++ b/TelegramUI/OpenInActionSheetController.swift @@ -198,14 +198,14 @@ private final class OpenInAppNode : ASDisplayNode { switch option.application { case .safari: if let image = UIImage(bundleImageName: "Open In/Safari") { - self.iconNode.setSignal(openInAppIcon(postbox: postbox, appIcon: .image(image))) + self.iconNode.setSignal(openInAppIcon(postbox: postbox, appIcon: .image(image: image))) } case .maps: if let image = UIImage(bundleImageName: "Open In/Maps") { - self.iconNode.setSignal(openInAppIcon(postbox: postbox, appIcon: .image(image))) + self.iconNode.setSignal(openInAppIcon(postbox: postbox, appIcon: .image(image: image))) } case let .other(_, identifier, _): - self.iconNode.setSignal(openInAppIcon(postbox: postbox, appIcon: .resource(OpenInAppIconResource(appStoreId: identifier)))) + self.iconNode.setSignal(openInAppIcon(postbox: postbox, appIcon: .resource(resource: OpenInAppIconResource(appStoreId: identifier)))) } self.action = { diff --git a/TelegramUI/OpenInOptions.swift b/TelegramUI/OpenInOptions.swift index a9f33e31a8..dabdff62cf 100644 --- a/TelegramUI/OpenInOptions.swift +++ b/TelegramUI/OpenInOptions.swift @@ -4,8 +4,8 @@ import CoreLocation import MapKit enum OpenInItem { - case url(_ url: String) - case location(_ location: TelegramMediaMap, withDirections: Bool) + case url(url: String) + case location(location: TelegramMediaMap, withDirections: Bool) } enum OpenInApplication { @@ -16,7 +16,7 @@ enum OpenInApplication { enum OpenInAction { case none - case openUrl(_ url: String) + case openUrl(url: String) case openLocation(latitude: Double, longitude: Double, withDirections: Bool) } @@ -58,14 +58,14 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it switch item { case let .url(url): options.append(OpenInOption(application: .safari, action: { - return .openUrl(url) + return .openUrl(url: url) })) options.append(OpenInOption(application: .other(title: "Chrome", identifier: 535886823, scheme: "chrome"), action: { if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) { components.scheme = components.scheme == "https" ? "googlechromes" : "googlechrome" if let url = components.string { - return .openUrl(url) + return .openUrl(url: url) } } return .none @@ -73,7 +73,7 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it options.append(OpenInOption(application: .other(title: "Firefox", identifier: 989804926, scheme: "firefox"), action: { if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) { - return .openUrl("firefox://open-url?url=\(escapedUrl)") + return .openUrl(url: "firefox://open-url?url=\(escapedUrl)") } return .none })) @@ -82,7 +82,7 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it if let url = URL(string: url), var components = URLComponents(url: url, resolvingAgainstBaseURL: true) { components.scheme = components.scheme == "https" ? "opera-https" : "opera-http" if let url = components.string { - return .openUrl(url) + return .openUrl(url: url) } } return .none @@ -90,7 +90,7 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it options.append(OpenInOption(application: .other(title: "Yandex", identifier: 483693909, scheme: "yandexbrowser-open-url"), action: { if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) { - return .openUrl("yandexbrowser-open-url://\(escapedUrl)") + return .openUrl(url: "yandexbrowser-open-url://\(escapedUrl)") } return .none })) @@ -102,7 +102,7 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "foursquare" { options.append(OpenInOption(application: .other(title: "Foursquare", identifier: 306934924, scheme: "foursquare"), action: { - return .openUrl("foursquare://venues/\(venueId)") + return .openUrl(url: "foursquare://venues/\(venueId)") })) } @@ -113,17 +113,17 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it options.append(OpenInOption(application: .other(title: "Google Maps", identifier: 585027354, scheme: "comgooglemaps-x-callback"), action: { let coordinates = "\(lat),\(lon)" if withDirections { - return .openUrl("comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram") + return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram") } else { - return .openUrl("comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram") + return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram") } })) options.append(OpenInOption(application: .other(title: "Yandex.Maps", identifier: 313877526, scheme: "yandexmaps"), action: { if withDirections { - return .openUrl("yandexmaps://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)") + return .openUrl(url: "yandexmaps://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)") } else { - return .openUrl("yandexmaps://maps.yandex.ru/?pt=\(lon),\(lat)&z=16") + return .openUrl(url: "yandexmaps://maps.yandex.ru/?pt=\(lon),\(lat)&z=16") } })) @@ -140,11 +140,11 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it } else { dropoffAddress = "" } - return .openUrl("uber://?client_id=&action=setPickup&pickup=my_location&dropoff[latitude]=\(lat)&dropoff[longitude]=\(lon)&dropoff[nickname]=\(dropoffName)&dropoff[formatted_address]=\(dropoffAddress)") + return .openUrl(url: "uber://?client_id=&action=setPickup&pickup=my_location&dropoff[latitude]=\(lat)&dropoff[longitude]=\(lon)&dropoff[nickname]=\(dropoffName)&dropoff[formatted_address]=\(dropoffAddress)") })) options.append(OpenInOption(application: .other(title: "Lyft", identifier: 529379082, scheme: "lyft"), action: { - return .openUrl("lyft://ridetype?id=lyft&destination[latitude]=\(lat)&destination[longitude]=\(lon)") + return .openUrl(url: "lyft://ridetype?id=lyft&destination[latitude]=\(lat)&destination[longitude]=\(lon)") })) options.append(OpenInOption(application: .other(title: "Citymapper", identifier: 469463298, scheme: "citymapper"), action: { @@ -160,25 +160,25 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it } else { endAddress = "" } - return .openUrl("citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)") + return .openUrl(url: "citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)") })) if withDirections { options.append(OpenInOption(application: .other(title: "Yandex.Navi", identifier: 474500851, scheme: "yandexnavi"), action: { - return .openUrl("yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)") + return .openUrl(url: "yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)") })) } options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location"), action: { - return .openUrl("here-location://\(lat),\(lon)") + return .openUrl(url: "here-location://\(lat),\(lon)") })) options.append(OpenInOption(application: .other(title: "Waze", identifier: 323229106, scheme: "waze"), action: { let url = "waze://?ll=\(lat),\(lon)" if withDirections { - return .openUrl(url.appending("&navigate=yes")) + return .openUrl(url: url.appending("&navigate=yes")) } else { - return .openUrl(url) + return .openUrl(url: url) } })) } diff --git a/TelegramUI/OverlayPlayerControllerNode.swift b/TelegramUI/OverlayPlayerControllerNode.swift index 7716494906..6a6d628d77 100644 --- a/TelegramUI/OverlayPlayerControllerNode.swift +++ b/TelegramUI/OverlayPlayerControllerNode.swift @@ -54,7 +54,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec } else { return false } - }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in + }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in }, navigationController: { return nil }, presentGlobalOverlayController: { _, _ in diff --git a/TelegramUI/PeerAvatarImageGalleryItem.swift b/TelegramUI/PeerAvatarImageGalleryItem.swift index c68c04b201..8f424dd74a 100644 --- a/TelegramUI/PeerAvatarImageGalleryItem.swift +++ b/TelegramUI/PeerAvatarImageGalleryItem.swift @@ -147,7 +147,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { self.footerContentNode.share = { [weak self] interaction in if let strongSelf = self, let entry = strongSelf.entry, !entry.representations.isEmpty { - let shareController = ShareController(account: strongSelf.account, subject: .image(entry.representations), saveToCameraRoll: true) + let shareController = ShareController(account: strongSelf.account, subject: .image(entry.representations), preferredAction: .saveToCameraRoll) interaction.presentController(shareController, nil) } } diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index 785a420fa5..a48fa0153b 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -154,8 +154,8 @@ public class PeerMediaCollectionController: TelegramController { }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in - }, openUrl: { [weak self] url, _ in - self?.openUrl(url) + }, openUrl: { [weak self] url, _, external in + self?.openUrl(url, external: external ?? false) }, shareCurrentLocation: { }, shareAccountContact: { }, sendBotCommand: { _, _ in @@ -258,7 +258,15 @@ public class PeerMediaCollectionController: TelegramController { })) } if actions.options.contains(.deleteLocally) { - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe, color: .destructive, action: { [weak actionSheet] in + var localOptionText = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe + if strongSelf.account.peerId == strongSelf.peerId { + if messageIds.count == 1 { + localOptionText = strongSelf.presentationData.strings.Conversation_Moderate_Delete + } else { + localOptionText = strongSelf.presentationData.strings.Conversation_DeleteManyMessages + } + } + items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateInterfaceState(animated: true, { $0.withoutSelectionState() }) @@ -551,7 +559,7 @@ public class PeerMediaCollectionController: TelegramController { } } - private func openUrl(_ url: String) { + private func openUrl(_ url: String, external: Bool = false) { let disposable: MetaDisposable if let current = self.resolveUrlDisposable { disposable = current @@ -559,7 +567,15 @@ public class PeerMediaCollectionController: TelegramController { disposable = MetaDisposable() self.resolveUrlDisposable = disposable } - disposable.set((resolveUrl(account: self.account, url: url) |> deliverOnMainQueue).start(next: { [weak self] result in + + let resolvedUrl: Signal + if external { + resolvedUrl = .single(.externalUrl(url)) + } else { + resolvedUrl = resolveUrl(account: self.account, url: url) + } + + disposable.set((resolvedUrl |> deliverOnMainQueue).start(next: { [weak self] result in if let strongSelf = self { openResolvedUrl(result, account: strongSelf.account, navigationController: strongSelf.navigationController as? NavigationController, openPeer: { peerId, navigation in if let strongSelf = self { diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index 7cda707f48..dd04e9e98b 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -2491,8 +2491,8 @@ private func drawOpenInAppIconBorder(into c: CGContext, arguments: TransformImag } enum OpenInAppIcon { - case resource(_ resource: TelegramMediaResource) - case image(_ image: UIImage) + case resource(resource: TelegramMediaResource) + case image(image: UIImage) } func openInAppIcon(postbox: Postbox, appIcon: OpenInAppIcon) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { diff --git a/TelegramUI/PresentationResourceKey.swift b/TelegramUI/PresentationResourceKey.swift index b21a988e25..610f7ef906 100644 --- a/TelegramUI/PresentationResourceKey.swift +++ b/TelegramUI/PresentationResourceKey.swift @@ -91,6 +91,7 @@ enum PresentationResourceKey: Int32 { case chatServiceBubbleFillImage case chatBubbleSecretMediaIcon + case chatBubbleSecretMediaCompactIcon case chatFreeformContentAdditionalInfoBackgroundImage diff --git a/TelegramUI/PresentationResourcesChat.swift b/TelegramUI/PresentationResourcesChat.swift index 8a70a4609b..9b79210694 100644 --- a/TelegramUI/PresentationResourcesChat.swift +++ b/TelegramUI/PresentationResourcesChat.swift @@ -184,6 +184,20 @@ struct PresentationResourcesChat { }) } + static func chatBubbleSecretMediaCompactIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.chatBubbleSecretMediaCompactIcon.rawValue, { theme in + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/SecretMediaIcon"), color: theme.chat.bubble.mediaOverlayControlForegroundColor) { + let factor: CGFloat = 0.6 + return generateImage(CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) + }) + } else { + return nil + } + }) + } + static func chatFreeformContentAdditionalInfoBackgroundImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatFreeformContentAdditionalInfoBackgroundImage.rawValue, { theme in return generateStretchableFilledCircleImage(radius: 4.0, color: theme.chat.serviceMessage.serviceMessageFillColor) diff --git a/TelegramUI/PresentationResourcesChatList.swift b/TelegramUI/PresentationResourcesChatList.swift index b4d641b859..1b32a8cc86 100644 --- a/TelegramUI/PresentationResourcesChatList.swift +++ b/TelegramUI/PresentationResourcesChatList.swift @@ -62,25 +62,52 @@ struct PresentationResourcesChatList { static func lockTopLockedImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatListLockTopLockedImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/LockLockedTop"), color: theme.rootController.navigationBar.accentTextColor) + return generateImage(CGSize(width: 7.0, height: 6.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.rootController.navigationBar.accentTextColor.cgColor) + context.setStrokeColor(theme.rootController.navigationBar.accentTextColor.cgColor) + context.setLineWidth(1.5) + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.75, y: 0.75, width: 5.5, height: 12.0), cornerRadius: 2.5).cgPath) + context.strokePath() + }) }) } static func lockBottomLockedImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatListLockBottomLockedImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/LockLockedBottom"), color: theme.rootController.navigationBar.accentTextColor) + return generateImage(CGSize(width: 10.0, height: 7.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.rootController.navigationBar.accentTextColor.cgColor) + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 7.0), cornerRadius: 1.33).cgPath) + context.fillPath() + }) }) } static func lockTopUnlockedImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatListLockTopUnlockedImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/LockUnlockedTop"), color: theme.rootController.navigationBar.primaryTextColor) + return generateImage(CGSize(width: 7.0, height: 6.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.rootController.navigationBar.primaryTextColor.cgColor) + context.setStrokeColor(theme.rootController.navigationBar.primaryTextColor.cgColor) + context.setLineWidth(1.5) + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.75, y: 0.75, width: 5.5, height: 12.0), cornerRadius: 2.5).cgPath) + context.strokePath() + context.setBlendMode(.clear) + context.setFillColor(UIColor.clear.cgColor) + context.fill(CGRect(x: 4.0, y: 5.33, width: 3.0, height: 2.0)) + }) }) } static func lockBottomUnlockedImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatListLockBottomUnlockedImage.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/LockUnlockedBottom"), color: theme.rootController.navigationBar.primaryTextColor) + return generateImage(CGSize(width: 10.0, height: 7.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.rootController.navigationBar.primaryTextColor.cgColor) + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 7.0), cornerRadius: 1.33).cgPath) + context.fillPath() + }) }) } diff --git a/TelegramUI/SelectivePrivacySettingsController.swift b/TelegramUI/SelectivePrivacySettingsController.swift index 3a0906051f..f6b11d6d5d 100644 --- a/TelegramUI/SelectivePrivacySettingsController.swift +++ b/TelegramUI/SelectivePrivacySettingsController.swift @@ -47,13 +47,11 @@ private enum SelectivePrivacySettingsSection: Int32 { case peers } -private func stringForUserCount(_ count: Int) -> String { +private func stringForUserCount(_ count: Int, strings: PresentationStrings) -> String { if count == 0 { - return "Add Users" - } else if count == 1 { - return "1 user" + return strings.PrivacyLastSeenSettings_EmpryUsersPlaceholder } else { - return "\(count) users" + return strings.UserCount(Int32(count)) } } @@ -273,12 +271,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present switch state.setting { case .everybody: - entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor.count))) + entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor.count, strings: presentationData.strings))) case .contacts: - entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor.count))) - entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor.count))) + entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor.count, strings: presentationData.strings))) + entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor.count, strings: presentationData.strings))) case .nobody: - entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor.count))) + entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor.count, strings: presentationData.strings))) } entries.append(.peersInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_CustomShareSettingsHelp)) @@ -286,6 +284,8 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present } func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, updated: @escaping (SelectivePrivacySettings) -> Void) -> ViewController { + let strings = account.telegramApplicationContext.currentPresentationData.with { $0 }.strings + var initialEnableFor = Set() var initialDisableFor = Set() switch current { @@ -322,11 +322,11 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy let title: String switch kind { case .presence: - title = "Always Share With" + title = strings.PrivacyLastSeenSettings_AlwaysShareWith_Title case .groupInvitations: - title = "Always Allow" + title = strings.Privacy_GroupsAndChannels_AlwaysAllow_Title case .voiceCalls: - title = "Always Allow" + title = strings.Privacy_Calls_AlwaysAllow_Title } var peerIds = Set() updateState { state in @@ -342,11 +342,11 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy let title: String switch kind { case .presence: - title = "Never Share With" + title = strings.PrivacyLastSeenSettings_NeverShareWith_Title case .groupInvitations: - title = "Never Allow" + title = strings.Privacy_GroupsAndChannels_NeverAllow_Title case .voiceCalls: - title = "Never Allow" + title = strings.Privacy_Calls_NeverAllow_Title } var peerIds = Set() updateState { state in diff --git a/TelegramUI/ShareController.swift b/TelegramUI/ShareController.swift index b4913a5761..71970ac1b5 100644 --- a/TelegramUI/ShareController.swift +++ b/TelegramUI/ShareController.swift @@ -10,6 +10,12 @@ public struct ShareControllerAction { let action: () -> Void } +public enum ShareControllerPreferredAction { + case `default` + case saveToCameraRoll + case custom(action: ShareControllerAction) +} + public enum ShareControllerExternalStatus { case preparing case progress(Float) @@ -171,7 +177,7 @@ public final class ShareController: ViewController { public var dismissed: (() -> Void)? - public init(account: Account, subject: ShareControllerSubject, saveToCameraRoll: Bool = false, showInChat: ((Message) -> Void)? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false) { + public init(account: Account, subject: ShareControllerSubject, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false) { self.account = account self.externalShare = externalShare self.immediateExternalShare = immediateExternalShare @@ -191,8 +197,8 @@ public final class ShareController: ViewController { break case let .mapMedia(media): self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in - let coordinates = "\(media.latitude),\(media.longitude)" - let url = "https://maps.apple.com/maps?ll=\(coordinates)&q=\(coordinates)&t=m" + let latLong = "\(media.latitude),\(media.longitude)" + let url = "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m" UIPasteboard.general.string = url self?.controllerNode.cancel?() }) @@ -200,7 +206,7 @@ public final class ShareController: ViewController { case .quote: break case let .image(representations): - if saveToCameraRoll { + if case .saveToCameraRoll = preferredAction { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in self?.saveToCameraRoll(image: representations) }) @@ -212,13 +218,13 @@ public final class ShareController: ViewController { } else if mediaReference.media is TelegramMediaFile { canSave = true } - if saveToCameraRoll && canSave { + if case .saveToCameraRoll = preferredAction, canSave { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in self?.saveToCameraRoll(mediaReference: mediaReference) }) } case let .messages(messages): - if saveToCameraRoll { + if case .saveToCameraRoll = preferredAction { self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in self?.saveToCameraRoll(messages: messages) }) @@ -241,6 +247,10 @@ public final class ShareController: ViewController { break } + if case let .custom(action) = preferredAction { + self.defaultAction = action + } + self.peers.set(combineLatest(account.postbox.loadedPeerWithId(account.peerId) |> take(1), account.viewTracker.tailChatListView(groupId: nil, count: 150) |> take(1)) |> map { accountPeer, view -> ([Peer], Peer) in var peers: [Peer] = [] for entry in view.0.entries.reversed() { @@ -389,7 +399,7 @@ public final class ShareController: ViewController { collectableItems.append(CollectableExternalShareItem(url: "", text: "", mediaReference: mediaReference)) case let .mapMedia(media): let latLong = "\(media.latitude),\(media.longitude)" - collectableItems.append(CollectableExternalShareItem(url: "https://media: maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m", text: "", mediaReference: nil)) + collectableItems.append(CollectableExternalShareItem(url: "https://maps.apple.com/maps?ll=\(latLong)&q=\(latLong)&t=m", text: "", mediaReference: nil)) case let .messages(messages): for message in messages { var url: String? diff --git a/TelegramUI/ShareControllerNode.swift b/TelegramUI/ShareControllerNode.swift index 8f689a97f8..aee203ac3b 100644 --- a/TelegramUI/ShareControllerNode.swift +++ b/TelegramUI/ShareControllerNode.swift @@ -154,9 +154,9 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } if search && added { - strongSelf.controllerInteraction!.foundPeers.removeAll(where: { otherPeer in - return peer.id == otherPeer.id - }) + strongSelf.controllerInteraction!.foundPeers = strongSelf.controllerInteraction!.foundPeers.filter { otherPeer in + return peer.id != otherPeer.id + } strongSelf.controllerInteraction!.foundPeers.append(peer) strongSelf.peersContentNode?.updateFoundPeers() } diff --git a/TelegramUI/ShareSearchContainerNode.swift b/TelegramUI/ShareSearchContainerNode.swift index 27e011f513..326f7c1ec4 100644 --- a/TelegramUI/ShareSearchContainerNode.swift +++ b/TelegramUI/ShareSearchContainerNode.swift @@ -133,7 +133,7 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable { } func item(account: Account, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(account: account, theme: self.theme, strings: self.strings, peer: self.peer, chatPeer: nil, controllerInteraction: interfaceInteraction) + return ShareControllerPeerGridItem(account: account, theme: self.theme, strings: self.strings, peer: self.peer, chatPeer: nil, controllerInteraction: interfaceInteraction, search: true) } } diff --git a/TelegramUI/ThemeSettingsChatPreviewItem.swift b/TelegramUI/ThemeSettingsChatPreviewItem.swift index 6e4832b7fb..b6692e6559 100644 --- a/TelegramUI/ThemeSettingsChatPreviewItem.swift +++ b/TelegramUI/ThemeSettingsChatPreviewItem.swift @@ -89,7 +89,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) self.controllerInteraction = ChatControllerInteraction(openMessage: { _ in - return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in + return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in }, navigationController: { return nil }, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in diff --git a/TelegramUI/UniversalVideoCalleryItem.swift b/TelegramUI/UniversalVideoCalleryItem.swift index adc70d8de0..00f487051f 100644 --- a/TelegramUI/UniversalVideoCalleryItem.swift +++ b/TelegramUI/UniversalVideoCalleryItem.swift @@ -238,11 +238,22 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.statusButtonNode.isHidden = true } + var isAnimated = false + if let content = item.content as? NativeVideoContent { + isAnimated = content.fileReference.media.isAnimated + } else if let _ = item.content as? SystemVideoContent { + self._title.set(.single(item.strings.Message_Video)) + } + if let videoNode = self.videoNode { videoNode.canAttachContent = false videoNode.removeFromSupernode() } + if isAnimated { + self.footerContentNode.scrubberView = nil + } + let videoNode = UniversalVideoNode(postbox: item.account.postbox, audioSession: item.account.telegramApplicationContext.mediaManager.audioSession, manager: item.account.telegramApplicationContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .gallery) let videoSize = CGSize(width: item.content.dimensions.width * 2.0, height: item.content.dimensions.height * 2.0) videoNode.updateLayout(size: videoSize, transition: .immediate) @@ -314,7 +325,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if !item.hideControls { strongSelf.statusButtonNode.isHidden = !initialBuffering && (strongSelf.didPause || !isPaused || value == nil) } - if isPaused { + + if isAnimated { + strongSelf.footerContentNode.content = .info + } + else if isPaused { if strongSelf.didPause { strongSelf.footerContentNode.content = .playback(paused: true, seekable: seekable) } else { @@ -328,27 +343,17 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.zoomableContent = (videoSize, videoNode) - var isAnimated = false - var isInstagram = false - if let content = item.content as? NativeVideoContent { - isAnimated = content.fileReference.media.isAnimated - } else if let _ = item.content as? SystemVideoContent { - isInstagram = true - self._title.set(.single(item.strings.Message_Video)) - } - - if !isAnimated && !isInstagram { - //self._titleView.set(.single(self.scrubberView)) - } - if !isAnimated { let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed)) self._rightBarButtonItem.set(.single(rightBarButtonItem)) } - videoNode.playbackCompleted = { + videoNode.playbackCompleted = { [weak videoNode] in Queue.mainQueue().async { item.playbackCompleted() + if !isAnimated { + videoNode?.seek(0.0) + } } } diff --git a/TelegramUI/UserInfoController.swift b/TelegramUI/UserInfoController.swift index b11345d2e7..1bbc6ffa22 100644 --- a/TelegramUI/UserInfoController.swift +++ b/TelegramUI/UserInfoController.swift @@ -26,12 +26,13 @@ private final class UserInfoControllerArguments { let displayAboutContextMenu: (String) -> Void let openEncryptionKey: (SecretChatKeyFingerprint) -> Void let addBotToGroup: () -> Void + let shareBot: () -> Void let botSettings: () -> Void let botHelp: () -> Void let botPrivacy: () -> Void let report: () -> Void - init(account: Account, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, tapAvatarAction: @escaping () -> Void, openChat: @escaping () -> Void, addContact: @escaping () -> Void, shareContact: @escaping () -> Void, startSecretChat: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, changeNotificationSoundSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayCopyContextMenu: @escaping (UserInfoEntryTag, String) -> Void, call: @escaping () -> Void, openCallMenu: @escaping (String) -> Void, displayAboutContextMenu: @escaping (String) -> Void, openEncryptionKey: @escaping (SecretChatKeyFingerprint) -> Void, addBotToGroup: @escaping () -> Void, botSettings: @escaping () -> Void, botHelp: @escaping () -> Void, botPrivacy: @escaping () -> Void, report: @escaping () -> Void) { + init(account: Account, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, tapAvatarAction: @escaping () -> Void, openChat: @escaping () -> Void, addContact: @escaping () -> Void, shareContact: @escaping () -> Void, startSecretChat: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, changeNotificationSoundSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayCopyContextMenu: @escaping (UserInfoEntryTag, String) -> Void, call: @escaping () -> Void, openCallMenu: @escaping (String) -> Void, displayAboutContextMenu: @escaping (String) -> Void, openEncryptionKey: @escaping (SecretChatKeyFingerprint) -> Void, addBotToGroup: @escaping () -> Void, shareBot: @escaping () -> Void, botSettings: @escaping () -> Void, botHelp: @escaping () -> Void, botPrivacy: @escaping () -> Void, report: @escaping () -> Void) { self.account = account self.avatarAndNameInfoContext = avatarAndNameInfoContext self.updateEditingName = updateEditingName @@ -53,6 +54,7 @@ private final class UserInfoControllerArguments { self.displayAboutContextMenu = displayAboutContextMenu self.openEncryptionKey = openEncryptionKey self.addBotToGroup = addBotToGroup + self.shareBot = shareBot self.botSettings = botSettings self.botHelp = botHelp self.botPrivacy = botPrivacy @@ -89,6 +91,7 @@ private enum UserInfoEntry: ItemListNodeEntry { case groupsInCommon(PresentationTheme, String, Int32) case secretEncryptionKey(PresentationTheme, String, SecretChatKeyFingerprint) case botAddToGroup(PresentationTheme, String) + case botShare(PresentationTheme, String) case botSettings(PresentationTheme, String) case botHelp(PresentationTheme, String) case botPrivacy(PresentationTheme, String) @@ -99,13 +102,13 @@ private enum UserInfoEntry: ItemListNodeEntry { switch self { case .info, .about, .phoneNumber, .userName: return UserInfoSection.info.rawValue - case .sendMessage, .addContact, .shareContact, .startSecretChat: + case .sendMessage, .addContact, .shareContact, .startSecretChat, .botAddToGroup, .botShare: return UserInfoSection.actions.rawValue + case .botSettings, .botHelp, .botPrivacy: + return UserInfoSection.bot.rawValue case .sharedMedia, .notifications, .notificationSound, .secretEncryptionKey, .groupsInCommon: return UserInfoSection.sharedMediaAndNotifications.rawValue - case .botAddToGroup, .botSettings, .botHelp, .botPrivacy, .botReport: - return UserInfoSection.bot.rawValue - case .block: + case .botReport, .block: return UserInfoSection.block.rawValue } } @@ -234,6 +237,12 @@ private enum UserInfoEntry: ItemListNodeEntry { } else { return false } + case let .botShare(lhsTheme, lhsText): + if case let .botShare(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } case let .botSettings(lhsTheme, lhsText): if case let .botSettings(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -285,28 +294,30 @@ private enum UserInfoEntry: ItemListNodeEntry { return 1003 case .startSecretChat: return 1004 - case .sharedMedia: - return 1005 - case .notifications: - return 1006 - case .notificationSound: - return 1007 - case .groupsInCommon: - return 1008 - case .secretEncryptionKey: - return 1009 case .botAddToGroup: - return 1010 + return 1005 + case .botShare: + return 1006 case .botSettings: - return 1011 + return 1007 case .botHelp: - return 1012 + return 1008 case .botPrivacy: + return 1009 + case .sharedMedia: + return 1010 + case .notifications: + return 1011 + case .notificationSound: + return 1012 + case .groupsInCommon: return 1013 - case .botReport: + case .secretEncryptionKey: return 1014 - case .block: + case .botReport: return 1015 + case .block: + return 1016 } } @@ -380,6 +391,10 @@ private enum UserInfoEntry: ItemListNodeEntry { return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { arguments.addBotToGroup() }) + case let .botShare(theme, text): + return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { + arguments.shareBot() + }) case let .botSettings(theme, text): return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { arguments.botSettings() @@ -544,7 +559,13 @@ private func userInfoEntries(account: Account, presentationData: PresentationDat } if let cachedUserData = view.cachedData as? CachedUserData, let about = cachedUserData.about, !about.isEmpty { - entries.append(UserInfoEntry.about(presentationData.theme, presentationData.strings.Profile_About, about)) + let title: String + if let peer = peer as? TelegramUser, let _ = peer.botInfo { + title = presentationData.strings.Profile_BotInfo + } else { + title = presentationData.strings.Profile_About + } + entries.append(UserInfoEntry.about(presentationData.theme, title, about)) } if !isEditing { @@ -564,6 +585,26 @@ private func userInfoEntries(account: Account, presentationData: PresentationDat entries.append(UserInfoEntry.startSecretChat(presentationData.theme, presentationData.strings.UserInfo_StartSecretChat)) } } + + if let peer = peer as? TelegramUser, let botInfo = peer.botInfo { + if botInfo.flags.contains(.worksWithGroups) { + entries.append(UserInfoEntry.botAddToGroup(presentationData.theme, presentationData.strings.UserInfo_InviteBotToGroup)) + } + entries.append(UserInfoEntry.botShare(presentationData.theme, presentationData.strings.UserInfo_ShareBot)) + + if let cachedUserData = view.cachedData as? CachedUserData, let botInfo = cachedUserData.botInfo { + for command in botInfo.commands { + if command.text == "settings" { + entries.append(UserInfoEntry.botSettings(presentationData.theme, presentationData.strings.UserInfo_BotSettings)) + } else if command.text == "help" { + entries.append(UserInfoEntry.botHelp(presentationData.theme, presentationData.strings.UserInfo_BotHelp)) + } else if command.text == "privacy" { + entries.append(UserInfoEntry.botPrivacy(presentationData.theme, presentationData.strings.UserInfo_BotPrivacy)) + } + } + } + } + entries.append(UserInfoEntry.sharedMedia(presentationData.theme, presentationData.strings.GroupInfo_SharedMedia)) } let notificationsLabel: String @@ -593,23 +634,7 @@ private func userInfoEntries(account: Account, presentationData: PresentationDat entries.append(UserInfoEntry.secretEncryptionKey(presentationData.theme, presentationData.strings.Profile_EncryptionKey, keyFingerprint)) } - if let peer = peer as? TelegramUser, let botInfo = peer.botInfo { - if botInfo.flags.contains(.worksWithGroups) { - entries.append(UserInfoEntry.botAddToGroup(presentationData.theme, presentationData.strings.UserInfo_InviteBotToGroup)) - } - - if let cachedUserData = view.cachedData as? CachedUserData, let botInfo = cachedUserData.botInfo { - for command in botInfo.commands { - if command.text == "settings" { - entries.append(UserInfoEntry.botSettings(presentationData.theme, presentationData.strings.UserInfo_BotSettings)) - } else if command.text == "help" { - entries.append(UserInfoEntry.botHelp(presentationData.theme, presentationData.strings.UserInfo_BotHelp)) - } else if command.text == "privacy" { - entries.append(UserInfoEntry.botPrivacy(presentationData.theme, presentationData.strings.UserInfo_BotPrivacy)) - } - } - } - + if let peer = peer as? TelegramUser, let _ = peer.botInfo { entries.append(UserInfoEntry.botReport(presentationData.theme, presentationData.strings.ReportPeer_Report)) } @@ -651,6 +676,7 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll var shareContactImpl: (() -> Void)? var startSecretChatImpl: (() -> Void)? var botAddToGroupImpl: (() -> Void)? + var shareBotImpl: (() -> Void)? var dismissInputImpl: (() -> Void)? let actionsDisposable = DisposableSet() @@ -921,6 +947,8 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll }) }, addBotToGroup: { botAddToGroupImpl?() + }, shareBot: { + shareBotImpl?() }, botSettings: { let _ = (account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { peer in @@ -1089,19 +1117,9 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll let _ = (getUserPeer(postbox: account.postbox, peerId: peerId) |> deliverOnMainQueue).start(next: { peer in if let peer = peer as? TelegramUser, let phone = peer.phone { - let selectionController = PeerSelectionController(account: account) - selectionController.peerSelected = { [weak selectionController] peerId in - let _ = (enqueueMessages(account: account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)), replyToMessageId: nil, localGroupingKey: nil)]) |> deliverOnMainQueue).start(completed: { - if let controller = controller { - let ready = ValuePromise() - let _ = (ready.get() |> take(1) |> deliverOnMainQueue).start(next: { _ in - selectionController?.dismiss() - }) - (controller.navigationController as? NavigationController)?.replaceTopController(ChatController(account: account, chatLocation: .peer(peerId)), animated: false, ready: ready) - } - }) - } - controller?.present(selectionController, in: .window(.root)) + let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil) + let shareController = ShareController(account: account, subject: .media(.standalone(media: contact))) + controller?.present(shareController, in: .window(.root)) } }) } @@ -1158,6 +1176,15 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll dismissInputImpl?() }) } + shareBotImpl = { [weak controller] in + let _ = (getUserPeer(postbox: account.postbox, peerId: peerId) + |> deliverOnMainQueue).start(next: { peer in + if let peer = peer as? TelegramUser, let username = peer.username { + let shareController = ShareController(account: account, subject: .url("https://t.me/\(username)")) + controller?.present(shareController, in: .window(.root)) + } + }) + } avatarGalleryTransitionArguments = { [weak controller] entry in if let controller = controller { var result: ((ASDisplayNode, () -> UIView?), CGRect)?