mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Merge afe5370e42 into 6e370e06d1
This commit is contained in:
commit
3b62f814e8
7 changed files with 149 additions and 27 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Empty>?
|
||||
if self.controller?.tabContainerData != nil || !panels.isEmpty {
|
||||
var tabs: AnyComponent<Empty>?
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue