diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index bd9d21d0c2..1ad149bc9c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11616,3 +11616,6 @@ Sorry for the inconvenience."; "ChannelBoost.NoAds" = "Switch Off Ads"; "ChannelBoost.EnableNoAdsLevelText" = "Your channel needs **Level %1$@** to switch off ads."; + +"WebApp.TermsOfUse" = "Terms of Use"; +"WebApp.TermsOfUse_URL" = "https://telegram.org/tos/mini-apps"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index b85f4ef104..c1eb7bce93 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1001,7 +1001,7 @@ public protocol SharedAccountContext: AnyObject { func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController - func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController + func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, @escaping () -> Void) -> Void) -> ViewController func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift index 45cbfa89ff..0d9f444f23 100644 --- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift +++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift @@ -72,7 +72,7 @@ public enum ContactMultiselectionControllerMode { case peerSelection(searchChatList: Bool, searchGroups: Bool, searchChannels: Bool) case channelCreation case chatSelection(ChatSelection) - case premiumGifting + case premiumGifting(topSectionTitle: String?, topSectionPeers: [EnginePeer.Id]) case requestedUsersSelection } diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift index fc27194fcd..4000f84c53 100644 --- a/submodules/AccountContext/Sources/Premium.swift +++ b/submodules/AccountContext/Sources/Premium.swift @@ -45,7 +45,7 @@ public enum PremiumGiftSource: Equatable { case profile case attachMenu case settings - case chatList + case chatList([EnginePeer.Id]) case channelBoost case deeplink(String?) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 0ac6bf755a..9ef9b72a99 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -2308,8 +2308,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }, openStorageManagement: { }, openPasswordSetup: { }, openPremiumIntro: { - }, openPremiumGift: { + }, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { }, hideChatFolderUpdates: { @@ -3685,7 +3686,8 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() - }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: { + }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 109edbc4db..f9fc5f30c8 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -156,7 +156,7 @@ public final class ChatListShimmerNode: ASDisplayNode { let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() - }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in + }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in }, editPeer: { _ in }) interaction.isInlineMode = isInlineMode diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 8c949a6350..0bc6ae5aa3 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -101,8 +101,9 @@ public final class ChatListNodeInteraction { let openStorageManagement: () -> Void let openPasswordSetup: () -> Void let openPremiumIntro: () -> Void - let openPremiumGift: () -> Void + let openPremiumGift: ([EnginePeer.Id]) -> Void let openActiveSessions: () -> Void + let openBirthdaySetup: () -> Void let performActiveSessionAction: (NewSessionReview, Bool) -> Void let openChatFolderUpdates: () -> Void let hideChatFolderUpdates: () -> Void @@ -154,8 +155,9 @@ public final class ChatListNodeInteraction { openStorageManagement: @escaping () -> Void, openPasswordSetup: @escaping () -> Void, openPremiumIntro: @escaping () -> Void, - openPremiumGift: @escaping () -> Void, + openPremiumGift: @escaping ([EnginePeer.Id]) -> Void, openActiveSessions: @escaping () -> Void, + openBirthdaySetup: @escaping () -> Void, performActiveSessionAction: @escaping (NewSessionReview, Bool) -> Void, openChatFolderUpdates: @escaping () -> Void, hideChatFolderUpdates: @escaping () -> Void, @@ -196,6 +198,7 @@ public final class ChatListNodeInteraction { self.openPremiumIntro = openPremiumIntro self.openPremiumGift = openPremiumGift self.openActiveSessions = openActiveSessions + self.openBirthdaySetup = openBirthdaySetup self.performActiveSessionAction = performActiveSessionAction self.openChatFolderUpdates = openChatFolderUpdates self.hideChatFolderUpdates = hideChatFolderUpdates @@ -732,7 +735,11 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore: nodeInteraction?.openPremiumIntro() case .xmasPremiumGift: - nodeInteraction?.openPremiumGift() + nodeInteraction?.openPremiumGift([]) + case .setupBirthday: + nodeInteraction?.openBirthdaySetup() + case let .birthdayPremiumGift(peers): + nodeInteraction?.openPremiumGift(peers.map { $0.id }) case .reviewLogin: break } @@ -1064,7 +1071,11 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore: nodeInteraction?.openPremiumIntro() case .xmasPremiumGift: - nodeInteraction?.openPremiumGift() + nodeInteraction?.openPremiumGift([]) + case .setupBirthday: + nodeInteraction?.openBirthdaySetup() + case let .birthdayPremiumGift(peers): + nodeInteraction?.openPremiumGift(peers.map { $0.id }) case .reviewLogin: break } @@ -1682,11 +1693,11 @@ public final class ChatListNode: ListView { } let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads, forceDark: false, dismissed: nil) self.push?(controller) - }, openPremiumGift: { [weak self] in + }, openPremiumGift: { [weak self] peerIds in guard let self else { return } - let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList, completion: nil) + let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList(peerIds), completion: nil) self.push?(controller) }, openActiveSessions: { [weak self] in guard let self else { @@ -1707,6 +1718,8 @@ public final class ChatListNode: ListView { let recentSessionsController = self.context.sharedContext.makeRecentSessionsController(context: self.context, activeSessionsContext: activeSessionsContext) self.push?(recentSessionsController) }) + }, openBirthdaySetup: { + }, performActiveSessionAction: { [weak self] newSessionReview, isPositive in guard let self else { return @@ -1785,6 +1798,12 @@ public final class ChatListNode: ListView { self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in return true })) + case .setupBirthday: + //TODO:localize + let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .setupBirthday).startStandalone() + self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in + return true + })) default: break } @@ -1899,7 +1918,9 @@ public final class ChatListNode: ListView { return .single(.setupPassword) } } - if suggestions.contains(.xmasPremiumGift) { + if suggestions.contains(.setupBirthday) { + return .single(.setupBirthday) + } else if suggestions.contains(.xmasPremiumGift) { return .single(.xmasPremiumGift) } else if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager { return inAppPurchaseManager.availableProducts diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index d9e147d61a..ff79f1ef78 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -86,6 +86,8 @@ public enum ChatListNotice: Equatable { case premiumAnnualDiscount(discount: Int32) case premiumRestore(discount: Int32) case xmasPremiumGift + case setupBirthday + case birthdayPremiumGift(peers: [EnginePeer]) case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int) } diff --git a/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift b/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift index 5d4f82c091..62e391c799 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift @@ -220,6 +220,23 @@ class ChatListStorageInfoItemNode: ItemListRevealOptionsItemNode { case .xmasPremiumGift: titleString = parseMarkdownIntoAttributedString(item.strings.ChatList_PremiumXmasGiftTitle, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil })) textString = NSAttributedString(string: item.strings.ChatList_PremiumXmasGiftText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case .setupBirthday: + //TODO:localize + titleString = NSAttributedString(string: "Add your birthday! 🎂", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + textString = NSAttributedString(string: "Let your contacts know when you're celebrating.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .birthdayPremiumGift(peers): + //TODO:localize + let title: String + let text: String + if peers.count == 1, let peer = peers.first { + title = "It's \(peer.compactDisplayTitle)'s [birthday]() today! 🎂" + text = "Gift them Telegram Premium." + } else { + title = "\(peers.count) contacts have [birthdays]() today! 🎂" + text = "Gift them Telegram Premium." + } + titleString = parseMarkdownIntoAttributedString(title, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil })) + textString = NSAttributedString(string: text, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) case let .reviewLogin(newSessionReview, totalCount): spacing = 2.0 alignment = .center diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index 3cc929b696..75406cdcf6 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -231,52 +231,17 @@ public enum ChatRecordedMediaPreview: Equatable { case video(Video) } -public final class ChatManagingBot: Equatable { - public let bot: EnginePeer - public let isPaused: Bool - public let canReply: Bool - public let settingsUrl: String? - - public init(bot: EnginePeer, isPaused: Bool, canReply: Bool, settingsUrl: String?) { - self.bot = bot - self.isPaused = isPaused - self.canReply = canReply - self.settingsUrl = settingsUrl - } - - public static func ==(lhs: ChatManagingBot, rhs: ChatManagingBot) -> Bool { - if lhs === rhs { - return true - } - if lhs.bot != rhs.bot { - return false - } - if lhs.isPaused != rhs.isPaused { - return false - } - if lhs.canReply != rhs.canReply { - return false - } - if lhs.settingsUrl != rhs.settingsUrl { - return false - } - return true - } -} - public struct ChatContactStatus: Equatable { public var canAddContact: Bool public var canReportIrrelevantLocation: Bool public var peerStatusSettings: PeerStatusSettings? public var invitedBy: Peer? - public var managingBot: ChatManagingBot? - public init(canAddContact: Bool, canReportIrrelevantLocation: Bool, peerStatusSettings: PeerStatusSettings?, invitedBy: Peer?, managingBot: ChatManagingBot?) { + public init(canAddContact: Bool, canReportIrrelevantLocation: Bool, peerStatusSettings: PeerStatusSettings?, invitedBy: Peer?) { self.canAddContact = canAddContact self.canReportIrrelevantLocation = canReportIrrelevantLocation self.peerStatusSettings = peerStatusSettings self.invitedBy = invitedBy - self.managingBot = managingBot } public var isEmpty: Bool { @@ -305,9 +270,6 @@ public struct ChatContactStatus: Equatable { if !arePeersEqual(lhs.invitedBy, rhs.invitedBy) { return false } - if lhs.managingBot != rhs.managingBot { - return false - } return true } } diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index ef2b12b7b0..0d58ac6686 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -369,7 +369,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } } -private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactListPeer], presences: [EnginePeer.Id: EnginePeer.Presence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds: Set, peerRequiresPremiumForMessaging: [EnginePeer.Id: Bool], authorizationStatus: AccessType, warningSuppressed: (Bool, Bool), displaySortOptions: Bool, displayCallIcons: Bool, storySubscriptions: EngineStorySubscriptions?, topPeers: [EnginePeer], interaction: ContactListNodeInteraction) -> [ContactListNodeEntry] { +private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactListPeer], presences: [EnginePeer.Id: EnginePeer.Presence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds: Set, peerRequiresPremiumForMessaging: [EnginePeer.Id: Bool], authorizationStatus: AccessType, warningSuppressed: (Bool, Bool), displaySortOptions: Bool, displayCallIcons: Bool, storySubscriptions: EngineStorySubscriptions?, topPeers: [EnginePeer], topSectionTitle: String?, interaction: ContactListNodeInteraction) -> [ContactListNodeEntry] { var entries: [ContactListNodeEntry] = [] var commonHeader: ListViewItemHeader? @@ -528,7 +528,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis if !topPeers.isEmpty { let hasDeselectAll = !(selectionState?.selectedPeerIndices ?? [:]).isEmpty - let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { + let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(topSectionTitle ?? strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { interaction.deselectAll() }) @@ -722,8 +722,14 @@ public enum ContactListPresentation { } } + public enum TopPeers { + case none + case recent + case custom(title: String, peerIds: [EnginePeer.Id]) + } + case orderedByPresence(options: [ContactListAdditionalOption]) - case natural(options: [ContactListAdditionalOption], includeChatList: Bool, topPeers: Bool) + case natural(options: [ContactListAdditionalOption], includeChatList: Bool, topPeers: TopPeers) case search(Search) public var sortOrder: ContactsSortOrder? { @@ -1110,11 +1116,11 @@ public final class ContactListNode: ASDisplayNode { |> mapToSignal { presentation in var generateSections = false var includeChatList = false - var displayTopPeers = false - if case let .natural(_, includeChatListValue, displayTopPeersValue) = presentation { + var displayTopPeers: ContactListPresentation.TopPeers = .none + if case let .natural(_, includeChatListValue, topPeersValue) = presentation { generateSections = true includeChatList = includeChatListValue - displayTopPeers = displayTopPeersValue + displayTopPeers = topPeersValue } if case let .search(search) = presentation { @@ -1421,7 +1427,7 @@ public final class ContactListNode: ASDisplayNode { peers.append(.deviceContact(stableId, contact.0)) } - let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: peerRequiresPremiumForMessaging, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false, displayCallIcons: displayCallIcons, storySubscriptions: nil, topPeers: [], interaction: interaction) + let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: peerRequiresPremiumForMessaging, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false, displayCallIcons: displayCallIcons, storySubscriptions: nil, topPeers: [], topSectionTitle: nil, interaction: interaction) let previous = previousEntries.swap(entries) return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none, isSearch: isSearch)) } @@ -1481,11 +1487,36 @@ public final class ContactListNode: ASDisplayNode { chatListSignal = .single([]) } - let recentPeers: Signal - if displayTopPeers { - recentPeers = context.engine.peers.recentPeers() - } else { - recentPeers = .single(.disabled) + let topPeers: Signal<[EnginePeer], NoError> + let topPeersSectionTitle: String? + switch displayTopPeers { + case .recent: + topPeers = context.engine.peers.recentPeers() + |> map { recentPeers -> [EnginePeer] in + var topPeers: [EnginePeer] = [] + if case let .peers(peers) = recentPeers { + topPeers = peers.map(EnginePeer.init) + } + return topPeers + } + topPeersSectionTitle = nil + case let .custom(title, peerIds): + topPeers = context.engine.data.get( + EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) + ) + |> map { peers in + var result: [EnginePeer] = [] + for peer in peers.values { + if let peer { + result.append(peer) + } + } + return result + } + topPeersSectionTitle = title + case .none: + topPeers = .single([]) + topPeersSectionTitle = nil } return (combineLatest( @@ -1497,9 +1528,9 @@ public final class ContactListNode: ASDisplayNode { contactsAuthorization.get(), contactsWarningSuppressed.get(), self.storySubscriptions.get(), - recentPeers + topPeers ) - |> mapToQueue { view, chatListPeers, selectionState, pendingRemovalPeerIds, presentationData, authorizationStatus, warningSuppressed, storySubscriptions, recentPeers -> Signal in + |> mapToQueue { view, chatListPeers, selectionState, pendingRemovalPeerIds, presentationData, authorizationStatus, warningSuppressed, storySubscriptions, topPeers -> Signal in let signal = deferred { () -> Signal in if !view.2.isEmpty { context.account.viewTracker.refreshCanSendMessagesForPeerIds(peerIds: Array(view.2.keys)) @@ -1540,16 +1571,11 @@ public final class ContactListNode: ASDisplayNode { } } - var topPeers: [EnginePeer] = [] - if case let .peers(peers) = recentPeers { - topPeers = peers.map(EnginePeer.init) - } - var isEmpty = false if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty { isEmpty = true } - let entries = contactListNodeEntries(accountPeer: view.1, peers: peers, presences: view.0.presences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: view.2, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons, storySubscriptions: storySubscriptions, topPeers: topPeers, interaction: interaction) + let entries = contactListNodeEntries(accountPeer: view.1, peers: peers, presences: view.0.presences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: view.2, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons, storySubscriptions: storySubscriptions, topPeers: topPeers, topSectionTitle: topPeersSectionTitle, interaction: interaction) let previous = previousEntries.swap(entries) let previousSelection = previousSelectionState.swap(selectionState) let previousPendingRemovalPeerIds = previousPendingRemovalPeerIds.swap(pendingRemovalPeerIds) diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 32642969bf..b23c24310c 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -108,7 +108,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { case .presence: return .orderedByPresence(options: options) case .natural: - return .natural(options: options, includeChatList: false, topPeers: false) + return .natural(options: options, includeChatList: false, topPeers: .none) } } diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 2986c8bd50..ba800ab20b 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -48,8 +48,6 @@ public enum ContextMenuActionItemTextColor { public enum ContextMenuActionResult { case `default` case dismissWithoutContent - /// Temporary - static var safeStreamRecordingDismissWithoutContent: ContextMenuActionResult { .dismissWithoutContent } case custom(ContainedViewLayoutTransition) } @@ -116,9 +114,11 @@ public final class ContextMenuActionItem { public struct IconAnimation: Equatable { public var name: String + public var loop: Bool - public init(name: String) { + public init(name: String, loop: Bool = false) { self.name = name + self.loop = loop } } diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index eeac765ad8..fca0ee07ae 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -74,7 +74,7 @@ public protocol ContextControllerActionsStackItem: AnyObject { var dismissed: (() -> Void)? { get } } -protocol ContextControllerActionsListItemNode: ASDisplayNode { +public protocol ContextControllerActionsListItemNode: ASDisplayNode { func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void) func canBeHighlighted() -> Bool @@ -82,7 +82,7 @@ protocol ContextControllerActionsListItemNode: ASDisplayNode { func performAction() } -private final class ContextControllerActionsListActionItemNode: HighlightTrackingButtonNode, ContextControllerActionsListItemNode { +public final class ContextControllerActionsListActionItemNode: HighlightTrackingButtonNode, ContextControllerActionsListItemNode { private let getController: () -> ContextControllerProtocol? private let requestDismiss: (ContextMenuActionResult) -> Void private let requestUpdateAction: (AnyHashable, ContextMenuActionItem) -> Void @@ -103,7 +103,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin private var iconDisposable: Disposable? - init( + public init( getController: @escaping () -> ContextControllerProtocol?, requestDismiss: @escaping (ContextMenuActionResult) -> Void, requestUpdateAction: @escaping (AnyHashable, ContextMenuActionItem) -> Void, @@ -168,7 +168,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin self.iconDisposable?.dispose() } - override func didLoad() { + public override func didLoad() { super.didLoad() self.view.isExclusiveTouch = true @@ -196,19 +196,19 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin )) } - func canBeHighlighted() -> Bool { + public func canBeHighlighted() -> Bool { return self.item.action != nil } - func updateIsHighlighted(isHighlighted: Bool) { + public func updateIsHighlighted(isHighlighted: Bool) { self.highlightBackgroundNode.alpha = isHighlighted ? 1.0 : 0.0 } - func performAction() { + public func performAction() { self.pressed() } - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if self.titleLabelNode.tapAttributeAction != nil { if let result = self.titleLabelNode.hitTest(self.view.convert(point, to: self.titleLabelNode.view), with: event) { return result @@ -223,7 +223,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin self.accessibilityLabel = item.text } - func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void) { + public func update(presentationData: PresentationData, constrainedSize: CGSize) -> (minSize: CGSize, apply: (_ size: CGSize, _ transition: ContainedViewLayoutTransition) -> Void) { let sideInset: CGFloat = 16.0 let verticalInset: CGFloat = 11.0 let titleSubtitleSpacing: CGFloat = 1.0 @@ -365,8 +365,8 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: iconAnimation.name), color: titleColor, - startingPosition: .end, - loop: false + startingPosition: iconAnimation.loop ? .begin : .end, + loop: iconAnimation.loop )), environment: {}, containerSize: animatedIconSize diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index f2ea14db13..3ef03cf0d4 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -95,8 +95,9 @@ public final class HashtagSearchController: TelegramBaseController { }, openStorageManagement: { }, openPasswordSetup: { }, openPremiumIntro: { - }, openPremiumGift: { + }, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { }, hideChatFolderUpdates: { diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift index 6e39581e13..2e0fb376bd 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift @@ -719,7 +719,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll @objc private func createActionButtonPressed() { var proceedImpl: ((String, String?) -> Void)? - let titleController = stickerPackEditTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 128, apply: { [weak self] title in + let titleController = stickerPackEditTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 64, apply: { [weak self] title in if let strongSelf = self, let title = title { strongSelf.shortNameSuggestionDisposable.set((strongSelf.context.engine.stickers.getStickerSetShortNameSuggestion(title: title) |> deliverOnMainQueue).start(next: { suggestedShortName in @@ -735,7 +735,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll guard let strongSelf = self else { return } - let controller = importStickerPackShortNameController(context: strongSelf.context, title: strongSelf.presentationData.strings.ImportStickerPack_ChooseLink, text: strongSelf.presentationData.strings.ImportStickerPack_ChooseLinkDescription, placeholder: "", value: suggestedShortName, maxLength: 60, existingAlertController: titleController, apply: { [weak self] shortName in + let controller = importStickerPackShortNameController(context: strongSelf.context, title: strongSelf.presentationData.strings.ImportStickerPack_ChooseLink, text: strongSelf.presentationData.strings.ImportStickerPack_ChooseLinkDescription, placeholder: "", value: suggestedShortName, maxLength: 64, existingAlertController: titleController, apply: { [weak self] shortName in if let shortName = shortName { self?.createStickerSet(title: title, shortName: shortName) } diff --git a/submodules/InstantPageCache/Sources/CachedInternalInstantPages.swift b/submodules/InstantPageCache/Sources/CachedInternalInstantPages.swift index aa5308d68c..3a3d109d25 100644 --- a/submodules/InstantPageCache/Sources/CachedInternalInstantPages.swift +++ b/submodules/InstantPageCache/Sources/CachedInternalInstantPages.swift @@ -48,6 +48,14 @@ public func cachedPrivacyPage(context: AccountContext) -> Signal Signal { + var privacyUrl = context.sharedContext.currentPresentationData.with { $0 }.strings.WebApp_TermsOfUse_URL + if privacyUrl == "WebApp.TermsOfUse_URL" || privacyUrl.isEmpty { + privacyUrl = "https://telegram.org/tos/mini-apps" + } + return cachedInternalInstantPage(context: context, url: privacyUrl) +} + private func cachedInternalInstantPage(context: AccountContext, url: String) -> Signal { let (cachedUrl, anchor) = extractAnchor(string: url) return cachedInstantPage(engine: context.engine, url: cachedUrl) diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 43710530f4..c86a2fa029 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -488,8 +488,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { var thumbnailItem: StickerPackThumbnailItem? var resourceReference: MediaResourceReference? if let thumbnail = item.packInfo.thumbnail { - if item.packInfo.flags.contains(.isAnimated) || item.packInfo.flags.contains(.isVideo) { - thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, item.packInfo.flags.contains(.isVideo), item.packInfo.flags.contains(.isCustomTemplateEmoji)) + if thumbnail.typeHint != .generic { + thumbnailItem = .animated(thumbnail.resource, thumbnail.dimensions, thumbnail.typeHint == .video, item.packInfo.flags.contains(.isCustomTemplateEmoji)) } else { thumbnailItem = .still(thumbnail) } @@ -844,7 +844,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { var imageSize = PixelDimensions(width: 512, height: 512) var immediateThumbnailData: Data? if let data = item.packInfo.immediateThumbnailData { - if item.packInfo.flags.contains(.isVideo) { + if item.packInfo.thumbnail?.typeHint == .video || item.topItem?.file.isVideoSticker == true { imageSize = PixelDimensions(width: 100, height: 100) } immediateThumbnailData = data diff --git a/submodules/LocalAuth/Sources/LocalAuth.swift b/submodules/LocalAuth/Sources/LocalAuth.swift index ad67c038e6..7796fc355e 100644 --- a/submodules/LocalAuth/Sources/LocalAuth.swift +++ b/submodules/LocalAuth/Sources/LocalAuth.swift @@ -21,23 +21,6 @@ public struct LocalAuth { case error(Error) } - #if targetEnvironment(simulator) - public final class PrivateKey { - public let publicKeyRepresentation: Data - - fileprivate init() { - self.publicKeyRepresentation = Data(count: 32) - } - - public func encrypt(data: Data) -> Data? { - return data - } - - public func decrypt(data: Data) -> DecryptionResult { - return .result(data) - } - } - #else public final class PrivateKey { private let privateKey: SecKey private let publicKey: SecKey @@ -81,7 +64,6 @@ public struct LocalAuth { return .result(result) } } - #endif public static var biometricAuthentication: LocalAuthBiometricAuthentication? { let context = LAContext() @@ -175,18 +157,7 @@ public struct LocalAuth { return seedId; } - public static func getOrCreatePrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? { - if let key = self.getPrivateKey(baseAppBundleId: baseAppBundleId, keyId: keyId) { - return key - } else { - return self.addPrivateKey(baseAppBundleId: baseAppBundleId, keyId: keyId) - } - } - - private static func getPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? { - #if targetEnvironment(simulator) - return PrivateKey() - #else + public static func getPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? { guard let bundleSeedId = self.bundleSeedId() else { return nil } @@ -225,7 +196,6 @@ public struct LocalAuth { let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data) return result - #endif } public static func removePrivateKey(baseAppBundleId: String, keyId: Data) -> Bool { @@ -251,10 +221,7 @@ public struct LocalAuth { return true } - private static func addPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? { - #if targetEnvironment(simulator) - return PrivateKey() - #else + public static func addPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? { guard let bundleSeedId = self.bundleSeedId() else { return nil } @@ -295,6 +262,5 @@ public struct LocalAuth { let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data) return result - #endif } } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index c4859c5482..d04d19a559 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -2248,7 +2248,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { if !items.isEmpty { items.append(.separator) } - items.append(.action(ContextMenuActionItem(text: hasGeneric ? strings.Attachment_EnableSpoiler : strings.Attachment_DisableSpoiler, icon: { _ in return nil }, animationName: "anim_spoiler", action: { [weak self] _, f in + items.append(.action(ContextMenuActionItem(text: hasGeneric ? strings.Attachment_EnableSpoiler : strings.Attachment_DisableSpoiler, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation( + name: "anim_spoiler", + loop: true + ), action: { [weak self] _, f in f(.default) guard let strongSelf = self else { return diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTRequestErrorContext.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTRequestErrorContext.h index c7105cb69c..b1e9361e08 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTRequestErrorContext.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTRequestErrorContext.h @@ -8,7 +8,6 @@ @property (nonatomic) NSUInteger internalServerErrorCount; @property (nonatomic) NSUInteger floodWaitSeconds; -@property (nonatomic, strong) NSString *floodWaitErrorText; @property (nonatomic) bool waitingForTokenExport; @property (nonatomic, strong) id waitingForRequestToComplete; diff --git a/submodules/MtProtoKit/Sources/MTRequestMessageService.m b/submodules/MtProtoKit/Sources/MTRequestMessageService.m index a81adedb34..83457c3c61 100644 --- a/submodules/MtProtoKit/Sources/MTRequestMessageService.m +++ b/submodules/MtProtoKit/Sources/MTRequestMessageService.m @@ -808,7 +808,7 @@ } restartRequest = true; } - else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound || [rpcError.errorDescription rangeOfString:@"FLOOD_PREMIUM_WAIT_"].location != NSNotFound) { + else if (rpcError.errorCode == 420 || [rpcError.errorDescription rangeOfString:@"FLOOD_WAIT_"].location != NSNotFound) { if (request.errorContext == nil) request.errorContext = [[MTRequestErrorContext alloc] init]; @@ -821,32 +821,6 @@ if ([scanner scanInt:&errorWaitTime]) { request.errorContext.floodWaitSeconds = errorWaitTime; - request.errorContext.floodWaitErrorText = rpcError.errorDescription; - - if (request.shouldContinueExecutionWithErrorContext != nil) - { - if (request.shouldContinueExecutionWithErrorContext(request.errorContext)) - { - restartRequest = true; - request.errorContext.minimalExecuteTime = MAX(request.errorContext.minimalExecuteTime, MTAbsoluteSystemTime() + (CFAbsoluteTime)errorWaitTime); - } - } - else - { - restartRequest = true; - request.errorContext.minimalExecuteTime = MAX(request.errorContext.minimalExecuteTime, MTAbsoluteSystemTime() + (CFAbsoluteTime)errorWaitTime); - } - } - } else if ([rpcError.errorDescription rangeOfString:@"FLOOD_PREMIUM_WAIT_"].location != NSNotFound) { - int errorWaitTime = 0; - - NSScanner *scanner = [[NSScanner alloc] initWithString:rpcError.errorDescription]; - [scanner scanUpToString:@"FLOOD_PREMIUM_WAIT_" intoString:nil]; - [scanner scanString:@"FLOOD_PREMIUM_WAIT_" intoString:nil]; - if ([scanner scanInt:&errorWaitTime]) - { - request.errorContext.floodWaitSeconds = errorWaitTime; - request.errorContext.floodWaitErrorText = rpcError.errorDescription; if (request.shouldContinueExecutionWithErrorContext != nil) { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 7c6b01b67f..40a62f57d8 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -2999,7 +2999,6 @@ final class PostboxImpl { let startTime = CFAbsoluteTimeGetCurrent() - self.valueBox.begin() let transaction = Transaction(queue: self.queue, postbox: self) self.afterBegin(transaction: transaction) @@ -3014,7 +3013,6 @@ final class PostboxImpl { postboxLog("Postbox transaction took \(transactionDuration * 1000.0) ms, from: \(file), on:\(line)") } - let _ = self.isInTransaction.swap(false) if let currentUpdatedState = self.currentUpdatedState { diff --git a/submodules/Postbox/Sources/TimeBasedCleanup.swift b/submodules/Postbox/Sources/TimeBasedCleanup.swift index d0b9fddc61..1c1a0be7eb 100644 --- a/submodules/Postbox/Sources/TimeBasedCleanup.swift +++ b/submodules/Postbox/Sources/TimeBasedCleanup.swift @@ -19,9 +19,8 @@ public func printOpenFiles() { var flags: Int32 = 0 var fd: Int32 = 0 var buf = Data(count: Int(MAXPATHLEN) + 1) - let maxFd = min(1024, FD_SETSIZE) - while fd < maxFd { + while fd < FD_SETSIZE { errno = 0; flags = fcntl(fd, F_GETFD, 0); if flags == -1 && errno != 0 { diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index a4132d0775..7aff6f6db1 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -848,7 +848,6 @@ struct PremiumIntroConfiguration { } #endif - var businessPerks: [PremiumPerk] = [] if let values = data["business_promo_order"] as? [String] { for value in values { diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index edaffa43d0..3ff0e43f50 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -332,6 +332,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { public var displayTail: Bool = true public var forceTailToRight: Bool = false public var forceDark: Bool = false + public var hideBackground: Bool = false private var didAnimateIn: Bool = false public private(set) var isAnimatingOut: Bool = false @@ -1900,7 +1901,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { externalExpansionView: self.view, customContentView: nil, useOpaqueTheme: false, - hideBackground: false, + hideBackground: self.hideBackground, stateContext: nil, addImage: nil ) diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 9bf1d44c49..3d74e47806 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -222,7 +222,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() - }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: { + }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index f9201ecc1c..63976f42a8 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -371,7 +371,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() }, present: { _ in - }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: { + }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index b3fd88b49e..6c3ab733c3 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -211,7 +211,7 @@ private final class ShareContentInfoView: UIView { let accentColor = params.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0) if self.arrowIcon == nil { - if let templateImage = UIImage(bundleImageName: "Item List/InlineTextRightArrow") { + if let templateImage = UIImage(bundleImageName: "Settings/TextArrowRight") { let scaleFactor: CGFloat = 0.8 let imageSize = CGSize(width: floor(templateImage.size.width * scaleFactor), height: floor(templateImage.size.height * scaleFactor)) self.arrowIcon = generateImage(imageSize, contextGenerator: { size, context in diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift index ba685bfdb4..1ba3c3e66d 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift @@ -297,8 +297,8 @@ public func preloadedStickerPackThumbnail(account: Account, info: StickerPackCol let signal = Signal { subscriber in let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)).start() let dataDisposable: Disposable - if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) { - dataDisposable = chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: thumbnail.resource, isVideo: info.flags.contains(.isVideo), width: 80, height: 80, synchronousLoad: false).start(next: { data in + if thumbnail.typeHint != .generic { + dataDisposable = chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: thumbnail.resource, isVideo: thumbnail.typeHint == .video, width: 80, height: 80, synchronousLoad: false).start(next: { data in if data.complete { subscriber.putNext(true) subscriber.putCompletion() diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index a2058cde6a..cf1eb65ab7 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -1140,6 +1140,7 @@ private final class StickerPackContainer: ASDisplayNode { private let stickerPickerInputData = Promise() private func presentAddStickerOptions() { + //TODO:localize let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] items.append(ActionSheetButtonItem(title: "Create a New Sticker", color: .accent, action: { [weak actionSheet, weak self] in @@ -1167,22 +1168,6 @@ private final class StickerPackContainer: ASDisplayNode { ])]) self.presentInGlobalOverlay(actionSheet, nil) - - let emojiItems = EmojiPagerContentComponent.emojiInputData( - context: self.context, - animationCache: self.context.animationCache, - animationRenderer: self.context.animationRenderer, - isStandalone: false, - subject: .emoji, - hasTrending: true, - topReactionItems: [], - areUnicodeEmojiEnabled: true, - areCustomEmojiEnabled: true, - chatPeerId: self.context.account.peerId, - hasSearch: true, - forceHasPremium: true - ) - let stickerItems = EmojiPagerContentComponent.stickerInputData( context: self.context, animationCache: self.context.animationCache, @@ -1191,16 +1176,14 @@ private final class StickerPackContainer: ASDisplayNode { stickerOrderedItemListCollectionIds: [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers, Namespaces.OrderedItemList.CloudAllPremiumStickers], chatPeerId: self.context.account.peerId, hasSearch: true, - hasTrending: true, + hasTrending: false, forceHasPremium: true ) - let signal = combineLatest( - queue: .mainQueue(), - emojiItems, - stickerItems - ) |> map { emoji, stickers -> StickerPickerInput in - return StickerPickerInputData(emoji: emoji, stickers: stickers, gifs: nil) + let signal = stickerItems + |> deliverOnMainQueue + |> map { stickers -> StickerPickerInput in + return StickerPickerInputData(emoji: nil, stickers: stickers, gifs: nil) } self.stickerPickerInputData.set(signal) @@ -1224,7 +1207,7 @@ private final class StickerPackContainer: ASDisplayNode { context: context, source: result, transitionArguments: (transitionView, transitionRect, transitionImage), - completion: { file in + completion: { file, commit in dismissImpl?() let sticker = ImportSticker( resource: file.resource, @@ -1236,6 +1219,8 @@ private final class StickerPackContainer: ASDisplayNode { let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker) |> deliverOnMainQueue).start(completed: { + commit() + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) @@ -1300,7 +1285,7 @@ private final class StickerPackContainer: ASDisplayNode { context: context, source: initialFile, transitionArguments: nil, - completion: { file in + completion: { file, commit in let sticker = ImportSticker( resource: file.resource, emojis: ["😀"], @@ -1312,6 +1297,8 @@ private final class StickerPackContainer: ASDisplayNode { let _ = (context.engine.stickers.replaceSticker(previousSticker: .stickerPack(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), media: initialFile), sticker: sticker) |> deliverOnMainQueue).start(completed: { + commit() + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) }) @@ -1327,7 +1314,7 @@ private final class StickerPackContainer: ASDisplayNode { let context = self.context //TODO:localize var dismissImpl: (() -> Void)? - let controller = stickerPackEditTitleController(context: context, title: "Edit Sticker Set Name", text: "Choose a new name for your set.", placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: self.updatedTitle ?? info.title, maxLength: 128, apply: { [weak self] title in + let controller = stickerPackEditTitleController(context: context, title: "Edit Sticker Set Name", text: "Choose a new name for your set.", placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: self.updatedTitle ?? info.title, maxLength: 64, apply: { [weak self] title in guard let self, let title else { return } diff --git a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift index 02b443717c..8f0ec3483c 100644 --- a/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPeekUI/Sources/StickerPreviewPeekContent.swift @@ -392,7 +392,7 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode { items.append(reaction) } - let selectedItems = ValuePromise>() + let selectedItems = ValuePromise>(Set()) //TODO:localize let reactionContextNode = ReactionContextNode( context: self.context, @@ -440,6 +440,7 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode { layoutImpl?(transition) } ) + reactionContextNode.hideBackground = true reactionContextNode.displayTail = true reactionContextNode.forceTailToRight = true reactionContextNode.forceDark = true diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 787db8d696..43ee98d43f 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -671,7 +671,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) } dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) } dict[-1721619444] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) } - dict[-1395233698] = { return Api.PeerSettings.parse_peerSettings($0) } + dict[-1525149427] = { return Api.PeerSettings.parse_peerSettings($0) } dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) } dict[-1770029977] = { return Api.PhoneCall.parse_phoneCall($0) } dict[912311057] = { return Api.PhoneCall.parse_phoneCallAccepted($0) } diff --git a/submodules/TelegramApi/Sources/Api16.swift b/submodules/TelegramApi/Sources/Api16.swift index 88dfb1d60c..258955a62f 100644 --- a/submodules/TelegramApi/Sources/Api16.swift +++ b/submodules/TelegramApi/Sources/Api16.swift @@ -898,28 +898,26 @@ public extension Api { } public extension Api { enum PeerSettings: TypeConstructorDescription { - case peerSettings(flags: Int32, geoDistance: Int32?, requestChatTitle: String?, requestChatDate: Int32?, businessBotId: Int64?, businessBotManageUrl: String?) + case peerSettings(flags: Int32, geoDistance: Int32?, requestChatTitle: String?, requestChatDate: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate, let businessBotId, let businessBotManageUrl): + case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate): if boxed { - buffer.appendInt32(-1395233698) + buffer.appendInt32(-1525149427) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 6) != 0 {serializeInt32(geoDistance!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 9) != 0 {serializeString(requestChatTitle!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 9) != 0 {serializeInt32(requestChatDate!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {serializeInt64(businessBotId!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 13) != 0 {serializeString(businessBotManageUrl!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate, let businessBotId, let businessBotManageUrl): - return ("peerSettings", [("flags", flags as Any), ("geoDistance", geoDistance as Any), ("requestChatTitle", requestChatTitle as Any), ("requestChatDate", requestChatDate as Any), ("businessBotId", businessBotId as Any), ("businessBotManageUrl", businessBotManageUrl as Any)]) + case .peerSettings(let flags, let geoDistance, let requestChatTitle, let requestChatDate): + return ("peerSettings", [("flags", flags as Any), ("geoDistance", geoDistance as Any), ("requestChatTitle", requestChatTitle as Any), ("requestChatDate", requestChatDate as Any)]) } } @@ -932,18 +930,12 @@ public extension Api { if Int(_1!) & Int(1 << 9) != 0 {_3 = parseString(reader) } var _4: Int32? if Int(_1!) & Int(1 << 9) != 0 {_4 = reader.readInt32() } - var _5: Int64? - if Int(_1!) & Int(1 << 13) != 0 {_5 = reader.readInt64() } - var _6: String? - if Int(_1!) & Int(1 << 13) != 0 {_6 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 6) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 9) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 9) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 13) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 13) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.PeerSettings.peerSettings(flags: _1!, geoDistance: _2, requestChatTitle: _3, requestChatDate: _4, businessBotId: _5, businessBotManageUrl: _6) + if _c1 && _c2 && _c3 && _c4 { + return Api.PeerSettings.peerSettings(flags: _1!, geoDistance: _2, requestChatTitle: _3, requestChatDate: _4) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index 9338176e30..a505f25c98 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -221,21 +221,6 @@ public extension Api.functions.account { }) } } -public extension Api.functions.account { - static func disablePeerConnectedBot(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1581481689) - peer.serialize(buffer, true) - return (FunctionDescription(name: "account.disablePeerConnectedBot", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} public extension Api.functions.account { static func finishTakeoutSession(flags: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -1269,22 +1254,6 @@ public extension Api.functions.account { }) } } -public extension Api.functions.account { - static func toggleConnectedBotPaused(peer: Api.InputPeer, paused: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1684934807) - peer.serialize(buffer, true) - paused.serialize(buffer, true) - return (FunctionDescription(name: "account.toggleConnectedBotPaused", parameters: [("peer", String(describing: peer)), ("paused", String(describing: paused))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } -} public extension Api.functions.account { static func toggleUsername(username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 1114aaa8bc..6ed005ceef 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -144,13 +144,13 @@ public class UnauthorizedAccount { return accountManager.transaction { transaction -> (LocalizationSettings?, ProxySettings?) in return (transaction.getSharedData(SharedDataKeys.localizationSettings)?.get(LocalizationSettings.self), transaction.getSharedData(SharedDataKeys.proxySettings)?.get(ProxySettings.self)) } - |> mapToSignal { localizationSettings, proxySettings -> Signal<(LocalizationSettings?, ProxySettings?, NetworkSettings?, AppConfiguration), NoError> in - return self.postbox.transaction { transaction -> (LocalizationSettings?, ProxySettings?, NetworkSettings?, AppConfiguration) in - return (localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self), transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue) + |> mapToSignal { localizationSettings, proxySettings -> Signal<(LocalizationSettings?, ProxySettings?, NetworkSettings?), NoError> in + return self.postbox.transaction { transaction -> (LocalizationSettings?, ProxySettings?, NetworkSettings?) in + return (localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self)) } } - |> mapToSignal { localizationSettings, proxySettings, networkSettings, appConfiguration -> Signal in - return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: false, appConfiguration: appConfiguration) + |> mapToSignal { (localizationSettings, proxySettings, networkSettings) -> Signal in + return initializedNetwork(accountId: self.id, arguments: self.networkArguments, supplementary: false, datacenterId: Int(masterDatacenterId), keychain: keychain, basePath: self.basePath, testingEnvironment: self.testingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: false) |> map { network in let updated = UnauthorizedAccount(networkArguments: self.networkArguments, id: self.id, rootPath: self.rootPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network) updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get()) @@ -248,7 +248,7 @@ public func accountWithId(accountManager: AccountManager map { network -> AccountResult in return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: unauthorizedState.isTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) } @@ -257,7 +257,7 @@ public func accountWithId(accountManager: AccountManager mapToSignal { phoneNumber in - return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig) + return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: phoneNumber, useRequestTimeoutTimers: useRequestTimeoutTimers) |> map { network -> AccountResult in return .authorized(Account(accountManager: accountManager, id: id, basePath: path, testingEnvironment: authorizedState.isTestingEnvironment, postbox: postbox, network: network, networkArguments: networkArguments, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods, supplementary: supplementary)) } @@ -267,7 +267,7 @@ public func accountWithId(accountManager: AccountManager map { network -> AccountResult in return .unauthorized(UnauthorizedAccount(networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) } @@ -889,11 +889,6 @@ public func accountBackupData(postbox: Postbox) -> Signal map { network -> AccountStateManager? in Logger.shared.log("StandaloneStateManager", "received network") diff --git a/submodules/TelegramCore/Sources/Network/Download.swift b/submodules/TelegramCore/Sources/Network/Download.swift index 7966a99445..2ebe6cff22 100644 --- a/submodules/TelegramCore/Sources/Network/Download.swift +++ b/submodules/TelegramCore/Sources/Network/Download.swift @@ -103,7 +103,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { self.context.authTokenForDatacenter(withIdRequired: self.datacenterId, authToken:self.mtProto.requiredAuthToken, masterDatacenterId: self.mtProto.authTokenMasterDatacenterId) } - static func uploadPart(multiplexedManager: MultiplexedRequestManager, datacenterId: Int, consumerId: Int64, tag: MediaResourceFetchTag?, fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false, onFloodWaitError: ((String) -> Void)? = nil) -> Signal { + static func uploadPart(multiplexedManager: MultiplexedRequestManager, datacenterId: Int, consumerId: Int64, tag: MediaResourceFetchTag?, fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false) -> Signal { let saveFilePart: (FunctionDescription, Buffer, DeserializeFunctionResponse) if asBigPart { let totalParts: Int32 @@ -117,7 +117,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { saveFilePart = Api.functions.upload.saveFilePart(fileId: fileId, filePart: Int32(index), bytes: Buffer(data: data)) } - return multiplexedManager.request(to: .main(datacenterId), consumerId: consumerId, resourceId: nil, data: wrapMethodBody(saveFilePart, useCompression: useCompression), tag: tag, continueInBackground: true, onFloodWaitError: onFloodWaitError, expectedResponseSize: nil) + return multiplexedManager.request(to: .main(datacenterId), consumerId: consumerId, resourceId: nil, data: wrapMethodBody(saveFilePart, useCompression: useCompression), tag: tag, continueInBackground: true, expectedResponseSize: nil) |> mapError { error -> UploadPartError in if error.errorCode == 400 { return .invalidMedia @@ -130,7 +130,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { } } - func uploadPart(fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false, onFloodWaitError: ((String) -> Void)? = nil) -> Signal { + func uploadPart(fileId: Int64, index: Int, data: Data, asBigPart: Bool, bigTotalParts: Int? = nil, useCompression: Bool = false) -> Signal { return Signal { subscriber in let request = MTRequest() @@ -159,13 +159,6 @@ class Download: NSObject, MTRequestMessageServiceDelegate { request.dependsOnPasswordEntry = false request.shouldContinueExecutionWithErrorContext = { errorContext in - guard let errorContext = errorContext else { - return true - } - if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText { - onFloodWaitError(errorText) - } - return true } @@ -302,7 +295,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { |> retryRequest } - func request(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), expectedResponseSize: Int32? = nil, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil) -> Signal { + func request(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), expectedResponseSize: Int32? = nil, automaticFloodWait: Bool = true) -> Signal { return Signal { subscriber in let request = MTRequest() request.expectedResponseSize = expectedResponseSize ?? 0 @@ -321,9 +314,6 @@ class Download: NSObject, MTRequestMessageServiceDelegate { guard let errorContext = errorContext else { return true } - if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText { - onFloodWaitError(errorText) - } if errorContext.floodWaitSeconds > 0 && !automaticFloodWait { return false } @@ -354,7 +344,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { } } - func requestWithAdditionalData(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, failOnServerErrors: Bool = false, expectedResponseSize: Int32? = nil) -> Signal<(T, Double), (MTRpcError, Double)> { + func requestWithAdditionalData(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), automaticFloodWait: Bool = true, failOnServerErrors: Bool = false, expectedResponseSize: Int32? = nil) -> Signal<(T, Double), (MTRpcError, Double)> { return Signal { subscriber in let request = MTRequest() request.expectedResponseSize = expectedResponseSize ?? 0 @@ -373,9 +363,6 @@ class Download: NSObject, MTRequestMessageServiceDelegate { guard let errorContext = errorContext else { return true } - if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText { - onFloodWaitError(errorText) - } if errorContext.floodWaitSeconds > 0 && !automaticFloodWait { return false } @@ -409,7 +396,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate { } } - func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, failOnServerErrors: Bool = false, logPrefix: String = "", expectedResponseSize: Int32? = nil) -> Signal<(Any, NetworkResponseInfo), (MTRpcError, Double)> { + func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true, failOnServerErrors: Bool = false, logPrefix: String = "", expectedResponseSize: Int32? = nil) -> Signal<(Any, NetworkResponseInfo), (MTRpcError, Double)> { let requestService = self.requestService return Signal { subscriber in let request = MTRequest() @@ -429,9 +416,6 @@ class Download: NSObject, MTRequestMessageServiceDelegate { guard let errorContext = errorContext else { return true } - if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText { - onFloodWaitError(errorText) - } if errorContext.floodWaitSeconds > 0 && !automaticFloodWait { return false } diff --git a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift index 83d0c4b40c..5c9a473a3a 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift @@ -104,14 +104,14 @@ private struct DownloadWrapper { self.useMainConnection = useMainConnection } - func request(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, expectedResponseSize: Int32?, onFloodWaitError: @escaping (String) -> Void) -> Signal<(T, NetworkResponseInfo), MTRpcError> { + func request(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, expectedResponseSize: Int32?) -> Signal<(T, NetworkResponseInfo), MTRpcError> { let target: MultiplexedRequestTarget if self.isCdn { target = .cdn(Int(self.datacenterId)) } else { target = .main(Int(self.datacenterId)) } - return network.multiplexedRequestManager.requestWithAdditionalInfo(to: target, consumerId: self.consumerId, resourceId: self.resourceId, data: data, tag: tag, continueInBackground: continueInBackground, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize) + return network.multiplexedRequestManager.requestWithAdditionalInfo(to: target, consumerId: self.consumerId, resourceId: self.resourceId, data: data, tag: tag, continueInBackground: continueInBackground, expectedResponseSize: expectedResponseSize) |> mapError { error, _ -> MTRpcError in return error } @@ -192,7 +192,7 @@ private final class MultipartCdnHashSource { clusterContext = ClusterContext(disposable: disposable) self.clusterContexts[offset] = clusterContext - disposable.set((self.masterDownload.request(Api.functions.upload.getCdnFileHashes(fileToken: Buffer(data: self.fileToken), offset: offset), tag: nil, continueInBackground: self.continueInBackground, expectedResponseSize: nil, onFloodWaitError: { _ in }) + disposable.set((self.masterDownload.request(Api.functions.upload.getCdnFileHashes(fileToken: Buffer(data: self.fileToken), offset: offset), tag: nil, continueInBackground: self.continueInBackground, expectedResponseSize: nil) |> map { partHashes, _ -> [Int64: Data] in var parsedPartHashes: [Int64: Data] = [:] for part in partHashes { @@ -322,7 +322,7 @@ private enum MultipartFetchSource { } } - func request(offset: Int64, limit: Int64, tag: MediaResourceFetchTag?, resource: TelegramMediaResource, resourceReference: FetchResourceReference, fileReference: Data?, continueInBackground: Bool, onFloodWaitError: @escaping (String) -> Void) -> Signal<(Data, NetworkResponseInfo), MultipartFetchDownloadError> { + func request(offset: Int64, limit: Int64, tag: MediaResourceFetchTag?, resource: TelegramMediaResource, resourceReference: FetchResourceReference, fileReference: Data?, continueInBackground: Bool) -> Signal<(Data, NetworkResponseInfo), MultipartFetchDownloadError> { var resourceReferenceValue: MediaResourceReference? switch resourceReference { case .forceRevalidate: @@ -348,9 +348,7 @@ private enum MultipartFetchSource { case .revalidate: return .fail(.revalidateMediaReference) case let .location(parsedLocation): - return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit), onFloodWaitError: { error in - onFloodWaitError(error) - }) + return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit)) |> mapError { error -> MultipartFetchDownloadError in if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") { return .revalidateMediaReference @@ -382,9 +380,7 @@ private enum MultipartFetchSource { } } case let .web(_, location): - return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit), onFloodWaitError: { error in - onFloodWaitError(error) - }) + return download.request(Api.functions.upload.getWebFile(location: location, offset: Int32(offset), limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground, expectedResponseSize: Int32(limit)) |> mapError { error -> MultipartFetchDownloadError in if error.errorDescription == "WEBFILE_NOT_AVAILABLE" { return .webfileNotAvailable @@ -408,9 +404,7 @@ private enum MultipartFetchSource { updatedLength += 1 } - let part = download.request(Api.functions.upload.getCdnFile(fileToken: Buffer(data: fileToken), offset: offset, limit: Int32(updatedLength)), tag: nil, continueInBackground: continueInBackground, expectedResponseSize: Int32(updatedLength), onFloodWaitError: { error in - onFloodWaitError(error) - }) + let part = download.request(Api.functions.upload.getCdnFile(fileToken: Buffer(data: fileToken), offset: offset, limit: Int32(updatedLength)), tag: nil, continueInBackground: continueInBackground, expectedResponseSize: Int32(updatedLength)) |> mapError { _ -> MultipartFetchDownloadError in return .generic } @@ -729,13 +723,6 @@ private final class MultipartFetchManager { } } - - private func processFloodWaitError(error: String) { - if error.hasPrefix("FLOOD_PREMIUM_WAIT") { - self.network.addNetworkSpeedLimitedEvent(event: .download) - } - } - func checkState() { guard let currentIntervals = self.currentIntervals else { return @@ -849,15 +836,7 @@ private final class MultipartFetchManager { } let partSize: Int32 = Int32(downloadRange.upperBound - downloadRange.lowerBound) - let queue = self.queue - let part = self.source.request(offset: downloadRange.lowerBound, limit: downloadRange.upperBound - downloadRange.lowerBound, tag: self.parameters?.tag, resource: self.resource, resourceReference: self.resourceReference, fileReference: self.fileReference, continueInBackground: self.continueInBackground, onFloodWaitError: { [weak self] error in - queue.async { - guard let self else { - return - } - self.processFloodWaitError(error: error) - } - }) + let part = self.source.request(offset: downloadRange.lowerBound, limit: downloadRange.upperBound - downloadRange.lowerBound, tag: self.parameters?.tag, resource: self.resource, resourceReference: self.resourceReference, fileReference: self.fileReference, continueInBackground: self.continueInBackground) |> deliverOn(self.queue) let partDisposable = MetaDisposable() self.fetchingParts[downloadRange.lowerBound] = FetchingPart(size: Int64(downloadRange.count), disposable: partDisposable) @@ -940,7 +919,7 @@ private final class MultipartFetchManager { case let .cdn(_, _, fileToken, _, _, _, masterDownload, _): if !strongSelf.reuploadingToCdn { strongSelf.reuploadingToCdn = true - let reupload: Signal<[Api.FileHash], NoError> = masterDownload.request(Api.functions.upload.reuploadCdnFile(fileToken: Buffer(data: fileToken), requestToken: Buffer(data: token)), tag: nil, continueInBackground: strongSelf.continueInBackground, expectedResponseSize: nil, onFloodWaitError: { _ in }) + let reupload: Signal<[Api.FileHash], NoError> = masterDownload.request(Api.functions.upload.reuploadCdnFile(fileToken: Buffer(data: fileToken), requestToken: Buffer(data: token)), tag: nil, continueInBackground: strongSelf.continueInBackground, expectedResponseSize: nil) |> map { result, _ -> [Api.FileHash] in return result } diff --git a/submodules/TelegramCore/Sources/Network/MultipartUpload.swift b/submodules/TelegramCore/Sources/Network/MultipartUpload.swift index 3f07e3bb5e..95331fcf9f 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartUpload.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartUpload.swift @@ -470,21 +470,12 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload fetchedResource = .complete() } - let onFloodWaitError: (String) -> Void = { [weak network] error in - guard let network else { - return - } - if error.hasPrefix("FLOOD_PREMIUM_WAIT") { - network.addNetworkSpeedLimitedEvent(event: .upload) - } - } - let manager = MultipartUploadManager(headerSize: headerSize, data: dataSignal, encryptionKey: encryptionKey, hintFileSize: hintFileSize, hintFileIsLarge: hintFileIsLarge, forceNoBigParts: forceNoBigParts, useLargerParts: useLargerParts, increaseParallelParts: increaseParallelParts, uploadPart: { part in switch uploadInterface { case let .download(download): - return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression, onFloodWaitError: onFloodWaitError) + return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression) case let .multiplexed(multiplexed, datacenterId, consumerId): - return Download.uploadPart(multiplexedManager: multiplexed, datacenterId: datacenterId, consumerId: consumerId, tag: nil, fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression, onFloodWaitError: onFloodWaitError) + return Download.uploadPart(multiplexedManager: multiplexed, datacenterId: datacenterId, consumerId: consumerId, tag: nil, fileId: part.fileId, index: part.index, data: part.data, asBigPart: part.bigPart, bigTotalParts: part.bigTotalParts, useCompression: useCompression) } }, progress: { progress in subscriber.putNext(.progress(progress)) diff --git a/submodules/TelegramCore/Sources/Network/MultiplexedRequestManager.swift b/submodules/TelegramCore/Sources/Network/MultiplexedRequestManager.swift index 1172b3c739..7e1a846480 100644 --- a/submodules/TelegramCore/Sources/Network/MultiplexedRequestManager.swift +++ b/submodules/TelegramCore/Sources/Network/MultiplexedRequestManager.swift @@ -33,13 +33,12 @@ private final class RequestData { let tag: MediaResourceFetchTag? let continueInBackground: Bool let automaticFloodWait: Bool - let onFloodWaitError: ((String) -> Void)? let expectedResponseSize: Int32? let deserializeResponse: (Buffer) -> Any? let completed: (Any, NetworkResponseInfo) -> Void let error: (MTRpcError, Double) -> Void - init(id: Int32, consumerId: Int64, resourceId: String?, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, onFloodWaitError: ((String) -> Void)?, expectedResponseSize: Int32?, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) { + init(id: Int32, consumerId: Int64, resourceId: String?, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, expectedResponseSize: Int32?, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) { self.id = id self.consumerId = consumerId self.resourceId = resourceId @@ -48,7 +47,6 @@ private final class RequestData { self.tag = tag self.continueInBackground = continueInBackground self.automaticFloodWait = automaticFloodWait - self.onFloodWaitError = onFloodWaitError self.expectedResponseSize = expectedResponseSize self.payload = payload self.deserializeResponse = deserializeResponse @@ -157,12 +155,12 @@ private final class MultiplexedRequestManagerContext { } } - func request(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, onFloodWaitError: ((String) -> Void)? = nil, expectedResponseSize: Int32?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) -> Disposable { + func request(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, expectedResponseSize: Int32?, completed: @escaping (Any, NetworkResponseInfo) -> Void, error: @escaping (MTRpcError, Double) -> Void) -> Disposable { let targetKey = MultiplexedRequestTargetKey(target: target, continueInBackground: continueInBackground) let requestId = self.nextId self.nextId += 1 - self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, resourceId: resourceId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize, deserializeResponse: { buffer in + self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, resourceId: resourceId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, expectedResponseSize: expectedResponseSize, deserializeResponse: { buffer in return data.2(buffer) }, completed: { result, info in completed(result, info) @@ -256,7 +254,7 @@ private final class MultiplexedRequestManagerContext { let requestId = request.id selectedContext.requests.append(ExecutingRequestData(requestId: requestId, disposable: disposable)) let queue = self.queue - disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait, onFloodWaitError: request.onFloodWaitError, expectedResponseSize: request.expectedResponseSize).start(next: { [weak self, weak selectedContext] result, info in + disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait, expectedResponseSize: request.expectedResponseSize).start(next: { [weak self, weak selectedContext] result, info in queue.async { guard let strongSelf = self else { return @@ -356,13 +354,13 @@ final class MultiplexedRequestManager { return disposable } - func request(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, expectedResponseSize: Int32?) -> Signal { + func request(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, expectedResponseSize: Int32?) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.context.with { context in disposable.set(context.request(to: target, consumerId: consumerId, resourceId: resourceId, data: (data.0, data.1, { buffer in return data.2.parse(buffer) - }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize, completed: { result, _ in + }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, expectedResponseSize: expectedResponseSize, completed: { result, _ in if let result = result as? T { subscriber.putNext(result) subscriber.putCompletion() @@ -377,13 +375,13 @@ final class MultiplexedRequestManager { } } - func requestWithAdditionalInfo(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil, expectedResponseSize: Int32?) -> Signal<(T, NetworkResponseInfo), (MTRpcError, Double)> { + func requestWithAdditionalInfo(to target: MultiplexedRequestTarget, consumerId: Int64, resourceId: String?, data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true, expectedResponseSize: Int32?) -> Signal<(T, NetworkResponseInfo), (MTRpcError, Double)> { return Signal { subscriber in let disposable = MetaDisposable() self.context.with { context in disposable.set(context.request(to: target, consumerId: consumerId, resourceId: resourceId, data: (data.0, data.1, { buffer in return data.2.parse(buffer) - }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, onFloodWaitError: onFloodWaitError, expectedResponseSize: expectedResponseSize, completed: { result, info in + }), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, expectedResponseSize: expectedResponseSize, completed: { result, info in if let result = result as? T { subscriber.putNext((result, info)) subscriber.putCompletion() diff --git a/submodules/TelegramCore/Sources/Network/Network.swift b/submodules/TelegramCore/Sources/Network/Network.swift index 4d82d66fba..2914559d88 100644 --- a/submodules/TelegramCore/Sources/Network/Network.swift +++ b/submodules/TelegramCore/Sources/Network/Network.swift @@ -459,7 +459,7 @@ public struct NetworkInitializationArguments { private let cloudDataContext = Atomic(value: nil) #endif -func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?, useRequestTimeoutTimers: Bool, appConfiguration: AppConfiguration) -> Signal { +func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?, useRequestTimeoutTimers: Bool) -> Signal { return Signal { subscriber in let queue = Queue() queue.async { @@ -612,11 +612,6 @@ func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializa let useExperimentalFeatures = networkSettings?.useExperimentalDownload ?? false let network = Network(queue: queue, datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath, appDataDisposable: appDataDisposable, encryptionProvider: arguments.encryptionProvider, useRequestTimeoutTimers: useRequestTimeoutTimers, useBetaFeatures: arguments.useBetaFeatures, useExperimentalFeatures: useExperimentalFeatures) - - if let data = appConfiguration.data, let notifyInterval = data["upload_premium_speedup_notify_period"] as? Double { - network.updateNetworkSpeedLimitedEventNotifyInterval(value: notifyInterval) - } - appDataUpdatedImpl = { [weak network] data in guard let data = data else { return @@ -739,22 +734,6 @@ public enum NetworkRequestResult { case progress(Float, Int32) } -private final class NetworkSpeedLimitedEventState { - var notifyInterval: Double = 60.0 * 60.0 - var lastNotifyTimestamp: Double = 0.0 - - func add(event: NetworkSpeedLimitedEvent) -> Bool { - let timestamp = CFAbsoluteTimeGetCurrent() - - if self.lastNotifyTimestamp + self.notifyInterval < timestamp { - self.lastNotifyTimestamp = timestamp - return true - } else { - return false - } - } -} - public final class Network: NSObject, MTRequestMessageServiceDelegate { public let encryptionProvider: EncryptionProvider @@ -787,12 +766,6 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { return self._connectionStatus.get() |> distinctUntilChanged } - public var networkSpeedLimitedEvents: Signal { - return self.networkSpeedLimitedEventPipe.signal() - } - private let networkSpeedLimitedEventPipe = ValuePipe() - private let networkSpeedLimitedEventState = Atomic(value: NetworkSpeedLimitedEventState()) - public func dropConnectionStatus() { _connectionStatus.set(.single(.waitingForNetwork)) } @@ -853,18 +826,18 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { let array = NSMutableArray() if let result = result { switch result { - case let .cdnConfig(publicKeys): - for key in publicKeys { - switch key { - case let .cdnPublicKey(dcId, publicKey): - if id == Int(dcId) { - let dict = NSMutableDictionary() - dict["key"] = publicKey - dict["fingerprint"] = MTRsaFingerprint(encryptionProvider, publicKey) - array.add(dict) + case let .cdnConfig(publicKeys): + for key in publicKeys { + switch key { + case let .cdnPublicKey(dcId, publicKey): + if id == Int(dcId) { + let dict = NSMutableDictionary() + dict["key"] = publicKey + dict["fingerprint"] = MTRsaFingerprint(encryptionProvider, publicKey) + array.add(dict) + } } } - } } } return array @@ -894,12 +867,12 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { let isCdn: Bool let isMedia: Bool = true switch target { - case let .main(id): - datacenterId = id - isCdn = false - case let .cdn(id): - datacenterId = id - isCdn = true + case let .main(id): + datacenterId = id + isCdn = false + case let .cdn(id): + datacenterId = id + isCdn = true } return strongSelf.makeWorker(datacenterId: datacenterId, isCdn: isCdn, isMedia: isMedia, tag: tag, continueInBackground: continueInBackground) } @@ -907,7 +880,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { }) let shouldKeepConnectionSignal = self.shouldKeepConnection.get() - |> distinctUntilChanged |> deliverOn(queue) + |> distinctUntilChanged |> deliverOn(queue) self.shouldKeepConnectionDisposable.set(shouldKeepConnectionSignal.start(next: { [weak self] value in if let strongSelf = self { if value { @@ -994,11 +967,11 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { self.context.addAddressForDatacenter(withId: Int(datacenterId), address: address) /*let currentScheme = self.context.transportSchemeForDatacenter(withId: Int(datacenterId), media: false, isProxy: false) - if let currentScheme = currentScheme, currentScheme.address.isEqual(to: address) { - } else { - let scheme = MTTransportScheme(transport: MTTcpTransport.self, address: address, media: false) - self.context.updateTransportSchemeForDatacenter(withId: Int(datacenterId), transportScheme: scheme, media: false, isProxy: false) - }*/ + if let currentScheme = currentScheme, currentScheme.address.isEqual(to: address) { + } else { + let scheme = MTTransportScheme(transport: MTTcpTransport.self, address: address, media: false) + self.context.updateTransportSchemeForDatacenter(withId: Int(datacenterId), transportScheme: scheme, media: false, isProxy: false) + }*/ let currentSchemes = self.context.transportSchemesForDatacenter(withId: Int(datacenterId), media: false, enforceMedia: false, isProxy: false) var found = false @@ -1015,7 +988,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { } } - public func requestWithAdditionalInfo(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), info: NetworkRequestAdditionalInfo, tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil) -> Signal, MTRpcError> { + public func requestWithAdditionalInfo(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), info: NetworkRequestAdditionalInfo, tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true) -> Signal, MTRpcError> { let requestService = self.requestService return Signal { subscriber in let request = MTRequest() @@ -1033,9 +1006,6 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { guard let errorContext = errorContext else { return true } - if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText { - onFloodWaitError(errorText) - } if errorContext.floodWaitSeconds > 0 && !automaticFloodWait { return false } @@ -1086,8 +1056,8 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { } } } - - public func request(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true, onFloodWaitError: ((String) -> Void)? = nil) -> Signal { + + public func request(_ data: (FunctionDescription, Buffer, DeserializeFunctionResponse), tag: NetworkRequestDependencyTag? = nil, automaticFloodWait: Bool = true) -> Signal { let requestService = self.requestService return Signal { subscriber in let request = MTRequest() @@ -1105,9 +1075,6 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { guard let errorContext = errorContext else { return true } - if let onFloodWaitError, errorContext.floodWaitSeconds > 0, let errorText = errorContext.floodWaitErrorText { - onFloodWaitError(errorText) - } if errorContext.floodWaitSeconds > 0 && !automaticFloodWait { return false } @@ -1146,21 +1113,6 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate { } } } - - func updateNetworkSpeedLimitedEventNotifyInterval(value: Double) { - let _ = self.networkSpeedLimitedEventState.with { state in - state.notifyInterval = value - } - } - - func addNetworkSpeedLimitedEvent(event: NetworkSpeedLimitedEvent) { - let notify = self.networkSpeedLimitedEventState.with { state in - return state.add(event: event) - } - if notify { - self.networkSpeedLimitedEventPipe.putNext(event) - } - } } public func retryRequest(signal: Signal) -> Signal { diff --git a/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift b/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift index 2333ad61f0..c421160341 100644 --- a/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift +++ b/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift @@ -6,44 +6,33 @@ import SwiftSignalKit extension PeerStatusSettings { init(apiSettings: Api.PeerSettings) { switch apiSettings { - case let .peerSettings(flags, geoDistance, requestChatTitle, requestChatDate, businessBotId, businessBotManageUrl): - var result = PeerStatusSettings.Flags() - if (flags & (1 << 1)) != 0 { - result.insert(.canAddContact) - } - if (flags & (1 << 0)) != 0 { - result.insert(.canReport) - } - if (flags & (1 << 2)) != 0 { - result.insert(.canBlock) - } - if (flags & (1 << 3)) != 0 { - result.insert(.canShareContact) - } - if (flags & (1 << 4)) != 0 { - result.insert(.addExceptionWhenAddingContact) - } - if (flags & (1 << 5)) != 0 { - result.insert(.canReportIrrelevantGeoLocation) - } - if (flags & (1 << 7)) != 0 { - result.insert(.autoArchived) - } - if (flags & (1 << 8)) != 0 { - result.insert(.suggestAddMembers) - } - - var managingBot: ManagingBot? - if let businessBotId { - managingBot = ManagingBot( - id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(businessBotId)), - manageUrl: businessBotManageUrl, - isPaused: (flags & (1 << 11)) != 0, - canReply: (flags & (1 << 12)) != 0 - ) - } - - self = PeerStatusSettings(flags: result, geoDistance: geoDistance, requestChatTitle: requestChatTitle, requestChatDate: requestChatDate, requestChatIsChannel: (flags & (1 << 10)) != 0, managingBot: managingBot) + case let .peerSettings(flags, geoDistance, requestChatTitle, requestChatDate): + var result = PeerStatusSettings.Flags() + if (flags & (1 << 1)) != 0 { + result.insert(.canAddContact) + } + if (flags & (1 << 0)) != 0 { + result.insert(.canReport) + } + if (flags & (1 << 2)) != 0 { + result.insert(.canBlock) + } + if (flags & (1 << 3)) != 0 { + result.insert(.canShareContact) + } + if (flags & (1 << 4)) != 0 { + result.insert(.addExceptionWhenAddingContact) + } + if (flags & (1 << 5)) != 0 { + result.insert(.canReportIrrelevantGeoLocation) + } + if (flags & (1 << 7)) != 0 { + result.insert(.autoArchived) + } + if (flags & (1 << 8)) != 0 { + result.insert(.suggestAddMembers) + } + self = PeerStatusSettings(flags: result, geoDistance: geoDistance, requestChatTitle: requestChatTitle, requestChatDate: requestChatDate, requestChatIsChannel: (flags & (1 << 10)) != 0) } } } diff --git a/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift b/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift index 37e1406e1f..acd550c523 100644 --- a/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift +++ b/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift @@ -360,11 +360,11 @@ final class ChatHistoryPreloadManager { guard let strongSelf = self else { return } - /*#if DEBUG + #if DEBUG if "".isEmpty { return } - #endif*/ + #endif var indices: [(ChatHistoryPreloadIndex, Bool, Bool)] = [] for item in loadItems { diff --git a/submodules/TelegramCore/Sources/Suggestions.swift b/submodules/TelegramCore/Sources/Suggestions.swift index 970940e0fd..8a405838d3 100644 --- a/submodules/TelegramCore/Sources/Suggestions.swift +++ b/submodules/TelegramCore/Sources/Suggestions.swift @@ -13,6 +13,7 @@ public enum ServerProvidedSuggestion: String { case annualPremium = "PREMIUM_ANNUAL" case restorePremium = "PREMIUM_RESTORE" case xmasPremiumGift = "PREMIUM_CHRISTMAS" + case setupBirthday = "BIRTHDAY_SETUP" } private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set]>([:]) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift index 7c8fac5dd5..cfef517df3 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift @@ -551,7 +551,7 @@ public final class CachedChannelData: CachedPeerData { var peerIds = Set() if let legacyValue = decoder.decodeOptionalInt32ForKey("pcs") { - self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil, managingBot: nil) + self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil) } else if let peerStatusSettings = decoder.decodeObjectForKey("pss", decoder: { PeerStatusSettings(decoder: $0) }) as? PeerStatusSettings { self.peerStatusSettings = peerStatusSettings } else { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift index eb8391bec0..5959310a0b 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift @@ -203,7 +203,7 @@ public final class CachedGroupData: CachedPeerData { self.exportedInvitation = decoder.decode(ExportedInvitation.self, forKey: "i") self.botInfos = decoder.decodeObjectArrayWithDecoderForKey("b") as [CachedPeerBotInfo] if let legacyValue = decoder.decodeOptionalInt32ForKey("pcs") { - self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil, managingBot: nil) + self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil) } else if let peerStatusSettings = decoder.decodeObjectForKey("pss", decoder: { PeerStatusSettings(decoder: $0) }) as? PeerStatusSettings { self.peerStatusSettings = peerStatusSettings } else { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index e6cb200972..5e9a6f657b 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -558,7 +558,7 @@ public final class CachedUserData: CachedPeerData { self.botInfo = decoder.decodeObjectForKey("bi") as? BotInfo self.editableBotInfo = decoder.decodeObjectForKey("ebi") as? EditableBotInfo if let legacyValue = decoder.decodeOptionalInt32ForKey("pcs") { - self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil, managingBot: nil) + self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil) } else if let peerStatusSettings = decoder.decodeObjectForKey("pss", decoder: { PeerStatusSettings(decoder: $0) }) as? PeerStatusSettings { self.peerStatusSettings = peerStatusSettings } else { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_PeerStatusSettings.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_PeerStatusSettings.swift index d12b6e71cb..ed6ca78314 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_PeerStatusSettings.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_PeerStatusSettings.swift @@ -16,20 +16,7 @@ public struct PeerStatusSettings: PostboxCoding, Equatable { public static let canReportIrrelevantGeoLocation = Flags(rawValue: 1 << 6) public static let autoArchived = Flags(rawValue: 1 << 7) public static let suggestAddMembers = Flags(rawValue: 1 << 8) - } - - public struct ManagingBot: Codable, Equatable { - public var id: PeerId - public var manageUrl: String? - public var isPaused: Bool - public var canReply: Bool - - public init(id: PeerId, manageUrl: String?, isPaused: Bool, canReply: Bool) { - self.id = id - self.manageUrl = manageUrl - self.isPaused = isPaused - self.canReply = canReply - } + } public var flags: PeerStatusSettings.Flags @@ -37,23 +24,20 @@ public struct PeerStatusSettings: PostboxCoding, Equatable { public var requestChatTitle: String? public var requestChatDate: Int32? public var requestChatIsChannel: Bool? - public var managingBot: ManagingBot? public init() { self.flags = PeerStatusSettings.Flags() self.geoDistance = nil self.requestChatTitle = nil self.requestChatDate = nil - self.managingBot = nil } - public init(flags: PeerStatusSettings.Flags, geoDistance: Int32? = nil, requestChatTitle: String? = nil, requestChatDate: Int32? = nil, requestChatIsChannel: Bool? = nil, managingBot: ManagingBot?) { + public init(flags: PeerStatusSettings.Flags, geoDistance: Int32? = nil, requestChatTitle: String? = nil, requestChatDate: Int32? = nil, requestChatIsChannel: Bool? = nil) { self.flags = flags self.geoDistance = geoDistance self.requestChatTitle = requestChatTitle self.requestChatDate = requestChatDate self.requestChatIsChannel = requestChatIsChannel - self.managingBot = managingBot } public init(decoder: PostboxDecoder) { @@ -62,7 +46,6 @@ public struct PeerStatusSettings: PostboxCoding, Equatable { self.requestChatTitle = decoder.decodeOptionalStringForKey("requestChatTitle") self.requestChatDate = decoder.decodeOptionalInt32ForKey("requestChatDate") self.requestChatIsChannel = decoder.decodeOptionalBoolForKey("requestChatIsChannel") - self.managingBot = decoder.decodeCodable(ManagingBot.self, forKey: "managingBot") } public func encode(_ encoder: PostboxEncoder) { @@ -87,11 +70,6 @@ public struct PeerStatusSettings: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "requestChatIsChannel") } - if let managingBot = self.managingBot { - encoder.encodeCodable(managingBot, forKey: "managingBot") - } else { - encoder.encodeNil(forKey: "managingBot") - } } public func contains(_ member: PeerStatusSettings.Flags) -> Bool { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift index 1823edd5ff..a9f717f43d 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift @@ -21,12 +21,6 @@ public struct StickerPackCollectionInfoFlags: OptionSet { if flags.contains(StickerPackCollectionInfoFlags.isOfficial) { rawValue |= StickerPackCollectionInfoFlags.isOfficial.rawValue } - if flags.contains(StickerPackCollectionInfoFlags.isAnimated) { - rawValue |= StickerPackCollectionInfoFlags.isAnimated.rawValue - } - if flags.contains(StickerPackCollectionInfoFlags.isVideo) { - rawValue |= StickerPackCollectionInfoFlags.isVideo.rawValue - } if flags.contains(StickerPackCollectionInfoFlags.isEmoji) { rawValue |= StickerPackCollectionInfoFlags.isEmoji.rawValue } @@ -39,8 +33,6 @@ public struct StickerPackCollectionInfoFlags: OptionSet { public static let isMasks = StickerPackCollectionInfoFlags(rawValue: 1 << 0) public static let isOfficial = StickerPackCollectionInfoFlags(rawValue: 1 << 1) - public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2) - public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3) public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4) public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5) public static let isCustomTemplateEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 6) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaImage.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaImage.swift index d98ebeecdd..c16f7d8f6d 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaImage.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaImage.swift @@ -360,20 +360,36 @@ public final class TelegramMediaImage: Media, Equatable, Codable { } public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, CustomStringConvertible { + public enum TypeHint: Int32 { + case generic + case animated + case video + } + public let dimensions: PixelDimensions public let resource: TelegramMediaResource public let progressiveSizes: [Int32] public let immediateThumbnailData: Data? public let hasVideo: Bool public let isPersonal: Bool + public let typeHint: TypeHint - public init(dimensions: PixelDimensions, resource: TelegramMediaResource, progressiveSizes: [Int32], immediateThumbnailData: Data?, hasVideo: Bool, isPersonal: Bool) { + public init( + dimensions: PixelDimensions, + resource: TelegramMediaResource, + progressiveSizes: [Int32], + immediateThumbnailData: Data?, + hasVideo: Bool = false, + isPersonal: Bool = false, + typeHint: TypeHint = .generic + ) { self.dimensions = dimensions self.resource = resource self.progressiveSizes = progressiveSizes self.immediateThumbnailData = immediateThumbnailData self.hasVideo = hasVideo self.isPersonal = isPersonal + self.typeHint = typeHint } public init(decoder: PostboxDecoder) { @@ -383,6 +399,7 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C self.immediateThumbnailData = decoder.decodeDataForKey("th") self.hasVideo = decoder.decodeBoolForKey("hv", orElse: false) self.isPersonal = decoder.decodeBoolForKey("ip", orElse: false) + self.typeHint = TypeHint(rawValue: decoder.decodeInt32ForKey("th", orElse: 0)) ?? .generic } public func encode(_ encoder: PostboxEncoder) { @@ -397,6 +414,7 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C } encoder.encodeBool(self.hasVideo, forKey: "hv") encoder.encodeBool(self.isPersonal, forKey: "ip") + encoder.encodeInt32(self.typeHint.rawValue, forKey: "th") } public var description: String { @@ -422,6 +440,9 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C if self.isPersonal != other.isPersonal { return false } + if self.typeHint != other.typeHint { + return false + } return true } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramSecretChat.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramSecretChat.swift index be745fbed8..78150dc1d7 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramSecretChat.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramSecretChat.swift @@ -93,7 +93,7 @@ public final class CachedSecretChatData: CachedPeerData { public init(decoder: PostboxDecoder) { if let legacyValue = decoder.decodeOptionalInt32ForKey("pcs") { - self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil, managingBot: nil) + self.peerStatusSettings = PeerStatusSettings(flags: PeerStatusSettings.Flags(rawValue: legacyValue), geoDistance: nil) } else if let peerStatusSettings = decoder.decodeObjectForKey("pss", decoder: { PeerStatusSettings(decoder: $0) }) as? PeerStatusSettings { self.peerStatusSettings = peerStatusSettings } else { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift index 05b6c9825c..85899d79ae 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelMembers.swift @@ -90,19 +90,15 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId: var items: [RenderedChannelParticipant] = [] switch result { case let .channelParticipants(_, participants, chats, users): - postboxLog("channel users insertion started, count: \(participants.count)") let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) - postboxLog("channel users parsed") updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) - postboxLog("channel users postbox updated, started mapping ids") var peers: [PeerId: Peer] = [:] for id in parsedPeers.allIds { if let peer = transaction.getPeer(id) { peers[peer.id] = peer } } - postboxLog("channel users finish mapping, started updating participants") - + for participant in CachedChannelParticipants(apiParticipants: participants).participants { if let peer = parsedPeers.get(participant.peerId) { var renderedPresences: [PeerId: PeerPresence] = [:] @@ -112,7 +108,6 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId: items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: renderedPresences)) } } - postboxLog("channel participants finish updating") case .channelParticipantsNotModified: return nil } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift index 04ffbec511..1deae7bbed 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift @@ -214,7 +214,7 @@ func _internal_dismissPeerStatusOptions(account: Account, peerId: PeerId) -> Sig if let current = current as? CachedUserData { var peerStatusSettings = current.peerStatusSettings ?? PeerStatusSettings() peerStatusSettings.flags = [] - return current.withUpdatedPeerStatusSettings(peerStatusSettings) + return current.withUpdatedPeerStatusSettings(PeerStatusSettings(flags: [])) } else if let current = current as? CachedGroupData { var peerStatusSettings = current.peerStatusSettings ?? PeerStatusSettings() peerStatusSettings.flags = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index df655e5377..775b5d7c2e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -56,12 +56,12 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountP var peerStatusSettings: PeerStatusSettings if let peer = transaction.getPeer(peer.id), let associatedPeerId = peer.associatedPeerId, !transaction.isPeerContact(peerId: associatedPeerId) { if let peer = peer as? TelegramSecretChat, case .creator = peer.role { - peerStatusSettings = PeerStatusSettings(flags: [], managingBot: nil) + peerStatusSettings = PeerStatusSettings(flags: []) } else { - peerStatusSettings = PeerStatusSettings(flags: [.canReport], managingBot: nil) + peerStatusSettings = PeerStatusSettings(flags: [.canReport]) } } else { - peerStatusSettings = PeerStatusSettings(flags: [], managingBot: nil) + peerStatusSettings = PeerStatusSettings(flags: []) } transaction.updatePeerCachedData(peerIds: [peer.id], update: { peerId, current in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift index a6fb320ca7..e37fca3718 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift @@ -194,7 +194,7 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri flags |= (1 << 1) } - inputStickers.append(.inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.first ?? "", maskCoords: nil, keywords: sticker.keywords)) + inputStickers.append(.inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords)) } var thumbnailDocument: Api.InputDocument? if thumbnail != nil, let resource = resources.last { @@ -307,7 +307,7 @@ func _internal_addStickerToStickerSet(account: Account, packReference: StickerPa if sticker.keywords.count > 0 { flags |= (1 << 1) } - let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.first ?? "", maskCoords: nil, keywords: sticker.keywords) + let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords) return account.network.request(Api.functions.stickers.addStickerToSet(stickerset: packReference.apiInputStickerSet, sticker: inputSticker)) |> mapError { error -> AddStickerToSetError in @@ -416,7 +416,7 @@ func _internal_replaceSticker(account: Account, previousSticker: FileMediaRefere if sticker.keywords.count > 0 { flags |= (1 << 1) } - let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.first ?? "", maskCoords: nil, keywords: sticker.keywords) + let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords) return account.network.request(Api.functions.stickers.replaceSticker(sticker: .inputDocument(id: previousResource.fileId, accessHash: previousResource.accessHash, fileReference: Buffer(data: previousResource.fileReference ?? Data())), newSticker: inputSticker)) |> mapError { error -> ReplaceStickerError in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift index a8e6444d67..bce8775167 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift @@ -5,19 +5,31 @@ import SwiftSignalKit import MtProtoKit func telegramStickerPackThumbnailRepresentationFromApiSizes(datacenterId: Int32, thumbVersion: Int32?, sizes: [Api.PhotoSize]) -> (immediateThumbnail: Data?, representations: [TelegramMediaImageRepresentation]) { + func stickerTypeHint(for type: String) -> TelegramMediaImageRepresentation.TypeHint { + switch type { + case "s": + return .generic + case "a": + return .animated + case "v": + return .video + default: + return .generic + } + } var immediateThumbnailData: Data? var representations: [TelegramMediaImageRepresentation] = [] for size in sizes { switch size { - case let .photoCachedSize(_, w, h, _): + case let .photoCachedSize(type, w, h, _): let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, thumbVersion: thumbVersion, volumeId: nil, localId: nil) - representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) - case let .photoSize(_, w, h, _): + representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, typeHint: stickerTypeHint(for: type))) + case let .photoSize(type, w, h, _): let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, thumbVersion: thumbVersion, volumeId: nil, localId: nil) - representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) - case let .photoSizeProgressive(_, w, h, sizes): + representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, typeHint: stickerTypeHint(for: type))) + case let .photoSizeProgressive(type, w, h, sizes): let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, thumbVersion: thumbVersion, volumeId: nil, localId: nil) - representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes, immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) + representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes, immediateThumbnailData: nil, typeHint: stickerTypeHint(for: type))) case let .photoPathSize(_, data): immediateThumbnailData = data.makeData() case .photoStrippedSize: @@ -40,12 +52,6 @@ extension StickerPackCollectionInfo { if (flags & (1 << 3)) != 0 { setFlags.insert(.isMasks) } - if (flags & (1 << 5)) != 0 { - setFlags.insert(.isAnimated) - } - if (flags & (1 << 6)) != 0 { - setFlags.insert(.isVideo) - } if (flags & (1 << 7)) != 0 { setFlags.insert(.isEmoji) } diff --git a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift index 0900afd2ae..8f89eb64a8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift @@ -620,10 +620,12 @@ public final class ChatInlineSearchResultsListComponent: Component { }, openPremiumIntro: { }, - openPremiumGift: { + openPremiumGift: { _ in }, openActiveSessions: { }, + openBirthdaySetup: { + }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift index ef3ac7d6ff..69bc656b17 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/StringForMessageTimestampStatus.swift @@ -156,18 +156,6 @@ public func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Mess } } - if authorTitle == nil { - for attribute in message.attributes { - if let attribute = attribute as? InlineBusinessBotMessageAttribute { - if let title = attribute.title { - authorTitle = title - } else if let peerId = attribute.peerId, let peer = message.peers[peerId] { - authorTitle = peer.debugDisplayTitle - } - } - } - } - if let subject = associatedData.subject, case let .messageOptions(_, _, info) = subject, case .forward = info { authorTitle = nil } diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 5f8b66660a..be05c8b6a8 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -184,7 +184,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings - let stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hideBackground: hideBackground) + let stickerItems = EmojiPagerContentComponent.stickerInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, stickerNamespaces: stickerNamespaces, stickerOrderedItemListCollectionIds: stickerOrderedItemListCollectionIds, chatPeerId: chatPeerId, hasSearch: hasSearch, hasTrending: hasTrending, forceHasPremium: false, hasEdit: true, hideBackground: hideBackground) let reactions: Signal<[String], NoError> = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()) |> map { appConfiguration -> [String] in diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index ffca635585..b9c9081609 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -7540,12 +7540,12 @@ private final class FadingMaskLayer: SimpleLayer { } public struct StickerPickerInputData: StickerPickerInput, Equatable { - public var emoji: EmojiPagerContentComponent + public var emoji: EmojiPagerContentComponent? public var stickers: EmojiPagerContentComponent? public var gifs: GifPagerContentComponent? public init( - emoji: EmojiPagerContentComponent, + emoji: EmojiPagerContentComponent?, stickers: EmojiPagerContentComponent?, gifs: GifPagerContentComponent? ) { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift index 1c05326830..4195137f15 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentSignals.swift @@ -1162,11 +1162,6 @@ public extension EmojiPagerContentComponent { } } else if case .stickerAlt = subject { for reactionItem in topReactionItems { -// if existingIds.contains(reactionItem.reaction) { -// continue -// } -// existingIds.insert(reactionItem.reaction) - let icon: EmojiPagerContentComponent.Item.Icon if case .reaction(onlyTop: true) = subject { icon = .none @@ -1612,6 +1607,7 @@ public extension EmojiPagerContentComponent { hasSearch: Bool, hasTrending: Bool, forceHasPremium: Bool, + hasEdit: Bool = false, searchIsPlaceholderOnly: Bool = true, isProfilePhotoEmojiSelection: Bool = false, isGroupPhotoEmojiSelection: Bool = false, @@ -1944,11 +1940,11 @@ public extension EmojiPagerContentComponent { var title = "" var headerItem: EntityKeyboardAnimationData? - var hasEdit = false + var groupHasEdit = false inner: for (id, info, _) in view.collectionInfos { if id == groupId, let info = info as? StickerPackCollectionInfo { title = info.title - hasEdit = info.flags.contains(.isCreator) + groupHasEdit = info.flags.contains(.isCreator) if let thumbnail = info.thumbnail { let type: EntityKeyboardAnimationData.ItemType @@ -1974,7 +1970,7 @@ public extension EmojiPagerContentComponent { break inner } } - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: true, hasEdit: hasEdit, headerItem: headerItem, items: [resultItem])) + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: true, hasEdit: hasEdit && groupHasEdit, headerItem: headerItem, items: [resultItem])) } } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index de6ec17e31..7c8a5e342d 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -5676,11 +5676,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let navigationController = self.navigationController as? NavigationController { navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate) } - -// mediaEditor.stop() -// mediaEditor.invalidate() -// self.node.entitiesView.invalidate() - + let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) } let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView) mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) @@ -5772,36 +5768,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } }))) - let thumbSize = CGSize(width: 24.0, height: 24.0) - for (pack, firstItem) in self.myStickerPacks { - let thumbnailResource = pack.thumbnail?.resource ?? firstItem?.file.resource - let thumbnailIconSource: ContextMenuActionItemIconSource? - if let thumbnailResource { - var resourceId: Int64 = 0 - if let resource = thumbnailResource as? CloudDocumentMediaResource { - resourceId = resource.fileId - } - let thumbnailFile = firstItem?.file ?? TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: resourceId), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: thumbnailResource.size ?? 0, attributes: []) - - let _ = freeMediaFileInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: .stickerPack(stickerPack: .id(id: pack.id.id, accessHash: pack.accessHash), media: thumbnailFile)).start() - thumbnailIconSource = ContextMenuActionItemIconSource( - size: thumbSize, - signal: chatMessageStickerPackThumbnail(postbox: self.context.account.postbox, resource: thumbnailResource) - |> map { generator -> UIImage? in - return generator(TransformImageArguments(corners: ImageCorners(), imageSize: thumbSize, boundingSize: thumbSize, intrinsicInsets: .zero))?.generateImage() - } - ) - } else { - thumbnailIconSource = nil + contextItems.append(.custom(StickerPackListContextItem(context: self.context, packs: self.myStickerPacks, packSelected: { [weak self] pack in + guard let self else { + return } - contextItems.append(.action(ContextMenuActionItem(text: pack.title, icon: { _ in return nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { [weak self] _, f in - guard let self else { - return - } - f(.default) - self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title)) - }))) - } + self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title)) + }), false)) let items = ContextController.Items( id: 1, @@ -5877,7 +5849,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) var dismissImpl: (() -> Void)? - let controller = stickerPackEditTitleController(context: self.context, forceDark: true, title: "New Sticker Set", text: "Choose a name for your sticker set.", placeholder: presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: nil, maxLength: 128, apply: { [weak self] title in + let controller = stickerPackEditTitleController(context: self.context, forceDark: true, title: "New Sticker Set", text: "Choose a name for your sticker set.", placeholder: presentationData.strings.ImportStickerPack_NamePlaceholder, actionTitle: presentationData.strings.Common_Done, value: nil, maxLength: 64, apply: { [weak self] title in guard let self else { return } @@ -5972,7 +5944,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate case let .createStickerPack(title): let sticker = ImportSticker( resource: resource, - emojis: ["😀"], + emojis: ["😀😂"], dimensions: dimensions, mimeType: "image/webp", keywords: "" @@ -5991,7 +5963,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate case let .addToStickerPack(pack, _): let sticker = ImportSticker( resource: resource, - emojis: ["😀"], + emojis: ["😀😂"], dimensions: dimensions, mimeType: "image/webp", keywords: "" diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift new file mode 100644 index 0000000000..aca3995de1 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift @@ -0,0 +1,191 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import SwiftSignalKit +import Postbox +import TelegramCore +import AccountContext +import TelegramPresentationData +import StickerResources +import ContextUI + +final class StickerPackListContextItem: ContextMenuCustomItem { + let context: AccountContext + let packs: [(StickerPackCollectionInfo, StickerPackItem?)] + let packSelected: (StickerPackCollectionInfo) -> Void + + init(context: AccountContext, packs: [(StickerPackCollectionInfo, StickerPackItem?)], packSelected: @escaping (StickerPackCollectionInfo) -> Void) { + self.context = context + self.packs = packs + self.packSelected = packSelected + } + + func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode { + return StickerPackListContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected) + } +} + +private final class StickerPackListContextItemNode: ASDisplayNode, ContextMenuCustomNode, ContextActionNodeProtocol, UIScrollViewDelegate { + private let item: StickerPackListContextItem + private let presentationData: PresentationData + private let getController: () -> ContextControllerProtocol? + private let actionSelected: (ContextMenuActionResult) -> Void + + private let scrollNode: ASScrollNode + private let actionNodes: [ContextControllerActionsListActionItemNode] + private let separatorNodes: [ASDisplayNode] + + init(presentationData: PresentationData, item: StickerPackListContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { + self.item = item + self.presentationData = presentationData + self.getController = getController + self.actionSelected = actionSelected + + self.scrollNode = ASScrollNode() + + var actionNodes: [ContextControllerActionsListActionItemNode] = [] + var separatorNodes: [ASDisplayNode] = [] + + var i = 0 + for (pack, topItem) in item.packs { + let thumbSize = CGSize(width: 24.0, height: 24.0) + let thumbnailResource = pack.thumbnail?.resource ?? topItem?.file.resource + let thumbnailIconSource: ContextMenuActionItemIconSource? + if let thumbnailResource { + var resourceId: Int64 = 0 + if let resource = thumbnailResource as? CloudDocumentMediaResource { + resourceId = resource.fileId + } + let thumbnailFile = topItem?.file ?? TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: resourceId), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: thumbnailResource.size ?? 0, attributes: []) + + let _ = freeMediaFileInteractiveFetched(account: item.context.account, userLocation: .other, fileReference: .stickerPack(stickerPack: .id(id: pack.id.id, accessHash: pack.accessHash), media: thumbnailFile)).start() + thumbnailIconSource = ContextMenuActionItemIconSource( + size: thumbSize, + signal: chatMessageStickerPackThumbnail(postbox: item.context.account.postbox, resource: thumbnailResource) + |> map { generator -> UIImage? in + return generator(TransformImageArguments(corners: ImageCorners(), imageSize: thumbSize, boundingSize: thumbSize, intrinsicInsets: .zero))?.generateImage() + } + ) + } else { + thumbnailIconSource = nil + } + + let action = ContextMenuActionItem(text: pack.title, textLayout: .singleLine, icon: { _ in nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { _, f in + f(.dismissWithoutContent) + + item.packSelected(pack) + }) + let actionNode = ContextControllerActionsListActionItemNode(getController: getController, requestDismiss: actionSelected, requestUpdateAction: { _, _ in }, item: action) + actionNodes.append(actionNode) + if actionNodes.count != item.packs.count { + let separatorNode = ASDisplayNode() + separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor + separatorNodes.append(separatorNode) + } + i += 1 + } + self.actionNodes = actionNodes + self.separatorNodes = separatorNodes + + super.init() + + self.addSubnode(self.scrollNode) + for separatorNode in self.separatorNodes { + self.scrollNode.addSubnode(separatorNode) + } + for actionNode in self.actionNodes { + self.scrollNode.addSubnode(actionNode) + } + } + + override func didLoad() { + super.didLoad() + + self.scrollNode.view.delegate = self + self.scrollNode.view.alwaysBounceVertical = false + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 5.0, right: 0.0) + } + + func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { + let minActionsWidth: CGFloat = 250.0 + let maxActionsWidth: CGFloat = 300.0 + let constrainedWidth = min(constrainedWidth, maxActionsWidth) + var maxWidth: CGFloat = 0.0 + var contentHeight: CGFloat = 0.0 + var heightsAndCompletions: [(CGFloat, (CGSize, ContainedViewLayoutTransition) -> Void)?] = [] + for i in 0 ..< self.actionNodes.count { + let itemNode = self.actionNodes[i] + let (minSize, complete) = itemNode.update(presentationData: self.presentationData, constrainedSize: CGSize(width: constrainedWidth, height: constrainedHeight)) + maxWidth = max(maxWidth, minSize.width) + heightsAndCompletions.append((minSize.height, complete)) + contentHeight += minSize.height + } + + maxWidth = max(maxWidth, minActionsWidth) + + let maxHeight: CGFloat = min(155.0, constrainedHeight - 108.0) + + return (CGSize(width: maxWidth, height: min(maxHeight, contentHeight)), { size, transition in + var verticalOffset: CGFloat = 0.0 + for i in 0 ..< heightsAndCompletions.count { + let itemNode = self.actionNodes[i] + if let (itemHeight, itemCompletion) = heightsAndCompletions[i] { + let itemSize = CGSize(width: maxWidth, height: itemHeight) + transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: itemSize)) + itemCompletion(itemSize, transition) + verticalOffset += itemHeight + } + + if i < self.actionNodes.count - 1 { + let separatorNode = self.separatorNodes[i] + separatorNode.frame = CGRect(x: 0, y: verticalOffset, width: size.width, height: UIScreenPixel) + } + } + transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size)) + self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight) + + }) + } + + func updateTheme(presentationData: PresentationData) { +// for actionNode in self.actionNodes { +// actionNode.updateTheme(presentationData: presentationData) +// } + } + + var isActionEnabled: Bool { + return true + } + + func performAction() { + } + + func setIsHighlighted(_ value: Bool) { + } + + func canBeHighlighted() -> Bool { + return self.isActionEnabled + } + + func updateIsHighlighted(isHighlighted: Bool) { + self.setIsHighlighted(isHighlighted) + } + + func actionNode(at point: CGPoint) -> ContextActionNodeProtocol { +// for actionNode in self.actionNodes { +// let frame = actionNode.convert(actionNode.bounds, to: self) +// if frame.contains(point) { +// return actionNode +// } +// } + return self + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + for actionNode in self.actionNodes { + actionNode.updateIsHighlighted(isHighlighted: false) + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBirthdatePickerItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBirthdatePickerItem.swift new file mode 100644 index 0000000000..11c8cee150 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBirthdatePickerItem.swift @@ -0,0 +1,8 @@ +// +// PeerInfoScreenBirthdatePickerItem.swift +// MediaEditorScreen +// +// Created by Ilya Laktyushin on 15.03.2024. +// + +import Foundation diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift index 369f19bb3e..63c1d698cf 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift @@ -38,9 +38,10 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem { let text: String let icon: UIImage? let iconSignal: Signal? + let hasArrow: Bool let action: (() -> Void)? - init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal? = nil, action: (() -> Void)?) { + init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal? = nil, hasArrow: Bool = true, action: (() -> Void)?) { self.id = id self.label = label self.additionalBadgeLabel = additionalBadgeLabel @@ -48,6 +49,7 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem { self.text = text self.icon = icon self.iconSignal = iconSignal + self.hasArrow = hasArrow self.action = action } @@ -139,7 +141,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { let sideInset: CGFloat = 16.0 + safeInsets.left let leftInset = (item.icon == nil && item.iconSignal == nil ? sideInset : sideInset + 29.0 + 16.0) - let rightInset = sideInset + 18.0 + let rightInset = sideInset + (item.hasArrow ? 18.0 : 0.0) let separatorInset = item.icon == nil && item.iconSignal == nil ? sideInset : leftInset - 1.0 let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) @@ -206,7 +208,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { self.iconNode.removeFromSupernode() } - if let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) { + if item.hasArrow, let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) { self.arrowNode.image = arrowImage let arrowFrame = CGRect(origin: CGPoint(x: width - 7.0 - arrowImage.size.width - safeInsets.right, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size) transition.updateFrame(node: self.arrowNode, frame: arrowFrame) diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 72859b0b55..cb557759d0 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -1354,7 +1354,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.controller?.containerLayoutUpdated(layout, transition: .immediate) } } else { - let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false, topPeers: false)), onlyWriteable: self.filter.contains(.onlyWriteable)) + let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false, topPeers: .none)), onlyWriteable: self.filter.contains(.onlyWriteable)) self.contactListNode = contactListNode contactListNode.enableUpdates = true contactListNode.selectionStateUpdated = { [weak self] selectionState in diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift index ad53e21b11..dc1e0bd903 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift @@ -186,10 +186,12 @@ final class GreetingMessageListItemComponent: Component { }, openPremiumIntro: { }, - openPremiumGift: { + openPremiumGift: { _ in }, openActiveSessions: { }, + openBirthdaySetup: { + }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index 22d8f7e0ca..84557799c5 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -201,10 +201,12 @@ final class QuickReplySetupScreenComponent: Component { }, openPremiumIntro: { }, - openPremiumGift: { + openPremiumGift: { _ in }, openActiveSessions: { }, + openBirthdaySetup: { + }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index c18ef7ae63..4d5ee92144 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -865,7 +865,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() }, present: { _ in - }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: { + }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in diff --git a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift index 43c3a1a738..d5446679df 100644 --- a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift +++ b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift @@ -30,6 +30,7 @@ private final class StickerSelectionComponent: Component { let theme: PresentationTheme let strings: PresentationStrings let deviceMetrics: DeviceMetrics + let topInset: CGFloat let bottomInset: CGFloat let content: StickerPickerInputData let backgroundColor: UIColor @@ -41,6 +42,7 @@ private final class StickerSelectionComponent: Component { theme: PresentationTheme, strings: PresentationStrings, deviceMetrics: DeviceMetrics, + topInset: CGFloat, bottomInset: CGFloat, content: StickerPickerInputData, backgroundColor: UIColor, @@ -51,6 +53,7 @@ private final class StickerSelectionComponent: Component { self.theme = theme self.strings = strings self.deviceMetrics = deviceMetrics + self.topInset = topInset self.bottomInset = bottomInset self.content = content self.backgroundColor = backgroundColor @@ -68,6 +71,9 @@ private final class StickerSelectionComponent: Component { if lhs.deviceMetrics != rhs.deviceMetrics { return false } + if lhs.topInset != rhs.topInset { + return false + } if lhs.bottomInset != rhs.bottomInset { return false } @@ -235,12 +241,13 @@ private final class StickerSelectionComponent: Component { self.backgroundColor = component.backgroundColor let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85) self.panelBackgroundView.updateColor(color: panelBackgroundColor, transition: .immediate) - self.panelSeparatorView.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.1) + self.panelSeparatorView.backgroundColor = component.separatorColor self.component = component self.state = state let topPanelHeight: CGFloat = 42.0 + let topInset = component.topInset let controller = component.getController() let defaultToEmoji = controller?.defaultToEmoji ?? false @@ -248,7 +255,7 @@ private final class StickerSelectionComponent: Component { let context = component.context let stickerPeekBehavior = EmojiContentPeekBehaviorImpl( context: context, - forceTheme: defaultDarkColorPresentationTheme, + forceTheme: controller?.forceDark == true ? defaultDarkColorPresentationTheme : nil, interaction: nil, chatPeerId: nil, present: { c, a in @@ -258,19 +265,20 @@ private final class StickerSelectionComponent: Component { } ) + let isFullscreen = controller?.isFullscreen == true let keyboardSize = self.keyboardView.update( transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)), component: AnyComponent(EntityKeyboardComponent( theme: component.theme, strings: component.strings, isContentInFocus: true, - containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: component.bottomInset, right: 0.0), + containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0 + topInset, left: 0.0, bottom: component.bottomInset, right: 0.0), topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), emojiContent: component.content.emoji, stickerContent: component.content.stickers, maskContent: nil, gifContent: component.content.gifs, - hasRecentGifs: true, + hasRecentGifs: !isFullscreen, availableGifSearchEmojies: [], defaultToEmojiTab: defaultToEmoji, externalTopPanelContainer: self.panelHostView, @@ -313,7 +321,10 @@ private final class StickerSelectionComponent: Component { mappedMode = .gif } - let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) + var presentationData = context.sharedContext.currentPresentationData.with { $0 } + if controller?.forceDark == true { + presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) + } let searchContainerNode = PaneSearchContainerNode( context: context, theme: presentationData.theme, @@ -344,7 +355,7 @@ private final class StickerSelectionComponent: Component { deviceMetrics: component.deviceMetrics, hiddenInputHeight: 0.0, inputHeight: 0.0, - displayBottomPanel: controller?.isFullscreen == false, + displayBottomPanel: !isFullscreen, isExpanded: true, clipContentToTopPanel: false, useExternalSearchContainer: false @@ -365,26 +376,34 @@ private final class StickerSelectionComponent: Component { self.keyboardClippingView.clipsToBounds = false } - transition.setFrame(view: self.keyboardClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: availableSize.width, height: availableSize.height - topPanelHeight))) - self.keyboardClippingView.hitEdgeInsets = UIEdgeInsets(top: -topPanelHeight, left: 0.0, bottom: 0.0, right: 0.0) + transition.setFrame(view: self.keyboardClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + topInset), size: CGSize(width: availableSize.width, height: availableSize.height - topPanelHeight - topInset))) + self.keyboardClippingView.hitEdgeInsets = UIEdgeInsets(top: -topPanelHeight - topInset, left: 0.0, bottom: 0.0, right: 0.0) - transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelHeight), size: keyboardSize)) - transition.setFrame(view: self.panelHostView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - 34.0), size: CGSize(width: keyboardSize.width, height: 0.0))) + transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelHeight - topInset), size: keyboardSize)) + transition.setFrame(view: self.panelHostView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + topInset - 34.0), size: CGSize(width: keyboardSize.width, height: 0.0))) - transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight))) + transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight + topInset))) self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition) let topPanelAlpha: CGFloat if self.searchVisible || self.keyboardContentId == AnyHashable("gifs") { topPanelAlpha = 0.0 + if isFullscreen, let navigationBar = controller?.navigationBar, navigationBar.alpha > 0.0 { + transition.setAlpha(view: navigationBar.view, alpha: 0.0) + } + } else if isFullscreen { + topPanelAlpha = 1.0 + if let navigationBar = controller?.navigationBar, navigationBar.alpha < 1.0 { + transition.setAlpha(view: navigationBar.view, alpha: 1.0) + } } else { topPanelAlpha = max(0.0, min(1.0, (self.topPanelScrollingOffset / 20.0))) } - + transition.setAlpha(view: self.panelBackgroundView, alpha: topPanelAlpha) transition.setAlpha(view: self.panelSeparatorView, alpha: topPanelAlpha) - transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel))) + transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + topInset - UIScreenPixel), size: CGSize(width: keyboardSize.width, height: UIScreenPixel))) } return availableSize @@ -642,35 +661,36 @@ public class StickerPickerScreen: ViewController { inputData.gifs = gifData?.component - let emoji = inputData.emoji - if let emojiSearchResult = emojiSearchState.result { - var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults? - if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) { - emptySearchResults = EmojiPagerContentComponent.EmptySearchResults( - text: presentationData.strings.EmojiSearch_SearchEmojiEmptyResult, - iconFile: nil - ) + if let emoji = inputData.emoji { + if let emojiSearchResult = emojiSearchState.result { + var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults? + if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) { + emptySearchResults = EmojiPagerContentComponent.EmptySearchResults( + text: presentationData.strings.EmojiSearch_SearchEmojiEmptyResult, + iconFile: nil + ) + } + + let defaultSearchState: EmojiPagerContentComponent.SearchState = emojiSearchResult.isPreset ? .active : .empty(hasResults: true) + inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: emojiSearchResult.id, version: emojiSearchResult.version), emptySearchResults: emptySearchResults, searchState: emojiSearchState.isSearching ? .searching : defaultSearchState) + } else if emojiSearchState.isSearching { + inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emoji.contentItemGroups, itemContentUniqueId: emoji.itemContentUniqueId, emptySearchResults: emoji.emptySearchResults, searchState: .searching) } - let defaultSearchState: EmojiPagerContentComponent.SearchState = emojiSearchResult.isPreset ? .active : .empty(hasResults: true) - inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: emojiSearchResult.id, version: emojiSearchResult.version), emptySearchResults: emptySearchResults, searchState: emojiSearchState.isSearching ? .searching : defaultSearchState) - } else if emojiSearchState.isSearching { - inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emoji.contentItemGroups, itemContentUniqueId: emoji.itemContentUniqueId, emptySearchResults: emoji.emptySearchResults, searchState: .searching) } - if let stickerSearchResult = stickerSearchState.result { - var stickerSearchResults: EmojiPagerContentComponent.EmptySearchResults? - if !stickerSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) { - stickerSearchResults = EmojiPagerContentComponent.EmptySearchResults( - text: presentationData.strings.EmojiSearch_SearchStickersEmptyResult, - iconFile: nil - ) - } - if let stickers = inputData.stickers { + if let stickers = inputData.stickers { + if let stickerSearchResult = stickerSearchState.result { + var stickerSearchResults: EmojiPagerContentComponent.EmptySearchResults? + if !stickerSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) { + stickerSearchResults = EmojiPagerContentComponent.EmptySearchResults( + text: presentationData.strings.EmojiSearch_SearchStickersEmptyResult, + iconFile: nil + ) + } + let defaultSearchState: EmojiPagerContentComponent.SearchState = stickerSearchResult.isPreset ? .active : .empty(hasResults: true) inputData.stickers = stickers.withUpdatedItemGroups(panelItemGroups: stickers.panelItemGroups, contentItemGroups: stickerSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: stickerSearchResult.id, version: stickerSearchResult.version), emptySearchResults: stickerSearchResults, searchState: stickerSearchState.isSearching ? .searching : defaultSearchState) - } - } else if stickerSearchState.isSearching { - if let stickers = inputData.stickers { + } else if stickerSearchState.isSearching { inputData.stickers = stickers.withUpdatedItemGroups(panelItemGroups: stickers.panelItemGroups, contentItemGroups: stickers.contentItemGroups, itemContentUniqueId: stickers.itemContentUniqueId, emptySearchResults: stickers.emptySearchResults, searchState: .searching) } } @@ -778,9 +798,9 @@ public class StickerPickerScreen: ViewController { text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string } controller.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { [weak controller] action in - if case .info = action { - let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: true, dismissed: nil) - controller?.push(premiumController) + if case .info = action, let controller { + let premiumController = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: controller.forceDark, dismissed: nil) + controller.push(premiumController) return true } return false @@ -802,7 +822,7 @@ public class StickerPickerScreen: ViewController { return } - content.emoji.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( + content.emoji?.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( performItemAction: { [weak self] groupId, item, _, _, _, _ in guard let strongSelf = self, let controller = strongSelf.controller else { return @@ -846,10 +866,10 @@ public class StickerPickerScreen: ViewController { for featuredStickerPack in stickerPacks { if featuredStickerPack.topItems.contains(where: { $0.file.fileId == file.fileId }) { if let componentView = self.hostView.componentView as? StickerSelectionComponent.View { - if let pagerView = componentView.keyboardView.view as? EntityKeyboardComponent.View, let emojiInputInteraction = self.content?.emoji.inputInteractionHolder.inputInteraction { + if let pagerView = componentView.keyboardView.view as? EntityKeyboardComponent.View, let emojiInputInteraction = self.content?.emoji?.inputInteractionHolder.inputInteraction, let controller = self.controller { pagerView.openCustomSearch(content: EmojiSearchContent( context: context, - forceTheme: defaultDarkPresentationTheme, + forceTheme: controller.forceDark ? defaultDarkPresentationTheme : nil, items: stickerPacks, initialFocusId: featuredStickerPack.info.id, hasPremiumForUse: hasPremium, @@ -858,7 +878,6 @@ public class StickerPickerScreen: ViewController { )) } } - break } } @@ -943,7 +962,10 @@ public class StickerPickerScreen: ViewController { guard let strongSelf = self, let controller = strongSelf.controller else { return } - let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) + var presentationData = controller.context.sharedContext.currentPresentationData.with { $0 } + if controller.forceDark { + presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) + } let context = controller.context if groupId == AnyHashable("recent") { let actionSheet = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme, fontSize: presentationData.listsFontSize)) @@ -1243,7 +1265,7 @@ public class StickerPickerScreen: ViewController { if let controller = self.controller { stickerPeekBehavior = EmojiContentPeekBehaviorImpl( context: controller.context, - forceTheme: defaultDarkColorPresentationTheme, + forceTheme: controller.forceDark ? defaultDarkColorPresentationTheme : nil, interaction: nil, chatPeerId: nil, present: { [weak controller] c, a in @@ -1348,7 +1370,10 @@ public class StickerPickerScreen: ViewController { } let context = controller.context if groupId == AnyHashable("recent") { - let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) + var presentationData = context.sharedContext.currentPresentationData.with { $0 } + if controller.forceDark { + presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) + } let actionSheet = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme, fontSize: presentationData.listsFontSize)) var items: [ActionSheetItem] = [] items.append(ActionSheetButtonItem(title: presentationData.strings.Stickers_ClearRecent, color: .destructive, action: { [weak actionSheet] in @@ -1532,7 +1557,7 @@ public class StickerPickerScreen: ViewController { self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) - if let controller = self.controller, !controller.isFullscreen { + if let controller = self.controller { controller.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) } } @@ -1711,10 +1736,11 @@ public class StickerPickerScreen: ViewController { theme: self.theme, strings: self.presentationData.strings, deviceMetrics: layout.deviceMetrics, + topInset: controller.isFullscreen ? navigationHeight : 0.0, bottomInset: bottomInset, content: content, backgroundColor: self.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.85), - separatorColor: self.theme.list.blocksBackgroundColor, + separatorColor: self.theme.rootController.navigationBar.separatorColor, getController: { [weak self] in if let self { return self.controller @@ -1752,12 +1778,12 @@ public class StickerPickerScreen: ViewController { } private var defaultTopInset: CGFloat { - guard let (layout, navigationBarHeight) = self.currentLayout else { + guard let (layout, _) = self.currentLayout else { return 210.0 } if let controller = self.controller, controller.isFullscreen { - return navigationBarHeight + return 0.0 } if case .compact = layout.metrics.widthClass { @@ -1978,6 +2004,7 @@ public class StickerPickerScreen: ViewController { private let context: AccountContext private let theme: PresentationTheme + fileprivate let forceDark: Bool private let inputData: Signal fileprivate let defaultToEmoji: Bool let isFullscreen: Bool @@ -2002,6 +2029,7 @@ public class StickerPickerScreen: ViewController { self.context = context let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.theme = forceDark ? defaultDarkColorPresentationTheme : presentationData.theme + self.forceDark = forceDark self.inputData = inputData self.isFullscreen = expanded self.defaultToEmoji = defaultToEmoji diff --git a/submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController/Sources/StickerPackEditTitleController.swift b/submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController/Sources/StickerPackEditTitleController.swift index c94161c26b..12339fecda 100644 --- a/submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController/Sources/StickerPackEditTitleController.swift +++ b/submodules/TelegramUI/Components/Stickers/StickerPackEditTitleController/Sources/StickerPackEditTitleController.swift @@ -255,7 +255,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF private let maxLength: Int - init(theme: PresentationTheme, placeholder: String, maxLength: Int, keyboardType: UIKeyboardType = .default, returnKeyType: UIReturnKeyType = .done) { + init(theme: PresentationTheme, placeholder: String, maxLength: Int, keyboardType: UIKeyboardType = .default, returnKeyType: UIReturnKeyType = .done, hasClearButton: Bool = false) { self.theme = theme self.maxLength = maxLength @@ -370,6 +370,10 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF return false } + if string == " " && updatedText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + return false + } + if self.textInputNode.keyboardType == .asciiCapable { var cleanString = string.folding(options: .diacriticInsensitive, locale: .current).replacingOccurrences(of: " ", with: "_") @@ -506,7 +510,7 @@ private final class ImportStickerPackTitleAlertContentNode: AlertContentNode { return self.isUserInteractionEnabled } - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?, maxLength: Int, asciiOnly: Bool = false) { + init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?, maxLength: Int, asciiOnly: Bool = false, hasClearButton: Bool) { self.strings = strings self.alertTheme = theme self.theme = ptheme @@ -524,7 +528,7 @@ private final class ImportStickerPackTitleAlertContentNode: AlertContentNode { self.activityIndicator = ActivityIndicator(type: .custom(ptheme.rootController.navigationBar.secondaryTextColor, 20.0, 1.5, false), speed: .slow) self.activityIndicator.isHidden = true - self.inputFieldNode = ImportStickerPackTitleInputFieldNode(theme: ptheme, placeholder: placeholder, maxLength: maxLength, keyboardType: asciiOnly ? .asciiCapable : .default, returnKeyType: asciiOnly ? .done : .next) + self.inputFieldNode = ImportStickerPackTitleInputFieldNode(theme: ptheme, placeholder: placeholder, maxLength: maxLength, keyboardType: asciiOnly ? .asciiCapable : .default, returnKeyType: asciiOnly ? .done : .next, hasClearButton: hasClearButton) if asciiOnly { self.inputFieldNode.prefix = "t.me/addstickers/" } @@ -743,7 +747,7 @@ public func stickerPackEditTitleController(context: AccountContext, forceDark: B applyImpl?() })] - let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength) + let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength, hasClearButton: false) contentNode.complete = { applyImpl?() } @@ -805,7 +809,7 @@ public func importStickerPackShortNameController(context: AccountContext, title: applyImpl?() })] - let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength, asciiOnly: true) + let contentNode = ImportStickerPackTitleAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength, asciiOnly: true, hasClearButton: true) contentNode.complete = { applyImpl?() } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift index 7bc8d4a842..42c75a2a00 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentCaptionComponent.swift @@ -209,31 +209,8 @@ final class StoryContentCaptionComponent: Component { override init(frame: CGRect) { self.shadowGradientView = UIImageView() - if let _ = StoryContentCaptionComponent.View.shadowImage { - let height: CGFloat = 128.0 - let baseGradientAlpha: CGFloat = 0.8 - let numSteps = 8 - let firstStep = 0 - let firstLocation = 0.0 - let colors = (0 ..< numSteps).map { i -> UIColor in - if i < firstStep { - return UIColor(white: 1.0, alpha: 1.0) - } else { - let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) - let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) - return UIColor(white: 0.0, alpha: baseGradientAlpha * value) - } - } - let locations = (0 ..< numSteps).map { i -> CGFloat in - if i < firstStep { - return 0.0 - } else { - let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) - return (firstLocation + (1.0 - firstLocation) * step) - } - } - - self.shadowGradientView.image = generateGradientImage(size: CGSize(width: 8.0, height: height), colors: colors.reversed(), locations: locations.reversed().map { 1.0 - $0 })!.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(height - 1.0)) + if let image = StoryContentCaptionComponent.View.shadowImage { + self.shadowGradientView.image = image.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(image.size.height - 1.0)) } self.scrollViewContainer = UIView() @@ -409,8 +386,7 @@ final class StoryContentCaptionComponent: Component { transition.setBounds(view: self.textSelectionKnobContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: self.scrollView.bounds.minY), size: CGSize())) - let shadowHeight: CGFloat = self.shadowGradientView.image?.size.height ?? 100.0 - let shadowOverflow: CGFloat = floor(shadowHeight * 0.6) + let shadowOverflow: CGFloat = 58.0 let shadowFrame = CGRect(origin: CGPoint(x: 0.0, y: -self.scrollView.contentOffset.y + itemLayout.containerSize.height - itemLayout.visibleTextHeight - itemLayout.verticalInset - shadowOverflow), size: CGSize(width: itemLayout.containerSize.width, height: itemLayout.visibleTextHeight + itemLayout.verticalInset + shadowOverflow)) let shadowGradientFrame = CGRect(origin: CGPoint(x: shadowFrame.minX, y: shadowFrame.minY), size: CGSize(width: shadowFrame.width, height: self.scrollView.contentSize.height + 1000.0)) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index ab245eef4b..26853ef250 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2690,7 +2690,7 @@ public final class StoryItemSetContainerComponent: Component { self.bottomContentGradientLayer.colors = colors self.bottomContentGradientLayer.type = .axial - self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.8) + self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.3) } let wasPanning = self.component?.isPanning ?? false diff --git a/submodules/TelegramUI/Images.xcassets/Item List/InlineTextRightArrow.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/InlineTextRightArrow.imageset/Contents.json deleted file mode 100644 index 9d3f9124cb..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Item List/InlineTextRightArrow.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "more.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/submodules/TelegramUI/Images.xcassets/Item List/InlineTextRightArrow.imageset/more.pdf b/submodules/TelegramUI/Images.xcassets/Item List/InlineTextRightArrow.imageset/more.pdf deleted file mode 100644 index 82da70ea58..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Item List/InlineTextRightArrow.imageset/more.pdf +++ /dev/null @@ -1,79 +0,0 @@ -%PDF-1.7 - -1 0 obj - << >> -endobj - -2 0 obj - << /Length 3 0 R >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -1.000000 0.000000 -0.000000 1.000000 1.500000 1.335754 cm -0.000000 0.000000 0.000000 scn -5.252930 4.662109 m -5.252930 4.527832 5.199219 4.409668 5.097168 4.307617 c -0.843262 0.145020 l -0.746582 0.048340 0.628418 0.000000 0.488770 0.000000 c -0.214844 0.000000 0.000000 0.209473 0.000000 0.488770 c -0.000000 0.628418 0.053711 0.746582 0.139648 0.837891 c -4.049805 4.662109 l -0.139648 8.486328 l -0.053711 8.577637 0.000000 8.701172 0.000000 8.835449 c -0.000000 9.114746 0.214844 9.324219 0.488770 9.324219 c -0.628418 9.324219 0.746582 9.275879 0.843262 9.184570 c -5.097168 5.016602 l -5.199219 4.919922 5.252930 4.796387 5.252930 4.662109 c -h -f -n -Q - -endstream -endobj - -3 0 obj - 675 -endobj - -4 0 obj - << /Annots [] - /Type /Page - /MediaBox [ 0.000000 0.000000 8.000000 12.000000 ] - /Resources 1 0 R - /Contents 2 0 R - /Parent 5 0 R - >> -endobj - -5 0 obj - << /Kids [ 4 0 R ] - /Count 1 - /Type /Pages - >> -endobj - -6 0 obj - << /Pages 5 0 R - /Type /Catalog - >> -endobj - -xref -0 7 -0000000000 65535 f -0000000010 00000 n -0000000034 00000 n -0000000765 00000 n -0000000787 00000 n -0000000959 00000 n -0000001033 00000 n -trailer -<< /ID [ (some) (id) ] - /Root 6 0 R - /Size 7 ->> -startxref -1092 -%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/anim_speed_low.tgs b/submodules/TelegramUI/Resources/Animations/anim_speed_low.tgs deleted file mode 100644 index bb9b6f86c6..0000000000 Binary files a/submodules/TelegramUI/Resources/Animations/anim_speed_low.tgs and /dev/null differ diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index bf657946b9..57a80c4980 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -453,6 +453,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G weak var checksTooltipController: TooltipController? weak var copyProtectionTooltipController: TooltipController? weak var emojiPackTooltipController: TooltipScreen? + weak var birthdayTooltipController: TooltipScreen? var currentMessageTooltipScreens: [(TooltipScreen, ListViewItemNode)] = [] @@ -587,8 +588,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var performTextSelectionAction: ((Message?, Bool, NSAttributedString, TextSelectionAction) -> Void)? var performOpenURL: ((Message?, String, Promise?) -> Void)? - var networkSpeedEventsDisposable: Disposable? - public var alwaysShowSearchResultsAsList: Bool = false { didSet { self.presentationInterfaceState = self.presentationInterfaceState.updatedDisplayHistoryFilterAsList(self.alwaysShowSearchResultsAsList) @@ -4910,43 +4909,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - let managingBot: Signal - if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser, !"".isEmpty { - managingBot = self.context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Peer.BusinessConnectedBot(id: self.context.account.peerId) - ) - |> mapToSignal { result -> Signal in - guard let result else { - return .single(nil) - } - return context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Peer.Peer(id: result.id) - ) - |> map { botPeer -> ChatManagingBot? in - guard let botPeer else { - return nil - } - - var isPaused = false - if result.recipients.exclude { - isPaused = result.recipients.additionalPeers.contains(peerId) - } else { - isPaused = !result.recipients.additionalPeers.contains(peerId) - } - - var settingsUrl: String? - if let username = botPeer.addressName { - settingsUrl = "https://t.me/\(username)" - } - - return ChatManagingBot(bot: botPeer, isPaused: isPaused, canReply: result.canReply, settingsUrl: settingsUrl) - } - } - |> distinctUntilChanged - } else { - managingBot = .single(nil) - } - do { let peerId = chatLocationPeerId if case let .peer(peerView) = self.chatLocationInfoData, let peerId = peerId { @@ -5335,11 +5297,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G threadInfo, hasSearchTags, hasSavedChats, - isPremiumRequiredForMessaging, - managingBot - ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot in + isPremiumRequiredForMessaging + ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging in if let strongSelf = self { - if strongSelf.peerView === peerView && strongSelf.reportIrrelvantGeoNotice == peerReportNotice && strongSelf.hasScheduledMessages == hasScheduledMessages && strongSelf.threadInfo == threadInfo && strongSelf.presentationInterfaceState.hasSearchTags == hasSearchTags && strongSelf.presentationInterfaceState.hasSavedChats == hasSavedChats && strongSelf.presentationInterfaceState.isPremiumRequiredForMessaging == isPremiumRequiredForMessaging && managingBot == strongSelf.presentationInterfaceState.contactStatus?.managingBot { + if strongSelf.peerView === peerView && strongSelf.reportIrrelvantGeoNotice == peerReportNotice && strongSelf.hasScheduledMessages == hasScheduledMessages && strongSelf.threadInfo == threadInfo && strongSelf.presentationInterfaceState.hasSearchTags == hasSearchTags && strongSelf.presentationInterfaceState.hasSavedChats == hasSavedChats && strongSelf.presentationInterfaceState.isPremiumRequiredForMessaging == isPremiumRequiredForMessaging { return } @@ -5434,7 +5395,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var contactStatus: ChatContactStatus? if let peer = peerView.peers[peerView.peerId] { if let cachedData = peerView.cachedData as? CachedUserData { - contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot) + contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil) } else if let cachedData = peerView.cachedData as? CachedGroupData { var invitedBy: Peer? if let invitedByPeerId = cachedData.invitedBy { @@ -5442,7 +5403,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G invitedBy = peer } } - contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy) } else if let cachedData = peerView.cachedData as? CachedChannelData { var canReportIrrelevantLocation = true if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member { @@ -5457,7 +5418,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G invitedBy = peer } } - contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy) } var peers = SimpleDictionary() @@ -5560,9 +5521,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, contactStatus.managingBot != nil { - didDisplayActionsPanel = true - } if strongSelf.presentationInterfaceState.search != nil && strongSelf.presentationInterfaceState.hasSearchTags { didDisplayActionsPanel = true } @@ -5583,9 +5541,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - if let contactStatus, contactStatus.managingBot != nil { - displayActionsPanel = true - } if strongSelf.presentationInterfaceState.search != nil && hasSearchTags { displayActionsPanel = true } @@ -5919,10 +5874,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G hasScheduledMessages, hasSearchTags, hasSavedChats, - isPremiumRequiredForMessaging, - managingBot + isPremiumRequiredForMessaging ) - |> deliverOnMainQueue).startStrict(next: { [weak self] peerView, messageAndTopic, savedMessagesPeer, onlineMemberCount, hasScheduledMessages, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot in + |> deliverOnMainQueue).startStrict(next: { [weak self] peerView, messageAndTopic, savedMessagesPeer, onlineMemberCount, hasScheduledMessages, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging in if let strongSelf = self { strongSelf.hasScheduledMessages = hasScheduledMessages @@ -5932,7 +5886,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let peer = peerView.peers[peerView.peerId] { copyProtectionEnabled = peer.isCopyProtectionEnabled if let cachedData = peerView.cachedData as? CachedUserData { - contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot) + contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil) } else if let cachedData = peerView.cachedData as? CachedGroupData { var invitedBy: Peer? if let invitedByPeerId = cachedData.invitedBy { @@ -5940,7 +5894,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G invitedBy = peer } } - contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy) } else if let cachedData = peerView.cachedData as? CachedChannelData { var canReportIrrelevantLocation = true if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member { @@ -5953,7 +5907,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G invitedBy = peer } } - contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot) + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy) } var peers = SimpleDictionary() @@ -6157,9 +6111,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, contactStatus.managingBot != nil { - didDisplayActionsPanel = true - } var displayActionsPanel = false if let contactStatus = contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { @@ -6177,9 +6128,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - if let contactStatus, contactStatus.managingBot != nil { - displayActionsPanel = true - } if displayActionsPanel != didDisplayActionsPanel { animated = true @@ -6851,7 +6799,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.preloadSavedMessagesChatsDisposable?.dispose() self.recorderDataDisposable.dispose() self.displaySendWhenOnlineTipDisposable.dispose() - self.networkSpeedEventsDisposable?.dispose() } deallocate() } @@ -11741,57 +11688,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - var lastEventTimestamp: Double = 0.0 - self.networkSpeedEventsDisposable = (self.context.account.network.networkSpeedLimitedEvents - |> deliverOnMainQueue).start(next: { [weak self] event in - guard let self else { - return - } - - let timestamp = CFAbsoluteTimeGetCurrent() - if lastEventTimestamp + 10.0 < timestamp { - lastEventTimestamp = timestamp - } else { - return - } - - //TODO:localize - let title: String - let text: String - switch event { - case .download: - var speedIncreaseFactor = 10 - if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["upload_premium_speedup_download"] as? Double { - speedIncreaseFactor = Int(value) - } - title = "Download speed limited" - text = "Subscribe to [Telegram Premium]() and increase download speeds \(speedIncreaseFactor) times." - case .upload: - var speedIncreaseFactor = 10 - if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["upload_premium_speedup_upload"] as? Double { - speedIncreaseFactor = Int(value) - } - title = "Upload speed limited" - text = "Subscribe to [Telegram Premium]() and increase upload speeds \(speedIncreaseFactor) times." - } - let content: UndoOverlayContent = .universal(animation: "anim_speed_low", scale: 0.066, colors: [:], title: title, text: text, customUndoText: nil, timeout: 5.0) - - self.present(UndoOverlayController(presentationData: self.presentationData, content: content, elevatedLayout: false, position: .top, action: { [weak self] action in - guard let self else { - return false - } - switch action { - case .info: - let controller = context.sharedContext.makePremiumIntroController(context: self.context, source: .reactions, forceDark: false, dismissed: nil) - self.push(controller) - return true - default: - break - } - return false - }), in: .current) - }) - self.displayNodeDidLoad() } @@ -12346,6 +12242,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) } + +// #if DEBUG +// Queue.mainQueue().after(0.5) { +// self.displayBirthdayTooltip() +// } +// #endif } override public func viewWillDisappear(_ animated: Bool) { @@ -16074,6 +15976,30 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } + func displayBirthdayTooltip() { + guard let rect = self.chatDisplayNode.frameForGiftButton(), self.effectiveNavigationController?.topViewController === self, let peer = self.presentationInterfaceState.renderedPeer?.peer.flatMap({ EnginePeer($0) }) else { + return + } + + let peerName = peer.compactDisplayTitle + let text = "🎂 \(peerName) is having a birthday today. You can give \(peerName) **Telegram Premium** as a birthday gift." + + let tooltipScreen = TooltipScreen( + context: self.context, + account: self.context.account, + sharedContext: self.context.sharedContext, + text: .markdown(text: text), + location: .point(rect.offsetBy(dx: 0.0, dy: -3.0), .bottom), + displayDuration: .default, + cornerRadius: 10.0, + shouldDismissOnTouch: { _, _ in + return .ignore + } + ) + self.present(tooltipScreen, in: .current) + self.birthdayTooltipController = tooltipScreen + } + func displayChecksTooltip() { self.checksTooltipController?.dismiss() diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 9eee014f4c..fd487a266f 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -3412,6 +3412,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { return nil } + func frameForGiftButton() -> CGRect? { + if let textInputPanelNode = self.textInputPanelNode, self.inputPanelNode === textInputPanelNode { + return textInputPanelNode.frameForGiftButton().flatMap { + return $0.offsetBy(dx: textInputPanelNode.frame.minX, dy: textInputPanelNode.frame.minY) + } + } + return nil + } + var isTextInputPanelActive: Bool { return self.inputPanelNode is ChatTextInputPanelNode } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift b/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift index 4c87600f81..555029ce7f 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceInputContexts.swift @@ -167,7 +167,11 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte if case .scheduledMessages = chatPresentationInterfaceState.subject { } else { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - let giftIsEnabled = !premiumConfiguration.isPremiumDisabled && premiumConfiguration.showPremiumGiftInAttachMenu && premiumConfiguration.showPremiumGiftInTextField + var giftIsEnabled = false + giftIsEnabled = !premiumConfiguration.isPremiumDisabled && premiumConfiguration.showPremiumGiftInAttachMenu && premiumConfiguration.showPremiumGiftInTextField +#if DEBUG + giftIsEnabled = true +#endif if isTextEmpty, giftIsEnabled, let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, !peer.isDeleted && peer.botInfo == nil && !peer.flags.contains(.isSupport) && !peer.isPremium && !chatPresentationInterfaceState.premiumGiftOptions.isEmpty && chatPresentationInterfaceState.suggestPremiumGift { accessoryItems.append(.gift) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 694a5266a2..d90cc96063 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -117,44 +117,32 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat } var displayActionsPanel = false - if !chatPresentationInterfaceState.peerIsBlocked && !inhibitTitlePanelDisplay, let contactStatus = chatPresentationInterfaceState.contactStatus { - if let peerStatusSettings = contactStatus.peerStatusSettings { - if !peerStatusSettings.flags.isEmpty { - if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { - displayActionsPanel = true - } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) || peerStatusSettings.contains(.autoArchived) { - displayActionsPanel = true - } else if peerStatusSettings.contains(.canShareContact) { - displayActionsPanel = true - } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { - displayActionsPanel = true - } else if peerStatusSettings.contains(.suggestAddMembers) { - displayActionsPanel = true - } - } - if peerStatusSettings.requestChatTitle != nil { + if !chatPresentationInterfaceState.peerIsBlocked && !inhibitTitlePanelDisplay, let contactStatus = chatPresentationInterfaceState.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) || peerStatusSettings.contains(.autoArchived) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + displayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.suggestAddMembers) { displayActionsPanel = true } } + if peerStatusSettings.requestChatTitle != nil { + displayActionsPanel = true + } } - if (selectedContext == nil || selectedContext! <= .pinnedMessage) { - if displayActionsPanel { - if let currentPanel = currentPanel as? ChatReportPeerTitlePanelNode { - return currentPanel - } else if let controllerInteraction = controllerInteraction { - let panel = ChatReportPeerTitlePanelNode(context: context, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) - panel.interfaceInteraction = interfaceInteraction - return panel - } - } else if !chatPresentationInterfaceState.peerIsBlocked && !inhibitTitlePanelDisplay, let contactStatus = chatPresentationInterfaceState.contactStatus, contactStatus.managingBot != nil { - if let currentPanel = currentPanel as? ChatManagingBotTitlePanelNode { - return currentPanel - } else { - let panel = ChatManagingBotTitlePanelNode(context: context) - panel.interfaceInteraction = interfaceInteraction - return panel - } + if displayActionsPanel && (selectedContext == nil || selectedContext! <= .pinnedMessage) { + if let currentPanel = currentPanel as? ChatReportPeerTitlePanelNode { + return currentPanel + } else if let controllerInteraction = controllerInteraction { + let panel = ChatReportPeerTitlePanelNode(context: context, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) + panel.interfaceInteraction = interfaceInteraction + return panel } } diff --git a/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift deleted file mode 100644 index 73e852e12d..0000000000 --- a/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift +++ /dev/null @@ -1,472 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import TelegramPresentationData -import ChatPresentationInterfaceState -import ComponentFlow -import AvatarNode -import MultilineTextComponent -import PlainButtonComponent -import ComponentDisplayAdapters -import AccountContext -import TelegramCore -import BundleIconComponent -import ContextUI -import SwiftSignalKit - -private final class ChatManagingBotTitlePanelComponent: Component { - let context: AccountContext - let theme: PresentationTheme - let strings: PresentationStrings - let insets: UIEdgeInsets - let peer: EnginePeer - let managesChat: Bool - let isPaused: Bool - let toggleIsPaused: () -> Void - let openSettings: (UIView) -> Void - - init( - context: AccountContext, - theme: PresentationTheme, - strings: PresentationStrings, - insets: UIEdgeInsets, - peer: EnginePeer, - managesChat: Bool, - isPaused: Bool, - toggleIsPaused: @escaping () -> Void, - openSettings: @escaping (UIView) -> Void - ) { - self.context = context - self.theme = theme - self.strings = strings - self.insets = insets - self.peer = peer - self.managesChat = managesChat - self.isPaused = isPaused - self.toggleIsPaused = toggleIsPaused - self.openSettings = openSettings - } - - static func ==(lhs: ChatManagingBotTitlePanelComponent, rhs: ChatManagingBotTitlePanelComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.strings != rhs.strings { - return false - } - if lhs.insets != rhs.insets { - return false - } - if lhs.peer != rhs.peer { - return false - } - if lhs.managesChat != rhs.managesChat { - return false - } - if lhs.isPaused != rhs.isPaused { - return false - } - return true - } - - final class View: UIView { - private let title = ComponentView() - private let text = ComponentView() - private var avatarNode: AvatarNode? - private let actionButton = ComponentView() - private let settingsButton = ComponentView() - - private var component: ChatManagingBotTitlePanelComponent? - - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(component: ChatManagingBotTitlePanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { - self.component = component - - let topInset: CGFloat = 6.0 - let bottomInset: CGFloat = 6.0 - let avatarDiameter: CGFloat = 36.0 - let avatarTextSpacing: CGFloat = 10.0 - let titleTextSpacing: CGFloat = 1.0 - let leftInset: CGFloat = component.insets.left + 12.0 - let rightInset: CGFloat = component.insets.right + 10.0 - let actionAndSettingsButtonsSpacing: CGFloat = 8.0 - - //TODO:localize - let actionButtonSize = self.actionButton.update( - transition: transition, - component: AnyComponent(PlainButtonComponent( - content: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: component.isPaused ? "START" : "STOP", font: Font.semibold(15.0), textColor: component.theme.list.itemCheckColors.foregroundColor)) - )), - background: AnyComponent(RoundedRectangle( - color: component.theme.list.itemCheckColors.fillColor, - cornerRadius: nil - )), - effectAlignment: .center, - contentInsets: UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0), - action: { [weak self] in - guard let self, let component = self.component else { - return - } - component.toggleIsPaused() - }, - animateAlpha: true, - animateScale: false, - animateContents: false - )), - environment: {}, - containerSize: CGSize(width: 150.0, height: 100.0) - ) - - let settingsButtonSize = self.settingsButton.update( - transition: transition, - component: AnyComponent(PlainButtonComponent( - content: AnyComponent(BundleIconComponent( - name: "Chat/Context Menu/Customize", - tintColor: component.theme.rootController.navigationBar.controlColor - )), - effectAlignment: .center, - minSize: CGSize(width: 1.0, height: 40.0), - contentInsets: UIEdgeInsets(top: 0.0, left: 2.0, bottom: 0.0, right: 2.0), - action: { [weak self] in - guard let self, let component = self.component else { - return - } - guard let settingsButtonView = self.settingsButton.view else { - return - } - component.openSettings(settingsButtonView) - }, - animateAlpha: true, - animateScale: false, - animateContents: false - )), - environment: {}, - containerSize: CGSize(width: 150.0, height: 100.0) - ) - - let maxTextWidth: CGFloat = availableSize.width - leftInset - avatarDiameter - avatarTextSpacing - rightInset - actionButtonSize.width - actionAndSettingsButtonsSpacing - settingsButtonSize.width - 8.0 - - let titleSize = self.title.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: component.peer.displayTitle(strings: component.strings, displayOrder: .firstLast), font: Font.semibold(16.0), textColor: component.theme.rootController.navigationBar.primaryTextColor)) - )), - environment: {}, - containerSize: CGSize(width: maxTextWidth, height: 100.0) - ) - //TODO:localize - let textSize = self.text.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "bot has access to this chat", font: Font.regular(15.0), textColor: component.theme.rootController.navigationBar.secondaryTextColor)) - )), - environment: {}, - containerSize: CGSize(width: maxTextWidth, height: 100.0) - ) - - let size = CGSize(width: availableSize.width, height: topInset + titleSize.height + titleTextSpacing + textSize.height + bottomInset) - - let titleFrame = CGRect(origin: CGPoint(x: leftInset + avatarDiameter + avatarTextSpacing, y: topInset), size: titleSize) - if let titleView = self.title.view { - if titleView.superview == nil { - titleView.layer.anchorPoint = CGPoint() - self.addSubview(titleView) - } - titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) - transition.setPosition(view: titleView, position: titleFrame.origin) - } - - let textFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleTextSpacing), size: textSize) - if let textView = self.text.view { - if textView.superview == nil { - textView.layer.anchorPoint = CGPoint() - self.addSubview(textView) - } - textView.bounds = CGRect(origin: CGPoint(), size: textFrame.size) - transition.setPosition(view: textView, position: textFrame.origin) - } - - let avatarFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - avatarDiameter) * 0.5)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) - let avatarNode: AvatarNode - if let current = self.avatarNode { - avatarNode = current - } else { - avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 15.0)) - self.avatarNode = avatarNode - self.addSubview(avatarNode.view) - } - avatarNode.frame = avatarFrame - avatarNode.updateSize(size: avatarFrame.size) - avatarNode.setPeer(context: component.context, theme: component.theme, peer: component.peer) - - let settingsButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - settingsButtonSize.width, y: floor((size.height - settingsButtonSize.height) * 0.5)), size: settingsButtonSize) - if let settingsButtonView = self.settingsButton.view { - if settingsButtonView.superview == nil { - self.addSubview(settingsButtonView) - } - transition.setFrame(view: settingsButtonView, frame: settingsButtonFrame) - } - - let actionButtonFrame = CGRect(origin: CGPoint(x: settingsButtonFrame.minX - actionAndSettingsButtonsSpacing - actionButtonSize.width, y: floor((size.height - actionButtonSize.height) * 0.5)), size: actionButtonSize) - if let actionButtonView = self.actionButton.view { - if actionButtonView.superview == nil { - self.addSubview(actionButtonView) - } - transition.setFrame(view: actionButtonView, frame: actionButtonFrame) - } - - return size - } - } - - func makeView() -> View { - return View(frame: CGRect()) - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - -final class ChatManagingBotTitlePanelNode: ChatTitleAccessoryPanelNode { - private let context: AccountContext - private let separatorNode: ASDisplayNode - private let content = ComponentView() - - private var chatLocation: ChatLocation? - private var theme: PresentationTheme? - private var managingBot: ChatManagingBot? - - init(context: AccountContext) { - self.context = context - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true - - super.init() - - self.addSubnode(self.separatorNode) - } - - private func toggleIsPaused() { - guard let chatPeerId = self.chatLocation?.peerId else { - return - } - - let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.BusinessConnectedBot(id: self.context.account.peerId) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] bot in - guard let self else { - return - } - guard let bot else { - return - } - - var recipients = bot.recipients - var additionalPeers = recipients.additionalPeers - if additionalPeers.contains(chatPeerId) { - additionalPeers.remove(chatPeerId) - } else { - additionalPeers.insert(chatPeerId) - } - recipients = TelegramBusinessRecipients( - categories: recipients.categories, - additionalPeers: additionalPeers, - exclude: recipients.exclude - ) - - let _ = self.context.engine.accountData.setAccountConnectedBot(bot: TelegramAccountConnectedBot( - id: bot.id, - recipients: recipients, - canReply: bot.canReply - )).startStandalone() - }) - } - - private func openSettingsMenu(sourceView: UIView) { - guard let interfaceInteraction = self.interfaceInteraction else { - return - } - guard let chatController = interfaceInteraction.chatController() else { - return - } - guard let managingBot = self.managingBot else { - return - } - let _ = managingBot - - let strings = self.context.sharedContext.currentPresentationData.with { $0 }.strings - let _ = strings - - var items: [ContextMenuItem] = [] - - //TODO:localize - items.append(.action(ContextMenuActionItem(text: "Remove bot from this chat", textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, a in - a(.default) - - guard let self else { - return - } - let _ = self - }))) - if let url = managingBot.settingsUrl { - items.append(.action(ContextMenuActionItem(text: "Manage Bot", icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Settings"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, a in - a(.default) - - guard let self else { - return - } - let _ = (self.context.sharedContext.resolveUrl(context: self.context, peerId: nil, url: url, skipUrlAuth: false) - |> deliverOnMainQueue).start(next: { [weak self] result in - guard let self else { - return - } - guard let chatController = interfaceInteraction.chatController() else { - return - } - self.context.sharedContext.openResolvedUrl( - result, - context: self.context, - urlContext: .generic, - navigationController: chatController.navigationController as? NavigationController, - forceExternal: false, - openPeer: { [weak self] peer, navigation in - guard let self, let chatController = interfaceInteraction.chatController() else { - return - } - guard let navigationController = chatController.navigationController as? NavigationController else { - return - } - switch navigation { - case let .chat(_, subject, peekData): - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), subject: subject, peekData: peekData)) - case let .withBotStartPayload(botStart): - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), botStart: botStart, keepStack: .always)) - case let .withAttachBot(attachBotStart): - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), attachBotStart: attachBotStart)) - case let .withBotApp(botAppStart): - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), botAppStart: botAppStart)) - case .info: - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer, let chatController = interfaceInteraction.chatController() else { - return - } - guard let navigationController = chatController.navigationController as? NavigationController else { - return - } - if let controller = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { - navigationController.pushViewController(controller) - } - }) - default: - break - } - }, - sendFile: nil, - sendSticker: nil, - requestMessageActionUrlAuth: nil, - joinVoiceChat: nil, - present: { [weak chatController] c, a in - chatController?.present(c, in: .window(.root), with: a) - }, - dismissInput: { - }, - contentContext: nil, - progress: nil, - completion: nil - ) - }) - }))) - } - - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: chatController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) - interfaceInteraction.presentController(contextController, nil) - } - - override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { - self.chatLocation = interfaceState.chatLocation - self.managingBot = interfaceState.contactStatus?.managingBot - - if interfaceState.theme !== self.theme { - self.theme = interfaceState.theme - - self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor - } - - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) - - if let managingBot = interfaceState.contactStatus?.managingBot { - let contentSize = self.content.update( - transition: Transition(transition), - component: AnyComponent(ChatManagingBotTitlePanelComponent( - context: self.context, - theme: interfaceState.theme, - strings: interfaceState.strings, - insets: UIEdgeInsets(top: 0.0, left: leftInset, bottom: 0.0, right: rightInset), - peer: managingBot.bot, - managesChat: managingBot.canReply, - isPaused: managingBot.isPaused, - toggleIsPaused: { [weak self] in - guard let self else { - return - } - self.toggleIsPaused() - }, - openSettings: { [weak self] sourceView in - guard let self else { - return - } - self.openSettingsMenu(sourceView: sourceView) - } - )), - environment: {}, - containerSize: CGSize(width: width, height: 1000.0) - ) - if let contentView = self.content.view { - if contentView.superview == nil { - self.view.addSubview(contentView) - } - transition.updateFrame(view: contentView, frame: CGRect(origin: CGPoint(), size: contentSize)) - } - - return LayoutResult(backgroundHeight: contentSize.height, insetHeight: contentSize.height, hitTestSlop: 0.0) - } else { - return LayoutResult(backgroundHeight: 0.0, insetHeight: 0.0, hitTestSlop: 0.0) - } - - } -} - -private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { - private let controller: ViewController - private let sourceView: UIView - - init(controller: ViewController, sourceView: UIView) { - self.controller = controller - self.sourceView = sourceView - } - - func transitionInfo() -> ContextControllerReferenceViewInfo? { - return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) - } -} - diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index d55b4b900b..709a548cf5 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -282,8 +282,9 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe }, openStorageManagement: { }, openPasswordSetup: { }, openPremiumIntro: { - }, openPremiumGift: { + }, openPremiumGift: { _ in }, openActiveSessions: { + }, openBirthdaySetup: { }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { }, hideChatFolderUpdates: { diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 3139f0967a..5401f3e9a7 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -4594,6 +4594,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } return nil } + + func frameForGiftButton() -> CGRect? { + for (item, button) in self.accessoryItemButtons { + if case .gift = item { + return button.frame.insetBy(dx: 0.0, dy: 6.0) + } + } + return nil + } func makeSnapshotForTransition() -> ChatMessageTransitionNodeImpl.Source.TextInput? { guard let backgroundImage = self.transparentTextInputBackgroundImage else { diff --git a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift index 18b7c7bed8..c8fe99abd0 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift @@ -156,10 +156,12 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable { }, openPremiumIntro: { }, - openPremiumGift: { + openPremiumGift: { _ in }, openActiveSessions: { }, + openBirthdaySetup: { + }, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: { diff --git a/submodules/TelegramUI/Sources/ComposeControllerNode.swift b/submodules/TelegramUI/Sources/ComposeControllerNode.swift index 9213c2fd39..59c8835503 100644 --- a/submodules/TelegramUI/Sources/ComposeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ComposeControllerNode.swift @@ -52,7 +52,7 @@ final class ComposeControllerNode: ASDisplayNode { ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: { openCreateNewChannelImpl?() }) - ], includeChatList: false, topPeers: false)), onlyWriteable: false, displayPermissionPlaceholder: false) + ], includeChatList: false, topPeers: .none)), onlyWriteable: false, displayPermissionPlaceholder: false) super.init() diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index 177d376c49..798de3e5d8 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -169,11 +169,17 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } self.contentNode = .chats(chatListNode) } else { - var displayTopPeers = false - if case .premiumGifting = mode { - displayTopPeers = true + let displayTopPeers: ContactListPresentation.TopPeers + if case let .premiumGifting(topSectionTitle, topSectionPeers) = mode { + if let topSectionTitle { + displayTopPeers = .custom(title: topSectionTitle, peerIds: topSectionPeers) + } else { + displayTopPeers = .recent + } } else if case .requestedUsersSelection = mode { - displayTopPeers = true + displayTopPeers = .recent + } else { + displayTopPeers = .none } let contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, onlyWriteable: onlyWriteable, selectionState: ContactListNodeGroupSelectionState()) self.contentNode = .contacts(contactListNode) diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift index 99084f28bd..0a446de4a4 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift @@ -68,7 +68,7 @@ final class ContactSelectionControllerNode: ASDisplayNode { self.filters = filters var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? - self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false, topPeers: false)), filters: filters, onlyWriteable: false, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in + self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false, topPeers: .none)), filters: filters, onlyWriteable: false, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in contextActionImpl?(peer, node, gesture, nil) } : nil, multipleSelection: multipleSelection) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 7eaa2431f5..8b8ffa67ec 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2115,7 +2115,15 @@ public final class SharedAccountContextImpl: SharedAccountContext { let limit: Int32 = 10 var reachedLimitImpl: ((Int32) -> Void)? - let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .premiumGifting, options: [], isPeerEnabled: { peer in + + let mode: ContactMultiselectionControllerMode + if case let .chatList(peerIds) = source { + mode = .premiumGifting(topSectionTitle: "🎂 BIRTHDAY TODAY", topSectionPeers: peerIds) + } else { + mode = .premiumGifting(topSectionTitle: nil, topSectionPeers: []) + } + + let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: mode, options: [], isPeerEnabled: { peer in if case let .user(user) = peer, user.botInfo == nil && !peer.isService && !user.flags.contains(.isSupport) { return true } else { @@ -2314,7 +2322,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, isEditing: isEditing, parentNavigationController: parentNavigationController, sendSticker: sendSticker) } - public func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController { + public func makeStickerEditorScreen(context: AccountContext, source: Any, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, @escaping () -> Void) -> Void) -> ViewController { let subject: MediaEditorScreen.Subject let mode: MediaEditorScreen.Mode.StickerEditorMode if let file = source as? TelegramMediaFile { @@ -2347,9 +2355,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { } return nil }, completion: { result, commit in - commit({}) if case let .sticker(file) = result.media { - completion(file) + completion(file, { + commit({}) + }) } } as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void ) diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 16b60c0dad..7e703d8abd 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -441,8 +441,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { var resourceReference: MediaResourceReference? if let thumbnail = info.thumbnail { - if info.flags.contains(.isAnimated) || info.flags.contains(.isVideo) { - thumbnailItem = .animated(EngineMediaResource(thumbnail.resource), thumbnail.dimensions, info.flags.contains(.isVideo)) + if thumbnail.typeHint != .generic { + thumbnailItem = .animated(EngineMediaResource(thumbnail.resource), thumbnail.dimensions, thumbnail.typeHint == .video) } else { thumbnailItem = .still(thumbnail) } diff --git a/submodules/WebUI/BUILD b/submodules/WebUI/BUILD index 3973bdc9fc..9fdbf7604b 100644 --- a/submodules/WebUI/BUILD +++ b/submodules/WebUI/BUILD @@ -34,6 +34,7 @@ swift_library( "//submodules/Markdown:Markdown", "//submodules/TextFormat:TextFormat", "//submodules/LocalAuth", + "//submodules/InstantPageCache" ], visibility = [ "//visibility:public", diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 5894b1e08b..4991caf7a8 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -23,6 +23,7 @@ import PromptUI import PhoneNumberFormat import QrCodeUI import InstantPageUI +import InstantPageCache import LocalAuth private let durgerKingBotIds: [Int64] = [5104055776, 2200339955] @@ -1073,8 +1074,8 @@ public final class WebAppController: ViewController, AttachmentContainable { self.requestBiometryAuth() case "web_app_biometry_update_token": var tokenData: Data? - if let json, let tokenDataValue = json["token"] as? String, !tokenDataValue.isEmpty { - tokenData = tokenDataValue.data(using: .utf8) + if let json, let tokenDataValue = json["token"] as? Data { + tokenData = tokenDataValue } self.requestBiometryUpdateToken(tokenData: tokenData) default: @@ -1513,7 +1514,10 @@ public final class WebAppController: ViewController, AttachmentContainable { let appBundleId = self.context.sharedContext.applicationBindings.appBundleId Thread { [weak self] in - let key = LocalAuth.getOrCreatePrivateKey(baseAppBundleId: appBundleId, keyId: keyId) + var key = LocalAuth.getPrivateKey(baseAppBundleId: appBundleId, keyId: keyId) + if key == nil { + key = LocalAuth.addPrivateKey(baseAppBundleId: appBundleId, keyId: keyId) + } let decryptedData: LocalAuth.DecryptionResult if let key { @@ -1563,9 +1567,9 @@ public final class WebAppController: ViewController, AttachmentContainable { data["status"] = isAuthorized ? "authorized" : "failed" if isAuthorized { if let tokenData { - data["token"] = String(data: tokenData, encoding: .utf8) ?? "" + data["token"] = tokenData } else { - data["token"] = "" + data["token"] = Data() } } @@ -1589,7 +1593,10 @@ public final class WebAppController: ViewController, AttachmentContainable { if let tokenData { let appBundleId = self.context.sharedContext.applicationBindings.appBundleId Thread { [weak self] in - let key = LocalAuth.getOrCreatePrivateKey(baseAppBundleId: appBundleId, keyId: keyId) + var key = LocalAuth.getPrivateKey(baseAppBundleId: appBundleId, keyId: keyId) + if key == nil { + key = LocalAuth.addPrivateKey(baseAppBundleId: appBundleId, keyId: keyId) + } var encryptedData: TelegramBotBiometricsState.OpaqueToken? if let key { @@ -1612,28 +1619,6 @@ public final class WebAppController: ViewController, AttachmentContainable { state.opaqueToken = encryptedData return state }) - - var data: [String: Any] = [:] - data["status"] = "updated" - - guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else { - return - } - guard let jsonDataString = String(data: jsonData, encoding: .utf8) else { - return - } - self.webView?.sendEvent(name: "biometry_token_updated", data: jsonDataString) - } else { - var data: [String: Any] = [:] - data["status"] = "failed" - - guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else { - return - } - guard let jsonDataString = String(data: jsonData, encoding: .utf8) else { - return - } - self.webView?.sendEvent(name: "biometry_token_updated", data: jsonDataString) } } }.start() @@ -1643,17 +1628,6 @@ public final class WebAppController: ViewController, AttachmentContainable { state.opaqueToken = nil return state }) - - var data: [String: Any] = [:] - data["status"] = "removed" - - guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else { - return - } - guard let jsonDataString = String(data: jsonData, encoding: .utf8) else { - return - } - self.webView?.sendEvent(name: "biometry_token_updated", data: jsonDataString) } } } @@ -1876,6 +1850,25 @@ public final class WebAppController: ViewController, AttachmentContainable { self?.controllerNode.webView?.reload() }))) + items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_TermsOfUse, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + c.dismiss(completion: nil) + + guard let self, let navigationController = self.getNavigationController() else { + return + } + + let context = self.context + let _ = (cachedWebAppTermsPage(context: context) + |> deliverOnMainQueue).startStandalone(next: { resolvedUrl in + context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: true, openPeer: { peer, navigation in + }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak self] c, arguments in + self?.push(c) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) + }) + }))) + if let _ = attachMenuBot, [.attachMenu, .settings, .generic].contains(source) { items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_RemoveBot, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 28c66b0361..169383d6f7 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -159,7 +159,7 @@ final class WebAppWebView: WKWebView { self.interactiveTransitionGestureRecognizerTest = { point -> Bool in return point.x > 30.0 } - self.allowsBackForwardNavigationGestures = true + self.allowsBackForwardNavigationGestures = false if #available(iOS 16.4, *) { self.isInspectable = true }