diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index fd62f89092..70c452a8de 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -39,6 +39,8 @@ 0941A9A0210B057200EBE194 /* OpenInActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */; }; 0941A9A4210B0E2E00EBE194 /* OpenInAppIconResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */; }; 0941A9A6210B822D00EBE194 /* OpenInOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */; }; + 0947FCAE224043450086741C /* SettingsSearchRecentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0947FCAD224043450086741C /* SettingsSearchRecentItem.swift */; }; + 0947FCB0224055990086741C /* StringHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0947FCAF224055990086741C /* StringHash.swift */; }; 0952D1752176DEB500194860 /* NotificationMuteSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0952D1742176DEB500194860 /* NotificationMuteSettingsController.swift */; }; 0952D1772177FB5400194860 /* WatchPresetSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0952D1762177FB5400194860 /* WatchPresetSettings.swift */; }; 0958FBB9218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0958FBB8218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift */; }; @@ -1196,6 +1198,8 @@ 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInActionSheetController.swift; sourceTree = ""; }; 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInAppIconResources.swift; sourceTree = ""; }; 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInOptions.swift; sourceTree = ""; }; + 0947FCAD224043450086741C /* SettingsSearchRecentItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSearchRecentItem.swift; sourceTree = ""; }; + 0947FCAF224055990086741C /* StringHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringHash.swift; sourceTree = ""; }; 0952D1742176DEB500194860 /* NotificationMuteSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationMuteSettingsController.swift; sourceTree = ""; }; 0952D1762177FB5400194860 /* WatchPresetSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchPresetSettings.swift; sourceTree = ""; }; 0958FBB8218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageFeedbackItem.swift; sourceTree = ""; }; @@ -2589,6 +2593,7 @@ 09CE95072237A53900A7D2C3 /* SettingsSearchableItems.swift */, 09CE95092237B93500A7D2C3 /* SettingsSearchResultItem.swift */, 09CE95102237F3C100A7D2C3 /* SettingsSearchRecentQueries.swift */, + 0947FCAD224043450086741C /* SettingsSearchRecentItem.swift */, ); name = Search; sourceTree = ""; @@ -2763,6 +2768,7 @@ D064EF861F69A06F00AC0398 /* MessageContentKind.swift */, D0471B501EFD872F0074D609 /* CurrencyFormat.swift */, 0900678E21ED8E0E00530762 /* HexColor.swift */, + 0947FCAF224055990086741C /* StringHash.swift */, ); name = Strings; sourceTree = ""; @@ -5409,6 +5415,7 @@ D0EC6CFB1EB9F58800EBF1C3 /* ManagedAudioRecorder.swift in Sources */, D048B339203C532800038D05 /* ChatMediaInputPane.swift in Sources */, D0E817502012027900B82BBB /* ChatMessageEventLogPreviousLinkContentNode.swift in Sources */, + 0947FCAE224043450086741C /* SettingsSearchRecentItem.swift in Sources */, D0EC6CFD1EB9F58800EBF1C3 /* AudioWaveform.swift in Sources */, D0EC6CFF1EB9F58800EBF1C3 /* OverlayMediaController.swift in Sources */, D0EC6D001EB9F58800EBF1C3 /* OverlayMediaControllerNode.swift in Sources */, @@ -5841,6 +5848,7 @@ D0EC6DC21EB9F58900EBF1C3 /* ChatMediaInputStickerPackItem.swift in Sources */, 091346982183498A00846E49 /* InstantPageArticleNode.swift in Sources */, D0EC6DC31EB9F58900EBF1C3 /* ChatMediaInputStickerGridItem.swift in Sources */, + 0947FCB0224055990086741C /* StringHash.swift in Sources */, D0BE30432061B80B00FBE6D8 /* SecureIdAuthControllerNode.swift in Sources */, 099529B221D24F5800805E13 /* RadialDownloadContentNode.swift in Sources */, D0E9BAE81F0574FF00F079A4 /* STPCustomer.m in Sources */, diff --git a/TelegramUI/BotCheckoutControllerNode.swift b/TelegramUI/BotCheckoutControllerNode.swift index 28065f4896..568f94d6a5 100644 --- a/TelegramUI/BotCheckoutControllerNode.swift +++ b/TelegramUI/BotCheckoutControllerNode.swift @@ -488,7 +488,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, var text = strongSelf.presentationData.strings.Checkout_NewCard_SaveInfoEnableHelp text = text.replacingOccurrences(of: "[", with: "") text = text.replacingOccurrences(of: "]", with: "") - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_NotNow, action: { + present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_NotNow, action: { var updatedToken = webToken updatedToken.saveOnServer = false applyPaymentMethod(.webToken(updatedToken)) @@ -544,7 +544,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing let allowSaving = paymentForm.canSaveCredentials && !paymentForm.passwordMissing if canSave { - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Checkout_NewCard_SaveInfoHelp, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_NotNow, action: { + present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Checkout_NewCard_SaveInfoHelp, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_NotNow, action: { var updatedToken = token updatedToken.saveOnServer = false applyPaymentMethod(.webToken(updatedToken)) @@ -583,7 +583,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, applyPaymentMethod(.webToken(updatedToken)) if allowSaving { - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Checkout_NewCard_SaveInfoEnableHelp.replacingOccurrences(of: "]", with: "").replacingOccurrences(of: "[", with: ""), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Checkout_NewCard_SaveInfoEnableHelp.replacingOccurrences(of: "]", with: "").replacingOccurrences(of: "[", with: ""), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { })]), nil) } } @@ -874,7 +874,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, if value { strongSelf.pay(savedCredentialsToken: savedCredentialsToken, liabilityNoticeAccepted: true) } else { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Checkout_LiabilityAlertTitle, text: strongSelf.presentationData.strings.Checkout_LiabilityAlert(botPeer.displayTitle, providerPeer.displayTitle).0, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Checkout_LiabilityAlertTitle, text: strongSelf.presentationData.strings.Checkout_LiabilityAlert(botPeer.displayTitle, providerPeer.displayTitle).0, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = ApplicationSpecificNotice.setBotPaymentLiability(accountManager: strongSelf.context.sharedContext.accountManager, peerId: strongSelf.messageId.peerId).start() strongSelf.pay(savedCredentialsToken: savedCredentialsToken, liabilityNoticeAccepted: true) @@ -945,7 +945,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, text = strongSelf.presentationData.strings.Checkout_ErrorGeneric } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) } })) } @@ -976,7 +976,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, alertText = strongSelf.presentationData.strings.Checkout_SavePasswordTimeout(durationString).0 } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: alertText, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: alertText, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_No, action: { if let strongSelf = self { strongSelf.pay(savedCredentialsToken: token) diff --git a/TelegramUI/BotCheckoutInfoControllerNode.swift b/TelegramUI/BotCheckoutInfoControllerNode.swift index 0a7f6536d0..11013677fd 100644 --- a/TelegramUI/BotCheckoutInfoControllerNode.swift +++ b/TelegramUI/BotCheckoutInfoControllerNode.swift @@ -360,7 +360,7 @@ final class BotCheckoutInfoControllerNode: ViewControllerTracingNode, UIScrollVi text = strongSelf.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_OK, action: {})]), nil) } })) diff --git a/TelegramUI/CachedFaqInstantPage.swift b/TelegramUI/CachedFaqInstantPage.swift index befd263111..0275510554 100644 --- a/TelegramUI/CachedFaqInstantPage.swift +++ b/TelegramUI/CachedFaqInstantPage.swift @@ -71,7 +71,7 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt return cachedFaqInstantPage(context: context) |> map { resolvedUrl -> [SettingsSearchableItem] in var results: [SettingsSearchableItem] = [] - var nextIndex: Int = 1 + var nextIndex: Int32 = 2 if case let .instantView(webPage, _) = resolvedUrl { if case let .Loaded(content) = webPage.content, let instantPage = content.instantPage { var processingQuestions = false @@ -111,14 +111,14 @@ func faqSearchableItems(context: AccountContext) -> Signal<[SettingsSearchableIt let (_, anchor) = extractAnchor(string: url) var index = nextIndex if anchor?.contains("delete-my-account") ?? false { - index = 0 + index = 1 } else { nextIndex += 1 } let item = SettingsSearchableItem(id: .faq(index), title: text.plainText, alternate: [], icon: .faq, breadcrumbs: [strings.SettingsSearch_FAQ, currentSection], present: { context, _, present in present(.push, InstantPageController(context: context, webPage: webPage, sourcePeerType: .channel, anchor: anchor)) }) - if index == 0 { + if index == 1 { results.insert(item, at: 0) } else { results.append(item) diff --git a/TelegramUI/CachedInstantPages.swift b/TelegramUI/CachedInstantPages.swift index 36dbff0633..474950ad32 100644 --- a/TelegramUI/CachedInstantPages.swift +++ b/TelegramUI/CachedInstantPages.swift @@ -26,7 +26,7 @@ final class CachedInstantPage: PostboxCoding { func cachedInstantPage(postbox: Postbox, url: String) -> Signal { return postbox.transaction { transaction -> CachedInstantPage? in let key = ValueBoxKey(length: 8) - key.setInt64(0, value: Int64(url.hashValue)) + key.setInt64(0, value: Int64(bitPattern: url.persistentHashValue)) if let entry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedInstantPages, key: key)) as? CachedInstantPage { return entry } else { @@ -40,7 +40,7 @@ private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 5, highW func updateCachedInstantPage(postbox: Postbox, url: String, webPage: TelegramMediaWebpage?) -> Signal { return postbox.transaction { transaction -> Void in let key = ValueBoxKey(length: 8) - key.setInt64(0, value: Int64(url.hashValue)) + key.setInt64(0, value: Int64(bitPattern: url.persistentHashValue)) let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedInstantPages, key: key) if let webPage = webPage { transaction.putItemCacheEntry(id: id, entry: CachedInstantPage(webPage: webPage, timestamp: Int32(CFAbsoluteTimeGetCurrent())), collectionSpec: collectionSpec) diff --git a/TelegramUI/CallListController.swift b/TelegramUI/CallListController.swift index d54db9f038..d9b88fbcec 100644 --- a/TelegramUI/CallListController.swift +++ b/TelegramUI/CallListController.swift @@ -260,7 +260,7 @@ public final class CallListController: ViewController { if let cachedUserData = view.cachedData as? CachedUserData, cachedUserData.callsPrivate { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return } @@ -276,7 +276,7 @@ public final class CallListController: ViewController { return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId)) } |> deliverOnMainQueue).start(next: { [weak self] peer, current in if let strongSelf = self, let peer = peer, let current = current { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = strongSelf.context.sharedContext.callManager?.requestCall(account: strongSelf.context.account, peerId: peerId, endCurrentIfAny: true) began?() diff --git a/TelegramUI/ChangePhoneNumberCodeController.swift b/TelegramUI/ChangePhoneNumberCodeController.swift index 94ce216723..66b33f0646 100644 --- a/TelegramUI/ChangePhoneNumberCodeController.swift +++ b/TelegramUI/ChangePhoneNumberCodeController.swift @@ -258,7 +258,7 @@ func changePhoneNumberCodeController(context: AccountContext, phoneNumber: Strin case .limitExceeded: alertText = presentationData.strings.Login_CodeFloodError } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, completed: { updateState { return $0.withUpdatedChecking(false) diff --git a/TelegramUI/ChangePhoneNumberController.swift b/TelegramUI/ChangePhoneNumberController.swift index e71f0d3ef5..29163b3b90 100644 --- a/TelegramUI/ChangePhoneNumberController.swift +++ b/TelegramUI/ChangePhoneNumberController.swift @@ -128,7 +128,7 @@ final class ChangePhoneNumberController: ViewController { text = presentationData.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) } else { diff --git a/TelegramUI/ChangePhoneNumberIntroController.swift b/TelegramUI/ChangePhoneNumberIntroController.swift index 5412e5d104..afda7d3e01 100644 --- a/TelegramUI/ChangePhoneNumberIntroController.swift +++ b/TelegramUI/ChangePhoneNumberIntroController.swift @@ -130,7 +130,7 @@ final class ChangePhoneNumberIntroController: ViewController { } func proceed() { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: self.presentationData.theme), title: nil, text: self.presentationData.strings.PhoneNumberHelp_Alert, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in + self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.PhoneNumberHelp_Alert, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in if let strongSelf = self { (strongSelf.navigationController as? NavigationController)?.replaceTopController(ChangePhoneNumberController(context: strongSelf.context), animated: true) } diff --git a/TelegramUI/ChannelAdminsController.swift b/TelegramUI/ChannelAdminsController.swift index 94e4fe1822..426dde6c8d 100644 --- a/TelegramUI/ChannelAdminsController.swift +++ b/TelegramUI/ChannelAdminsController.swift @@ -556,7 +556,7 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa } } if !canUnban { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Members_AddAdminErrorBlacklisted, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_Members_AddAdminErrorBlacklisted, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } } diff --git a/TelegramUI/ChannelBannedMemberController.swift b/TelegramUI/ChannelBannedMemberController.swift index 16f5536520..736359ce92 100644 --- a/TelegramUI/ChannelBannedMemberController.swift +++ b/TelegramUI/ChannelBannedMemberController.swift @@ -491,7 +491,7 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI presentControllerImpl?(actionSheet, nil) }, notifyPermissionGloballyDisabled: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.GroupPermission_PermissionGloballyDisabled, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.GroupPermission_PermissionGloballyDisabled, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) var keys: [PostboxViewKey] = [.peer(peerId: peerId, components: .all), .peer(peerId: memberId, components: .all)] diff --git a/TelegramUI/ChannelBlacklistController.swift b/TelegramUI/ChannelBlacklistController.swift index 698953cb62..ee5c4bce03 100644 --- a/TelegramUI/ChannelBlacklistController.swift +++ b/TelegramUI/ChannelBlacklistController.swift @@ -296,7 +296,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId) return case let .member(_, _, adminInfo, _): if let adminInfo = adminInfo, adminInfo.promotedBy != context.account.peerId { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Members_AddBannedErrorAdmin, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_Members_AddBannedErrorAdmin, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } } diff --git a/TelegramUI/ChannelInfoController.swift b/TelegramUI/ChannelInfoController.swift index c9d1ac23e1..098d0d6da4 100644 --- a/TelegramUI/ChannelInfoController.swift +++ b/TelegramUI/ChannelInfoController.swift @@ -779,7 +779,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi pushControllerImpl?(ChannelStatsController(context: context, url: url, peerId: peerId)) }, error: { _ in let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) })) }, openAdmins: { pushControllerImpl?(channelAdminsController(context: context, peerId: peerId)) diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index 044df4ba07..9bc6e32e42 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -368,7 +368,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) -> case .restricted: text = presentationData.strings.Channel_ErrorAddBlocked } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) contactsController?.dismiss() })) diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index 003f0c3b0c..48c6be48ec 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -987,6 +987,10 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod } } + override func scrollToTop() { + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + } + @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { self.cancel?() diff --git a/TelegramUI/ChannelPermissionsController.swift b/TelegramUI/ChannelPermissionsController.swift index 9f6e6e29bd..faf698eb00 100644 --- a/TelegramUI/ChannelPermissionsController.swift +++ b/TelegramUI/ChannelPermissionsController.swift @@ -523,7 +523,7 @@ public func channelPermissionsController(context: AccountContext, peerId: PeerId return case let .member(_, _, adminInfo, _): if let adminInfo = adminInfo, adminInfo.promotedBy != context.account.peerId { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Members_AddBannedErrorAdmin, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_Members_AddBannedErrorAdmin, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } } @@ -581,7 +581,7 @@ public func channelPermissionsController(context: AccountContext, peerId: PeerId } else { text = presentationData.strings.GroupPermission_NotAvailableInPublicGroups } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) let previousParticipants = Atomic<[RenderedChannelParticipant]?>(value: nil) diff --git a/TelegramUI/ChannelVisibilityController.swift b/TelegramUI/ChannelVisibilityController.swift index c33093761d..257d8f7e78 100644 --- a/TelegramUI/ChannelVisibilityController.swift +++ b/TelegramUI/ChannelVisibilityController.swift @@ -976,7 +976,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, updateState { state in return state.withUpdatedUpdatingAddressName(false) } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }, completed: { updateState { state in @@ -995,9 +995,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, _ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in if showAlert { - let confirm = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]) - - presentControllerImpl?(confirm, nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil) } else { invokeAction() } @@ -1078,16 +1076,14 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, updateState { state in return state.withUpdatedUpdatingAddressName(false) } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) })) } _ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in if showAlert { - let confirm = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]) - - presentControllerImpl?(confirm, nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_Edit_PrivatePublicLinkAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil) } else { invokeAction() } diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 6706fde6ed..646a80b069 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -281,7 +281,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal return true } if let _ = strongSelf.presentationInterfaceState.inputTextPanelState.mediaRecordingState { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Conversation_DiscardVoiceMessageDescription, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_DiscardVoiceMessageDescription, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { self?.stopMediaRecorder() action() })]), in: .window(.root)) @@ -625,7 +625,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal case .none: break case let .alert(text): - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) case let .toast(text): let message: Signal = .single(text) let noMessage: Signal = .single(nil) @@ -658,7 +658,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }, shareCurrentLocation: { [weak self] in if let strongSelf = self { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self, let locationManager = strongSelf.context.sharedContext.locationManager { let _ = (currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0) |> deliverOnMainQueue).start(next: { coordinate in @@ -666,7 +666,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if let coordinate = coordinate { strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil)), replyToMessageId: nil, localGroupingKey: nil)]) } else { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})]), in: .window(.root)) } } }) @@ -675,7 +675,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }, shareAccountContact: { [weak self] in if let strongSelf = self { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotContactConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId) |> deliverOnMainQueue).start(next: { peer in @@ -836,7 +836,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData, cachedUserData.callsPrivate { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return } @@ -851,7 +851,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } |> deliverOnMainQueue).start(next: { peer, current in if let peer = peer, let current = current { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: true) })]), in: .window(.root)) } @@ -2261,14 +2261,10 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } if isAction && (actions.options == .deleteGlobally || actions.options == .deleteLocally) { let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: actions.options == .deleteLocally ? .forLocalPeer : .forEveryone).start() - } else if actions.options == .cancelSending { + } else if (messages.first?.flags.isSending ?? false) { let _ = deleteMessagesInteractively(postbox: strongSelf.context.account.postbox, messageIds: Array(messageIds), type: .forEveryone).start() } else { - var options = actions.options - if messages.first?.flags.isSending ?? false { - options = .cancelSending - } - strongSelf.presentDeleteMessageOptions(messageIds: messageIds, options: options) + strongSelf.presentDeleteMessageOptions(messageIds: messageIds, options: actions.options) } } } @@ -2389,7 +2385,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal case .restricted: text = strongSelf.presentationData.strings.Group_ErrorSendRestrictedMedia } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { })]), in: .window(.root)) })) } @@ -2576,7 +2572,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal 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: { + strongSelf.present(textAlertController(context: strongSelf.context, 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 { @@ -2587,7 +2583,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }) } - DeviceAccess.authorizeAccess(to: .microphone(isVideo ? .video : .audio), presentationData: strongSelf.presentationData, present: { c, a in + DeviceAccess.authorizeAccess(to: .microphone(isVideo ? .video : .audio), context: strongSelf.context, presentationData: strongSelf.presentationData, present: { c, a in self?.present(c, in: .window(.root), with: a) }, openSettings: { self?.context.sharedContext.applicationBindings.openSettings() @@ -2596,7 +2592,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal return } if isVideo { - DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in + DeviceAccess.authorizeAccess(to: .camera, context: strongSelf.context, presentationData: strongSelf.presentationData, present: { c, a in self?.present(c, in: .window(.root), with: a) }, openSettings: { self?.context.sharedContext.applicationBindings.openSettings() @@ -2857,7 +2853,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if pinImmediately { pinAction(true) } else { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Conversation_PinMessageAlertGroup, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_PinMessageAlertGroup, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_PinMessageAlert_OnlyPin, action: { pinAction(false) }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: { pinAction(true) @@ -2894,7 +2890,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } if canManagePin { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_No, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Yes, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_No, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Yes, action: { if let strongSelf = self { let disposable: MetaDisposable if let current = strongSelf.unpinMessageDisposable { @@ -3209,7 +3205,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } else { actions = [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})] } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: actions), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: actions), in: .window(.root)) } })) case let .group(groupId): @@ -3582,7 +3578,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal |> deliverOnMainQueue).start(next: { [weak self] value in if let strongSelf = self, !value { let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: strongSelf.context.sharedContext.accountManager).start() - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } }) } @@ -3624,7 +3620,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { linkPreviews = interactiveChatLinkPreviewsEnabled(accountManager: self.context.sharedContext.accountManager, displayAlert: { [weak self] f in if let strongSelf = self { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Conversation_SecretLinkPreviewAlert, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretLinkPreviewAlert, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: { f.f(true) }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_No, action: { @@ -5338,7 +5334,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } strongSelf.openResolved(.peer(peer.id, navigation)) } else { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } })) @@ -5557,7 +5553,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if displayUrl.count > maxLength { displayUrl = String(displayUrl[.. mapToSignal { [weak self] hasRecentPeers, peers, presentationData, state -> Signal<(ChatListSearchContainerRecentTransition, Bool), NoError> in + var recentItems = combineLatest(hasRecentPeers, fixedRecentlySearchedPeers, presentationDataPromise.get(), self.statePromise.get()) + |> mapToSignal { hasRecentPeers, peers, presentationData, state -> Signal<[ChatListRecentEntry], NoError> in var entries: [ChatListRecentEntry] = [] if !filter.contains(.onlyGroups) { if groupId == nil, hasRecentPeers { @@ -921,34 +921,37 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { index += 1 } } - let previousEntries = previousRecentItems.swap(entries) - - let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, filter: filter, peerSelected: { peer in - openPeer(peer, true) - let _ = addRecentlySearchedPeer(postbox: context.account.postbox, peerId: peer.id).start() - self?.recentListNode.clearHighlightAnimated(true) - }, peerLongTapped: { peer in - openRecentPeerOptions(peer) - }, clearRecentlySearchedPeers: { - self?.clearRecentSearch() - }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in - interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) - }, deletePeer: { peerId in - if let strongSelf = self { - let _ = removeRecentlySearchedPeer(postbox: strongSelf.context.account.postbox, peerId: peerId).start() - } - }) - return .single((transition, previousEntries == nil)) + + return .single(entries) } if filter.contains(.excludeRecent) { - recentItemsTransition = .single((ChatListSearchContainerRecentTransition(deletions: [], insertions: [], updates: []), true)) + recentItems = .single([]) } self.updatedRecentPeersDisposable.set(managedUpdatedRecentPeers(accountPeerId: context.account.peerId, postbox: context.account.postbox, network: context.account.network).start()) - self.recentDisposable.set((recentItemsTransition |> deliverOnMainQueue).start(next: { [weak self] (transition, firstTime) in + self.recentDisposable.set((recentItems + |> deliverOnMainQueue).start(next: { [weak self] entries in if let strongSelf = self { + let previousEntries = previousRecentItems.swap(entries) + + let firstTime = previousEntries == nil + let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, filter: filter, peerSelected: { peer in + openPeer(peer, true) + let _ = addRecentlySearchedPeer(postbox: context.account.postbox, peerId: peer.id).start() + self?.recentListNode.clearHighlightAnimated(true) + }, peerLongTapped: { peer in + openRecentPeerOptions(peer) + }, clearRecentlySearchedPeers: { + self?.clearRecentSearch() + }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in + interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) + }, deletePeer: { peerId in + if let strongSelf = self { + let _ = removeRecentlySearchedPeer(postbox: strongSelf.context.account.postbox, peerId: peerId).start() + } + }) strongSelf.enqueueRecentTransition(transition, firstTime: firstTime) } })) @@ -970,8 +973,6 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { let previousTheme = strongSelf.presentationData.theme - //let previousStrings = strongSelf.presentationData.strings - strongSelf.presentationData = presentationData strongSelf.presentationDataPromise.set(.single(ChatListPresentationData(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations))) @@ -1032,7 +1033,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { } private func enqueueRecentTransition(_ transition: ChatListSearchContainerRecentTransition, firstTime: Bool) { - enqueuedRecentTransitions.append((transition, firstTime)) + self.enqueuedRecentTransitions.append((transition, firstTime)) if self.validLayout != nil { while !self.enqueuedRecentTransitions.isEmpty { @@ -1068,16 +1069,12 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { } private func dequeueTransition() { - if let (transition, firstTime) = self.enqueuedTransitions.first { + if let (transition, _) = self.enqueuedTransitions.first { self.enqueuedTransitions.remove(at: 0) var options = ListViewDeleteAndInsertOptions() options.insert(.PreferSynchronousDrawing) options.insert(.PreferSynchronousResourceLoading) - if firstTime { - } else { - //options.insert(.AnimateAlpha) - } let displayingResults = transition.displayingResults self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in diff --git a/TelegramUI/ChatMediaInputGifPane.swift b/TelegramUI/ChatMediaInputGifPane.swift index d4db9ae58b..87c2da5694 100644 --- a/TelegramUI/ChatMediaInputGifPane.swift +++ b/TelegramUI/ChatMediaInputGifPane.swift @@ -32,6 +32,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { private let emptyNode: ImmediateTextNode private let disposable = MetaDisposable() + let trendingPromise = Promise<[FileMediaReference]?>(nil) private var validLayout: (CGSize, CGFloat, CGFloat, Bool, Bool)? private var didScrollPreviousOffset: CGFloat? @@ -124,6 +125,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { super.willEnterHierarchy() if self.multiplexedNode == nil { + self.trendingPromise.set(paneGifSearchForQuery(account: account, query: "", updateActivity: nil)) + let multiplexedNode = MultiplexedVideoNode(account: account) self.multiplexedNode = multiplexedNode if let layout = self.validLayout { diff --git a/TelegramUI/ChatMediaInputNode.swift b/TelegramUI/ChatMediaInputNode.swift index ca2be687ae..b30a19c73e 100644 --- a/TelegramUI/ChatMediaInputNode.swift +++ b/TelegramUI/ChatMediaInputNode.swift @@ -521,7 +521,7 @@ final class ChatMediaInputNode: ChatInputNode { if let current = strongSelf.searchContainerNode { searchContainerNode = current } else { - searchContainerNode = PaneSearchContainerNode(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, controllerInteraction: strongSelf.controllerInteraction, inputNodeInteraction: strongSelf.inputNodeInteraction, mode: searchMode, cancel: { + searchContainerNode = PaneSearchContainerNode(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, controllerInteraction: strongSelf.controllerInteraction, inputNodeInteraction: strongSelf.inputNodeInteraction, mode: searchMode, trendingGifsPromise: strongSelf.gifPane.trendingPromise, cancel: { self?.searchContainerNode?.deactivate() self?.inputNodeInteraction.toggleSearch(false, nil) }) @@ -1160,7 +1160,7 @@ final class ChatMediaInputNode: ChatInputNode { let panelHeight: CGFloat var isExpanded: Bool = false - if case let .media(mode, maybeExpanded) = interfaceState.inputMode, let expanded = maybeExpanded { + if case let .media(_, maybeExpanded) = interfaceState.inputMode, let expanded = maybeExpanded { isExpanded = true switch expanded { case .content: @@ -1204,7 +1204,9 @@ final class ChatMediaInputNode: ChatInputNode { } if let placeholderNode = placeholderNode { - searchContainerNode.animateIn(from: placeholderNode, transition: transition) + searchContainerNode.animateIn(from: placeholderNode, transition: transition, completion: { [weak self] in + self?.gifPane.removeFromSupernode() + }) } } } @@ -1263,8 +1265,12 @@ final class ChatMediaInputNode: ChatInputNode { switch pane { case .gifs: if self.gifPane.supernode == nil { - self.insertSubnode(self.gifPane, belowSubnode: self.collectionListContainer) - self.gifPane.frame = CGRect(origin: CGPoint(x: -width, y: 0.0), size: CGSize(width: width, height: panelHeight)) + if !displaySearch { + self.insertSubnode(self.gifPane, belowSubnode: self.collectionListContainer) + if self.searchContainerNode == nil { + self.gifPane.frame = CGRect(origin: CGPoint(x: -width, y: 0.0), size: CGSize(width: width, height: panelHeight)) + } + } } if self.gifPane.frame != paneFrame { self.gifPane.layer.removeAnimation(forKey: "position") diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index 7d675dc900..fb6cd830cd 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -81,7 +81,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { private var secretTimer: SwiftSignalKit.Timer? - var visibilityPromise = ValuePromise(.none) + var visibilityPromise = ValuePromise(.none, ignoreRepeated: true) var visibility: ListViewItemNodeVisibility = .none { didSet { if let videoNode = self.videoNode { diff --git a/TelegramUI/ChatMessageItem.swift b/TelegramUI/ChatMessageItem.swift index e7e07a9f76..4bfb3a9b9f 100644 --- a/TelegramUI/ChatMessageItem.swift +++ b/TelegramUI/ChatMessageItem.swift @@ -271,6 +271,14 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { if let forwardInfo = content.firstMessage.forwardInfo { effectiveAuthor = forwardInfo.author } + if effectiveAuthor == nil { + for attribute in content.firstMessage.attributes { + if let attribute = attribute as? SourceReferenceMessageAttribute, let sourcePeer = content.firstMessage.peers[attribute.messageId.peerId] { + effectiveAuthor = sourcePeer + break + } + } + } displayAuthorInfo = incoming && effectiveAuthor != nil } else { effectiveAuthor = content.firstMessage.author diff --git a/TelegramUI/ChatRecentActionsController.swift b/TelegramUI/ChatRecentActionsController.swift index 624abf7e4a..5deff24558 100644 --- a/TelegramUI/ChatRecentActionsController.swift +++ b/TelegramUI/ChatRecentActionsController.swift @@ -33,7 +33,7 @@ final class ChatRecentActionsController: TelegramController { self.interaction = ChatRecentActionsInteraction(displayInfoAlert: { [weak self] in if let strongSelf = self { - self?.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Channel_AdminLog_InfoPanelAlertTitle, text: strongSelf.presentationData.strings.Channel_AdminLog_InfoPanelAlertText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self?.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Channel_AdminLog_InfoPanelAlertTitle, text: strongSelf.presentationData.strings.Channel_AdminLog_InfoPanelAlertText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } }) diff --git a/TelegramUI/ComposeController.swift b/TelegramUI/ComposeController.swift index b9e6459d37..4cf4181f00 100644 --- a/TelegramUI/ComposeController.swift +++ b/TelegramUI/ComposeController.swift @@ -148,7 +148,7 @@ public class ComposeController: ViewController { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } controller.displayNavigationActivity = false - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(context: strongSelf.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) } diff --git a/TelegramUI/ConfirmPhoneNumberController.swift b/TelegramUI/ConfirmPhoneNumberController.swift index bda608a028..1a0131deb7 100644 --- a/TelegramUI/ConfirmPhoneNumberController.swift +++ b/TelegramUI/ConfirmPhoneNumberController.swift @@ -250,7 +250,7 @@ func confirmPhoneNumberCodeController(context: AccountContext, phoneNumber: Stri case .limitExceeded: alertText = presentationData.strings.Login_CodeFloodError } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, completed: { updateState { state in var state = state @@ -258,7 +258,7 @@ func confirmPhoneNumberCodeController(context: AccountContext, phoneNumber: Stri return state } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.CancelResetAccount_Success(formatPhoneNumber(phoneNumber)).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.CancelResetAccount_Success(formatPhoneNumber(phoneNumber)).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) dismissImpl?() })) } diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index d9e8db1408..af7e6eb3d6 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -1185,7 +1185,7 @@ final class ContactListNode: ASDisplayNode { |> deliverOnMainQueue).start(next: { status in switch status { case .notDetermined: - DeviceAccess.authorizeAccess(to: .contacts) + DeviceAccess.authorizeAccess(to: .contacts, context: context) case .denied, .restricted: context.sharedContext.applicationBindings.openSettings() default: diff --git a/TelegramUI/ContactMultiselectionController.swift b/TelegramUI/ContactMultiselectionController.swift index 5356b86c96..a3260812a9 100644 --- a/TelegramUI/ContactMultiselectionController.swift +++ b/TelegramUI/ContactMultiselectionController.swift @@ -224,7 +224,7 @@ class ContactMultiselectionController: ViewController { strongSelf.requestLayout(transition: ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)) if displayCountAlert { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.CreateGroup_SoftUserLimitAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.CreateGroup_SoftUserLimitAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } } diff --git a/TelegramUI/ContactsController.swift b/TelegramUI/ContactsController.swift index e26ffc7b7b..669bc6205e 100644 --- a/TelegramUI/ContactsController.swift +++ b/TelegramUI/ContactsController.swift @@ -70,6 +70,8 @@ public class ContactsController: ViewController { private var searchContentNode: NavigationBarSearchContentNode? + var switchToChatsController: (() -> Void)? + public init(context: AccountContext) { self.context = context @@ -187,12 +189,38 @@ public class ContactsController: ViewController { self.contactsNode.navigationBar = self.navigationBar - self.contactsNode.requestDeactivateSearch = { [weak self] in - self?.deactivateSearch() + let openPeer: (ContactListPeer, Bool) -> Void = { [weak self] peer, fromSearch in + if let strongSelf = self { + strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) + switch peer { + case let .peer(peer, _): + if let navigationController = strongSelf.navigationController as? NavigationController { + navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in + if fromSearch { + self?.deactivateSearch(animated: false) + self?.switchToChatsController?() + } + }) + } + case let .deviceContact(id, _): + let _ = ((strongSelf.context.sharedContext.contactDataManager?.extendedData(stableId: id) ?? .single(nil)) + |> take(1) + |> deliverOnMainQueue).start(next: { value in + guard let strongSelf = self, let value = value else { + return + } + (strongSelf.navigationController as? NavigationController)?.pushViewController(deviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, id, value))) + }) + } + } } - self.contactsNode.requestOpenPeerFromSearch = { [weak self] peer in - self?.contactsNode.contactListNode.openPeer?(peer) + self.contactsNode.requestDeactivateSearch = { [weak self] in + self?.deactivateSearch(animated: true) + } + + self.contactsNode.requestOpenPeerFromSearch = { peer in + openPeer(peer, true) } self.contactsNode.contactListNode.openPrivacyPolicy = { [weak self] in @@ -213,23 +241,8 @@ public class ContactsController: ViewController { self?.activateSearch() } - self.contactsNode.contactListNode.openPeer = { [weak self] peer in - if let strongSelf = self { - strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) - switch peer { - case let .peer(peer, _): - (strongSelf.navigationController as? NavigationController)?.pushViewController(ChatController(context: strongSelf.context, chatLocation: .peer(peer.id))) - case let .deviceContact(id, _): - let _ = ((strongSelf.context.sharedContext.contactDataManager?.extendedData(stableId: id) ?? .single(nil)) - |> take(1) - |> deliverOnMainQueue).start(next: { value in - guard let strongSelf = self, let value = value else { - return - } - (strongSelf.navigationController as? NavigationController)?.pushViewController(deviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, id, value))) - }) - } - } + self.contactsNode.contactListNode.openPeer = { peer in + openPeer(peer, false) } self.contactsNode.openInvite = { [weak self] in @@ -295,11 +308,11 @@ public class ContactsController: ViewController { } } - private func deactivateSearch() { + private func deactivateSearch(animated: Bool) { if !self.displayNavigationBar { - self.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring)) + self.setDisplayNavigationBar(true, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate) if let searchContentNode = self.searchContentNode { - self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode) + self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) } } } @@ -362,7 +375,7 @@ public class ContactsController: ViewController { DeviceAccess.authorizeAccess(to: .contacts, context: strongSelf.context) default: let presentationData = strongSelf.presentationData - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { self?.context.sharedContext.applicationBindings.openSettings() })]), in: .window(.root)) } diff --git a/TelegramUI/ContactsControllerNode.swift b/TelegramUI/ContactsControllerNode.swift index b74f3d2a5f..57cb2d8972 100644 --- a/TelegramUI/ContactsControllerNode.swift +++ b/TelegramUI/ContactsControllerNode.swift @@ -80,10 +80,10 @@ final class ContactsControllerNode: ASDisplayNode { case .allowed: strongSelf.openInvite?() case .notDetermined: - DeviceAccess.authorizeAccess(to: .contacts) + DeviceAccess.authorizeAccess(to: .contacts, context: strongSelf.context) default: let presentationData = strongSelf.presentationData - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { + present(textAlertController(context: strongSelf.context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { self?.context.sharedContext.applicationBindings.openSettings() })]), nil) } @@ -145,9 +145,9 @@ final class ContactsControllerNode: ASDisplayNode { }, placeholder: placeholderNode) } - func deactivateSearch(placeholderNode: SearchBarPlaceholderNode) { + func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) { if let searchDisplayController = self.searchDisplayController { - searchDisplayController.deactivate(placeholder: placeholderNode) + searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) self.searchDisplayController = nil } } diff --git a/TelegramUI/ConvertToSupergroupController.swift b/TelegramUI/ConvertToSupergroupController.swift index b2af2718e3..2d2d1fd440 100644 --- a/TelegramUI/ConvertToSupergroupController.swift +++ b/TelegramUI/ConvertToSupergroupController.swift @@ -130,7 +130,7 @@ public func convertToSupergroupController(context: AccountContext, peerId: PeerI let arguments = ConvertToSupergroupArguments(convert: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Group_UpgradeConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Group_UpgradeConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { var alreadyConverting = false updateState { state in if state.isConverting { diff --git a/TelegramUI/CreatePasswordController.swift b/TelegramUI/CreatePasswordController.swift index 34b24a5c2c..2a230d44d8 100644 --- a/TelegramUI/CreatePasswordController.swift +++ b/TelegramUI/CreatePasswordController.swift @@ -247,7 +247,7 @@ func createPasswordController(context: AccountContext, createPasswordContext: Cr let presentationData = context.sharedContext.currentPresentationData.with { $0 } if state.passwordText.isEmpty { } else if state.passwordText != state.passwordConfirmationText { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } else { let saveImpl: () -> Void = { var currentPassword: String? @@ -287,7 +287,7 @@ func createPasswordController(context: AccountContext, createPasswordContext: Cr } } }, error: { _ in - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) })) } @@ -304,7 +304,7 @@ func createPasswordController(context: AccountContext, createPasswordContext: Cr } if emailAlert { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_EmailSkipAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: presentationData.strings.TwoStepAuth_EmailSkip, action: { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.TwoStepAuth_EmailSkipAlert, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: presentationData.strings.TwoStepAuth_EmailSkip, action: { saveImpl() })]), nil) } else { diff --git a/TelegramUI/CreatePollController.swift b/TelegramUI/CreatePollController.swift index fe4187bfd9..146dcb85ba 100644 --- a/TelegramUI/CreatePollController.swift +++ b/TelegramUI/CreatePollController.swift @@ -347,7 +347,7 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple } if hasNonEmptyOptions || !state.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.CreatePoll_CancelConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.CreatePoll_CancelConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { dismissImpl?() })]), nil) } else { diff --git a/TelegramUI/DataPrivacySettingsController.swift b/TelegramUI/DataPrivacySettingsController.swift index c751bca6bc..6e685ae654 100644 --- a/TelegramUI/DataPrivacySettingsController.swift +++ b/TelegramUI/DataPrivacySettingsController.swift @@ -379,7 +379,7 @@ public func dataPrivacyController(context: AccountContext) -> ViewController { } if canBegin { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Privacy_ContactsResetConfirmation, actions: [TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_ContactsResetConfirmation, actions: [TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { var begin = false updateState { state in var state = state @@ -433,7 +433,7 @@ public func dataPrivacyController(context: AccountContext) -> ViewController { } if !value { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Privacy_TopPeersWarning, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_TopPeersWarning, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { apply() })])) } else { diff --git a/TelegramUI/DeclareEncodables.swift b/TelegramUI/DeclareEncodables.swift index 923adc72d3..74cbe3daa3 100644 --- a/TelegramUI/DeclareEncodables.swift +++ b/TelegramUI/DeclareEncodables.swift @@ -35,6 +35,7 @@ private var telegramUIDeclaredEncodables: Void = { declareEncodable(WebSearchSettings.self, f: { WebSearchSettings(decoder: $0) }) declareEncodable(RecentWebSearchQueryItem.self, f: { RecentWebSearchQueryItem(decoder: $0) }) declareEncodable(RecentWallpaperSearchQueryItem.self, f: { RecentWallpaperSearchQueryItem(decoder: $0) }) + declareEncodable(RecentSettingsSearchQueryItem.self, f: { RecentSettingsSearchQueryItem(decoder: $0) }) declareEncodable(VoipDerivedState.self, f: { VoipDerivedState(decoder: $0) }) return }() diff --git a/TelegramUI/DeviceContactInfoController.swift b/TelegramUI/DeviceContactInfoController.swift index 412df6c9c9..2611e3b4d8 100644 --- a/TelegramUI/DeviceContactInfoController.swift +++ b/TelegramUI/DeviceContactInfoController.swift @@ -782,7 +782,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device return (transaction.getPeer(user.id), transaction.getPeer(currentPeerId)) } |> deliverOnMainQueue).start(next: { peer, current in if let peer = peer, let current = current { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: true) })]), nil) } diff --git a/TelegramUI/GifPaneSearchContentNode.swift b/TelegramUI/GifPaneSearchContentNode.swift index 856b4fd89a..595b4c9f8e 100644 --- a/TelegramUI/GifPaneSearchContentNode.swift +++ b/TelegramUI/GifPaneSearchContentNode.swift @@ -5,7 +5,95 @@ import SwiftSignalKit import Postbox import TelegramCore -private let trendingGifsPromise = Promise<[FileMediaReference]?>(nil) +func paneGifSearchForQuery(account: Account, query: String, updateActivity: ((Bool) -> Void)?) -> Signal<[FileMediaReference]?, NoError> { + let delayRequest = true + + let contextBot = resolvePeerByName(account: account, name: "gif") + |> mapToSignal { peerId -> Signal in + if let peerId = peerId { + return account.postbox.loadedPeerWithId(peerId) + |> map { peer -> Peer? in + return peer + } + |> take(1) + } else { + return .single(nil) + } + } + |> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> in + if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder { + let results = requestContextResults(account: account, botId: user.id, query: query, peerId: account.peerId, limit: 64) + |> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in + return { _ in + return .contextRequestResult(user, results) + } + } + + let botResult: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> = .single({ previousResult in + var passthroughPreviousResult: ChatContextResultCollection? + if let previousResult = previousResult { + if case let .contextRequestResult(previousUser, previousResults) = previousResult { + if previousUser?.id == user.id { + passthroughPreviousResult = previousResults + } + } + } + return .contextRequestResult(nil, passthroughPreviousResult) + }) + + let maybeDelayedContextResults: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> + if delayRequest { + maybeDelayedContextResults = results |> delay(0.4, queue: Queue.concurrentDefaultQueue()) + } else { + maybeDelayedContextResults = results + } + + return botResult |> then(maybeDelayedContextResults) + } else { + return .single({ _ in return nil }) + } + } + return contextBot + |> mapToSignal { result -> Signal<[FileMediaReference]?, NoError> in + if let r = result(nil), case let .contextRequestResult(_, collection) = r, let results = collection?.results { + var references: [FileMediaReference] = [] + for result in results { + switch result { + case let .externalReference(_, _, type, _, _, _, content, thumbnail, _): + var imageResource: TelegramMediaResource? + var uniqueId: Int64? + if let content = content { + imageResource = content.resource + if let resource = content.resource as? WebFileReferenceMediaResource { + uniqueId = Int64(murMurHashString32(resource.url)) + } + } else if let thumbnail = thumbnail { + imageResource = thumbnail.resource + } + + if type == "gif", let thumbnailResource = imageResource, let content = content, let dimensions = content.dimensions { + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]) + references.append(FileMediaReference.standalone(media: file)) + } + case let .internalReference(_, _, _, _, _, _, file, _): + if let file = file { + references.append(FileMediaReference.standalone(media: file)) + } + } + } + return .single(references) + } else { + return .complete() + } + } + |> deliverOnMainQueue + |> beforeStarted { + updateActivity?(true) + } + |> afterCompleted { + updateActivity?(false) + } +} final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { private let context: AccountContext @@ -21,6 +109,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { private var validLayout: CGSize? + private let trendingPromise: Promise<[FileMediaReference]?> private let searchDisposable = MetaDisposable() private let _ready = Promise() @@ -31,10 +120,11 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { var deactivateSearchBar: (() -> Void)? var updateActivity: ((Bool) -> Void)? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, trendingPromise: Promise<[FileMediaReference]?>) { self.context = context self.controllerInteraction = controllerInteraction self.inputNodeInteraction = inputNodeInteraction + self.trendingPromise = trendingPromise self.theme = theme self.strings = strings @@ -53,14 +143,6 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { self.notFoundNode.isHidden = true - let _ = (trendingGifsPromise.get() - |> take(1) - |> deliverOnMainQueue).start(next: { next in - if next == nil { - trendingGifsPromise.set(self.signalForQuery("")) - } - }) - self._ready.set(.single(Void())) self.addSubnode(self.notFoundNode) @@ -75,10 +157,10 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { func updateText(_ text: String, languageCode: String?) { let signal: Signal<[FileMediaReference]?, NoError> if !text.isEmpty { - signal = self.signalForQuery(text) + signal = paneGifSearchForQuery(account: self.context.account, query: text, updateActivity: self.updateActivity) self.updateActivity?(true) } else { - signal = trendingGifsPromise.get() + signal = self.trendingPromise.get() self.updateActivity?(false) } @@ -181,95 +263,4 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { transition.updateAlpha(layer: multiplexedNode.layer, alpha: 0.0, completion: { _ in }) } - - private func signalForQuery(_ query: String) -> Signal<[FileMediaReference]?, NoError> { - let delayRequest = true - - let account = self.context.account - let contextBot = resolvePeerByName(account: account, name: "gif") - |> mapToSignal { peerId -> Signal in - if let peerId = peerId { - return account.postbox.loadedPeerWithId(peerId) - |> map { peer -> Peer? in - return peer - } - |> take(1) - } else { - return .single(nil) - } - } - |> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> in - if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder { - let results = requestContextResults(account: account, botId: user.id, query: query, peerId: self.context.account.peerId, limit: 64) - |> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in - return { _ in - return .contextRequestResult(user, results) - } - } - - let botResult: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> = .single({ previousResult in - var passthroughPreviousResult: ChatContextResultCollection? - if let previousResult = previousResult { - if case let .contextRequestResult(previousUser, previousResults) = previousResult { - if previousUser?.id == user.id { - passthroughPreviousResult = previousResults - } - } - } - return .contextRequestResult(nil, passthroughPreviousResult) - }) - - let maybeDelayedContextResults: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> - if delayRequest { - maybeDelayedContextResults = results |> delay(0.4, queue: Queue.concurrentDefaultQueue()) - } else { - maybeDelayedContextResults = results - } - - return botResult |> then(maybeDelayedContextResults) - } else { - return .single({ _ in return nil }) - } - } - return contextBot - |> mapToSignal { result -> Signal<[FileMediaReference]?, NoError> in - if let r = result(nil), case let .contextRequestResult(_, collection) = r, let results = collection?.results { - var references: [FileMediaReference] = [] - for result in results { - switch result { - case let .externalReference(_, _, type, _, _, _, content, thumbnail, _): - var imageResource: TelegramMediaResource? - var uniqueId: Int64? - if let content = content { - imageResource = content.resource - if let resource = content.resource as? WebFileReferenceMediaResource { - uniqueId = Int64(murMurHashString32(resource.url)) - } - } else if let thumbnail = thumbnail { - imageResource = thumbnail.resource - } - - if type == "gif", let thumbnailResource = imageResource, let content = content, let dimensions = content.dimensions { - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]) - references.append(FileMediaReference.standalone(media: file)) - } - case let .internalReference(_, _, _, _, _, _, file, _): - if let file = file { - references.append(FileMediaReference.standalone(media: file)) - } - } - } - return .single(references) - } else { - return .complete() - } - } - |> deliverOnMainQueue - |> beforeStarted { [weak self] in - self?.updateActivity?(true) - } - |> afterCompleted { [weak self] in - self?.updateActivity?(false) - } - } } diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 05ebba863e..d8a7eb5d68 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1501,7 +1501,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId: let result = ValuePromise() let presentationData = context.sharedContext.currentPresentationData.with { $0 } if let contactsController = contactsController { - let alertController = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.GroupInfo_AddParticipantConfirmation(peer.displayTitle).0, actions: [ + let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddParticipantConfirmation(peer.displayTitle).0, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: { result.set(false) }), @@ -1671,26 +1671,24 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId: if let contactsController = contactsController as? ContactMultiselectionController { selectAddMemberDisposable.set((contactsController.result |> deliverOnMainQueue).start(next: { [weak contactsController] peers in - - contactsController?.displayProgress = true - addMemberDisposable.set((addMembers(peers) - |> deliverOnMainQueue).start(error: { error in - if peers.count == 1, error == .restricted { - switch peers[0] { - case let .peer(peerId): - _ = (context.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { peer in - let alert = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]) - presentControllerImpl?(alert, nil) - }) - default: - break - } + contactsController?.displayProgress = true + addMemberDisposable.set((addMembers(peers) + |> deliverOnMainQueue).start(error: { error in + if peers.count == 1, error == .restricted { + switch peers[0] { + case let .peer(peerId): + _ = (context.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { peer in + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), nil) + }) + default: + break } - - contactsController?.dismiss() - },completed: { - contactsController?.dismiss() - })) + } + + contactsController?.dismiss() + },completed: { + contactsController?.dismiss() + })) })) contactsController.dismissed = { selectAddMemberDisposable.set(nil) diff --git a/TelegramUI/GroupInfoSearchItem.swift b/TelegramUI/GroupInfoSearchItem.swift index d95b068906..1847a925c9 100644 --- a/TelegramUI/GroupInfoSearchItem.swift +++ b/TelegramUI/GroupInfoSearchItem.swift @@ -54,10 +54,11 @@ final class ChannelMembersSearchItem: ItemListControllerSearch { } func titleContentNode(current: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> NavigationBarContentNode & ItemListControllerSearchNavigationContentNode { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } if let current = current as? GroupInfoSearchNavigationContentNode { + current.updateTheme(presentationData.theme) return current } else { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } return GroupInfoSearchNavigationContentNode(theme: presentationData.theme, strings: presentationData.strings, mode: self.searchMode, cancel: self.cancel, updateActivity: { [weak self] value in self?.updateActivity = value }) @@ -93,6 +94,10 @@ private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode { self.containerNode.searchTextUpdated(text: query) } + override func scrollToTop() { + self.containerNode.scrollToTop() + } + override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight))) self.containerNode.containerLayoutUpdated(layout, navigationBarHeight: 0.0, transition: transition) diff --git a/TelegramUI/GroupInfoSearchNavigationContentNode.swift b/TelegramUI/GroupInfoSearchNavigationContentNode.swift index 1c3b9c475b..de21aaa7b0 100644 --- a/TelegramUI/GroupInfoSearchNavigationContentNode.swift +++ b/TelegramUI/GroupInfoSearchNavigationContentNode.swift @@ -7,14 +7,14 @@ import TelegramCore private let searchBarFont = Font.regular(17.0) final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode { - private let theme: PresentationTheme + private var theme: PresentationTheme private let strings: PresentationStrings + private let searchMode: ChannelMembersSearchMode private let cancel: () -> Void private let searchBar: SearchBarNode - private var queryUpdated: ((String) -> Void)? var activity: Bool = false { didSet { @@ -24,18 +24,11 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item init(theme: PresentationTheme, strings: PresentationStrings, mode: ChannelMembersSearchMode, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) { self.theme = theme self.strings = strings + self.searchMode = mode self.cancel = cancel self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern) - let placeholderText: String - switch mode { - case .searchBanned: - placeholderText = strings.GroupInfo_Permissions_SearchPlaceholder - default: - placeholderText = strings.Conversation_SearchByName_Placeholder - } - self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.activeNavigationSearchBar.inputPlaceholderTextColor) super.init() @@ -53,12 +46,31 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item updateActivity({ [weak self] value in self?.activity = value }) + + self.updatePlaceholder() } func setQueryUpdated(_ f: @escaping (String) -> Void) { self.queryUpdated = f } + func updateTheme(_ theme: PresentationTheme) { + self.theme = theme + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings) + self.updatePlaceholder() + } + + func updatePlaceholder() { + let placeholderText: String + switch self.searchMode { + case .searchBanned: + placeholderText = self.strings.GroupInfo_Permissions_SearchPlaceholder + default: + placeholderText = self.strings.Conversation_SearchByName_Placeholder + } + self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: self.theme.rootController.activeNavigationSearchBar.inputPlaceholderTextColor) + } + override var nominalHeight: CGFloat { return 54.0 } diff --git a/TelegramUI/InviteContactsController.swift b/TelegramUI/InviteContactsController.swift index dcbe2c05fc..27366d4bcf 100644 --- a/TelegramUI/InviteContactsController.swift +++ b/TelegramUI/InviteContactsController.swift @@ -135,7 +135,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr if recipients.count < 100 { f() } else if let strongSelf = self { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Invite_LargeRecipientsCountWarning, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: f)]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Invite_LargeRecipientsCountWarning, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: f)]), in: .window(.root)) } } diff --git a/TelegramUI/ItemListControllerNode.swift b/TelegramUI/ItemListControllerNode.swift index 98b06bfa61..c559369276 100644 --- a/TelegramUI/ItemListControllerNode.swift +++ b/TelegramUI/ItemListControllerNode.swift @@ -595,6 +595,7 @@ class ItemListControllerNode: ASDisplayNode, UIScrollV func scrollToTop() { self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.searchNode?.scrollToTop() } func scrollViewDidScroll(_ scrollView: UIScrollView) { diff --git a/TelegramUI/ItemListControllerSearch.swift b/TelegramUI/ItemListControllerSearch.swift index 4b9f840fc3..c2f42955dc 100644 --- a/TelegramUI/ItemListControllerSearch.swift +++ b/TelegramUI/ItemListControllerSearch.swift @@ -26,6 +26,10 @@ class ItemListControllerSearchNode: ASDisplayNode { }) } + func scrollToTop() { + + } + func queryUpdated(_ query: String) { } diff --git a/TelegramUI/JoinLinkPreviewController.swift b/TelegramUI/JoinLinkPreviewController.swift index edad7a5f03..d4997d2a43 100644 --- a/TelegramUI/JoinLinkPreviewController.swift +++ b/TelegramUI/JoinLinkPreviewController.swift @@ -62,7 +62,7 @@ public final class JoinLinkPreviewController: ViewController { strongSelf.navigateToPeer(peerId) strongSelf.dismiss() case .invalidHash: - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.GroupInfo_InvitationLinkDoesNotExist, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.GroupInfo_InvitationLinkDoesNotExist, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongSelf.dismiss() } } diff --git a/TelegramUI/LanguageLinkPreviewController.swift b/TelegramUI/LanguageLinkPreviewController.swift index eb9c8c740f..6828cf5b99 100644 --- a/TelegramUI/LanguageLinkPreviewController.swift +++ b/TelegramUI/LanguageLinkPreviewController.swift @@ -63,7 +63,7 @@ public final class LanguageLinkPreviewController: ViewController { return } if result.languageCode == strongSelf.presentationData.strings.primaryComponent.languageCode { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.ApplyLanguage_ChangeLanguageAlreadyActive(result.localizedTitle).0, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ApplyLanguage_ChangeLanguageAlreadyActive(result.localizedTitle).0, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongSelf.dismiss() } else { strongSelf.localizationInfo = result @@ -73,7 +73,7 @@ public final class LanguageLinkPreviewController: ViewController { guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.ApplyLanguage_LanguageNotSupportedError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ApplyLanguage_LanguageNotSupportedError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongSelf.dismiss() })) self.ready.set(self.controllerNode.ready.get()) @@ -115,7 +115,7 @@ public final class LanguageLinkPreviewController: ViewController { return } strongSelf.controllerNode.setInProgress(false) - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, completed: { [weak self] in guard let strongSelf = self else { return diff --git a/TelegramUI/LegacyMediaPickers.swift b/TelegramUI/LegacyMediaPickers.swift index 37b94f4dca..8faf5b13e8 100644 --- a/TelegramUI/LegacyMediaPickers.swift +++ b/TelegramUI/LegacyMediaPickers.swift @@ -34,7 +34,7 @@ func legacyAssetPicker(context: AccountContext, presentationData: PresentationDa return Signal { subscriber in let intent = fileMode ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent - DeviceAccess.authorizeAccess(to: .mediaLibrary(.send), presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .mediaLibrary(.send), context: context, presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if !value { subscriber.putError(Void()) return diff --git a/TelegramUI/LegacyWallpaperEditor.swift b/TelegramUI/LegacyWallpaperEditor.swift index 67b1a952fe..541911946e 100644 --- a/TelegramUI/LegacyWallpaperEditor.swift +++ b/TelegramUI/LegacyWallpaperEditor.swift @@ -52,7 +52,7 @@ func legacyWallpaperPicker(context: AccountContext, presentationData: Presentati return Signal { subscriber in let intent = TGMediaAssetsControllerSetCustomWallpaperIntent - DeviceAccess.authorizeAccess(to: .mediaLibrary(.wallpaper), presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .mediaLibrary(.wallpaper), context: context, presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if !value { subscriber.putError(Void()) return diff --git a/TelegramUI/LogoutOptionsController.swift b/TelegramUI/LogoutOptionsController.swift index e4104397ec..33f79fe543 100644 --- a/TelegramUI/LogoutOptionsController.swift +++ b/TelegramUI/LogoutOptionsController.swift @@ -166,7 +166,7 @@ func logoutOptionsController(context: AccountContext, navigationController: Navi }) } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [ + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { openFaq(resolvedUrlPromise) dismissImpl?() @@ -184,7 +184,7 @@ func logoutOptionsController(context: AccountContext, navigationController: Navi ]), nil) }, logout: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let alertController = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Settings_LogoutConfirmationTitle, text: presentationData.strings.Settings_LogoutConfirmationText, actions: [ + let alertController = textAlertController(context: context, title: presentationData.strings.Settings_LogoutConfirmationTitle, text: presentationData.strings.Settings_LogoutConfirmationText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { diff --git a/TelegramUI/MediaPlayerTimeTextNode.swift b/TelegramUI/MediaPlayerTimeTextNode.swift index 87ac345c32..d88db91dc3 100644 --- a/TelegramUI/MediaPlayerTimeTextNode.swift +++ b/TelegramUI/MediaPlayerTimeTextNode.swift @@ -54,7 +54,11 @@ private final class MediaPlayerTimeTextNodeParameters: NSObject { final class MediaPlayerTimeTextNode: ASDisplayNode { var alignment: NSTextAlignment = .left var mode: MediaPlayerTimeTextNodeMode = .normal - private let textColor: UIColor + var textColor: UIColor { + didSet { + self.updateTimestamp() + } + } var defaultDuration: Double? { didSet { self.updateTimestamp() diff --git a/TelegramUI/OpenAddContact.swift b/TelegramUI/OpenAddContact.swift index 30d9deb8cf..c78fe81110 100644 --- a/TelegramUI/OpenAddContact.swift +++ b/TelegramUI/OpenAddContact.swift @@ -20,10 +20,10 @@ func openAddContact(context: AccountContext, firstName: String = "", lastName: S } }), completed: completed), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) case .notDetermined: - DeviceAccess.authorizeAccess(to: .contacts) + DeviceAccess.authorizeAccess(to: .contacts, context: context) default: let presentationData = context.sharedContext.currentPresentationData.with { $0 } - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { + present(textAlertController(context: context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { context.sharedContext.applicationBindings.openSettings() })]), nil) } diff --git a/TelegramUI/OpenResolvedUrl.swift b/TelegramUI/OpenResolvedUrl.swift index 29c9c381cb..ff8a7a0ca6 100644 --- a/TelegramUI/OpenResolvedUrl.swift +++ b/TelegramUI/OpenResolvedUrl.swift @@ -29,8 +29,7 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlCon if let peerId = peerId { openPeer(peerId, defaultNavigationForPeerId(peerId, navigation: navigation)) } else { - - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + present(textAlertController(context: context, title: nil, text: presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } case let .botStart(peerId, payload): openPeer(peerId, .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive))) @@ -109,7 +108,7 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlCon }) if !found { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.AuthCode_Alert(formattedConfirmationCode(code)).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + present(textAlertController(context: context, title: nil, text: presentationData.strings.AuthCode_Alert(formattedConfirmationCode(code)).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } } case let .cancelAccountReset(phone, hash): @@ -131,7 +130,7 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlCon text = presentationData.strings.Login_UnknownError } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) dismissInput() case let .share(url, text, to): diff --git a/TelegramUI/OverlayPlayerControllerNode.swift b/TelegramUI/OverlayPlayerControllerNode.swift index 5e717259fd..1bfaa87a79 100644 --- a/TelegramUI/OverlayPlayerControllerNode.swift +++ b/TelegramUI/OverlayPlayerControllerNode.swift @@ -10,7 +10,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec private let context: AccountContext private let peerId: PeerId - private let presentationData: PresentationData + private var presentationData: PresentationData private let type: MediaManagerPlayerType private let requestDismiss: () -> Void private let requestShare: (MessageId) -> Void @@ -31,6 +31,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec private var validLayout: ContainerViewLayout? + private var presentationDataDisposable: Disposable? private let replacementHistoryNodeReadyDisposable = MetaDisposable() init(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, requestDismiss: @escaping () -> Void, requestShare: @escaping (MessageId) -> Void) { @@ -170,12 +171,21 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec return false } + self.presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + if strongSelf.presentationData.theme !== presentationData.theme || strongSelf.presentationData.strings !== presentationData.strings { + strongSelf.updatePresentationData(presentationData) + } + } + }) + self.ready.set(self.historyNode.historyState.get() |> map { _ -> Bool in return true } |> take(1)) } deinit { + self.presentationDataDisposable?.dispose() self.replacementHistoryNodeReadyDisposable.dispose() } @@ -191,6 +201,13 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec self.view.addGestureRecognizer(panRecognizer) } + func updatePresentationData(_ presentationData: PresentationData) { + self.presentationData = presentationData + + self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor + self.controlsNode.updateTheme(self.presentationData.theme) + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { self.validLayout = layout diff --git a/TelegramUI/OverlayPlayerControlsNode.swift b/TelegramUI/OverlayPlayerControlsNode.swift index 20f6df8a97..1ab8a73d55 100644 --- a/TelegramUI/OverlayPlayerControlsNode.swift +++ b/TelegramUI/OverlayPlayerControlsNode.swift @@ -53,7 +53,7 @@ private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, them final class OverlayPlayerControlsNode: ASDisplayNode { private let accountManager: AccountManager private let postbox: Postbox - private let theme: PresentationTheme + private var theme: PresentationTheme private let backgroundNode: ASImageNode @@ -234,11 +234,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if strongSelf.currentIsPaused != isPaused { strongSelf.currentIsPaused = isPaused - if isPaused { - strongSelf.playPauseButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Play"), color: strongSelf.theme.list.itemPrimaryTextColor) - } else { - strongSelf.playPauseButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Pause"), color: strongSelf.theme.list.itemPrimaryTextColor) - } + strongSelf.updatePlayPauseButton(paused: isPaused) } strongSelf.playPauseButton.isEnabled = true @@ -247,30 +243,15 @@ final class OverlayPlayerControlsNode: ASDisplayNode { displayData = value.item.displayData - let baseColor = strongSelf.theme.list.itemSecondaryTextColor + let baseColor = strongSelf.theme.list.itemSecondaryTextColor if value.order != strongSelf.currentOrder { strongSelf.updateOrder?(value.order) strongSelf.currentOrder = value.order - switch value.order { - case .regular: - strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: baseColor) - case .reversed: - strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: strongSelf.theme.list.itemAccentColor) - case .random: - strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderRandom"), color: strongSelf.theme.list.itemAccentColor) - } + strongSelf.updateOrderButton(value.order) } if value.looping != strongSelf.currentLooping { strongSelf.currentLooping = value.looping - - switch value.looping { - case .none: - strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: baseColor) - case .item: - strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/RepeatOne"), color: strongSelf.theme.list.itemAccentColor) - case .all: - strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: strongSelf.theme.list.itemAccentColor) - } + strongSelf.updateLoopButton(value.looping) } } else { strongSelf.playPauseButton.isEnabled = false @@ -326,6 +307,32 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.albumArtNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.albumArtTap(_:)))) } + func updateTheme(_ theme: PresentationTheme) { + guard self.theme !== theme else { + return + } + self.theme = theme + + self.backgroundNode.image = generateBackground(theme: theme) + self.collapseNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/CollapseArrow"), color: theme.list.controlSecondaryColor), for: []) + self.shareNode.setImage(generateShareIcon(theme: theme), for: []) + self.scrubberNode.updateColors(backgroundColor: theme.list.controlSecondaryColor, foregroundColor: theme.list.itemAccentColor) + self.leftDurationLabel.textColor = theme.list.itemSecondaryTextColor + self.rightDurationLabel.textColor = theme.list.itemSecondaryTextColor + self.backwardButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Previous"), color: theme.list.itemPrimaryTextColor) + self.forwardButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Next"), color: theme.list.itemPrimaryTextColor) + if let isPaused = self.currentIsPaused { + self.updatePlayPauseButton(paused: isPaused) + } + if let order = self.currentOrder { + self.updateOrderButton(order) + } + if let looping = self.currentLooping { + self.updateLoopButton(looping) + } + self.separatorNode.backgroundColor = theme.list.itemPlainSeparatorColor + } + private func updateLabels(transition: ContainedViewLayoutTransition) { guard let (width, leftInset, rightInset, maxHeight) = self.validLayout else { return @@ -371,6 +378,38 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } } + private func updatePlayPauseButton(paused: Bool) { + if paused { + self.playPauseButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Pause"), color: self.theme.list.itemPrimaryTextColor) + } else { + self.playPauseButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Play"), color: self.theme.list.itemPrimaryTextColor) + } + } + + private func updateOrderButton(_ order: MusicPlaybackSettingsOrder) { + let baseColor = self.theme.list.itemSecondaryTextColor + switch order { + case .regular: + self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: baseColor) + case .reversed: + self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: self.theme.list.itemAccentColor) + case .random: + self.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderRandom"), color: self.theme.list.itemAccentColor) + } + } + + private func updateLoopButton(_ looping: MusicPlaybackSettingsLooping) { + let baseColor = self.theme.list.itemSecondaryTextColor + switch looping { + case .none: + self.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: baseColor) + case .item: + self.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/RepeatOne"), color: self.theme.list.itemAccentColor) + case .all: + self.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: self.theme.list.itemAccentColor) + } + } + static let basePanelHeight: CGFloat = 220.0 static func heightForLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, isExpanded: Bool) -> CGFloat { diff --git a/TelegramUI/PaneSearchBarNode.swift b/TelegramUI/PaneSearchBarNode.swift index 54ae54e2f4..914b81088f 100644 --- a/TelegramUI/PaneSearchBarNode.swift +++ b/TelegramUI/PaneSearchBarNode.swift @@ -321,22 +321,40 @@ class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate { self.textField.becomeFirstResponder() } - func animateIn(from node: PaneSearchBarPlaceholderNode, duration: Double, timingFunction: String) { + func animateIn(from node: PaneSearchBarPlaceholderNode, duration: Double, timingFunction: String, completion: @escaping () -> Void) { let initialTextBackgroundFrame = node.view.convert(node.backgroundNode.frame, to: self.view) + var backgroundCompleted = false + var separatorCompleted = false + var textBackgroundCompleted = false + let intermediateCompletion: () -> Void = { + if backgroundCompleted && separatorCompleted && textBackgroundCompleted { + completion() + } + } + let initialBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.bounds.size.width, height: max(0.0, initialTextBackgroundFrame.maxY + 8.0))) if let fromBackgroundColor = node.backgroundColor, let toBackgroundColor = self.backgroundNode.backgroundColor { self.backgroundNode.layer.animate(from: fromBackgroundColor.cgColor, to: toBackgroundColor.cgColor, keyPath: "backgroundColor", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration * 0.7) } else { self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) } - self.backgroundNode.layer.animateFrame(from: initialBackgroundFrame, to: self.backgroundNode.frame, duration: duration, timingFunction: timingFunction) + self.backgroundNode.layer.animateFrame(from: initialBackgroundFrame, to: self.backgroundNode.frame, duration: duration, timingFunction: timingFunction, completion: { _ in + backgroundCompleted = true + intermediateCompletion() + }) let initialSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: max(0.0, initialTextBackgroundFrame.maxY + 8.0)), size: CGSize(width: self.bounds.size.width, height: UIScreenPixel)) self.separatorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) - self.separatorNode.layer.animateFrame(from: initialSeparatorFrame, to: self.separatorNode.frame, duration: duration, timingFunction: timingFunction) + self.separatorNode.layer.animateFrame(from: initialSeparatorFrame, to: self.separatorNode.frame, duration: duration, timingFunction: timingFunction, completion: { _ in + separatorCompleted = true + intermediateCompletion() + }) - self.textBackgroundNode.layer.animateFrame(from: initialTextBackgroundFrame, to: self.textBackgroundNode.frame, duration: duration, timingFunction: timingFunction) + self.textBackgroundNode.layer.animateFrame(from: initialTextBackgroundFrame, to: self.textBackgroundNode.frame, duration: duration, timingFunction: timingFunction, completion: { _ in + textBackgroundCompleted = true + intermediateCompletion() + }) let labelFrame = self.textField.placeholderLabel.frame let initialLabelNodeFrame = CGRect(origin: node.labelNode.view.convert(node.labelNode.bounds, to: self.textField.superview).origin, size: labelFrame.size) diff --git a/TelegramUI/PaneSearchContainerNode.swift b/TelegramUI/PaneSearchContainerNode.swift index 65706911f1..d3997c8c6d 100644 --- a/TelegramUI/PaneSearchContainerNode.swift +++ b/TelegramUI/PaneSearchContainerNode.swift @@ -39,14 +39,14 @@ final class PaneSearchContainerNode: ASDisplayNode { return self.contentNode.ready } - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, mode: ChatMediaInputSearchMode, cancel: @escaping () -> Void) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, mode: ChatMediaInputSearchMode, trendingGifsPromise: Promise<[FileMediaReference]?>, cancel: @escaping () -> Void) { self.context = context self.mode = mode self.controllerInteraction = controllerInteraction self.inputNodeInteraction = inputNodeInteraction switch mode { case .gif: - self.contentNode = GifPaneSearchContentNode(context: context, theme: theme, strings: strings, controllerInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction) + self.contentNode = GifPaneSearchContentNode(context: context, theme: theme, strings: strings, controllerInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction, trendingPromise: trendingGifsPromise) case .sticker: self.contentNode = StickerPaneSearchContentNode(context: context, theme: theme, strings: strings, controllerInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction) } @@ -117,7 +117,7 @@ final class PaneSearchContainerNode: ASDisplayNode { self.searchBar.deactivate(clear: true) } - func animateIn(from placeholder: PaneSearchBarPlaceholderNode, transition: ContainedViewLayoutTransition) { + func animateIn(from placeholder: PaneSearchBarPlaceholderNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { let placeholderFrame = placeholder.view.convert(placeholder.bounds, to: self.view) let verticalOrigin = placeholderFrame.minY - 4.0 self.contentNode.animateIn(additivePosition: verticalOrigin, transition: transition) @@ -125,7 +125,7 @@ final class PaneSearchContainerNode: ASDisplayNode { switch transition { case let .animated(duration, curve): self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration / 2.0) - self.searchBar.animateIn(from: placeholder, duration: duration, timingFunction: curve.timingFunction) + self.searchBar.animateIn(from: placeholder, duration: duration, timingFunction: curve.timingFunction, completion: completion) if let size = self.validLayout { let initialBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: verticalOrigin), size: CGSize(width: size.width, height: max(0.0, size.height - verticalOrigin))) self.backgroundNode.layer.animateFrame(from: initialBackgroundFrame, to: self.backgroundNode.frame, duration: duration, timingFunction: curve.timingFunction) @@ -143,9 +143,7 @@ final class PaneSearchContainerNode: ASDisplayNode { self.backgroundNode.layer.animateFrame(from: self.backgroundNode.frame, to: CGRect(origin: CGPoint(x: 0.0, y: verticalOrigin), size: CGSize(width: size.width, height: max(0.0, size.height - verticalOrigin))), duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: false) } } - self.searchBar.transitionOut(to: placeholder, transition: transition, completion: { - completion() - }) + self.searchBar.transitionOut(to: placeholder, transition: transition, completion: completion) transition.updateAlpha(node: self.backgroundNode, alpha: 0.0) if animateOutSearchBar { transition.updateAlpha(node: self.searchBar, alpha: 0.0) diff --git a/TelegramUI/PeerReportController.swift b/TelegramUI/PeerReportController.swift index 7e681ae09f..46f02ec74f 100644 --- a/TelegramUI/PeerReportController.swift +++ b/TelegramUI/PeerReportController.swift @@ -70,18 +70,12 @@ func peerReportOptionsController(context: AccountContext, subject: PeerReportSub case let .peer(peerId): let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason) |> deliverOnMainQueue).start(completed: { - let alert = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: { - - })]) - present(alert, nil) + present(textAlertController(context: context, title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) case let .messages(messageIds): let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason) |> deliverOnMainQueue).start(completed: { - let alert = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: { - - })]) - present(alert, nil) + present(textAlertController(context: context, title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) } } else { @@ -229,21 +223,17 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu if !text.isEmpty { let completed: () -> Void = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - - let alert = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: { - - })]) - presentControllerImpl?(alert, nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) dismissImpl?() } switch subject { - case let .peer(peerId): - reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: .custom(text)) + case let .peer(peerId): + reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: .custom(text)) |> deliverOnMainQueue).start(completed: { completed() })) - case let .messages(messageIds): - reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: .custom(text)) + case let .messages(messageIds): + reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: .custom(text)) |> deliverOnMainQueue).start(completed: { completed() })) diff --git a/TelegramUI/PrivacyAndSecurityController.swift b/TelegramUI/PrivacyAndSecurityController.swift index f751c3e812..dfaf0d62e6 100644 --- a/TelegramUI/PrivacyAndSecurityController.swift +++ b/TelegramUI/PrivacyAndSecurityController.swift @@ -284,7 +284,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { case let .accountTimeout(theme, text, value): return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: { arguments.setupAccountAutoremove() - }) + }, tag: PrivacyAndSecurityEntryTag.accountTimeout) case let .accountInfo(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) case let .dataSettings(theme, text): @@ -383,7 +383,7 @@ private func privacyAndSecurityControllerEntries(presentationData: PresentationD return entries } -public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil) -> ViewController { +public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil) -> ViewController { let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true) let stateValue = Atomic(value: PrivacyAndSecurityControllerState()) let updateState: ((PrivacyAndSecurityControllerState) -> PrivacyAndSecurityControllerState) -> Void = { f in @@ -630,7 +630,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.PrivacySettings_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) - let listState = ItemListNodeState(entries: privacyAndSecurityControllerEntries(presentationData: presentationData, state: state, privacySettings: privacySettings), style: .blocks, animateChanges: false) + let listState = ItemListNodeState(entries: privacyAndSecurityControllerEntries(presentationData: presentationData, state: state, privacySettings: privacySettings), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) return (controllerState, (listState, arguments)) } diff --git a/TelegramUI/ResetPasswordController.swift b/TelegramUI/ResetPasswordController.swift index 42f12b1e99..b512f892bc 100644 --- a/TelegramUI/ResetPasswordController.swift +++ b/TelegramUI/ResetPasswordController.swift @@ -129,7 +129,7 @@ func resetPasswordController(context: AccountContext, emailPattern: String, comp } }, openHelp: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) var initialFocusImpl: (() -> Void)? @@ -176,7 +176,7 @@ func resetPasswordController(context: AccountContext, emailPattern: String, comp text = presentationData.strings.Login_UnknownError } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }, completed: { completion() })) diff --git a/TelegramUI/SaveToCameraRoll.swift b/TelegramUI/SaveToCameraRoll.swift index 645de7b7fc..dba53ccd01 100644 --- a/TelegramUI/SaveToCameraRoll.swift +++ b/TelegramUI/SaveToCameraRoll.swift @@ -84,7 +84,7 @@ func saveToCameraRoll(context: AccountContext, postbox: Postbox, mediaReference: case let .data(data): if data.complete { return Signal { subscriber in - DeviceAccess.authorizeAccess(to: .mediaLibrary(.save), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in + DeviceAccess.authorizeAccess(to: .mediaLibrary(.save), context: context, presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in context.sharedContext.presentGlobalController(c, a) }, openSettings: context.sharedContext.applicationBindings.openSettings, { authorized in if !authorized { diff --git a/TelegramUI/SecureIdAuthController.swift b/TelegramUI/SecureIdAuthController.swift index 1314f20b29..eddca2b894 100644 --- a/TelegramUI/SecureIdAuthController.swift +++ b/TelegramUI/SecureIdAuthController.swift @@ -183,12 +183,12 @@ final class SecureIdAuthController: ViewController { if appUpdateRequired { let errorText = strongSelf.presentationData.strings.Passport_UpdateRequiredError - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Application_Update, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Application_Update, action: { context.sharedContext.applicationBindings.openAppStorePage() })]), in: .window(.root)) } else if let callbackUrl = callbackUrl, let peerId = peerId { let errorText = strongSelf.presentationData.strings.Login_UnknownError - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let error = passError { strongSelf.openUrl(secureIdCallbackUrl(with: callbackUrl, peerId: peerId, result: .error, parameters: ["error": error])) } @@ -461,7 +461,7 @@ final class SecureIdAuthController: ViewController { case .secretPasswordMismatch: errorText = strongSelf.presentationData.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) if let verificationState = strongSelf.state.verificationState, case let .passwordChallenge(hint, .checking, hasRecoveryEmail) = verificationState { strongSelf.updateState(animated: !inBackground, { state in @@ -487,7 +487,7 @@ final class SecureIdAuthController: ViewController { } if passwordChallenge.hasRecoveryEmail { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: self.presentationData.theme), title: self.presentationData.strings.Passport_ForgottenPassword, text: self.presentationData.strings.Passport_PasswordReset, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_ResetAccountProtected_Reset, action: { [weak self] in + self.present(textAlertController(context: self.context, title: self.presentationData.strings.Passport_ForgottenPassword, text: self.presentationData.strings.Passport_PasswordReset, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_ResetAccountProtected_Reset, action: { [weak self] in guard let strongSelf = self else { return } @@ -517,7 +517,7 @@ final class SecureIdAuthController: ViewController { })) })]), in: .window(.root)) } else { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: self.presentationData.theme), title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } @@ -633,7 +633,7 @@ final class SecureIdAuthController: ViewController { } @objc private func infoPressed() { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: self.presentationData.theme), title: self.presentationData.strings.Passport_InfoTitle, text: self.presentationData.strings.Passport_InfoText.replacingOccurrences(of: "**", with: ""), actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Passport_InfoLearnMore, action: { [weak self] in + self.present(textAlertController(context: self.context, title: self.presentationData.strings.Passport_InfoTitle, text: self.presentationData.strings.Passport_InfoText.replacingOccurrences(of: "**", with: ""), actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Passport_InfoLearnMore, action: { [weak self] in guard let strongSelf = self else { return } diff --git a/TelegramUI/SecureIdDocumentFormController.swift b/TelegramUI/SecureIdDocumentFormController.swift index 0e034c0418..fdd2422ed6 100644 --- a/TelegramUI/SecureIdDocumentFormController.swift +++ b/TelegramUI/SecureIdDocumentFormController.swift @@ -93,7 +93,7 @@ final class SecureIdDocumentFormController: FormController() resolvedUrlPromise.set(resolvedUrl) - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [ + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { - openFaq(resolvedUrlPromise) - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).start(next: { peerId in - if let peerId = peerId { - pushControllerImpl?(ChatController(context: context, chatLocation: .peer(peerId))) - } - })) - }) - ]), nil) + openFaq(resolvedUrlPromise) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).start(next: { peerId in + if let peerId = peerId { + pushControllerImpl?(ChatController(context: context, chatLocation: .peer(peerId))) + } + })) + })]), nil) }) }, openFaq: { let resolvedUrlPromise = Promise() @@ -1012,14 +1010,17 @@ public func settingsController(context: AccountContext, accountManager: AccountM ) } - let notifyExceptions = Promise(nil) + let notifyExceptions = Promise(NotificationExceptionsList(peers: [:], settings: [:])) let updateNotifyExceptions: () -> Void = { notifyExceptions.set( contextValue.get() |> take(1) |> mapToSignal { context -> Signal in - return notificationExceptionsList(network: context.account.network) - |> map(Optional.init) + return .single(NotificationExceptionsList(peers: [:], settings: [:])) + |> then( + notificationExceptionsList(network: context.account.network) + |> map(Optional.init) + ) } ) } @@ -1067,12 +1068,17 @@ public func settingsController(context: AccountContext, accountManager: AccountM } let searchItem = SettingsSearchItem(context: context, theme: presentationData.theme, placeholder: presentationData.strings.Common_Search, activated: state.isSearching, updateActivated: { value in - setDisplayNavigationBarImpl?(!value) + if !value { + setDisplayNavigationBarImpl?(true) + } updateState { state in var state = state state.isSearching = value return state } + if value { + setDisplayNavigationBarImpl?(false) + } }, presentController: { v, a in dismissInputImpl?() presentControllerImpl?(v, a) diff --git a/TelegramUI/SettingsSearchItem.swift b/TelegramUI/SettingsSearchItem.swift index fa40ec9e20..76cc75807a 100644 --- a/TelegramUI/SettingsSearchItem.swift +++ b/TelegramUI/SettingsSearchItem.swift @@ -144,24 +144,34 @@ final class SettingsSearchItem: ItemListControllerSearch { } } -private enum SettingsSearchItemId: Hashable { +final class SettingsSearchInteraction { + let openItem: (SettingsSearchableItem) -> Void + let deleteRecentItem: (SettingsSearchableItemId) -> Void + + init(openItem: @escaping (SettingsSearchableItem) -> Void, deleteRecentItem: @escaping (SettingsSearchableItemId) -> Void) { + self.openItem = openItem + self.deleteRecentItem = deleteRecentItem + } +} + +private enum SettingsSearchEntryStableId: Hashable { case result(SettingsSearchableItemId) case faq(String) } private enum SettingsSearchEntry: Comparable, Identifiable { - case result(index: Int, item: SettingsSearchableItem, title: String, breadcrumbs: [String], icon: UIImage?) + case result(index: Int, item: SettingsSearchableItem, icon: UIImage?) - var stableId: SettingsSearchItemId { + var stableId: SettingsSearchEntryStableId { switch self { - case let .result(_, item, _, _, _): + case let .result(_, item, _): return .result(item.id) } } private func index() -> Int { switch self { - case let .result(index, _, _, _, _): + case let .result(index, _, _): return index } } @@ -171,20 +181,18 @@ private enum SettingsSearchEntry: Comparable, Identifiable { } static func == (lhs: SettingsSearchEntry, rhs: SettingsSearchEntry) -> Bool { - if case let .result(lhsIndex, _, lhsTitle, lhsBreadcrumbs, _) = lhs { - if case let .result(rhsIndex, _, rhsTitle, rhsBreadcrumbs, _) = rhs, lhsIndex == rhsIndex, lhsTitle == rhsTitle, lhsBreadcrumbs == rhsBreadcrumbs { + if case let .result(lhsIndex, lhsItem, _) = lhs { + if case let .result(rhsIndex, rhsItem, _) = rhs, lhsIndex == rhsIndex, lhsItem.id == rhsItem.id { return true } } return false } - func item(theme: PresentationTheme, strings: PresentationStrings, openResult: @escaping (SettingsSearchableItem) -> Void) -> ListViewItem { + func item(theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction) -> ListViewItem { switch self { - case let .result(_, item, title, breadcrumbs, icon): - return SettingsSearchResultItem(theme: theme, strings: strings, title: title, breadcrumbs: breadcrumbs, icon: icon, action: { - openResult(item) - }, sectionId: 0) + case let .result(_, item, icon): + return SettingsSearchResultItem(theme: theme, strings: strings, item: item, icon: icon, interaction: interaction, sectionId: 0) } } } @@ -196,50 +204,146 @@ private struct SettingsSearchContainerTransition { let isSearching: Bool } -private func preparedSettingsSearchContainerTransition(theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [SettingsSearchEntry], to toEntries: [SettingsSearchEntry], openResult: @escaping (SettingsSearchableItem) -> Void, isSearching: Bool, forceUpdate: Bool) -> SettingsSearchContainerTransition { +private func preparedSettingsSearchContainerTransition(theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [SettingsSearchEntry], to toEntries: [SettingsSearchEntry], interaction: SettingsSearchInteraction, isSearching: Bool, forceUpdate: Bool) -> SettingsSearchContainerTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdate) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(theme: theme, strings: strings, openResult: openResult), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(theme: theme, strings: strings, openResult: openResult), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(theme: theme, strings: strings, interaction: interaction), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(theme: theme, strings: strings, interaction: interaction), directionHint: nil) } return SettingsSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching) } +private enum SettingsSearchRecentEntryStableId: Hashable { + case recent(SettingsSearchableItemId) + + static func ==(lhs: SettingsSearchRecentEntryStableId, rhs: SettingsSearchRecentEntryStableId) -> Bool { + switch lhs { + case let .recent(id): + if case .recent(id) = rhs { + return true + } else { + return false + } + } + } + + var hashValue: Int { + switch self { + case let .recent(id): + return id.hashValue + } + } +} + +private enum SettingsSearchRecentEntry: Comparable, Identifiable { + case recent(Int, SettingsSearchableItem) + + var stableId: SettingsSearchRecentEntryStableId { + switch self { + case let .recent(_, item): + return .recent(item.id) + } + } + + static func ==(lhs: SettingsSearchRecentEntry, rhs: SettingsSearchRecentEntry) -> Bool { + switch lhs { + case let .recent(lhsIndex, lhsItem): + if case let .recent(rhsIndex, rhsItem) = rhs, lhsIndex == rhsIndex, lhsItem.id == rhsItem.id { + return true + } else { + return false + } + } + } + + static func <(lhs: SettingsSearchRecentEntry, rhs: SettingsSearchRecentEntry) -> Bool { + switch lhs { + case let .recent(lhsIndex, _): + switch rhs { + case let .recent(rhsIndex, _): + return lhsIndex <= rhsIndex + } + } + } + + func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction, header: ListViewItemHeader) -> ListViewItem { + switch self { + case let .recent(_, item): + return SettingsSearchRecentItem(account: account, theme: theme, strings: strings, title: item.title, breadcrumbs: item.breadcrumbs, action: { + interaction.openItem(item) + }, deleted: { + interaction.deleteRecentItem(item.id) + }, header: header) + } + } +} + +private struct SettingsSearchContainerRecentTransition { + let deletions: [ListViewDeleteItem] + let insertions: [ListViewInsertItem] + let updates: [ListViewUpdateItem] + let isEmpty: Bool +} + +private func preparedSettingsSearchContainerRecentTransition(from fromEntries: [SettingsSearchRecentEntry], to toEntries: [SettingsSearchRecentEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: SettingsSearchInteraction, header: ListViewItemHeader) -> SettingsSearchContainerRecentTransition { + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) + + let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, header: header), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, header: header), directionHint: nil) } + + return SettingsSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates, isEmpty: toEntries.isEmpty) +} + + private final class SettingsSearchContainerNode: SearchDisplayControllerContentNode { private let dimNode: ASDisplayNode private let listNode: ListView + private let recentListNode: ListView private var enqueuedTransitions: [SettingsSearchContainerTransition] = [] + private var enqueuedRecentTransitions: [(SettingsSearchContainerRecentTransition, Bool)] = [] private var hasValidLayout = false private let searchQuery = Promise() private let searchDisposable = MetaDisposable() + private var recentDisposable: Disposable? + private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - - private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)> + private let presentationDataPromise: Promise init(context: AccountContext, openResult: @escaping (SettingsSearchableItem) -> Void, exceptionsList: Signal) { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - - self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) + self.presentationDataPromise = Promise(self.presentationData) self.dimNode = ASDisplayNode() self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) self.listNode = ListView() - - super.init() - self.listNode.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.listNode.isHidden = true + self.recentListNode = ListView() + self.recentListNode.backgroundColor = .clear + self.recentListNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor + + super.init() + self.addSubnode(self.dimNode) + self.addSubnode(self.recentListNode) self.addSubnode(self.listNode) - let queryAndFoundItems = combineLatest(settingsSearchableItems(context: context, exceptionsList: exceptionsList), faqSearchableItems(context: context)) + let interaction = SettingsSearchInteraction(openItem: openResult, deleteRecentItem: { id in + removeRecentSettingsSearchItem(postbox: context.account.postbox, item: id) + }) + + let searchableItems = Promise<[SettingsSearchableItem]>() + searchableItems.set(settingsSearchableItems(context: context, notificationExceptionsList: exceptionsList)) + + let queryAndFoundItems = combineLatest(searchableItems.get(), faqSearchableItems(context: context)) |> mapToSignal { searchableItems, faqSearchableItems -> Signal<(String, [SettingsSearchableItem])?, NoError> in return self.searchQuery.get() |> mapToSignal { query -> Signal<(String, [SettingsSearchableItem])?, NoError> in @@ -247,7 +351,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN let results = searchSettingsItems(items: searchableItems, query: query) let faqResults = searchSettingsItems(items: faqSearchableItems, query: query) let finalResults: [SettingsSearchableItem] - if faqResults.first?.id == .faq(0) { + if faqResults.first?.id == .faq(1) { finalResults = faqResults + results } else { finalResults = results + faqResults @@ -259,8 +363,45 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN } } + self.recentListNode.isHidden = false + + let recentSearchItems = combineLatest(searchableItems.get(), settingsSearchRecentItems(postbox: context.account.postbox)) + |> map { searchableItems, recentItems -> [SettingsSearchableItem] in + let searchableItemsMap = searchableItems.reduce([SettingsSearchableItemId : SettingsSearchableItem]()) { (map, item) -> [SettingsSearchableItemId: SettingsSearchableItem] in + var map = map + map[item.id] = item + return map + } + var result: [SettingsSearchableItem] = [] + for itemId in recentItems { + if let searchItem = searchableItemsMap[itemId] { + result.append(searchItem) + } + } + return result + } + + let previousRecentItems = Atomic<[SettingsSearchRecentEntry]?>(value: nil) + self.recentDisposable = (combineLatest(recentSearchItems, self.presentationDataPromise.get()) + |> deliverOnMainQueue).start(next: { [weak self] recentSearchItems, presentationData in + if let strongSelf = self { + var entries: [SettingsSearchRecentEntry] = [] + for i in 0 ..< recentSearchItems.count { + entries.append(.recent(i, recentSearchItems[i])) + } + + let header = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear.uppercased(), action: { + clearRecentSettingsSearchItems(postbox: context.account.postbox) + }) + + let previousEntries = previousRecentItems.swap(entries) + let transition = preparedSettingsSearchContainerRecentTransition(from: previousEntries ?? [], to: entries, account: context.account, theme: presentationData.theme, strings: presentationData.strings, interaction: interaction, header: header) + strongSelf.enqueueRecentTransition(transition, firstTime: previousEntries == nil) + } + }) + let previousEntriesHolder = Atomic<([SettingsSearchEntry], PresentationTheme, PresentationStrings)?>(value: nil) - self.searchDisposable.set(combineLatest(queue: .mainQueue(), queryAndFoundItems, self.themeAndStringsPromise.get()).start(next: { [weak self] queryAndFoundItems, themeAndStrings in + self.searchDisposable.set(combineLatest(queue: .mainQueue(), queryAndFoundItems, self.presentationDataPromise.get()).start(next: { [weak self] queryAndFoundItems, presentationData in guard let strongSelf = self else { return } @@ -274,14 +415,14 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN if previousIcon != item.icon { image = item.icon.image() } - entries.append(.result(index: entries.count, item: item, title: item.title, breadcrumbs: item.breadcrumbs, icon: image)) + entries.append(.result(index: entries.count, item: item, icon: image)) previousIcon = item.icon } } if !entries.isEmpty || currentQuery == nil { - let previousEntriesAndPresentationData = previousEntriesHolder.swap((entries, themeAndStrings.0, themeAndStrings.1)) - let transition = preparedSettingsSearchContainerTransition(theme: themeAndStrings.0, strings: themeAndStrings.1, from: previousEntriesAndPresentationData?.0 ?? [], to: entries, openResult: openResult, isSearching: queryAndFoundItems != nil, forceUpdate: previousEntriesAndPresentationData?.1 !== themeAndStrings.0 || previousEntriesAndPresentationData?.2 !== themeAndStrings.1) + let previousEntriesAndPresentationData = previousEntriesHolder.swap((entries, presentationData.theme, presentationData.strings)) + let transition = preparedSettingsSearchContainerTransition(theme: presentationData.theme, strings: presentationData.strings, from: previousEntriesAndPresentationData?.0 ?? [], to: entries, interaction: interaction, isSearching: queryAndFoundItems != nil, forceUpdate: previousEntriesAndPresentationData?.1 !== presentationData.theme || previousEntriesAndPresentationData?.2 !== presentationData.strings) strongSelf.enqueueTransition(transition) } })) @@ -296,7 +437,7 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings) - strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings))) + strongSelf.presentationDataPromise.set(.single(presentationData)) } } }) @@ -304,10 +445,15 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN self.listNode.beganInteractiveDragging = { [weak self] in self?.dismissInput?() } + + self.recentListNode.beganInteractiveDragging = { [weak self] in + self?.dismissInput?() + } } deinit { self.searchDisposable.dispose() + self.recentDisposable?.dispose() self.presentationDataDisposable?.dispose() } @@ -319,6 +465,10 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { self.listNode.backgroundColor = theme.chatList.backgroundColor + if self.recentListNode.backgroundColor != .clear { + self.recentListNode.backgroundColor = theme.chatList.backgroundColor + } + self.recentListNode.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor } override func searchTextUpdated(text: String) { @@ -348,12 +498,40 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN let isSearching = transition.isSearching self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in + self?.recentListNode.isHidden = isSearching self?.listNode.isHidden = !isSearching self?.dimNode.isHidden = isSearching }) } } + private func enqueueRecentTransition(_ transition: SettingsSearchContainerRecentTransition, firstTime: Bool) { + self.enqueuedRecentTransitions.append((transition, firstTime)) + + if self.hasValidLayout { + while !self.enqueuedRecentTransitions.isEmpty { + self.dequeueRecentTransition() + } + } + } + + private func dequeueRecentTransition() { + if let (transition, firstTime) = self.enqueuedRecentTransitions.first { + self.enqueuedRecentTransitions.remove(at: 0) + + var options = ListViewDeleteAndInsertOptions() + if firstTime { + options.insert(.PreferSynchronousDrawing) + } else { + options.insert(.AnimateInsertion) + } + + self.recentListNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in + self?.recentListNode.backgroundColor = transition.isEmpty ? .clear : self?.presentationData.theme.chatList.backgroundColor + }) + } + } + override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) @@ -387,6 +565,9 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN insets.left += layout.safeInsets.left insets.right += layout.safeInsets.right + self.recentListNode.frame = CGRect(origin: CGPoint(), size: layout.size) + self.recentListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size) self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) @@ -398,6 +579,16 @@ private final class SettingsSearchContainerNode: SearchDisplayControllerContentN } } + override func scrollToTop() { + let listNodeToScroll: ListView + if !self.listNode.isHidden { + listNodeToScroll = self.listNode + } else { + listNodeToScroll = self.recentListNode + } + listNodeToScroll.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + } + @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { self.cancel?() @@ -442,6 +633,8 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: SettingsSearchContainerNode(context: self.context, openResult: { [weak self] result in if let strongSelf = self { + addRecentSettingsSearchItem(postbox: strongSelf.context.account.postbox, item: result.id) + result.present(strongSelf.context, strongSelf.getNavigationController?(), { [weak self] mode, controller in if let strongSelf = self { switch mode { @@ -484,6 +677,10 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode { return self.searchDisplayController != nil } + override func scrollToTop() { + self.searchDisplayController?.contentNode.scrollToTop() + } + override func queryUpdated(_ query: String) { //self.containerNode.searchTextUpdated(text: query) } diff --git a/TelegramUI/SettingsSearchRecentItem.swift b/TelegramUI/SettingsSearchRecentItem.swift new file mode 100644 index 0000000000..52b7022b47 --- /dev/null +++ b/TelegramUI/SettingsSearchRecentItem.swift @@ -0,0 +1,259 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Postbox +import Display +import SwiftSignalKit +import TelegramCore + +private enum RevealOptionKey: Int32 { + case delete +} + +class SettingsSearchRecentItem: ListViewItem { + let theme: PresentationTheme + let strings: PresentationStrings + let account: Account + let title: String + let breadcrumbs: [String] + let action: () -> Void + let deleted: () -> Void + + let header: ListViewItemHeader? + + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, title: String, breadcrumbs: [String], action: @escaping () -> Void, deleted: @escaping () -> Void, header: ListViewItemHeader) { + self.theme = theme + self.strings = strings + self.account = account + self.title = title + self.breadcrumbs = breadcrumbs + self.action = action + self.deleted = deleted + self.header = header + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = SettingsSearchRecentItemNode() + let makeLayout = node.asyncLayout() + let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem == nil, !(previousItem is SettingsSearchRecentItem)) + node.contentSize = nodeLayout.contentSize + node.insets = nodeLayout.insets + + completion(node, nodeApply) + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? SettingsSearchRecentItemNode { + let layout = nodeValue.asyncLayout() + async { + let (nodeLayout, apply) = layout(self, params, nextItem == nil, !(previousItem is SettingsSearchRecentItem)) + Queue.mainQueue().async { + completion(nodeLayout, { info in + apply().1(info) + }) + } + } + } + } + } + + var selectable: Bool { + return true + } + + func selected(listView: ListView) { + listView.clearHighlightAnimated(true) + self.action() + } +} + +private let titleFont = Font.regular(17.0) +private let subtitleFont = Font.regular(13.0) + +class SettingsSearchRecentItemNode: ItemListRevealOptionsItemNode { + private let backgroundNode: ASDisplayNode + private let separatorNode: ASDisplayNode + private let highlightedBackgroundNode: ASDisplayNode + private let titleNode: TextNode + private let subtitleNode: TextNode + + private var item: SettingsSearchRecentItem? + private var layoutParams: ListViewItemLayoutParams? + + required init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.separatorNode = ASDisplayNode() + self.separatorNode.isLayerBacked = true + + self.highlightedBackgroundNode = ASDisplayNode() + self.highlightedBackgroundNode.isLayerBacked = true + + self.titleNode = TextNode() + self.titleNode.isUserInteractionEnabled = false + self.titleNode.contentMode = .left + self.titleNode.contentsScale = UIScreenScale + + self.subtitleNode = TextNode() + self.subtitleNode.isUserInteractionEnabled = false + self.subtitleNode.contentMode = .left + self.subtitleNode.contentsScale = UIScreenScale + + super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.separatorNode) + self.addSubnode(self.titleNode) + self.addSubnode(self.subtitleNode) + } + + override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { + if let item = self.item { + let makeLayout = self.asyncLayout() + let (nodeLayout, nodeApply) = makeLayout(item, params, nextItem == nil, previousItem == nil) + self.contentSize = nodeLayout.contentSize + self.insets = nodeLayout.insets + let _ = nodeApply() + } + } + + override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { + super.setHighlighted(highlighted, at: point, animated: animated) + + if highlighted { + self.highlightedBackgroundNode.alpha = 1.0 + if self.highlightedBackgroundNode.supernode == nil { + self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode) + } + } else { + if self.highlightedBackgroundNode.supernode != nil { + if animated { + self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in + if let strongSelf = self { + if completed { + strongSelf.highlightedBackgroundNode.removeFromSupernode() + } + } + }) + self.highlightedBackgroundNode.alpha = 0.0 + } else { + self.highlightedBackgroundNode.removeFromSupernode() + } + } + } + } + + func asyncLayout() -> (_ item: SettingsSearchRecentItem, _ params: ListViewItemLayoutParams, _ last: Bool, _ firstWithHeader: Bool) -> (ListViewItemNodeLayout, () -> (Signal?, (ListViewItemApply) -> Void)) { + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode) + + let currentItem = self.item + + return { [weak self] item, params, last, firstWithHeader in + let leftInset: CGFloat = 15.0 + params.leftInset + let rightInset: CGFloat = params.rightInset + + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let subtitle = item.breadcrumbs.joined(separator: " → ") + + let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: subtitle, font: subtitleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + var height = titleLayout.size.height + if subtitle.isEmpty { + height += 22.0 + } else { + height += 39.0 + } + let contentSize = CGSize(width: params.width, height: height) + let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets(top: firstWithHeader ? 29.0 : 0.0, left: 0.0, bottom: 0.0, right: 0.0)) + return (nodeLayout, { [weak self] in + var updatedTheme: PresentationTheme? + if currentItem?.theme !== item.theme { + updatedTheme = item.theme + } + + return (nil, { _ in + if let strongSelf = self { + strongSelf.item = item + strongSelf.layoutParams = params + + if let _ = updatedTheme { + strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor + strongSelf.backgroundNode.backgroundColor = item.theme.list.plainBackgroundColor + strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor + } + + let _ = titleApply() + let _ = subtitleApply() + + let titleY: CGFloat = subtitle.isEmpty ? 11.0 : 11.0 + strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: titleY), size: titleLayout.size) + strongSelf.subtitleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: titleY + titleLayout.size.height + 1.0), size: subtitleLayout.size) + + let separatorHeight = UIScreenPixel + let topHighlightInset: CGFloat = (firstWithHeader || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height)) + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset)) + strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width, height: separatorHeight)) + strongSelf.separatorNode.isHidden = last + + strongSelf.updateLayout(size: nodeLayout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) + + strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) + } + }) + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false) + } + + override public func header() -> ListViewItemHeader? { + if let item = self.item { + return item.header + } else { + return nil + } + } + + override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + super.updateRevealOffset(offset: offset, transition: transition) + + if let params = self.layoutParams { + let leftInset: CGFloat = 15.0 + params.leftInset + + var titleFrame = self.titleNode.frame + titleFrame.origin.x = leftInset + offset + transition.updateFrame(node: self.titleNode, frame: titleFrame) + + var subtitleFrame = self.subtitleNode.frame + subtitleFrame.origin.x = leftInset + offset + transition.updateFrame(node: self.subtitleNode, frame: subtitleFrame) + } + } + + override func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { + if let item = self.item { + switch option.key { + case RevealOptionKey.delete.rawValue: + item.deleted() + default: + break + } + } + self.setRevealOptionsOpened(false, animated: true) + self.revealOptionsInteractivelyClosed() + } +} diff --git a/TelegramUI/SettingsSearchRecentQueries.swift b/TelegramUI/SettingsSearchRecentQueries.swift index f2811ab853..acb810792e 100644 --- a/TelegramUI/SettingsSearchRecentQueries.swift +++ b/TelegramUI/SettingsSearchRecentQueries.swift @@ -5,20 +5,17 @@ import SwiftSignalKit private struct SettingsSearchRecentQueryItemId { public let rawValue: MemoryBuffer - var value: String { - return String(data: self.rawValue.makeData(), encoding: .utf8) ?? "" + var value: Int64 { + return self.rawValue.makeData().withUnsafeBytes { $0.pointee } as Int64 } init(_ rawValue: MemoryBuffer) { self.rawValue = rawValue } - init?(_ value: String) { - if let data = value.data(using: .utf8) { - self.rawValue = MemoryBuffer(data: data) - } else { - return nil - } + init(_ value: Int64) { + var value = value + self.rawValue = MemoryBuffer(data: Data(bytes: &value, count: MemoryLayout.size(ofValue: value))) } } @@ -33,39 +30,39 @@ final class RecentSettingsSearchQueryItem: OrderedItemListEntryContents { } } -func addRecentSettingsSearchItem(postbox: Postbox, item: SettingsSearchableItem) -> Signal { - return postbox.transaction { transaction in -// if let itemId = WallpaperSearchRecentQueryItemId(string) { -// transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems, item: OrderedItemListEntry(id: itemId.rawValue, contents: RecentWallpaperSearchQueryItem()), removeTailIfCountExceeds: 100) -// } - } +func addRecentSettingsSearchItem(postbox: Postbox, item: SettingsSearchableItemId) { + let _ = (postbox.transaction { transaction in + let itemId = SettingsSearchRecentQueryItemId(item.index) + transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems, item: OrderedItemListEntry(id: itemId.rawValue, contents: RecentSettingsSearchQueryItem()), removeTailIfCountExceeds: 100) + }).start() } -func removeRecentSettingsSearchItem(postbox: Postbox, item: SettingsSearchableItem) -> Signal { - return postbox.transaction { transaction -> Void in -// if let itemId = WallpaperSearchRecentQueryItemId(string) { -// transaction.removeOrderedItemListItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems, itemId: itemId.rawValue) -// } - } +func removeRecentSettingsSearchItem(postbox: Postbox, item: SettingsSearchableItemId) { + let _ = (postbox.transaction { transaction -> Void in + let itemId = SettingsSearchRecentQueryItemId(item.index) + transaction.removeOrderedItemListItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems, itemId: itemId.rawValue) + }).start() } -func clearRecentSettingsSearchItems(postbox: Postbox) -> Signal { - return postbox.transaction { transaction -> Void in +func clearRecentSettingsSearchItems(postbox: Postbox) { + let _ = (postbox.transaction { transaction -> Void in transaction.replaceOrderedItemListItems(collectionId: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems, items: []) - } + }).start() } -func settingsSearchRecentQueries(postbox: Postbox) -> Signal<[String], NoError> { +func settingsSearchRecentItems(postbox: Postbox) -> Signal<[SettingsSearchableItemId], NoError> { return postbox.combinedView(keys: [.orderedItemList(id: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems)]) - |> mapToSignal { view -> Signal<[String], NoError> in - return postbox.transaction { transaction -> [String] in - var result: [String] = [] -// if let view = view.views[.orderedItemList(id: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems)] as? OrderedItemListView { -// for item in view.items { -// let value = WallpaperSearchRecentQueryItemId(item.id).value -// result.append(value) -// } -// } + |> mapToSignal { view -> Signal<[SettingsSearchableItemId], NoError> in + return postbox.transaction { transaction -> [SettingsSearchableItemId] in + var result: [SettingsSearchableItemId] = [] + if let view = view.views[.orderedItemList(id: ApplicationSpecificOrderedItemListCollectionId.settingsSearchRecentItems)] as? OrderedItemListView { + for item in view.items { + let index = SettingsSearchRecentQueryItemId(item.id).value + if let itemId = SettingsSearchableItemId(index: index) { + result.append(itemId) + } + } + } return result } } diff --git a/TelegramUI/SettingsSearchResultItem.swift b/TelegramUI/SettingsSearchResultItem.swift index 509a907570..484f552b0e 100644 --- a/TelegramUI/SettingsSearchResultItem.swift +++ b/TelegramUI/SettingsSearchResultItem.swift @@ -6,19 +6,17 @@ import SwiftSignalKit class SettingsSearchResultItem: ListViewItem, ItemListItem { let theme: PresentationTheme let strings: PresentationStrings - let title: String - let breadcrumbs: [String] + let item: SettingsSearchableItem let icon: UIImage? - let action: () -> Void + let interaction: SettingsSearchInteraction let sectionId: ItemListSectionId - init(theme: PresentationTheme, strings: PresentationStrings, title: String, breadcrumbs: [String], icon: UIImage?, action: @escaping () -> Void, sectionId: ItemListSectionId) { + init(theme: PresentationTheme, strings: PresentationStrings, item: SettingsSearchableItem, icon: UIImage?, interaction: SettingsSearchInteraction, sectionId: ItemListSectionId) { self.theme = theme self.strings = strings - self.title = title - self.breadcrumbs = breadcrumbs + self.item = item self.icon = icon - self.action = action + self.interaction = interaction self.sectionId = sectionId } @@ -67,7 +65,7 @@ class SettingsSearchResultItem: ListViewItem, ItemListItem { func selected(listView: ListView){ listView.clearHighlightAnimated(true) - self.action() + self.interaction.openItem(self.item) } } @@ -136,9 +134,9 @@ class SettingsSearchResultItemNode: ListViewItemNode { leftInset += contentInset - let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.item.title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let subtitle = item.breadcrumbs.joined(separator: " → ") + let subtitle = item.item.breadcrumbs.joined(separator: " → ") let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: subtitle, font: subtitleFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) diff --git a/TelegramUI/SettingsSearchableItems.swift b/TelegramUI/SettingsSearchableItems.swift index a1a8990822..60dd5c0da2 100644 --- a/TelegramUI/SettingsSearchableItems.swift +++ b/TelegramUI/SettingsSearchableItems.swift @@ -23,21 +23,117 @@ enum SettingsSearchableItemIcon { case faq } + + enum SettingsSearchableItemId: Hashable { - case profile(Int) - case proxy(Int) - case savedMessages(Int) - case calls(Int) - case stickers(Int) - case notifications(Int) - case privacy(Int) - case data(Int) - case appearance(Int) - case language(Int) - case watch(Int) - case passport(Int) - case support(Int) - case faq(Int) + case profile(Int32) + case proxy(Int32) + case savedMessages(Int32) + case calls(Int32) + case stickers(Int32) + case notifications(Int32) + case privacy(Int32) + case data(Int32) + case appearance(Int32) + case language(Int32) + case watch(Int32) + case passport(Int32) + case support(Int32) + case faq(Int32) + + private var namespace: Int32 { + switch self { + case .profile: + return 1 + case .proxy: + return 2 + case .savedMessages: + return 3 + case .calls: + return 4 + case .stickers: + return 5 + case .notifications: + return 6 + case .privacy: + return 7 + case .data: + return 8 + case .appearance: + return 9 + case .language: + return 10 + case .watch: + return 11 + case .passport: + return 12 + case .support: + return 13 + case .faq: + return 14 + } + } + + private var id: Int32 { + switch self { + case let .profile(id), + let .proxy(id), + let .savedMessages(id), + let .calls(id), + let .stickers(id), + let .notifications(id), + let .privacy(id), + let .data(id), + let .appearance(id), + let .language(id), + let .watch(id), + let .passport(id), + let .support(id), + let .faq(id): + return id + } + } + + var index: Int64 { + return (Int64(self.namespace) << 32) | Int64(self.id) + } + + init?(index: Int64) { + let namespace = Int32((index >> 32) & 0x7fffffff) + let id = Int32(bitPattern: UInt32(index & 0xffffffff)) + switch namespace { + case 1: + self = .profile(id) + case 2: + self = .proxy(id) + case 3: + self = .savedMessages(id) + case 4: + self = .calls(id) + case 5: + self = .stickers(id) + case 6: + self = .notifications(id) + case 7: + self = .privacy(id) + case 8: + self = .data(id) + case 9: + self = .appearance(id) + case 10: + self = .language(id) + case 11: + self = .watch(id) + case 12: + self = .passport(id) + case 13: + self = .support(id) + case 14: + self = .faq(id) + default: + return nil + } + } } enum SettingsSearchableItemPresentation { @@ -156,15 +252,11 @@ private func stickerSearchableItems(context: AccountContext) -> [SettingsSearcha }), SettingsSearchableItem(id: .stickers(4), title: strings.MaskStickerSettings_Title, alternate: synonyms(strings.SettingsSearch_Synonyms_Stickers_Masks), icon: icon, breadcrumbs: [strings.ChatSettings_Stickers], present: { context, _, present in present(.push, installedStickerPacksController(context: context, mode: .masks, archivedPacks: nil, updatedPacks: { _ in})) - }), - SettingsSearchableItem(id: .stickers(5), title: strings.StickerPacksSettings_ArchivedMasks, alternate: synonyms(strings.SettingsSearch_Synonyms_Stickers_ArchivedMasks), icon: icon, breadcrumbs: [strings.ChatSettings_Stickers, strings.MaskStickerSettings_Title], present: { context, _, present in - present(.push, archivedStickerPacksController(context: context, mode: .masks, archived: nil, updatedPacks: { _ in - })) }) ] } -private func notificationSearchableItems(context: AccountContext, exceptionsList: NotificationExceptionsList?) -> [SettingsSearchableItem] { +private func notificationSearchableItems(context: AccountContext, settings: GlobalNotificationSettingsSet, exceptionsList: NotificationExceptionsList?) -> [SettingsSearchableItem] { let icon: SettingsSearchableItemIcon = .notifications let strings = context.sharedContext.currentPresentationData.with { $0 }.strings @@ -215,6 +307,14 @@ private func notificationSearchableItems(context: AccountContext, exceptionsList return (.users(users), .groups(groups), .channels(channels)) } + func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound { + if case .default = sound { + return .bundledModern(id: 0) + } else { + return sound + } + } + return [ SettingsSearchableItem(id: .notifications(0), title: strings.Settings_NotificationsAndSounds, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_Title), icon: icon, breadcrumbs: [], present: { context, _, present in presentNotificationSettings(context, present, nil) @@ -226,7 +326,15 @@ private func notificationSearchableItems(context: AccountContext, exceptionsList presentNotificationSettings(context, present, .messagePreviews) }), SettingsSearchableItem(id: .notifications(3), title: strings.Notifications_MessageNotificationsSound, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsSound), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, _, present in - presentNotificationSettings(context, present, nil) + + let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: filteredGlobalSound(settings.privateChats.sound), defaultSound: nil, completion: { value in + let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in + var settings = settings + settings.privateChats.sound = value + return settings + }).start() + }) + present(.modal, controller) }), SettingsSearchableItem(id: .notifications(4), title: strings.Notifications_MessageNotificationsExceptions, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications.capitalized], present: { context, _, present in present(.push, NotificationExceptionsController(context: context, mode: exceptions().0, updatedMode: { _ in})) @@ -238,7 +346,14 @@ private func notificationSearchableItems(context: AccountContext, exceptionsList presentNotificationSettings(context, present, .groupPreviews) }), SettingsSearchableItem(id: .notifications(7), title: strings.Notifications_GroupNotificationsSound, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsSound), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, _, present in - presentNotificationSettings(context, present, nil) + let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: filteredGlobalSound(settings.groupChats.sound), defaultSound: nil, completion: { value in + let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in + var settings = settings + settings.groupChats.sound = value + return settings + }).start() + }) + present(.modal, controller) }), SettingsSearchableItem(id: .notifications(8), title: strings.Notifications_GroupNotificationsExceptions, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications.capitalized], present: { context, _, present in present(.push, NotificationExceptionsController(context: context, mode: exceptions().1, updatedMode: { _ in})) @@ -250,7 +365,14 @@ private func notificationSearchableItems(context: AccountContext, exceptionsList presentNotificationSettings(context, present, .channelPreviews) }), SettingsSearchableItem(id: .notifications(11), title: strings.Notifications_ChannelNotificationsSound, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, _, present in - presentNotificationSettings(context, present, nil) + let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: filteredGlobalSound(settings.channels.sound), defaultSound: nil, completion: { value in + let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in + var settings = settings + settings.channels.sound = value + return settings + }).start() + }) + present(.modal, controller) }), SettingsSearchableItem(id: .notifications(12), title: strings.Notifications_MessageNotificationsExceptions, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications.capitalized], present: { context, _, present in present(.push, NotificationExceptionsController(context: context, mode: exceptions().2, updatedMode: { _ in})) @@ -292,8 +414,8 @@ private func privacySearchableItems(context: AccountContext) -> [SettingsSearcha let icon: SettingsSearchableItemIcon = .privacy let strings = context.sharedContext.currentPresentationData.with { $0 }.strings - let presentPrivacySettings: (AccountContext, (SettingsSearchableItemPresentation, ViewController) -> Void) -> Void = { context, present in - present(.push, privacyAndSecurityController(context: context)) + let presentPrivacySettings: (AccountContext, (SettingsSearchableItemPresentation, ViewController) -> Void, PrivacyAndSecurityEntryTag?) -> Void = { context, present, itemTag in + present(.push, privacyAndSecurityController(context: context, focusOnItemTag: itemTag)) } let presentSelectivePrivacySettings: (AccountContext, SelectivePrivacySettingsKind, @escaping (SettingsSearchableItemPresentation, ViewController) -> Void) -> Void = { context, kind, present in @@ -359,7 +481,7 @@ private func privacySearchableItems(context: AccountContext) -> [SettingsSearcha return [ SettingsSearchableItem(id: .privacy(0), title: strings.Settings_PrivacySettings, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_Title), icon: icon, breadcrumbs: [], present: { context, _, present in - presentPrivacySettings(context, present) + presentPrivacySettings(context, present, nil) }), SettingsSearchableItem(id: .privacy(1), title: strings.Settings_BlockedUsers, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_BlockedUsers), icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in present(.push, blockedPeersController(context: context)) @@ -400,7 +522,7 @@ private func privacySearchableItems(context: AccountContext) -> [SettingsSearcha present(.push, recentSessionsController(context: context)) }), SettingsSearchableItem(id: .privacy(10), title: strings.PrivacySettings_DeleteAccountTitle.capitalized, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor), icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in - presentPrivacySettings(context, present) + presentPrivacySettings(context, present, .accountTimeout) }), SettingsSearchableItem(id: .privacy(11), title: strings.PrivacySettings_DataSettings, alternate: synonyms(strings.SettingsSearch_Synonyms_Privacy_Data_Title), icon: icon, breadcrumbs: [strings.Settings_PrivacySettings], present: { context, _, present in presentDataPrivacySettings(context, present) @@ -481,7 +603,7 @@ private func dataSearchableItems(context: AccountContext) -> [SettingsSearchable ] } -private func proxySearchableItems(context: AccountContext) -> [SettingsSearchableItem] { +private func proxySearchableItems(context: AccountContext, servers: [ProxyServerSettings]) -> [SettingsSearchableItem] { let icon: SettingsSearchableItemIcon = .proxy let strings = context.sharedContext.currentPresentationData.with { $0 }.strings @@ -489,17 +611,27 @@ private func proxySearchableItems(context: AccountContext) -> [SettingsSearchabl present(.push, proxySettingsController(context: context)) } - return [ - SettingsSearchableItem(id: .proxy(0), title: strings.Settings_Proxy, alternate: synonyms(strings.SettingsSearch_Synonyms_Proxy_Title), icon: icon, breadcrumbs: [], present: { context, _, present in + var items: [SettingsSearchableItem] = [] + items.append(SettingsSearchableItem(id: .proxy(0), title: strings.Settings_Proxy, alternate: synonyms(strings.SettingsSearch_Synonyms_Proxy_Title), icon: icon, breadcrumbs: [], present: { context, _, present in presentProxySettings(context, present) - }), - SettingsSearchableItem(id: .proxy(1), title: strings.SocksProxySetup_AddProxy, alternate: synonyms(strings.SettingsSearch_Synonyms_Proxy_AddProxy), icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, _, present in + })) + items.append(SettingsSearchableItem(id: .proxy(1), title: strings.SocksProxySetup_AddProxy, alternate: synonyms(strings.SettingsSearch_Synonyms_Proxy_AddProxy), icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, _, present in present(.modal, proxyServerSettingsController(context: context)) - }), - SettingsSearchableItem(id: .proxy(2), title: strings.SocksProxySetup_UseForCalls, alternate: synonyms(strings.SettingsSearch_Synonyms_Proxy_UseForCalls), icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, _, present in - presentProxySettings(context, present) - }) - ] + })) + + var hasSocksServers = false + for server in servers { + if case .socks5 = server.connection { + hasSocksServers = true + break + } + } + if hasSocksServers { + items.append(SettingsSearchableItem(id: .proxy(2), title: strings.SocksProxySetup_UseForCalls, alternate: synonyms(strings.SettingsSearch_Synonyms_Proxy_UseForCalls), icon: icon, breadcrumbs: [strings.Settings_Proxy], present: { context, _, present in + presentProxySettings(context, present) + })) + } + return items } private func appearanceSearchableItems(context: AccountContext) -> [SettingsSearchableItem] { @@ -540,16 +672,42 @@ private func appearanceSearchableItems(context: AccountContext) -> [SettingsSear ] } -func settingsSearchableItems(context: AccountContext, exceptionsList: Signal) -> Signal<[SettingsSearchableItem], NoError> { +func settingsSearchableItems(context: AccountContext, notificationExceptionsList: Signal) -> Signal<[SettingsSearchableItem], NoError> { let watchAppInstalled = (context.watchManager?.watchAppInstalled ?? .single(false)) |> take(1) + let canAddAccount = activeAccountsAndPeers(context: context) |> take(1) |> map { accountsAndPeers -> Bool in return accountsAndPeers.1.count + 1 < maximumNumberOfAccounts } - return combineLatest(watchAppInstalled, canAddAccount, exceptionsList) - |> map { watchAppInstalled, canAddAccount, exceptionsList in + + let notificationSettings = context.account.postbox.preferencesView(keys: [PreferencesKeys.globalNotifications]) + |> take(1) + |> map { view -> GlobalNotificationSettingsSet in + let viewSettings: GlobalNotificationSettingsSet + if let settings = view.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings { + viewSettings = settings.effective + } else { + viewSettings = GlobalNotificationSettingsSet.defaultSettings + } + return viewSettings + } + + let proxyServers = context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.proxySettings]) + |> map { sharedData -> ProxySettings in + if let value = sharedData.entries[SharedDataKeys.proxySettings] as? ProxySettings { + return value + } else { + return ProxySettings.defaultSettings + } + } + |> map { settings -> [ProxyServerSettings] in + return settings.servers + } + + return combineLatest(watchAppInstalled, canAddAccount, notificationSettings, notificationExceptionsList, proxyServers) + |> map { watchAppInstalled, canAddAccount, notificationSettings, notificationExceptionsList, proxyServers in let strings = context.sharedContext.currentPresentationData.with { $0 }.strings var allItems: [SettingsSearchableItem] = [] @@ -568,7 +726,7 @@ func settingsSearchableItems(context: AccountContext, exceptionsList: Signal [Set for item in items { var string = item.title if !item.alternate.isEmpty { - string += " \(item.alternate.joined(separator: " "))" + for alternate in item.alternate { + let trimmed = alternate.trimmingCharacters(in: .whitespacesAndNewlines) + if !trimmed.isEmpty { + string += " \(trimmed)" + } + } } if item.breadcrumbs.count > 1 { string += " \(item.breadcrumbs.suffix(from: 1).joined(separator: " "))" diff --git a/TelegramUI/SetupTwoStepVerificationControllerNode.swift b/TelegramUI/SetupTwoStepVerificationControllerNode.swift index ac0b3de572..b3124ee513 100644 --- a/TelegramUI/SetupTwoStepVerificationControllerNode.swift +++ b/TelegramUI/SetupTwoStepVerificationControllerNode.swift @@ -350,7 +350,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.updateState({ state in var state = state state.data.activity = false @@ -399,7 +399,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { case .generic: text = strongSelf.presentationData.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.updateState({ state in var state = state state.data.activity = false @@ -520,7 +520,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { if password == confirmation { state.data.state = .enterHint(mode: mode, password: password, hint: "") } else { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) } case let .enterHint(mode, password, hint): switch mode { @@ -552,7 +552,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.updateState({ state in var state = state state.data.activity = false @@ -596,7 +596,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { case .generic: text = strongSelf.presentationData.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.updateState({ state in var state = state state.data.activity = false @@ -638,7 +638,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.updateState({ state in var state = state state.data.activity = false @@ -667,7 +667,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { case .generic: text = strongSelf.presentationData.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) strongSelf.updateState({ state in var state = state @@ -692,7 +692,8 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { }, transition: .animated(duration: 0.5, curve: .spring)) } if case let .enterEmail(enterEmail)? = self.innerState.data.state, case .create = enterEmail.state, enterEmail.email.isEmpty { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: self.presentationData.theme), title: nil, text: self.presentationData.strings.TwoStepAuth_EmailSkipAlert, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: self.presentationData.strings.TwoStepAuth_EmailSkip, action: { + + self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.TwoStepAuth_EmailSkipAlert, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: self.presentationData.strings.TwoStepAuth_EmailSkip, action: { continueImpl() })]), nil) } else { diff --git a/TelegramUI/StickerPackPreviewController.swift b/TelegramUI/StickerPackPreviewController.swift index 679178ec44..ce33a74a9b 100644 --- a/TelegramUI/StickerPackPreviewController.swift +++ b/TelegramUI/StickerPackPreviewController.swift @@ -142,7 +142,7 @@ final class StickerPackPreviewController: ViewController { if let strongSelf = self { if case .none = next { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongSelf.dismiss() } else { strongSelf.controllerNode.updateStickerPack(next) diff --git a/TelegramUI/StringHash.swift b/TelegramUI/StringHash.swift new file mode 100644 index 0000000000..86f168c8e1 --- /dev/null +++ b/TelegramUI/StringHash.swift @@ -0,0 +1,12 @@ +import Foundation + +extension String { + var persistentHashValue: UInt64 { + var result = UInt64 (5381) + let buf = [UInt8](self.utf8) + for b in buf { + result = 127 * (result & 0x00ffffffffffffff) + UInt64(b) + } + return result + } +} diff --git a/TelegramUI/TelegramRootController.swift b/TelegramUI/TelegramRootController.swift index 9e686fc0ef..ea71e346e4 100644 --- a/TelegramUI/TelegramRootController.swift +++ b/TelegramUI/TelegramRootController.swift @@ -56,6 +56,9 @@ public final class TelegramRootController: NavigationController { var controllers: [ViewController] = [] let contactsController = ContactsController(context: self.context) + contactsController.switchToChatsController = { [weak self] in + self?.openChatsController(activateSearch: false) + } controllers.append(contactsController) if showCallsTab { @@ -95,18 +98,22 @@ public final class TelegramRootController: NavigationController { rootTabController.setControllers(controllers, selectedIndex: nil) } - public func openChatsSearch() { + public func openChatsController(activateSearch: Bool) { guard let rootTabController = self.rootTabController else { return } - self.popToRoot(animated: false) + if activateSearch { + self.popToRoot(animated: false) + } if let index = rootTabController.controllers.index(where: { $0 is ChatListController}) { rootTabController.selectedIndex = index } - self.chatListController?.activateSearch() + if activateSearch { + self.chatListController?.activateSearch() + } } public func openRootCompose() { diff --git a/TelegramUI/TermsOfServiceControllerNode.swift b/TelegramUI/TermsOfServiceControllerNode.swift index 3ad5bdacb4..b101da4a2a 100644 --- a/TelegramUI/TermsOfServiceControllerNode.swift +++ b/TelegramUI/TermsOfServiceControllerNode.swift @@ -141,7 +141,7 @@ final class TermsOfServiceControllerNode: ViewControllerTracingNode { setToProcceedBot(mention) rightAction() }) - ]), ActionSheetItemGroup.init(items: [ActionSheetButtonItem(title: strongSelf.strings.Common_Cancel, action: { [weak actionSheet] in + ]), ActionSheetItemGroup(items: [ActionSheetButtonItem(title: strongSelf.strings.Common_Cancel, action: { [weak actionSheet] in actionSheet?.dismissAnimated() })])]) strongSelf.present(actionSheet, nil) diff --git a/TelegramUI/ThemedTextAlertController.swift b/TelegramUI/ThemedTextAlertController.swift index 34174e3a3b..c9dc57e5a1 100644 --- a/TelegramUI/ThemedTextAlertController.swift +++ b/TelegramUI/ThemedTextAlertController.swift @@ -7,11 +7,12 @@ public func textAlertController(context: AccountContext, title: String?, text: S let theme = presentationData.theme let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: theme), title: title, text: text, actions: actions) - _ = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in - if let strongController = controller { - strongController.theme = AlertControllerTheme(presentationTheme: presentationData.theme) - } + let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in + controller?.theme = AlertControllerTheme(presentationTheme: presentationData.theme) }) + controller.dismissed = { + presentationDataDisposable.dispose() + } return controller } diff --git a/TelegramUI/TwoStepVerificationPasswordEntryController.swift b/TelegramUI/TwoStepVerificationPasswordEntryController.swift index de39994e12..67608e6d46 100644 --- a/TelegramUI/TwoStepVerificationPasswordEntryController.swift +++ b/TelegramUI/TwoStepVerificationPasswordEntryController.swift @@ -345,7 +345,7 @@ func twoStepVerificationPasswordEntryController(context: AccountContext, mode: T case .invalidEmail: alertText = presentationData.strings.TwoStepAuth_EmailInvalid } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) })) case let .setupEmail(password): updatePasswordDisposable.set((updateTwoStepVerificationEmail(network: context.account.network, currentPassword: password, updatedEmail: email) |> deliverOnMainQueue).start(next: { update in @@ -370,12 +370,12 @@ func twoStepVerificationPasswordEntryController(context: AccountContext, mode: T case .invalidEmail: alertText = presentationData.strings.TwoStepAuth_EmailInvalid } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) })) } } else if invalidReentry { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } } diff --git a/TelegramUI/TwoStepVerificationResetController.swift b/TelegramUI/TwoStepVerificationResetController.swift index 7590c27fa5..49c9d143be 100644 --- a/TelegramUI/TwoStepVerificationResetController.swift +++ b/TelegramUI/TwoStepVerificationResetController.swift @@ -40,7 +40,7 @@ private enum TwoStepVerificationResetTag: ItemListItemTag { } private enum TwoStepVerificationResetEntry: ItemListNodeEntry { - case codeEntry(PresentationTheme, String) + case codeEntry(PresentationTheme, String, String) case codeInfo(PresentationTheme, String) var section: ItemListSectionId { @@ -58,8 +58,8 @@ private enum TwoStepVerificationResetEntry: ItemListNodeEntry { static func ==(lhs: TwoStepVerificationResetEntry, rhs: TwoStepVerificationResetEntry) -> Bool { switch lhs { - case let .codeEntry(lhsTheme, lhsText): - if case let .codeEntry(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + case let .codeEntry(lhsTheme, lhsPlaceholder, lhsText): + if case let .codeEntry(rhsTheme, rhsPlaceholder, rhsText) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText { return true } else { return false @@ -79,8 +79,8 @@ private enum TwoStepVerificationResetEntry: ItemListNodeEntry { func item(_ arguments: TwoStepVerificationResetControllerArguments) -> ListViewItem { switch self { - case let .codeEntry(theme, text): - return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "Code", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .password, spacing: 10.0, tag: TwoStepVerificationResetTag.input, sectionId: self.section, textUpdated: { updatedText in + case let .codeEntry(theme, placeholder, text): + return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: placeholder, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .password, spacing: 10.0, tag: TwoStepVerificationResetTag.input, sectionId: self.section, textUpdated: { updatedText in arguments.updateEntryText(updatedText) }, action: { arguments.next() @@ -123,8 +123,8 @@ private struct TwoStepVerificationResetControllerState: Equatable { private func twoStepVerificationResetControllerEntries(presentationData: PresentationData, state: TwoStepVerificationResetControllerState, emailPattern: String) -> [TwoStepVerificationResetEntry] { var entries: [TwoStepVerificationResetEntry] = [] - entries.append(.codeEntry(presentationData.theme, state.codeText)) - entries.append(.codeInfo(presentationData.theme, "Please check your e-mail and enter the 6-digit code we've sent there to deactivate your cloud password.\n\n[Having trouble accessing your e-mail \(escapedPlaintextForMarkdown(emailPattern))?]()")) + entries.append(.codeEntry(presentationData.theme, presentationData.strings.TwoStepAuth_RecoveryCode, state.codeText)) + entries.append(.codeInfo(presentationData.theme, "\(presentationData.strings.TwoStepAuth_RecoveryCodeHelp)\n\n[\(presentationData.strings.TwoStepAuth_RecoveryEmailUnavailable(escapedPlaintextForMarkdown(emailPattern)).0)]()")) return entries } @@ -165,15 +165,15 @@ func twoStepVerificationResetController(context: AccountContext, emailPattern: S let alertText: String switch error { case .generic: - alertText = "An error occurred." + alertText = presentationData.strings.Login_UnknownError case .invalidCode: - alertText = "Invalid code. Please try again." + alertText = presentationData.strings.Login_InvalidCodeError case .codeExpired: - alertText = "Code expired." + alertText = presentationData.strings.Login_CodeExpiredError case .limitExceeded: - alertText = "You have entered invalid code too many times. Please try again later." + alertText = presentationData.strings.Login_CodeFloodError } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, completed: { updateState { return $0.withUpdatedChecking(false) @@ -191,7 +191,7 @@ func twoStepVerificationResetController(context: AccountContext, emailPattern: S checkCode() }, openEmailInaccessible: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: "Your remaining options are either to remember your password or to reset your account.", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }) let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) |> deliverOnMainQueue diff --git a/TelegramUI/TwoStepVerificationUnlockController.swift b/TelegramUI/TwoStepVerificationUnlockController.swift index e77d6393f3..3523401342 100644 --- a/TelegramUI/TwoStepVerificationUnlockController.swift +++ b/TelegramUI/TwoStepVerificationUnlockController.swift @@ -347,7 +347,7 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode: case .generic: text = presentationData.strings.Login_UnknownError } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }, completed: { let _ = (dataPromise.get() |> take(1) @@ -436,7 +436,7 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode: text = presentationData.strings.Login_UnknownError } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) })) } }, openForgotPassword: { @@ -482,10 +482,10 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode: state.checking = false return state } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) })) } else { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } case .notSet: break @@ -563,7 +563,7 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode: if hasSecureValues { text = presentationData.strings.TwoStepAuth_PasswordRemovePassportConfirmation } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { var disablePassword = false updateState { state in var state = state diff --git a/TelegramUI/UserInfoController.swift b/TelegramUI/UserInfoController.swift index 8db753ab88..445ba8d504 100644 --- a/TelegramUI/UserInfoController.swift +++ b/TelegramUI/UserInfoController.swift @@ -791,7 +791,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us if let cachedUserData = view.1 as? CachedUserData, cachedUserData.callsPrivate { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } @@ -805,7 +805,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us return (transaction.getPeer(peer.id), transaction.getPeer(currentPeerId)) } |> deliverOnMainQueue).start(next: { peer, current in if let peer = peer, let current = current { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { + presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: true) })]), nil) } @@ -1256,7 +1256,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us }, error: { _ in if let controller = controller { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) }