diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 47edc515ff..eb1871c1a9 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -826,6 +826,7 @@ public protocol SharedAccountContext: AnyObject { func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController + func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, action: @escaping () -> Void) -> ViewController func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController @@ -885,6 +886,14 @@ public enum PremiumDemoSubject { case translation } +public enum PremiumLimitSubject { + case folders + case chatsPerFolder + case pins + case files + case accounts +} + public protocol ComposeController: ViewController { } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 7f6796980f..c6d0ea6110 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1219,7 +1219,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let strongSelf = self else { return } - strongSelf.reorderingDonePressed() + let _ = strongSelf.reorderingDonePressed() } self.chatListDisplayNode.toolbarActionSelected = { [weak self] action in @@ -2130,16 +2130,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController tabContainerOffset += layout.statusBarHeight ?? 0.0 tabContainerOffset += 44.0 + 20.0 } - //tabContainerOffset += self.chatListDisplayNode.inlineStackContainerTransitionFraction * NavigationBar.defaultSecondaryContentHeight let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0 - - //transition.updateAlpha(node: self.tabContainerNode, alpha: self.chatListDisplayNode.inlineStackContainerTransitionFraction * 0.5 + (1.0 - self.chatListDisplayNode.inlineStackContainerTransitionFraction) * 1.0) - //self.tabContainerNode.isUserInteractionEnabled = self.chatListDisplayNode.inlineStackContainerNode == nil - transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0))) - self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) - + if !skipTabContainerUpdate { + self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + } self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: self.cleanNavigationHeight, transition: transition) } @@ -2186,7 +2182,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } @objc fileprivate func donePressed() { - self.reorderingDonePressed() + let skipLayoutUpdate = self.reorderingDonePressed() (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(nil, transition: .animated(duration: 0.4, curve: .spring)) self.searchContentNode?.setIsEnabled(true, animated: true) @@ -2200,15 +2196,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return state } self.chatListDisplayNode.isEditing = false - if let layout = self.validLayout { - self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) + + if !skipLayoutUpdate { + if let layout = self.validLayout { + self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) + } } } - @objc fileprivate func reorderingDonePressed() { + private var skipTabContainerUpdate = false + fileprivate func reorderingDonePressed() -> Bool { guard let defaultFilters = self.tabContainerData else { - return + return false } + self.skipTabContainerUpdate = true let defaultFilterIds = defaultFilters.0.compactMap { entry -> Int32? in switch entry { case .all: @@ -2227,6 +2228,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let strongSelf = self else { return } + strongSelf.skipTabContainerUpdate = false strongSelf.chatListDisplayNode.isReorderingFilters = false strongSelf.isReorderingTabsValue.set(false) (strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(true, transition: .animated(duration: 0.2, curve: .easeInOut)) @@ -2263,6 +2265,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } else { completion() } + return true } public func setInlineChatList(location: ChatListControllerLocation?) { @@ -4625,7 +4628,7 @@ private final class ChatListLocationContext { self.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent( content: .text(title: presentationData.strings.Common_Done, isBold: true), pressed: { [weak self] _ in - self?.parentController?.reorderingDonePressed() + let _ = self?.parentController?.reorderingDonePressed() } ))) @@ -4671,7 +4674,7 @@ private final class ChatListLocationContext { self.leftButton = AnyComponentWithIdentity(id: "done", component: AnyComponent(NavigationButtonComponent( content: .text(title: presentationData.strings.Common_Done, isBold: true), pressed: { [weak self] _ in - self?.parentController?.reorderingDonePressed() + let _ = self?.parentController?.reorderingDonePressed() } ))) } else { diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 75fb939684..e208322ea6 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -696,7 +696,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)? var didBeginSelectingChats: (() -> Void)? - var displayFilterLimit: (() -> Void)? + public var displayFilterLimit: (() -> Void)? public init(context: AccountContext, location: ChatListControllerLocation, chatListMode: ChatListNodeMode = .chatList, previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) { self.context = context @@ -814,6 +814,8 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele let translation = recognizer.translation(in: self.view) var transitionFraction = translation.x / layout.size.width + var transition: ContainedViewLayoutTransition = .immediate + func rubberBandingOffset(offset: CGFloat, bandingStart: CGFloat) -> CGFloat { let bandedOffset = offset - bandingStart let range: CGFloat = 600.0 @@ -832,9 +834,11 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele if let filtersLimit = self.filtersLimit, selectedIndex >= filtersLimit - 1 { transitionFraction = 0.0 + self.transitionFractionOffset = 0.0 recognizer.isEnabled = false recognizer.isEnabled = true + transition = .animated(duration: 0.45, curve: .spring) self.displayFilterLimit?() } } @@ -848,7 +852,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele } } self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, transition: .immediate) - self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, .immediate, false) + self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false) } case .cancelled, .ended: if let (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) { diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index cc24d31552..b0d1b6d8a6 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -501,7 +501,7 @@ public final class ChatListFilterTabContainerNode: ASDisplayNode { } } - var filtersCount: Int32 { + public var filtersCount: Int32 { if let (_, _, filters, _, _, _, _, _, _, _) = self.currentParams { let filters = filters.filter { filter in if case .all = filter { diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift index 52aeae5ffb..38f9888047 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift @@ -510,7 +510,6 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode { return } if let reorderingItemNodePair = strongSelf.itemNodePairs[reorderingItem], let (initial, _) = strongSelf.reorderingItemPosition, let reorderedItemIds = strongSelf.reorderedItemIds, let currentItemIndex = reorderedItemIds.firstIndex(of: reorderingItem) { - for (id, itemNodePair) in strongSelf.itemNodePairs { guard let itemIndex = reorderedItemIds.firstIndex(of: id) else { continue diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index 9cef9312b6..80bc21a037 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -752,12 +752,16 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s } else { if case let .user(adminPeer) = adminPeer, adminPeer.botInfo != nil, invite { if let initialParticipant = initialParticipant, case let .member(_, _, adminRights, _, _) = initialParticipant, adminRights != nil { - } else { entries.append(.adminRights(presentationData.theme, presentationData.strings.Bot_AddToChat_Add_AdminRights, state.adminRights)) } } + var accountIsCreator = false + if case .creator = group.role { + accountIsCreator = true + } + if !invite || state.adminRights { entries.append(.rightsTitle(presentationData.theme, presentationData.strings.Channel_EditAdmin_PermissionsHeader)) @@ -785,12 +789,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s } else { currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins).subtracting(.canBeAnonymous) } - - var accountIsCreator = false - if case .creator = group.role { - accountIsCreator = true - } - + var index = 0 for right in rightsOrder { if accountUserRightsFlags.contains(right) { @@ -813,8 +812,16 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s entries.append(.rankInfo(presentationData.theme, presentationData.strings.Group_EditAdmin_RankInfo(placeholder).string, invite)) } - if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _) = initialParticipant, admin.id != accountPeerId, adminInfo != nil { - entries.append(.dismiss(presentationData.theme, presentationData.strings.Channel_Moderator_AccessLevelRevoke)) + if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _) = initialParticipant, admin.id != accountPeerId, let adminInfo { + var canDismiss = false + if accountIsCreator { + canDismiss = true + } else if adminInfo.promotedBy == accountPeerId || adminInfo.canBeEditedByAccountPeer { + canDismiss = true + } + if canDismiss { + entries.append(.dismiss(presentationData.theme, presentationData.strings.Channel_Moderator_AccessLevelRevoke)) + } } } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index 9a25b3a9be..bc975f1429 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -1068,8 +1068,8 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer?, member: case .admin: switch member { case let .legacyGroupMember(_, _, invitedBy, _): + result.insert(.restrict) if invitedBy == accountPeerId { - result.insert(.restrict) result.insert(.promote) } case .channelMember: diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift index 4cc6c5c9b3..013acd41f3 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift @@ -175,7 +175,6 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self._ready.set(.never()) self.tabContainerNode = ChatListFilterTabContainerNode() - self.navigationBar?.setSecondaryContentNode(self.tabContainerNode, animated: false) self.reloadFilters() self.peerSelectionNode.mainContainerNode?.currentItemFilterUpdated = { [weak self] filter, fraction, transition, force in @@ -194,12 +193,24 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) } - self.tabContainerNode?.tabSelected = { [weak self] id, _ in + self.tabContainerNode?.tabSelected = { [weak self] id, isDisabled in guard let strongSelf = self else { return } - - strongSelf.selectTab(id: id) + if isDisabled { + let context = strongSelf.context + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumLimitController(context: context, subject: .folders, count: strongSelf.tabContainerNode?.filtersCount ?? 0, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folders) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + strongSelf.push(controller) + } else { + strongSelf.selectTab(id: id) + } } } } diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index 65eb223b09..d357fced28 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -285,6 +285,20 @@ final class PeerSelectionControllerNode: ASDisplayNode { } if let mainContainerNode = self.mainContainerNode { + mainContainerNode.displayFilterLimit = { [weak self] in + guard let strongSelf = self else { + return + } + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumLimitController(context: context, subject: .folders, count: strongSelf.controller?.tabContainerNode?.filtersCount ?? 0, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folders) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + strongSelf.controller?.push(controller) + } self.addSubnode(mainContainerNode) } if let chatListNode = self.chatListNode { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 02e20a8706..7500441be4 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1657,6 +1657,23 @@ public final class SharedAccountContextImpl: SharedAccountContext { return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) } + public func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, action: @escaping () -> Void) -> ViewController { + let mappedSubject: PremiumLimitScreen.Subject + switch subject { + case .folders: + mappedSubject = .folders + case .chatsPerFolder: + mappedSubject = .chatsPerFolder + case .pins: + mappedSubject = .pins + case .files: + mappedSubject = .files + case .accounts: + mappedSubject = .accounts + } + return PremiumLimitScreen(context: context, subject: mappedSubject, count: count, action: action) + } + public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController { return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker) }