diff --git a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift index 0d77a965cac..f384fcf80e6 100644 --- a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift +++ b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift @@ -306,6 +306,10 @@ final class ChatListContainerItemNode: ASDisplayNode { var listInsets = insets var additionalTopInset: CGFloat = 0.0 + let isCompactAvatarRail = size.width <= 160.0 + if isCompactAvatarRail { + listInsets.top += 74.0 + } if let chatFolderUpdates = self.chatFolderUpdates { let topPanel: TopPanelItem diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 61cbefe701d..958e5bfbf3e 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -249,7 +249,8 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele return } - if !self.isInlineMode, itemNode.listNode.isTracking && !self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset == 0.0 { + let isCompactAvatarRail = self.validLayout?.layout.deviceMetrics.type == .tablet && (self.validLayout?.layout.size.width ?? .greatestFiniteMagnitude) <= 160.0 + if !isCompactAvatarRail && !self.isInlineMode, itemNode.listNode.isTracking && !self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset == 0.0 { if case let .known(value) = offset { if value < -1.0 { if let controller = self.controller, let storySubscriptions = controller.orderedStorySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions, isHidden: controller.location == .chatList(groupId: .archive)) { @@ -293,7 +294,10 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele } let tempTopInset: CGFloat - if validLayout.inlineNavigationLocation != nil { + let isCompactAvatarRail = validLayout.layout.deviceMetrics.type == .tablet && validLayout.layout.size.width <= 160.0 + if isCompactAvatarRail { + tempTopInset = 0.0 + } else if validLayout.inlineNavigationLocation != nil { tempTopInset = 0.0 } else if self.currentItemNode.startedScrollingAtUpperBound && !self.isInlineMode { if let controller = self.controller, let storySubscriptions = controller.orderedStorySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions, isHidden: controller.location == .chatList(groupId: .archive)) { @@ -534,7 +538,12 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele self.applyItemNodeAsCurrent(id: .all, itemNode: itemNode) let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in - guard let self, self.availableFilters.count > 1 || (self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false)) else { + guard let self else { + return [] + } + let isCompactAvatarRail = self.validLayout?.layout.deviceMetrics.type == .tablet && (self.validLayout?.layout.size.width ?? .greatestFiniteMagnitude) <= 160.0 + let isStoryPostingAvailable = !isCompactAvatarRail && (self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false)) + guard self.availableFilters.count > 1 || isStoryPostingAvailable else { return [] } guard case .chatList(.root) = self.location else { @@ -639,7 +648,8 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele hasLiveStream = true } - if case .compact = layout.metrics.widthClass, self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false) { + let isCompactAvatarRail = layout.deviceMetrics.type == .tablet && layout.size.width <= 160.0 + if !isCompactAvatarRail, case .compact = layout.metrics.widthClass, self.controller?.isStoryPostingAvailable == true && !(self.context.sharedContext.callManager?.hasActiveCall ?? false) { if hasLiveStream { if translation.x >= 30.0 { self.panRecognizer?.cancel() @@ -1009,7 +1019,6 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele public func update(layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, storiesInset: CGFloat, transition: ContainedViewLayoutTransition) { self.validLayout = (layout, navigationBarHeight, visualNavigationHeight, originalNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) - self._validLayoutReady.set(.single(true)) transition.updateAlpha(node: self, alpha: isReorderingFilters ? 0.5 : 1.0) @@ -1024,7 +1033,6 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele self.panRecognizer?.isEnabled = !isEditing transition.updateFrame(layer: self.leftSeparatorLayer, frame: CGRect(origin: CGPoint(x: -UIScreenPixel, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height))) - if let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) { var validNodeIds: [ChatListFilterTabEntryId] = [] for i in max(0, selectedIndex - 1) ... min(self.availableFilters.count - 1, selectedIndex + 1) { @@ -1229,7 +1237,6 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } self.addSubnode(self.debugListView) - filterBecameEmpty = { [weak self] _ in guard let strongSelf = self else { return @@ -1504,7 +1511,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { var navigationHeaderPanels: AnyComponent? if self.controller?.tabContainerData != nil || !panels.isEmpty { var tabs: AnyComponent? - if let tabContainerData = self.controller?.tabContainerData, tabContainerData.0.count > 1 { + let isDesktopLikeCompactSidebar = layout.deviceMetrics.type == .tablet && layout.size.width <= 160.0 + if let tabContainerData = self.controller?.tabContainerData, tabContainerData.0.count > 1, !isDesktopLikeCompactSidebar { let folderFilterIndex: (ChatListFilterTabEntryId, [ChatListFilterTabEntry]) -> Int? = { id, entries in var index = 0 for entry in entries { @@ -1647,8 +1655,12 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { )) } + let isDesktopLikeCompactSidebar = layout.deviceMetrics.type == .tablet && layout.size.width <= 160.0 + var effectiveStorySubscriptions: EngineStorySubscriptions? - if let controller = self.controller, case .forum = controller.location { + if isDesktopLikeCompactSidebar { + effectiveStorySubscriptions = EngineStorySubscriptions(accountItem: nil, items: [], hasMoreToken: nil) + } else if let controller = self.controller, case .forum = controller.location { effectiveStorySubscriptions = nil } else { if let controller = self.controller, let storySubscriptions = controller.orderedStorySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions, isHidden: controller.location == .chatList(groupId: .archive)) { @@ -1658,6 +1670,14 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } } + if isDesktopLikeCompactSidebar { + if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { + navigationBarComponentView.isHidden = true + navigationBarComponentView.isUserInteractionEnabled = false + } + return (0.0, 0.0) + } + let navigationBarSize = self.navigationBarView.update( transition: transition, component: AnyComponent(ChatListNavigationBar( @@ -1715,6 +1735,8 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { containerSize: layout.size ) if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { + navigationBarComponentView.isHidden = false + navigationBarComponentView.isUserInteractionEnabled = true if deferScrollApplication { navigationBarComponentView.deferScrollApplication = true } @@ -1950,7 +1972,6 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { mainInsets.top = visualNavigationHeight } self.mainContainerNode.update(layout: layout, navigationBarHeight: mainNavigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: cleanMainNavigationBarHeight, insets: mainInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: self.inlineStackContainerNode?.location, inlineNavigationTransitionFraction: self.inlineStackContainerTransitionFraction, storiesInset: storiesInset, transition: transition) - if let inlineStackContainerNode = self.inlineStackContainerNode { var inlineStackContainerNodeTransition = transition var animateIn = false @@ -2006,6 +2027,9 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, self.searchDisplayController == nil else { return nil } + if containerLayout.deviceMetrics.type == .tablet && containerLayout.size.width <= 160.0 { + return nil + } let effectiveLocation = self.inlineStackContainerNode?.location ?? self.location diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 67257066fe6..1d0b048dbb9 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2475,6 +2475,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let currentAvatarBadgeCleanBackgroundImage: UIImage? = PresentationResourcesChatList.badgeBackgroundBorder(item.presentationData.theme, diameter: avatarBadgeDiameter + 4.0) let leftInset: CGFloat = params.leftInset + avatarLeftInset + let avatarRailMode = useChatListLayout && params.width <= 120.0 && !item.editing && !item.interaction.isInlineMode enum ContentData { case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) @@ -3518,7 +3519,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let layoutOffset: CGFloat = 0.0 - let rawContentWidth = params.width - leftInset - params.rightInset - 18.0 - editingOffset + let rawContentWidth = max(1.0, params.width - leftInset - params.rightInset - 18.0 - editingOffset) let (dateLayout, dateApply) = dateLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) @@ -3647,12 +3648,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { textCutout = TextNodeCutout(topLeft: textLeftCutout.isZero ? nil : CGSize(width: textLeftCutout, height: 10.0), topRight: nil, bottomRight: textBottomRightCutout.isZero ? nil : CGSize(width: textBottomRightCutout, height: 10.0)) } - var textMaxWidth = rawContentWidth - badgeSize + var textMaxWidth = max(1.0, rawContentWidth - badgeSize) var textArrowImage: UIImage? if isFirstForumThreadSelectable { textArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme) - textMaxWidth -= 18.0 + textMaxWidth = max(1.0, textMaxWidth - 18.0) } let textLineSpacing: CGFloat = min(0.2, item.presentationData.fontSize.itemListBaseFontSize * 0.2 / 17.0) @@ -3685,7 +3686,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { titleAttributedString = NSAttributedString(string: " ", font: titleFont, textColor: theme.titleColor) } - var titleRectWidth = rawContentWidth - dateLayout.size.width - 10.0 - statusWidth - titleIconsWidth + var titleRectWidth = max(1.0, rawContentWidth - dateLayout.size.width - 10.0 - statusWidth - titleIconsWidth) var titleCutout: TextNodeCutout? if !titleLeftCutout.isZero { titleCutout = TextNodeCutout(topLeft: CGSize(width: titleLeftCutout, height: 10.0), topRight: nil, bottomRight: nil) @@ -3695,7 +3696,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { if let titleBadgeText { let titleBadgeLayoutAndApplyValue = titleBadgeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleBadgeText, font: Font.semibold(11.0), textColor: theme.titleColor.withMultipliedAlpha(0.4)), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) titleBadgeLayoutAndApply = titleBadgeLayoutAndApplyValue - titleRectWidth = max(10.0, titleRectWidth - titleBadgeLayoutAndApplyValue.0.size.width - 8.0) + titleRectWidth = max(1.0, titleRectWidth - titleBadgeLayoutAndApplyValue.0.size.width - 8.0) } let (titleLayout, titleApply) = titleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: maxTitleLines, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: titleCutout, insets: UIEdgeInsets())) @@ -3709,11 +3710,11 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { chatPeerId = peerId } if let inputActivities = inputActivities, !inputActivities.isEmpty, let chatPeerId { - let (size, apply) = inputActivitiesLayout(CGSize(width: rawContentWidth - badgeSize, height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, chatPeerId, inputActivities) + let (size, apply) = inputActivitiesLayout(CGSize(width: max(1.0, rawContentWidth - badgeSize), height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, chatPeerId, inputActivities) inputActivitiesSize = size inputActivitiesApply = apply } else { - let (size, apply) = inputActivitiesLayout(CGSize(width: rawContentWidth - badgeSize, height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, nil, []) + let (size, apply) = inputActivitiesLayout(CGSize(width: max(1.0, rawContentWidth - badgeSize), height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, nil, []) inputActivitiesSize = size inputActivitiesApply = apply } @@ -3941,6 +3942,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { if useChatListLayout { mainContentFrame = CGRect(origin: CGPoint(x: leftInset - 2.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height)) mainContentBoundsOffset = mainContentFrame.origin.x + if avatarRailMode { + mainContentAlpha = 0.0 + } if let inlineNavigationLocation = item.interaction.inlineNavigationLocation { mainContentAlpha = 1.0 - inlineNavigationLocation.progress @@ -4025,7 +4029,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + revealOffset, dy: 0.0) - let avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + 6.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) + let avatarFrame: CGRect + if avatarRailMode { + avatarFrame = CGRect(origin: CGPoint(x: floor((params.width - avatarDiameter) / 2.0) + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) + } else { + avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + 6.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) + } var avatarScaleOffset: CGFloat = 0.0 var avatarScale: CGFloat = 1.0 if let inlineNavigationLocation = item.interaction.inlineNavigationLocation { @@ -4077,7 +4086,15 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } + let avatarBadgeProgress: CGFloat? if let inlineNavigationLocation = item.interaction.inlineNavigationLocation, badgeContent != .none { + avatarBadgeProgress = inlineNavigationLocation.progress + } else if avatarRailMode, badgeContent != .none { + avatarBadgeProgress = 1.0 + } else { + avatarBadgeProgress = nil + } + if let avatarBadgeProgress = avatarBadgeProgress { var animateIn = false let avatarBadgeBackground: ASImageNode @@ -4117,8 +4134,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: avatarBadgeNode, scale: 0.00001) ContainedViewLayoutTransition.immediate.updateTransformScale(layer: avatarBadgeBackground.layer, scale: 0.00001) } - transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: max(0.00001, inlineNavigationLocation.progress)) - transition.updateTransformScale(layer: avatarBadgeBackground.layer, scale: max(0.00001, inlineNavigationLocation.progress)) + transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: max(0.00001, avatarBadgeProgress)) + transition.updateTransformScale(layer: avatarBadgeBackground.layer, scale: max(0.00001, avatarBadgeProgress)) } else if let avatarBadgeNode = strongSelf.avatarBadgeNode { strongSelf.avatarBadgeNode = nil transition.updateSublayerTransformScale(node: avatarBadgeNode, scale: 0.00001, completion: { [weak avatarBadgeNode] _ in diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 2dc44bb69bf..ecf1e5d2e41 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1395,7 +1395,7 @@ public final class ChatListNode: ListViewImpl { self.keepMinimalScrollHeightWithTopInset = self.scrollHeightTopInset let nodeInteraction = ChatListNodeInteraction(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, activateSearch: { [weak self] in - if let strongSelf = self, let activateSearch = strongSelf.activateSearch { + if let strongSelf = self, strongSelf.bounds.width > 120.0, let activateSearch = strongSelf.activateSearch { activateSearch() } }, peerSelected: { [weak self] peer, _, threadId, promoInfo, _ in diff --git a/submodules/Display/Source/Navigation/NavigationSplitContainer.swift b/submodules/Display/Source/Navigation/NavigationSplitContainer.swift index b3900d15c51..1df11d672a7 100644 --- a/submodules/Display/Source/Navigation/NavigationSplitContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationSplitContainer.swift @@ -9,11 +9,21 @@ enum NavigationSplitContainerScrollToTop { } final class NavigationSplitContainer: ASDisplayNode { + private enum Metrics { + static let regularMasterMinWidth: CGFloat = 320.0 + static let compactMasterWidth: CGFloat = 96.0 + static let avatarRailMaximumContainerWidth: CGFloat = 1133.0 + } + private var theme: NavigationControllerTheme private let masterContainer: NavigationContainer private let detailContainer: NavigationContainer private let separator: ASDisplayNode + private let resizeHandle: ASDisplayNode + private var forceRegularMasterWidth: Bool = UserDefaults.standard.bool(forKey: "NavigationSplitContainer.forceRegularMasterWidth") + private var currentLayout: ContainerViewLayout? + private var currentDetailsPlaceholderNode: NavigationDetailsPlaceholderNode? private(set) var masterControllers: [ViewController] = [] private(set) var detailControllers: [ViewController] = [] @@ -48,12 +58,28 @@ final class NavigationSplitContainer: ASDisplayNode { self.separator = ASDisplayNode() self.separator.backgroundColor = theme.navigationBar.separatorColor + + self.resizeHandle = ASDisplayNode() + self.resizeHandle.backgroundColor = .clear + self.resizeHandle.isUserInteractionEnabled = true super.init() self.addSubnode(self.masterContainer) self.addSubnode(self.detailContainer) self.addSubnode(self.separator) + self.addSubnode(self.resizeHandle) + } + + override func didLoad() { + super.didLoad() + + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handleResizePan(_:))) + self.resizeHandle.view.addGestureRecognizer(panGesture) + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleResizeTap(_:))) + tapGesture.numberOfTapsRequired = 1 + self.resizeHandle.view.addGestureRecognizer(tapGesture) } func hasNonReadyControllers() -> Bool { @@ -71,7 +97,7 @@ final class NavigationSplitContainer: ASDisplayNode { } func scrollToTopProxyFrames(layout: ContainerViewLayout) -> (master: CGRect, detail: CGRect) { - let masterWidth: CGFloat = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0)) + let masterWidth = self.masterWidth(for: layout) let detailWidth = layout.size.width - masterWidth let scrollToTopHeight = max(layout.statusBarHeight ?? layout.safeInsets.top, 1.0) @@ -81,13 +107,60 @@ final class NavigationSplitContainer: ASDisplayNode { ) } + private func masterWidth(for layout: ContainerViewLayout) -> CGFloat { + if case .tablet = layout.deviceMetrics.type, layout.size.width <= Metrics.avatarRailMaximumContainerWidth, !self.forceRegularMasterWidth { + return Metrics.compactMasterWidth + } + return min(max(Metrics.regularMasterMinWidth, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0)) + } + + @objc private func handleResizeTap(_ recognizer: UITapGestureRecognizer) { + if recognizer.state == .ended { + self.setForceRegularMasterWidth(!self.forceRegularMasterWidth) + } + } + + @objc private func handleResizePan(_ recognizer: UIPanGestureRecognizer) { + guard recognizer.state == .ended || recognizer.state == .cancelled else { + return + } + let translation = recognizer.translation(in: self.view) + if translation.x > 24.0 { + self.setForceRegularMasterWidth(true) + } else if translation.x < -24.0 { + self.setForceRegularMasterWidth(false) + } + } + + private func setForceRegularMasterWidth(_ value: Bool) { + if self.forceRegularMasterWidth == value { + return + } + self.forceRegularMasterWidth = value + UserDefaults.standard.set(value, forKey: "NavigationSplitContainer.forceRegularMasterWidth") + + if let layout = self.currentLayout { + self.update( + layout: layout, + masterControllers: self.masterControllers, + detailControllers: self.detailControllers, + detailsPlaceholderNode: self.currentDetailsPlaceholderNode, + transition: .animated(duration: 0.28, curve: .easeInOut) + ) + } + } + func update(layout: ContainerViewLayout, masterControllers: [ViewController], detailControllers: [ViewController], detailsPlaceholderNode: NavigationDetailsPlaceholderNode?, transition: ContainedViewLayoutTransition) { - let masterWidth: CGFloat = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0)) + self.currentLayout = layout + self.currentDetailsPlaceholderNode = detailsPlaceholderNode + + let masterWidth = self.masterWidth(for: layout) let detailWidth = layout.size.width - masterWidth transition.updateFrame(node: self.masterContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: masterWidth, height: layout.size.height))) transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height))) transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height))) + transition.updateFrame(node: self.resizeHandle, frame: CGRect(origin: CGPoint(x: masterWidth - 12.0, y: 0.0), size: CGSize(width: 24.0, height: layout.size.height))) if let detailsPlaceholderNode { let needsTiling = layout.size.width > layout.size.height diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index a44d64ee049..6c0facfe8fb 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -227,6 +227,7 @@ final class TabBarControllerNode: ASDisplayNode { if self.tabBarView.view == nil { tabBarTransition = .immediate } + let isCompactMasterController = params.layout.deviceMetrics.type == .tablet && params.layout.size.width <= 1133.0 && !UserDefaults.standard.bool(forKey: "NavigationSplitContainer.forceRegularMasterWidth") let tabBarSize = self.tabBarView.update( transition: tabBarTransition, component: AnyComponent(TabBarComponent( @@ -265,7 +266,7 @@ final class TabBarControllerNode: ASDisplayNode { } ) }, - search: self.currentController?.tabBarSearchState.flatMap { tabBarSearchState in + search: isCompactMasterController ? nil : self.currentController?.tabBarSearchState.flatMap { tabBarSearchState in return TabBarComponent.Search( isActive: tabBarSearchState.isActive, activate: { [weak self] in @@ -295,7 +296,8 @@ final class TabBarControllerNode: ASDisplayNode { self.view.addSubview(tabBarComponentView) } transition.updateFrame(view: tabBarComponentView, frame: tabBarFrame) - transition.updateAlpha(layer: tabBarComponentView.layer, alpha: params.toolbar == nil ? 1.0 : 0.0) + transition.updateAlpha(layer: tabBarComponentView.layer, alpha: (params.toolbar == nil && !isCompactMasterController) ? 1.0 : 0.0) + tabBarComponentView.isUserInteractionEnabled = !isCompactMasterController } transition.updateFrame(node: self.disabledOverlayNode, frame: tabBarFrame) diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 0b19bc56fcd..6394b08f2b7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -1145,7 +1145,8 @@ public final class StoryPeerListComponent: Component { unseenCount = itemSet.unseenCount var composeContentOffset: CGFloat? - if peer.id == component.context.account.peerId && collapsedState.sideAlphaFraction == 1.0 && self.scrollView.contentOffset.x < 0.0 { + let isCompactAvatarRail = itemLayout.containerSize.width <= 120.0 + if !isCompactAvatarRail && peer.id == component.context.account.peerId && collapsedState.sideAlphaFraction == 1.0 && self.scrollView.contentOffset.x < 0.0 { composeContentOffset = self.scrollView.contentOffset.x * -1.0 } @@ -1723,7 +1724,8 @@ public final class StoryPeerListComponent: Component { self.sortedItems.removeAll(keepingCapacity: true) if let storySubscriptions = component.storySubscriptions { - if !component.useHiddenList, let accountItem = storySubscriptions.accountItem { + let isCompactAvatarRail = availableSize.width <= 120.0 + if !component.useHiddenList, !isCompactAvatarRail, let accountItem = storySubscriptions.accountItem { self.sortedItems.append(accountItem) }