This commit is contained in:
Isaac 2025-12-20 03:02:08 +08:00
parent 0ccec4b6b6
commit 3486393f99
84 changed files with 2842 additions and 2033 deletions

View file

@ -43,7 +43,7 @@ private final class DeleteAllButtonNode: ASDisplayNode {
self.buttonNode.addSubnode(self.titleNode)
self.contentNode.contentNode.addSubnode(self.buttonNode)
self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.CallList_DeleteAll, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationBar.accentTextColor)
self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.CallList_DeleteAll, font: Font.medium(17.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor)
//self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
@ -54,9 +54,10 @@ private final class DeleteAllButtonNode: ASDisplayNode {
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
let titleSize = self.titleNode.updateLayout(constrainedSize)
self.titleNode.frame = CGRect(origin: CGPoint(), size: titleSize)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: titleSize)
return titleSize
let size = CGSize(width: 10.0 * 2.0 + titleSize.width, height: 44.0)
self.titleNode.frame = CGRect(origin: CGPoint(x: 10.0, y: floorToScreenPixels((size.height - titleSize.height) * 0.5)), size: titleSize)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
return size
}
override public func layout() {

View file

@ -119,7 +119,7 @@ swift_library(
"//submodules/TelegramUI/Components/EdgeEffect",
"//submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode",
"//submodules/TelegramUI/Components/HeaderPanelContainerComponent",
"//submodules/TelegramUI/Components/ChatList/ChatListTabsComponent",
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
"//submodules/TelegramUI/Components/GlobalControlPanelsContext",
"//submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent",
"//submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent",

View file

@ -450,7 +450,7 @@ final class ChatListContainerItemNode: ASDisplayNode {
self.layoutAdditionalPanels(transition: transition)
let edgeEffectHeight: CGFloat = insets.bottom
let edgeEffectHeight: CGFloat = insets.bottom + 8.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - edgeEffectHeight), size: CGSize(width: size.width, height: edgeEffectHeight))
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectFrame.height, 40.0), transition: ComponentTransition(transition))

View file

@ -58,7 +58,7 @@ import AdsReportScreen
import SearchBarNode
import ChatListFilterTabContainerNode
import HeaderPanelContainerComponent
import ChatListTabsComponent
import HorizontalTabsComponent
import GlobalControlPanelsContext
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
@ -749,8 +749,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing, canReorderAllChats: strongSelf.isPremium, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition)
if let navigationBarView = strongSelf.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View, let headerPanelsView = navigationBarView.headerPanels as? HeaderPanelContainerComponent.View, let tabsView = headerPanelsView.tabs as? ChatListTabsComponent.View {
tabsView.updateTabSwitchFraction(fraction: fraction, transition: ComponentTransition(transition))
if let navigationBarView = strongSelf.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View, let headerPanelsView = navigationBarView.headerPanels as? HeaderPanelContainerComponent.View, let tabsView = headerPanelsView.tabs as? HorizontalTabsComponent.View {
tabsView.updateTabSwitchFraction(fraction: fraction, isDragging: strongSelf.chatListDisplayNode.mainContainerNode.isSwitchingCurrentItemFilterByDragging, transition: ComponentTransition(transition))
}
}
self.reloadFilters()
@ -965,6 +965,356 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.requestLayout(transition: .immediate)
}
func tabContextGesture(id: Int32?, sourceNode: ContextExtractedContentContainingNode?, sourceView: ContextExtractedContentContainingView?, gesture: ContextGesture?, keepInPlace: Bool, isDisabled: Bool) {
let context = self.context
let filterPeersAreMuted: Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> = self.context.engine.peers.currentChatListFilters()
|> take(1)
|> mapToSignal { filters -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in
guard let filter = filters.first(where: { $0.id == id }) else {
return .single(nil)
}
guard case let .filter(_, _, _, data) = filter else {
return .single(nil)
}
let filterPredicate: ChatListFilterPredicate = chatListFilterPredicate(filter: data, accountPeerId: context.account.peerId)
return context.engine.peers.getChatListPeers(filterPredicate: filterPredicate)
|> mapToSignal { peers -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in
let peerIds = peers.map(\.id)
return context.engine.data.get(
EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.NotificationSettings.init(id:))),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
)
|> map { list, globalSettings -> (areMuted: Bool, peerIds: [EnginePeer.Id])? in
for peer in peers {
switch list[peer.id]?.muteState {
case .unmuted:
return (false, peerIds)
case .default:
let globalValue: EngineGlobalNotificationSettings.CategorySettings
switch peer {
case .user, .secretChat:
globalValue = globalSettings.privateChats
case .legacyGroup:
globalValue = globalSettings.groupChats
case let .channel(channel):
if case .broadcast = channel.info {
globalValue = globalSettings.channels
} else {
globalValue = globalSettings.groupChats
}
}
if globalValue.enabled {
return (false, peerIds)
}
default:
break
}
}
return (true, peerIds)
}
}
}
let _ = combineLatest(
queue: Queue.mainQueue(),
self.context.engine.peers.currentChatListFilters(),
self.context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
),
filterPeersAreMuted
).startStandalone(next: { [weak self] filters, premiumLimits, filterPeersAreMuted in
guard let self else {
return
}
var items: [ContextMenuItem] = []
if let id = id {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_EditFolder, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: { [weak self] in
guard let self else {
return
}
if isDisabled {
let context = self.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .folders, count: self.tabContainerNode.filtersCount, action: {
let controller = PremiumIntroScreen(context: context, source: .folders)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.push(controller)
} else {
let _ = (self.context.engine.peers.currentChatListFilters()
|> deliverOnMainQueue).startStandalone(next: { [weak self] presetList in
guard let self else {
return
}
var found = false
for filter in presetList {
if filter.id == id {
self.push(chatListFilterPresetController(context: self.context, currentPreset: filter, updated: { _ in }))
f(.dismissWithoutContent)
found = true
break
}
}
if !found {
f(.default)
}
})
}
})
})))
if let _ = filters.first(where: { $0.id == id }) {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_AddChatsToFolder, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
guard let self else {
return
}
if isDisabled {
let context = self.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .folders, count: self.tabContainerNode.filtersCount, action: {
let controller = PremiumIntroScreen(context: context, source: .folders)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.push(controller)
} else {
let _ = combineLatest(
queue: Queue.mainQueue(),
self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
),
self.context.engine.peers.currentChatListFilters()
).startStandalone(next: { [weak self] result, presetList in
guard let self else {
return
}
var found = false
for filter in presetList {
if filter.id == id, case let .filter(_, _, _, data) = filter {
let (accountPeer, limits, premiumLimits) = result
let isPremium = accountPeer?.isPremium ?? false
let limit = limits.maxFolderChatsCount
let premiumLimit = premiumLimits.maxFolderChatsCount
if data.includePeers.peers.count >= premiumLimit {
let controller = PremiumLimitScreen(context: self.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {
return true
})
self.push(controller)
f(.dismissWithoutContent)
return
} else if data.includePeers.peers.count >= limit && !isPremium {
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: self.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {
let controller = PremiumIntroScreen(context: self.context, source: .chatsPerFolder)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.push(controller)
f(.dismissWithoutContent)
return
}
let _ = (self.context.engine.peers.currentChatListFilters()
|> deliverOnMainQueue).startStandalone(next: { [weak self] filters in
guard let self else {
return
}
self.push(chatListFilterAddChatsController(context: self.context, filter: filter, allFilters: filters, limit: limits.maxFolderChatsCount, premiumLimit: premiumLimits.maxFolderChatsCount, isPremium: isPremium, presentUndo: { [weak self] content in
guard let self else {
return
}
self.present(UndoOverlayController(presentationData: self.presentationData, content: content, elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}))
f(.dismissWithoutContent)
})
found = true
break
}
}
if !found {
f(.default)
}
})
}
})
})))
if let filterEntries = self.tabContainerData?.0 {
for filter in filterEntries {
if case let .filter(filterId, _, unread) = filter, filterId == id {
if unread.value > 0 {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_ReadAll, textColor: .primary, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadAll"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
guard let self else {
return
}
self.readAllInFilter(id: id)
})
})))
}
for filter in filters {
if filter.id == filterId, case let .filter(_, title, _, data) = filter {
if let filterPeersAreMuted, filterPeersAreMuted.peerIds.count <= 200 {
items.append(.action(ContextMenuActionItem(text: filterPeersAreMuted.areMuted ? self.presentationData.strings.ChatList_ContextUnmuteAll : self.presentationData.strings.ChatList_ContextMuteAll, textColor: .primary, badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: filterPeersAreMuted.areMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
})
guard let self else {
return
}
let _ = (self.context.engine.peers.updateMultiplePeerMuteSettings(peerIds: filterPeersAreMuted.peerIds, muted: !filterPeersAreMuted.areMuted)
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
guard let self else {
return
}
let iconColor: UIColor = .white
let overlayController: UndoOverlayController
if !filterPeersAreMuted.areMuted {
let text = NSMutableAttributedString(string: self.presentationData.strings.ChatList_ToastFolderMutedV2)
let folderNameRange = (text.string as NSString).range(of: "{folder}")
if folderNameRange.location != NSNotFound {
text.replaceCharacters(in: folderNameRange, with: "")
text.insert(title.attributedString(attributes: [
ChatTextInputAttributes.bold: true
]), at: folderNameRange.location)
}
overlayController = UndoOverlayController(presentationData: self.presentationData, content: .universalWithEntities(context: self.context, animation: "anim_profilemute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false })
} else {
let text = NSMutableAttributedString(string: self.presentationData.strings.ChatList_ToastFolderUnmutedV2)
let folderNameRange = (text.string as NSString).range(of: "{folder}")
if folderNameRange.location != NSNotFound {
text.replaceCharacters(in: folderNameRange, with: "")
text.insert(title.attributedString(attributes: [
ChatTextInputAttributes.bold: true
]), at: folderNameRange.location)
}
overlayController = UndoOverlayController(presentationData: self.presentationData, content: .universalWithEntities(context: self.context, animation: "anim_profileunmute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false })
}
self.present(overlayController, in: .current)
})
})))
}
if !data.includePeers.peers.isEmpty && data.categories.isEmpty && !data.excludeRead && !data.excludeMuted && !data.excludeArchived && data.excludePeers.isEmpty {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_ContextMenuShare, textColor: .primary, badge: data.hasSharedLinks ? nil : ContextMenuActionBadge(value: self.presentationData.strings.ChatList_ContextMenuBadgeNew, color: .accent, style: .label), icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
guard let self else {
return
}
self.shareFolder(filterId: filterId, data: data, title: title)
})
})))
}
break
}
}
break
}
}
}
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_RemoveFolder, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
guard let self else {
return
}
self.askForFilterRemoval(id: id)
})
})))
}
} else {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_EditFolders, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
guard let self else {
return
}
self.openFilterSettings()
})
})))
}
if filters.count > 1 {
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_ReorderTabs, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, f in
c?.dismiss(completion: {
guard let self else {
return
}
self.chatListDisplayNode.isReorderingFilters = true
self.isReorderingTabsValue.set(true)
(self.parent as? TabBarController)?.updateIsTabBarEnabled(false, transition: .animated(duration: 0.2, curve: .easeInOut))
if let layout = self.validLayout {
self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
}
})
})))
}
if let sourceNode {
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: sourceView, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
} else if let sourceView {
let controller = ContextController(presentationData: self.presentationData, source: .reference(ChatListHeaderBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
}
})
}
override public func loadDisplayNode() {
self.displayNode = ChatListControllerNode(context: self.context, location: self.location, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, controller: self)
@ -1671,7 +2021,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} else {
let contextContentSource: ContextContentSource
if peer.id.namespace == Namespaces.Peer.SecretChat, let node = node.subnodes?.first as? ContextExtractedContentContainingNode {
contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, keepInPlace: false))
contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, sourceView: nil, keepInPlace: false))
} else {
var subject: ChatControllerSubject?
if case let .search(messageId) = source, let id = messageId {
@ -1732,359 +2082,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
}
let tabContextGesture: (Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool, Bool) -> Void = { [weak self] id, sourceNode, gesture, keepInPlace, isDisabled in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let filterPeersAreMuted: Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> = strongSelf.context.engine.peers.currentChatListFilters()
|> take(1)
|> mapToSignal { filters -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in
guard let filter = filters.first(where: { $0.id == id }) else {
return .single(nil)
}
guard case let .filter(_, _, _, data) = filter else {
return .single(nil)
}
let filterPredicate: ChatListFilterPredicate = chatListFilterPredicate(filter: data, accountPeerId: context.account.peerId)
return context.engine.peers.getChatListPeers(filterPredicate: filterPredicate)
|> mapToSignal { peers -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in
let peerIds = peers.map(\.id)
return context.engine.data.get(
EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.NotificationSettings.init(id:))),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
)
|> map { list, globalSettings -> (areMuted: Bool, peerIds: [EnginePeer.Id])? in
for peer in peers {
switch list[peer.id]?.muteState {
case .unmuted:
return (false, peerIds)
case .default:
let globalValue: EngineGlobalNotificationSettings.CategorySettings
switch peer {
case .user, .secretChat:
globalValue = globalSettings.privateChats
case .legacyGroup:
globalValue = globalSettings.groupChats
case let .channel(channel):
if case .broadcast = channel.info {
globalValue = globalSettings.channels
} else {
globalValue = globalSettings.groupChats
}
}
if globalValue.enabled {
return (false, peerIds)
}
default:
break
}
}
return (true, peerIds)
}
}
}
let _ = combineLatest(
queue: Queue.mainQueue(),
strongSelf.context.engine.peers.currentChatListFilters(),
strongSelf.context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
),
filterPeersAreMuted
).startStandalone(next: { [weak self] filters, premiumLimits, filterPeersAreMuted in
guard let strongSelf = self else {
return
}
var items: [ContextMenuItem] = []
if let id = id {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_EditFolder, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
if isDisabled {
let context = strongSelf.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: {
let controller = PremiumIntroScreen(context: context, source: .folders)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
strongSelf.push(controller)
} else {
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|> deliverOnMainQueue).startStandalone(next: { presetList in
guard let strongSelf = self else {
return
}
var found = false
for filter in presetList {
if filter.id == id {
strongSelf.push(chatListFilterPresetController(context: strongSelf.context, currentPreset: filter, updated: { _ in }))
f(.dismissWithoutContent)
found = true
break
}
}
if !found {
f(.default)
}
})
}
})
})))
if let _ = filters.first(where: { $0.id == id }) {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_AddChatsToFolder, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
if isDisabled {
let context = strongSelf.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: {
let controller = PremiumIntroScreen(context: context, source: .folders)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
strongSelf.push(controller)
} else {
let _ = combineLatest(
queue: Queue.mainQueue(),
strongSelf.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
),
strongSelf.context.engine.peers.currentChatListFilters()
).startStandalone(next: { result, presetList in
guard let strongSelf = self else {
return
}
var found = false
for filter in presetList {
if filter.id == id, case let .filter(_, _, _, data) = filter {
let (accountPeer, limits, premiumLimits) = result
let isPremium = accountPeer?.isPremium ?? false
let limit = limits.maxFolderChatsCount
let premiumLimit = premiumLimits.maxFolderChatsCount
if data.includePeers.peers.count >= premiumLimit {
let controller = PremiumLimitScreen(context: strongSelf.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {
return true
})
strongSelf.push(controller)
f(.dismissWithoutContent)
return
} else if data.includePeers.peers.count >= limit && !isPremium {
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: strongSelf.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .chatsPerFolder)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
strongSelf.push(controller)
f(.dismissWithoutContent)
return
}
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|> deliverOnMainQueue).startStandalone(next: { filters in
guard let strongSelf = self else {
return
}
strongSelf.push(chatListFilterAddChatsController(context: strongSelf.context, filter: filter, allFilters: filters, limit: limits.maxFolderChatsCount, premiumLimit: premiumLimits.maxFolderChatsCount, isPremium: isPremium, presentUndo: { content in
guard let strongSelf = self else {
return
}
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}))
f(.dismissWithoutContent)
})
found = true
break
}
}
if !found {
f(.default)
}
})
}
})
})))
if let filterEntries = strongSelf.tabContainerData?.0 {
for filter in filterEntries {
if case let .filter(filterId, _, unread) = filter, filterId == id {
if unread.value > 0 {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReadAll, textColor: .primary, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadAll"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
strongSelf.readAllInFilter(id: id)
})
})))
}
for filter in filters {
if filter.id == filterId, case let .filter(_, title, _, data) = filter {
if let filterPeersAreMuted, filterPeersAreMuted.peerIds.count <= 200 {
items.append(.action(ContextMenuActionItem(text: filterPeersAreMuted.areMuted ? strongSelf.presentationData.strings.ChatList_ContextUnmuteAll : strongSelf.presentationData.strings.ChatList_ContextMuteAll, textColor: .primary, badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: filterPeersAreMuted.areMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
})
guard let strongSelf = self else {
return
}
let _ = (strongSelf.context.engine.peers.updateMultiplePeerMuteSettings(peerIds: filterPeersAreMuted.peerIds, muted: !filterPeersAreMuted.areMuted)
|> deliverOnMainQueue).startStandalone(completed: {
guard let strongSelf = self else {
return
}
let iconColor: UIColor = .white
let overlayController: UndoOverlayController
if !filterPeersAreMuted.areMuted {
let text = NSMutableAttributedString(string: strongSelf.presentationData.strings.ChatList_ToastFolderMutedV2)
let folderNameRange = (text.string as NSString).range(of: "{folder}")
if folderNameRange.location != NSNotFound {
text.replaceCharacters(in: folderNameRange, with: "")
text.insert(title.attributedString(attributes: [
ChatTextInputAttributes.bold: true
]), at: folderNameRange.location)
}
overlayController = UndoOverlayController(presentationData: strongSelf.presentationData, content: .universalWithEntities(context: strongSelf.context, animation: "anim_profilemute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false })
} else {
let text = NSMutableAttributedString(string: strongSelf.presentationData.strings.ChatList_ToastFolderUnmutedV2)
let folderNameRange = (text.string as NSString).range(of: "{folder}")
if folderNameRange.location != NSNotFound {
text.replaceCharacters(in: folderNameRange, with: "")
text.insert(title.attributedString(attributes: [
ChatTextInputAttributes.bold: true
]), at: folderNameRange.location)
}
overlayController = UndoOverlayController(presentationData: strongSelf.presentationData, content: .universalWithEntities(context: strongSelf.context, animation: "anim_profileunmute", scale: 0.075, colors: [
"Middle.Group 1.Fill 1": iconColor,
"Top.Group 1.Fill 1": iconColor,
"Bottom.Group 1.Fill 1": iconColor,
"EXAMPLE.Group 1.Fill 1": iconColor,
"Line.Group 1.Stroke 1": iconColor
], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false })
}
strongSelf.present(overlayController, in: .current)
})
})))
}
if !data.includePeers.peers.isEmpty && data.categories.isEmpty && !data.excludeRead && !data.excludeMuted && !data.excludeArchived && data.excludePeers.isEmpty {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ContextMenuShare, textColor: .primary, badge: data.hasSharedLinks ? nil : ContextMenuActionBadge(value: strongSelf.presentationData.strings.ChatList_ContextMenuBadgeNew, color: .accent, style: .label), icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
strongSelf.shareFolder(filterId: filterId, data: data, title: title)
})
})))
}
break
}
}
break
}
}
}
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_RemoveFolder, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
strongSelf.askForFilterRemoval(id: id)
})
})))
}
} else {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_EditFolders, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
strongSelf.openFilterSettings()
})
})))
}
if filters.count > 1 {
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReorderTabs, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c?.dismiss(completion: {
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.isReorderingFilters = true
strongSelf.isReorderingTabsValue.set(true)
//TODO:update search enabled
//strongSelf.searchContentNode?.setIsEnabled(false, animated: true)
(strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(false, transition: .animated(duration: 0.2, curve: .easeInOut))
if let layout = strongSelf.validLayout {
strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
}
})
})))
}
let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
})
}
self.tabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
tabContextGesture(id, sourceNode, gesture, false, isDisabled)
self.tabContextGesture(id: id, sourceNode: sourceNode, sourceView: nil, gesture: gesture, keepInPlace: false, isDisabled: isDisabled)
}
if case .chatList(.root) = self.location {
@ -3492,7 +3491,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})))
}
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: nil, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
})
}
@ -4528,7 +4527,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})
}
private func askForFilterRemoval(id: Int32) {
func askForFilterRemoval(id: Int32) {
let apply: () -> Void = { [weak self] in
guard let strongSelf = self else {
return
@ -6552,22 +6551,49 @@ private final class ChatListTabBarContextReferenceContentSource: ContextReferenc
}
}
private final class ChatListHeaderBarContextReferenceContentSource: ContextReferenceContentSource {
let keepInPlace: Bool = true
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
private let controller: ChatListController
private let sourceView: ContextExtractedContentContainingView
init(controller: ChatListController, sourceView: ContextExtractedContentContainingView) {
self.controller = controller
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(
referenceView: self.sourceView.contentView,
contentAreaInScreenSpace: UIScreen.main.bounds,
actionsPosition: .bottom
)
}
}
private final class ChatListHeaderBarContextExtractedContentSource: ContextExtractedContentSource {
let keepInPlace: Bool
let ignoreContentTouches: Bool = true
let blurBackground: Bool = true
private let controller: ChatListController
private let sourceNode: ContextExtractedContentContainingNode
private let sourceNode: ContextExtractedContentContainingNode?
private let sourceView: ContextExtractedContentContainingView?
init(controller: ChatListController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool) {
init(controller: ChatListController, sourceNode: ContextExtractedContentContainingNode?, sourceView: ContextExtractedContentContainingView?, keepInPlace: Bool) {
self.controller = controller
self.sourceNode = sourceNode
self.sourceView = sourceView
self.keepInPlace = keepInPlace
}
func takeView() -> ContextControllerTakeViewInfo? {
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
if let sourceNode = self.sourceNode {
return ContextControllerTakeViewInfo(containingItem: .node(sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
} else {
return ContextControllerTakeViewInfo(containingItem: .view(self.sourceView!), contentAreaInScreenSpace: UIScreen.main.bounds)
}
}
func putBack() -> ContextControllerPutBackViewInfo? {

View file

@ -21,10 +21,9 @@ import ChatFolderLinkPreviewScreen
import ChatListHeaderComponent
import StoryPeerListComponent
import TelegramNotices
import EdgeEffect
import ChatListFilterTabContainerNode
import HeaderPanelContainerComponent
import ChatListTabsComponent
import HorizontalTabsComponent
import PremiumUI
import MediaPlaybackHeaderPanelComponent
import LiveLocationHeaderPanelComponent
@ -139,6 +138,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
}
public var currentItemFilterUpdated: ((ChatListFilterTabEntryId, CGFloat, ContainedViewLayoutTransition, Bool) -> Void)?
public private(set) var isSwitchingCurrentItemFilterByDragging: Bool = false
public var currentItemFilter: ChatListFilterTabEntryId {
return self.currentItemNode.chatListFilter.flatMap { .filter($0.id) } ?? .all
}
@ -582,6 +582,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
itemNode.layer.removeAllAnimations()
}
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .immediate)
self.isSwitchingCurrentItemFilterByDragging = true
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, .immediate, true)
}
}
@ -658,6 +659,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
}
}
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .immediate)
self.isSwitchingCurrentItemFilterByDragging = true
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
}
case .cancelled, .ended:
@ -719,6 +721,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
if let switchToId = applyNodeAsCurrent, let itemNode = self.itemNodes[switchToId] {
self.applyItemNodeAsCurrent(id: switchToId, itemNode: itemNode)
}
self.isSwitchingCurrentItemFilterByDragging = false
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
}
default:
@ -1457,76 +1460,121 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
if self.controller?.tabContainerData != nil || !panels.isEmpty {
var tabs: AnyComponent<Empty>?
if let tabContainerData = self.controller?.tabContainerData, tabContainerData.0.count > 1 {
let selectedTab: ChatListTabsComponent.Tab.Id
let selectedTab: HorizontalTabsComponent.Tab.Id
switch self.effectiveContainerNode.currentItemFilter {
case .all:
selectedTab = .all
selectedTab = AnyHashable(Int32.min)
case let .filter(id):
selectedTab = .filter(id: id)
selectedTab = AnyHashable(id)
}
tabs = AnyComponent(ChatListTabsComponent(
let isEditing = self.isReorderingFilters || (self.mainContainerNode.currentItemNode.currentState.editing && !self.didBeginSelectingChatsWhileEditing)
tabs = AnyComponent(HorizontalTabsComponent(
context: self.context,
theme: self.presentationData.theme,
strings: self.presentationData.strings,
tabs: tabContainerData.0.map { entry -> ChatListTabsComponent.Tab in
tabs: tabContainerData.0.map { entry -> HorizontalTabsComponent.Tab in
let id: HorizontalTabsComponent.Tab.Id
let title: HorizontalTabsComponent.Tab.Title
var badge: HorizontalTabsComponent.Tab.Badge?
var isMainTab = false
switch entry {
case .all:
return .all
case let .filter(id, text, unread):
return .filter(id: id, text: text, unread: ChatListTabsComponent.Tab.UnreadCount(
value: unread.value,
hasUnmuted: unread.hasUnmuted
))
id = Int32.min
title = HorizontalTabsComponent.Tab.Title(text: self.presentationData.strings.ChatList_Tabs_All, entities: [], enableAnimations: false)
isMainTab = true
case let .filter(idValue, text, unread):
id = AnyHashable(idValue)
title = HorizontalTabsComponent.Tab.Title(text: text.text, entities: text.entities, enableAnimations: text.enableAnimations)
if unread.value != 0 {
badge = HorizontalTabsComponent.Tab.Badge(
title: "\(unread.value)",
isAccent: unread.hasUnmuted
)
}
}
return HorizontalTabsComponent.Tab(
id: id,
content: .title(title),
badge: badge,
action: { [weak self] in
guard let self, let tabContainerData = self.controller?.tabContainerData else {
return
}
let isPremium = self.context.isPremium
let mappedId: ChatListFilterTabEntryId = entry.id
var isDisabled = false
if let filtersLimit = tabContainerData.2 {
guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == mappedId }) else {
return
}
isDisabled = !isPremium && folderIndex >= filtersLimit
}
if isDisabled {
let filtersCount = tabContainerData.0.count(where: { item in
if case .all = item {
return false
} else {
return true
}
})
let context = self.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(filtersCount), action: {
let controller = PremiumIntroScreen(context: context, source: .folders)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.controller?.push(controller)
} else {
self.controller?.selectTab(id: mappedId)
}
},
contextAction: { [weak self] sourceView, gesture in
guard let self, let tabContainerData = self.controller?.tabContainerData else {
return
}
let isPremium = self.context.isPremium
let mappedId: Int32?
switch entry {
case .all:
mappedId = nil
case let .filter(idValue, _, _):
mappedId = idValue
}
var isDisabled = false
if let filtersLimit = tabContainerData.2 {
guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == entry.id }) else {
return
}
isDisabled = !isPremium && folderIndex >= filtersLimit
}
self.controller?.tabContextGesture(id: mappedId, sourceNode: nil, sourceView: sourceView, gesture: gesture, keepInPlace: false, isDisabled: isDisabled)
},
deleteAction: (!isEditing || isMainTab) ? nil : { [weak self] in
guard let self else {
return
}
if case let .filter(id) = entry.id {
self.controller?.askForFilterRemoval(id: id)
}
}
)
},
selectedTab: selectedTab,
selectTab: { [weak self] id in
guard let self, let tabContainerData = self.controller?.tabContainerData else {
return
}
let isPremium = self.context.isPremium
let mappedId: ChatListFilterTabEntryId
switch id {
case .all:
mappedId = .all
case let .filter(id):
mappedId = .filter(id)
}
var isDisabled = false
if let filtersLimit = tabContainerData.2 {
guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == mappedId }) else {
return
}
isDisabled = !isPremium && folderIndex >= filtersLimit
}
if isDisabled {
let filtersCount = tabContainerData.0.count(where: { item in
if case .all = item {
return false
} else {
return true
}
})
let context = self.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(filtersCount), action: {
let controller = PremiumIntroScreen(context: context, source: .folders)
replaceImpl?(controller)
return true
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.controller?.push(controller)
} else {
self.controller?.selectTab(id: mappedId)
}
}
isEditing: isEditing
))
}

View file

@ -987,7 +987,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
bottomInset += 44.0
let edgeEffectHeight: CGFloat = bottomInset
let edgeEffectHeight: CGFloat = bottomInset + 8.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight))
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: ComponentTransition(transition))

View file

@ -110,7 +110,7 @@ public class ChatTitleActivityContentNode: ASDisplayNode {
})
if case .slide = style {
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 14.0), duration: transitionDuration, additive: true)
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 4.0), duration: transitionDuration, additive: true)
}
}
@ -118,7 +118,7 @@ public class ChatTitleActivityContentNode: ASDisplayNode {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: transitionDuration)
if case .slide = style {
self.layer.animatePosition(from: CGPoint(x: 0.0, y: -14.0), to: CGPoint(), duration: transitionDuration, additive: true)
self.layer.animatePosition(from: CGPoint(x: 0.0, y: -4.0), to: CGPoint(), duration: transitionDuration, additive: true)
}
}

View file

@ -3,6 +3,7 @@ import UIKit
public final class Button: Component {
public let content: AnyComponent<Empty>
public let contentInsets: UIEdgeInsets
public let minSize: CGSize?
public let hitTestEdgeInsets: UIEdgeInsets?
public let tag: AnyObject?
@ -15,6 +16,7 @@ public final class Button: Component {
convenience public init(
content: AnyComponent<Empty>,
contentInsets: UIEdgeInsets = UIEdgeInsets(),
isEnabled: Bool = true,
automaticHighlight: Bool = true,
action: @escaping () -> Void,
@ -22,6 +24,7 @@ public final class Button: Component {
) {
self.init(
content: content,
contentInsets: contentInsets,
minSize: nil,
hitTestEdgeInsets: nil,
tag: nil,
@ -35,6 +38,7 @@ public final class Button: Component {
private init(
content: AnyComponent<Empty>,
contentInsets: UIEdgeInsets = UIEdgeInsets(),
minSize: CGSize? = nil,
hitTestEdgeInsets: UIEdgeInsets? = nil,
tag: AnyObject? = nil,
@ -46,6 +50,7 @@ public final class Button: Component {
highlightedAction: ActionSlot<Bool>?
) {
self.content = content
self.contentInsets = contentInsets
self.minSize = minSize
self.hitTestEdgeInsets = hitTestEdgeInsets
self.tag = tag
@ -60,6 +65,7 @@ public final class Button: Component {
public func minSize(_ minSize: CGSize?) -> Button {
return Button(
content: self.content,
contentInsets: self.contentInsets,
minSize: minSize,
hitTestEdgeInsets: self.hitTestEdgeInsets,
tag: self.tag,
@ -75,6 +81,7 @@ public final class Button: Component {
public func withHitTestEdgeInsets(_ hitTestEdgeInsets: UIEdgeInsets?) -> Button {
return Button(
content: self.content,
contentInsets: self.contentInsets,
minSize: self.minSize,
hitTestEdgeInsets: hitTestEdgeInsets,
tag: self.tag,
@ -90,6 +97,7 @@ public final class Button: Component {
public func withIsExclusive(_ isExclusive: Bool) -> Button {
return Button(
content: self.content,
contentInsets: self.contentInsets,
minSize: self.minSize,
hitTestEdgeInsets: self.hitTestEdgeInsets,
tag: self.tag,
@ -106,6 +114,7 @@ public final class Button: Component {
public func withHoldAction(_ holdAction: ((UIView) -> Void)?) -> Button {
return Button(
content: self.content,
contentInsets: self.contentInsets,
minSize: self.minSize,
hitTestEdgeInsets: self.hitTestEdgeInsets,
tag: self.tag,
@ -121,6 +130,7 @@ public final class Button: Component {
public func tagged(_ tag: AnyObject) -> Button {
return Button(
content: self.content,
contentInsets: self.contentInsets,
minSize: self.minSize,
hitTestEdgeInsets: self.hitTestEdgeInsets,
tag: tag,
@ -137,6 +147,9 @@ public final class Button: Component {
if lhs.content != rhs.content {
return false
}
if lhs.contentInsets != rhs.contentInsets {
return false
}
if lhs.minSize != rhs.minSize {
return false
}
@ -318,6 +331,8 @@ public final class Button: Component {
size.width = max(size.width, minSize.width)
size.height = max(size.height, minSize.height)
}
size.width += component.contentInsets.left + component.contentInsets.right
size.height += component.contentInsets.top + component.contentInsets.bottom
self.component = component

View file

@ -286,6 +286,8 @@ open class ViewControllerComponentContainer: ViewController {
}
super.init(navigationBarPresentationData: navigationBarPresentationData)
self._hasGlassStyle = true
self.setupPresentationData(effectiveUpdatedPresentationData, navigationBarAppearance: navigationBarAppearance, statusBarStyle: statusBarStyle, presentationMode: presentationMode)
}

View file

@ -42,7 +42,6 @@ swift_library(
"//submodules/ChatPresentationInterfaceState",
"//submodules/TelegramUI/Components/EmojiSuggestionsComponent",
"//submodules/TelegramUI/Components/ListComposePollOptionComponent",
"//submodules/TelegramUI/Components/EdgeEffect",
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
],
visibility = [

View file

@ -27,7 +27,6 @@ import EmojiSuggestionsComponent
import TextFormat
import TextFieldComponent
import ListComposePollOptionComponent
import EdgeEffect
import GlassBarButtonComponent
public final class ComposedPoll {
@ -76,6 +75,7 @@ final class ComposePollScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let peer: EnginePeer
let isQuiz: Bool?
let initialData: ComposePollScreen.InitialData
@ -83,12 +83,14 @@ final class ComposePollScreenComponent: Component {
init(
context: AccountContext,
overNavigationContainer: UIView,
peer: EnginePeer,
isQuiz: Bool?,
initialData: ComposePollScreen.InitialData,
completion: @escaping (ComposedPoll) -> Void
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.peer = peer
self.isQuiz = isQuiz
self.initialData = initialData
@ -112,7 +114,6 @@ final class ComposePollScreenComponent: Component {
final class View: UIView, UIScrollViewDelegate {
private let scrollView: UIScrollView
private let edgeEffectView: EdgeEffectView
private var reactionInput: ComponentView<Empty>?
private let pollTextSection = ComponentView<Empty>()
@ -184,8 +185,6 @@ final class ComposePollScreenComponent: Component {
self.scrollView.contentInsetAdjustmentBehavior = .never
self.scrollView.alwaysBounceVertical = true
self.edgeEffectView = EdgeEffectView()
self.pollOptionsSectionContainer = ListSectionContentView(frame: CGRect())
super.init(frame: frame)
@ -193,8 +192,6 @@ final class ComposePollScreenComponent: Component {
self.scrollView.delegate = self
self.addSubview(self.scrollView)
self.addSubview(self.edgeEffectView)
let reorderRecognizer = ReorderGestureRecognizer(
shouldBegin: { [weak self] point in
guard let self, let (id, item) = self.item(at: point) else {
@ -1623,11 +1620,6 @@ final class ComposePollScreenComponent: Component {
self.scrollView.verticalScrollIndicatorInsets = scrollInsets
}
let edgeEffectHeight: CGFloat = 80.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition)
let title = self.isQuiz ? environment.strings.CreatePoll_QuizTitle : environment.strings.CreatePoll_Title
let titleSize = self.title.update(
transition: .immediate,
@ -1648,19 +1640,19 @@ final class ComposePollScreenComponent: Component {
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((environment.navigationHeight - titleSize.height) / 2.0) + 3.0), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
self.addSubview(titleView)
component.overNavigationContainer.addSubview(titleView)
}
transition.setFrame(view: titleView, frame: titleFrame)
}
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
let cancelButtonSize = self.cancelButton.update(
transition: transition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: environment.theme.overallDarkAppearance,
state: .generic,
state: .glass,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",
@ -1680,7 +1672,7 @@ final class ComposePollScreenComponent: Component {
let cancelButtonFrame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: 16.0), size: cancelButtonSize)
if let cancelButtonView = self.cancelButton.view {
if cancelButtonView.superview == nil {
self.addSubview(cancelButtonView)
component.overNavigationContainer.addSubview(cancelButtonView)
}
transition.setFrame(view: cancelButtonView, frame: cancelButtonFrame)
}
@ -1716,7 +1708,7 @@ final class ComposePollScreenComponent: Component {
let doneButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - doneButtonSize.width, y: 16.0), size: doneButtonSize)
if let doneButtonView = self.doneButton.view {
if doneButtonView.superview == nil {
self.addSubview(doneButtonView)
component.overNavigationContainer.addSubview(doneButtonView)
}
transition.setFrame(view: doneButtonView, frame: doneButtonFrame)
}
@ -1797,6 +1789,8 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
fileprivate let completion: (ComposedPoll) -> Void
private var isDismissed: Bool = false
private let overNavigationContainer: UIView
fileprivate private(set) var sendButtonItem: UIBarButtonItem?
public var isMinimized: Bool = false
@ -1841,13 +1835,16 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
self.context = context
self.completion = completion
self.overNavigationContainer = SparseContainerView()
super.init(context: context, component: ComposePollScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
peer: peer,
isQuiz: isQuiz,
initialData: initialData,
completion: completion
), navigationBarAppearance: .transparent, theme: .default)
), navigationBarAppearance: .default, theme: .default)
self._hasGlassStyle = true
@ -1882,6 +1879,10 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont
return componentView.attemptNavigation(complete: complete)
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {

View file

@ -169,10 +169,12 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode:
return 54.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate() {

View file

@ -145,7 +145,7 @@ public protocol NavigationBar: ASDisplayNode {
var clippingNode: SparseNode { get }
var backgroundView: UIView { get }
var customOverBackgroundContentSubview: UIView? { get set }
var customOverBackgroundContentView: UIView { get }
var contentNode: NavigationBarContentNode? { get }
var secondaryContentNode: ASDisplayNode? { get }
var secondaryContentNodeDisplayFraction: CGFloat { get set }
@ -187,7 +187,7 @@ public protocol NavigationBar: ASDisplayNode {
func executeBack() -> Bool
func setHidden(_ hidden: Bool, animated: Bool)
var requestContainerLayout: (ContainedViewLayoutTransition) -> Void { get set }
var requestContainerLayout: ((ContainedViewLayoutTransition) -> Void)? { get set }
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition)
}

View file

@ -26,6 +26,7 @@ open class NavigationBarContentNode: ASDisplayNode {
return .replacement
}
open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
return size
}
}

View file

@ -1,8 +1,10 @@
import Foundation
import UIKit
public protocol NavigationBarTitleView {
public protocol NavigationBarTitleView: UIView {
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? { get set }
func animateLayoutTransition()
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition)
func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
}

View file

@ -1019,10 +1019,12 @@ private final class SearchNavigationContentNode: NavigationBarContentNode {
return 54.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 1.0 + size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate() {

View file

@ -14,6 +14,8 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
private let authorNameNode: ASTextNode
private let dateNode: ASTextNode
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
override init(frame: CGRect) {
self.authorNameNode = ASTextNode()
self.authorNameNode.displaysAsynchronously = false
@ -41,7 +43,9 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: .white)
}
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let size = availableSize
let leftInset: CGFloat = 0.0
let rightInset: CGFloat = 0.0
@ -55,6 +59,8 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
self.authorNameNode.frame = CGRect(origin: CGPoint(x: floor((size.width - authorNameSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0)), size: authorNameSize)
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
}
return availableSize
}
func animateLayoutTransition() {

View file

@ -32,6 +32,8 @@ swift_library(
"//submodules/TelegramUI/Components/AnimatedTextComponent",
"//submodules/Components/BlurredBackgroundComponent",
"//submodules/UIKitRuntimeUtils",
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
],
visibility = [
"//visibility:public",

View file

@ -8,7 +8,8 @@ import TelegramPresentationData
import SearchBarNode
import ComponentFlow
import ComponentDisplayAdapters
import TabSelectorComponent
import HorizontalTabsComponent
import GlassBackgroundComponent
private let searchBarFont = Font.regular(17.0)
@ -23,6 +24,9 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
var onReturn: (String) -> Void = { _ in }
private let searchBar: SearchBarNode
private let tabsBackgroundContainer: GlassBackgroundContainerView
private let tabsBackgroundView: GlassBackgroundView
private let tabSelector = ComponentView<Empty>()
private var queryUpdated: ((String) -> Void)?
@ -31,7 +35,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
var selectedIndex: Int = 0 {
didSet {
if let (size, leftInset, rightInset) = self.validLayout {
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.35, curve: .spring))
let _ = self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.35, curve: .spring))
}
}
}
@ -40,7 +44,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
didSet {
if self.transitionFraction != oldValue {
if let (size, leftInset, rightInset) = self.validLayout {
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: self.transitionFraction == nil ? .animated(duration: 0.35, curve: .spring) : .immediate)
let _ = self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: self.transitionFraction == nil ? .animated(duration: 0.35, curve: .spring) : .immediate)
}
}
}
@ -79,12 +83,18 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
var initialQuery = initialQuery
initialQuery.removeFirst()
self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: strings, fieldStyle: .modern, icon: icon, displayBackground: false)
self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: strings, fieldStyle: .glass, icon: icon, displayBackground: false)
self.searchBar.text = initialQuery
self.searchBar.placeholderString = NSAttributedString(string: strings.HashtagSearch_SearchPlaceholder, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
self.tabsBackgroundContainer = GlassBackgroundContainerView()
self.tabsBackgroundView = GlassBackgroundView()
super.init()
self.tabsBackgroundContainer.contentView.addSubview(self.tabsBackgroundView)
self.view.addSubview(self.tabsBackgroundContainer)
self.searchBar.autocapitalization = .none
if hasCurrentChat {
@ -120,7 +130,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
override var nominalHeight: CGFloat {
if self.hasCurrentChat {
return 54.0 + 44.0
return 64.0 + 44.0
} else {
return 45.0
}
@ -128,58 +138,98 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
private var validLayout: (CGSize, CGFloat, CGFloat)?
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.validLayout = (size, leftInset, rightInset)
let sideInset: CGFloat = 6.0
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight + 5.0), size: CGSize(width: size.width, height: 54.0))
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 6.0), size: CGSize(width: size.width, height: 44.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset + sideInset, rightInset: rightInset + sideInset, transition: transition)
if self.hasTabs {
var items: [TabSelectorComponent.Item] = []
var items: [HorizontalTabsComponent.Tab] = []
if self.hasCurrentChat {
items.append(TabSelectorComponent.Item(id: AnyHashable(0), title: self.strings.HashtagSearch_ThisChat))
items.append(HorizontalTabsComponent.Tab(
id: AnyHashable(0),
content: .title(HorizontalTabsComponent.Tab.Title(text: self.strings.HashtagSearch_ThisChat, entities: [], enableAnimations: false)),
badge: nil,
action: { [weak self] in
guard let self else {
return
}
self.indexUpdated?(0)
},
contextAction: nil,
deleteAction: nil
))
}
items.append(TabSelectorComponent.Item(id: AnyHashable(1), title: self.strings.HashtagSearch_MyMessages))
items.append(TabSelectorComponent.Item(id: AnyHashable(2), title: self.strings.HashtagSearch_PublicPosts))
items.append(HorizontalTabsComponent.Tab(
id: AnyHashable(1),
content: .title(HorizontalTabsComponent.Tab.Title(text: self.strings.HashtagSearch_MyMessages, entities: [], enableAnimations: false)),
badge: nil,
action: { [weak self] in
guard let self else {
return
}
self.indexUpdated?(1)
},
contextAction: nil,
deleteAction: nil
))
items.append(HorizontalTabsComponent.Tab(
id: AnyHashable(2),
content: .title(HorizontalTabsComponent.Tab.Title(text: self.strings.HashtagSearch_PublicPosts, entities: [], enableAnimations: false)),
badge: nil,
action: { [weak self] in
guard let self else {
return
}
self.indexUpdated?(2)
},
contextAction: nil,
deleteAction: nil
))
let tabSelectorSize = self.tabSelector.update(
transition: ComponentTransition(transition),
component: AnyComponent(TabSelectorComponent(
colors: TabSelectorComponent.Colors(
foreground: self.theme.list.itemSecondaryTextColor,
selection: self.theme.list.itemAccentColor
),
component: AnyComponent(HorizontalTabsComponent(
context: nil,
theme: self.theme,
customLayout: TabSelectorComponent.CustomLayout(
font: Font.medium(14.0),
spacing: self.hasCurrentChat ? 24.0 : 8.0,
lineSelection: true
),
items: items,
selectedId: AnyHashable(self.selectedIndex),
setSelectedId: { [weak self] id in
guard let self, let index = id.base as? Int else {
return
}
self.indexUpdated?(index)
},
transitionFraction: self.transitionFraction
tabs: items,
selectedTab: AnyHashable(self.selectedIndex),
isEditing: false
)),
environment: {},
containerSize: CGSize(width: size.width, height: 44.0)
containerSize: CGSize(width: size.width - (leftInset + 16.0) * 2.0, height: 44.0)
)
let tabSelectorFrameOriginX = floorToScreenPixels((size.width - tabSelectorSize.width) / 2.0)
let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: size.height - tabSelectorSize.height - 10.0), size: tabSelectorSize)
if let tabSelectorView = self.tabSelector.view {
let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: searchBarFrame.maxY + 10.0), size: tabSelectorSize)
transition.updateFrame(view: self.tabsBackgroundContainer, frame: tabSelectorFrame)
self.tabsBackgroundContainer.update(size: tabSelectorFrame.size, isDark: self.theme.overallDarkAppearance, transition: ComponentTransition(transition))
transition.updateFrame(view: self.tabsBackgroundView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size))
self.tabsBackgroundView.update(size: tabSelectorFrame.size, cornerRadius: tabSelectorFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: ComponentTransition(transition))
if let tabSelectorView = self.tabSelector.view as? HorizontalTabsComponent.View {
if tabSelectorView.superview == nil {
self.view.addSubview(tabSelectorView)
self.tabsBackgroundView.contentView.addSubview(tabSelectorView)
tabSelectorView.setOverlayContainerView(overlayContainerView: self.view)
}
transition.updateFrame(view: tabSelectorView, frame: tabSelectorFrame)
transition.updateFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size))
var transitionFraction: CGFloat = 0.0
if let transitionFractionValue = self.transitionFraction {
transitionFraction = -transitionFractionValue
}
tabSelectorView.updateTabSwitchFraction(fraction: transitionFraction, isDragging: false, transition: ComponentTransition(transition))
}
}
return size
}
func activate() {

View file

@ -78,10 +78,12 @@ final class SearchNavigationContentNode: NavigationBarContentNode, ItemListContr
return 56.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate() {

View file

@ -34,6 +34,8 @@ swift_library(
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/TelegramUI/Components/TextNodeWithEntities",
"//submodules/TelegramUI/Components/ListItemComponentAdaptor",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
],
visibility = [
"//visibility:public",

View file

@ -690,7 +690,8 @@ private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitl
private let titleNode: ImmediateTextNode
private let subtitleNode: ImmediateTextNode
private var validLayout: (CGSize, CGRect)?
private var validLayout: CGSize?
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
init(theme: PresentationTheme, title: String, subtitle: String) {
self.titleNode = ImmediateTextNode()
@ -720,21 +721,23 @@ private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitl
func updateTheme(theme: PresentationTheme) {
self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.medium(17.0), textColor: theme.rootController.navigationBar.primaryTextColor)
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: theme.rootController.navigationBar.secondaryTextColor)
if let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
override func layoutSubviews() {
super.layoutSubviews()
if let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, clearBounds)
func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let size = availableSize
self.validLayout = size
let titleSize = self.titleNode.updateLayout(size)
let subtitleSize = self.subtitleNode.updateLayout(size)
@ -745,6 +748,8 @@ private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitl
self.titleNode.frame = titleFrame
self.subtitleNode.frame = subtitleFrame
return availableSize
}
func animateLayoutTransition() {

View file

@ -3,9 +3,12 @@ import UIKit
import Display
import TelegramPresentationData
import ComponentFlow
import TabSelectorComponent
import GlassBackgroundComponent
import HorizontalTabsComponent
public final class ItemListControllerSegmentedTitleView: UIView {
private let backgroundContainer: GlassBackgroundContainerView
private let backgroundView: GlassBackgroundView
private let tabSelector = ComponentView<Empty>()
public var theme: PresentationTheme {
@ -42,7 +45,13 @@ public final class ItemListControllerSegmentedTitleView: UIView {
self.segments = segments
self.index = selectedIndex
self.backgroundContainer = GlassBackgroundContainerView()
self.backgroundView = GlassBackgroundView()
super.init(frame: CGRect())
self.addSubview(self.backgroundContainer)
self.backgroundContainer.contentView.addSubview(self.backgroundView)
}
required public init?(coder aDecoder: NSCoder) {
@ -62,10 +71,19 @@ public final class ItemListControllerSegmentedTitleView: UIView {
return
}
let mappedItems = zip(0 ..< self.segments.count, self.segments).map { index, segment in
return TabSelectorComponent.Item(
let mappedItems: [HorizontalTabsComponent.Tab] = zip(0 ..< self.segments.count, self.segments).map { index, segment in
return HorizontalTabsComponent.Tab(
id: AnyHashable(index),
title: segment
content: .title(HorizontalTabsComponent.Tab.Title(text: segment, entities: [], enableAnimations: false)),
badge: nil,
action: { [weak self] in
guard let self else {
return
}
self.indexUpdated?(index)
},
contextAction: nil,
deleteAction: nil
)
}
@ -77,34 +95,32 @@ public final class ItemListControllerSegmentedTitleView: UIView {
let tabSelectorSize = self.tabSelector.update(
transition: transition,
component: AnyComponent(TabSelectorComponent(
colors: TabSelectorComponent.Colors(
foreground: self.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.8),
selection: self.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05)
),
component: AnyComponent(HorizontalTabsComponent(
context: nil,
theme: self.theme,
customLayout: TabSelectorComponent.CustomLayout(
font: Font.medium(15.0),
spacing: 8.0
),
items: mappedItems,
selectedId: AnyHashable(self.index),
setSelectedId: { [weak self] id in
guard let self, let index = id.base as? Int else {
return
}
self.indexUpdated?(index)
}
tabs: mappedItems,
selectedTab: AnyHashable(self.index),
isEditing: false,
layout: .fit
)),
environment: {},
containerSize: CGSize(width: size.width, height: 44.0)
containerSize: CGSize(width: size.width, height: 40.0)
)
let tabSelectorFrame = CGRect(origin: CGPoint(x: floor((size.width - tabSelectorSize.width) / 2.0), y: floor((size.height - tabSelectorSize.height) / 2.0)), size: tabSelectorSize)
if let tabSelectorView = self.tabSelector.view {
transition.setFrame(view: self.backgroundContainer, frame: tabSelectorFrame)
self.backgroundContainer.update(size: tabSelectorFrame.size, isDark: self.theme.overallDarkAppearance, transition: transition)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size))
self.backgroundView.update(size: tabSelectorFrame.size, cornerRadius: tabSelectorFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition)
if let tabSelectorView = self.tabSelector.view as? HorizontalTabsComponent.View {
if tabSelectorView.superview == nil {
self.addSubview(tabSelectorView)
self.backgroundView.contentView.addSubview(tabSelectorView)
tabSelectorView.setOverlayContainerView(overlayContainerView: self)
}
transition.setFrame(view: tabSelectorView, frame: tabSelectorFrame)
transition.setFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size))
}
}
}

View file

@ -63,10 +63,10 @@ final class ItemListControllerTabsContentNode: NavigationBarContentNode {
guard let (size, leftInset, rightInset) = self.validLayout else {
return
}
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition)
let _ = self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition)
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let isFirstTime = self.validLayout == nil
self.validLayout = (size, leftInset, rightInset)
@ -116,6 +116,8 @@ final class ItemListControllerTabsContentNode: NavigationBarContentNode {
if isFirstTime {
self.requestContainerLayout(.immediate)
}
return size
}
override var height: CGFloat {

View file

@ -1382,7 +1382,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
transition.updateFrame(view: titleView, frame: titleFrame)
}
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
let cancelButtonSize = self.cancelButton.update(
transition: ComponentTransition(transition),
component: AnyComponent(GlassBarButtonComponent(

View file

@ -38,10 +38,12 @@ final class LocationSearchNavigationContentNode: NavigationBarContentNode {
return 56.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate() {

View file

@ -2548,7 +2548,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
let useGlassButtons = (isBack || !self.controllerNode.scrolledToTop) && !self.controllerNode.isSwitchingAssetGroup
let barButtonSideInset: CGFloat = 16.0
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
var buttonTransition = ComponentTransition.easeInOut(duration: 0.25)
if case let .animated(duration, _) = transition, duration > 0.25 {
@ -2580,9 +2580,9 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
transition: buttonTransition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: self.presentationData.theme.overallDarkAppearance,
state: useGlassButtons ? .glass : .generic,
state: .glass,
component: AnyComponentWithIdentity(id: isBack ? "back" : "close", component: AnyComponent(
BundleIconComponent(
name: isBack ? "Navigation/Back" : "Navigation/Close",
@ -2611,9 +2611,9 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
transition: buttonTransition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: self.presentationData.theme.overallDarkAppearance,
state: useGlassButtons ? .glass : .generic,
state: .glass,
component: AnyComponentWithIdentity(id: "more", component: AnyComponent(
LottieComponent(
content: LottieComponent.AppBundleContent(

View file

@ -136,7 +136,7 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC
didSet {
if self.activity != oldValue {
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
}
}
@ -213,7 +213,7 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC
func updateTheme(_ theme: PresentationTheme) {
self.theme = theme
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
self.updatePlaceholder()
}
@ -228,7 +228,7 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC
return 60.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.params = Params(size: size, leftInset: leftInset, rightInset: rightInset)
let transition = ComponentTransition(transition)
@ -304,6 +304,8 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC
transition.setFrame(view: self.close.background, frame: closeFrame)
self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
return size
}
func activate() {

View file

@ -49,7 +49,7 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item
didSet {
if self.activity != oldValue {
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
}
}
@ -128,7 +128,7 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item
func updateTheme(_ theme: PresentationTheme) {
self.theme = theme
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
self.updatePlaceholder()
}
@ -148,7 +148,7 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item
return 60.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.params = Params(size: size, leftInset: leftInset, rightInset: rightInset)
let transition = ComponentTransition(transition)
@ -224,6 +224,8 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item
transition.setFrame(view: self.close.background, frame: closeFrame)
self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
return size
}
func activate() {

View file

@ -4010,7 +4010,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
shareLink: { link in
shareLinkImpl?(link)
}
), navigationBarAppearance: .transparent, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData)
), navigationBarAppearance: .default, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData)
if modal {
let cancelItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed))

View file

@ -1205,6 +1205,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
if case .glass = self.fieldStyle, self.takenSearchPlaceholderContentView == nil {
transition.updateFrame(node: self.inlineSearchPlaceholder, frame: searchPlaceholderFrame)
var isFirstTime = false
if let theme = self.theme {
let _ = self.inlineSearchPlaceholder.updateLayout(
placeholderString: self.placeholderString,
@ -1218,6 +1219,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
transition: transition
)
if self.inlineSearchPlaceholderContentsView == nil {
isFirstTime = true
let inlineSearchPlaceholderContentsView = self.inlineSearchPlaceholder.takeContents()
inlineSearchPlaceholderContentsView.onCancel = { [weak self] in
guard let self else {
@ -1232,6 +1234,11 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
if let inlineSearchPlaceholderContentsView = self.inlineSearchPlaceholderContentsView {
inlineSearchPlaceholderContentsView.update(size: searchPlaceholderFrame.size, isActive: true, transition: transition)
transition.updateFrame(view: inlineSearchPlaceholderContentsView, frame: searchPlaceholderFrame)
if isFirstTime {
self.updateIsEmpty(animated: false)
inlineSearchPlaceholderContentsView.updateSearchIconVisibility(isVisible: !self.activity)
}
}
}

View file

@ -139,7 +139,7 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
fillColor = fillColor.withMultipliedBrightnessBy(0.8)
}
let backgroundColor = self.theme?.rootController.navigationBar.opaqueBackgroundColor ?? .clear
let backgroundColor = self.theme?.chatList.regularSearchBarColor ?? .clear
let controlColor = self.theme?.chat.inputPanel.panelControlColor ?? .black
let placeholderString = NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: textColor)
@ -158,10 +158,12 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode {
}
}
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.validLayout = (size, leftInset, rightInset)
self.updatePlaceholder(self.expansionProgress, size: size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
override public var height: CGFloat {

View file

@ -321,7 +321,7 @@ final class TabBarControllerNode: ASDisplayNode {
})
}
return params.layout.size.height - tabBarFrame.minY
return params.layout.size.height - tabBarFrame.minY - 6.0
}
func frameForControllerTab(at index: Int) -> CGRect? {

View file

@ -85,7 +85,7 @@ public extension NavigationBarPresentationData {
}
convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool, hideSeparator: Bool = false, style: NavigationBar.Style = .legacy) {
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator, style: style), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator, edgeEffectColor: hideBackground ? .clear : nil, style: style), strings: NavigationBarStrings(presentationStrings: presentationData.strings))
}
convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings, style: NavigationBar.Style = .legacy) {

View file

@ -371,7 +371,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
let navigationSearchBar = PresentationThemeNavigationSearchBar(
backgroundColor: UIColor(rgb: 0x1c1c1d),
accentColor: UIColor(rgb: 0xffffff),
inputFillColor: UIColor(rgb: 0x0f0f0f),
inputFillColor: UIColor(white: 1.0, alpha: 0.1),
inputTextColor: UIColor(rgb: 0xffffff),
inputPlaceholderTextColor: UIColor(rgb: 0x8f8f8f),
inputIconColor: UIColor(rgb: 0x8f8f8f),

View file

@ -108,6 +108,8 @@ public final class AnimatedTextComponent: Component {
public final class View: UIView {
private var characters: [CharacterKey: ComponentView<Empty>] = [:]
private var spaceSize: CGSize?
private var component: AnimatedTextComponent?
private weak var state: EmptyComponentState?
@ -120,6 +122,15 @@ public final class AnimatedTextComponent: Component {
}
func update(component: AnimatedTextComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
let spaceSize: CGSize
if let current = self.spaceSize, self.component?.font == component.font {
spaceSize = current
} else {
let spaceSizeValue = NSAttributedString(string: " ", font: component.font, textColor: .black).boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil).size
spaceSize = CGSize(width: ceil(spaceSizeValue.width), height: ceil(spaceSizeValue.height))
self.spaceSize = spaceSize
}
self.component = component
self.state = state
@ -243,15 +254,23 @@ public final class AnimatedTextComponent: Component {
let characterComponent: AnyComponent<Empty>
var characterOffset: CGPoint = .zero
var addTrailingSpace = false
switch character {
case let .text(text):
if text == " " {
let spaceSize = NSAttributedString(string: " ", font: component.font, textColor: .black).boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil).size
size.height = max(size.height, ceil(spaceSize.height))
size.width += max(0.0, ceil(spaceSize.width))
continue characterLoop
} else {
if text.hasPrefix(" ") {
size.height = max(size.height, ceil(spaceSize.height))
size.width += max(0.0, ceil(spaceSize.width))
}
if text.hasSuffix(" ") {
addTrailingSpace = true
}
characterComponent = AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: text, font: component.font, textColor: component.color))
))
@ -327,6 +346,11 @@ public final class AnimatedTextComponent: Component {
size.height = max(size.height, characterSize.height)
size.width += max(0.0, characterSize.width - UIScreenPixel)
if addTrailingSpace {
size.height = max(size.height, ceil(spaceSize.height))
size.width += max(0.0, ceil(spaceSize.width))
}
}
}

View file

@ -51,10 +51,12 @@ final class ChatRecentActionsSearchNavigationContentNode: NavigationBarContentNo
return 54.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate() {

View file

@ -126,7 +126,7 @@ public final class ChatSearchNavigationContentNode: NavigationBarContentNode {
if self.hasActivity != value {
self.hasActivity = value
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
}
})
@ -147,7 +147,7 @@ public final class ChatSearchNavigationContentNode: NavigationBarContentNode {
}
}
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.params = (size, leftInset, rightInset)
let transition = ComponentTransition(transition)
@ -223,6 +223,8 @@ public final class ChatSearchNavigationContentNode: NavigationBarContentNode {
transition.setFrame(view: self.close.background, frame: closeFrame)
self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
return size
}
public func activate() {
@ -290,7 +292,7 @@ public final class ChatSearchNavigationContentNode: NavigationBarContentNode {
if presentationInterfaceState.theme != self.theme {
self.theme = presentationInterfaceState.theme
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
}
}

View file

@ -1,490 +0,0 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
import AccountContext
import TelegramCore
import MultilineTextWithEntitiesComponent
import TextBadgeComponent
import LiquidLens
public final class ChatListTabsComponent: Component {
public enum Tab: Equatable {
public struct UnreadCount: Equatable {
public let value: Int
public let hasUnmuted: Bool
public init(value: Int, hasUnmuted: Bool) {
self.value = value
self.hasUnmuted = hasUnmuted
}
}
public enum Id: Hashable {
case all
case filter(id: Int32)
}
case all
case filter(id: Int32, text: ChatFolderTitle, unread: UnreadCount)
public var id: Id {
switch self {
case .all:
return .all
case let .filter(id, _, _):
return .filter(id: id)
}
}
}
public let context: AccountContext
public let theme: PresentationTheme
public let strings: PresentationStrings
public let tabs: [Tab]
public let selectedTab: Tab.Id?
public let selectTab: (Tab.Id) -> Void
public init(
context: AccountContext,
theme: PresentationTheme,
strings: PresentationStrings,
tabs: [Tab],
selectedTab: Tab.Id?,
selectTab: @escaping (Tab.Id) -> Void
) {
self.context = context
self.theme = theme
self.strings = strings
self.tabs = tabs
self.selectedTab = selectedTab
self.selectTab = selectTab
}
public static func ==(lhs: ChatListTabsComponent, rhs: ChatListTabsComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.tabs != rhs.tabs {
return false
}
if lhs.selectedTab != rhs.selectedTab {
return false
}
return true
}
private final class ScrollView: UIScrollView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return super.hitTest(point, with: event)
}
override func touchesShouldCancel(in view: UIView) -> Bool {
return true
}
}
private struct LayoutData {
var size: CGSize
var selectedItemFrame: CGRect
init(size: CGSize, selectedItemFrame: CGRect) {
self.size = size
self.selectedItemFrame = selectedItemFrame
}
}
public final class View: UIView, UIScrollViewDelegate {
private let lensView: LiquidLensView
private let scrollView: ScrollView
private let selectionView: UIImageView
private var itemViews: [Tab.Id: ComponentView<Empty>] = [:]
private var ignoreScrolling: Bool = false
private var tabSwitchFraction: CGFloat = 0.0
private var temporaryLiftTimer: Foundation.Timer?
private var layoutData: LayoutData?
private var component: ChatListTabsComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
self.lensView = LiquidLensView(kind: .noContainer)
self.scrollView = ScrollView()
self.selectionView = UIImageView()
//self.scrollView.addSubview(self.selectionView)
super.init(frame: frame)
self.scrollView.delaysContentTouches = false
self.scrollView.canCancelContentTouches = true
self.scrollView.contentInsetAdjustmentBehavior = .never
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
self.scrollView.showsVerticalScrollIndicator = false
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.alwaysBounceHorizontal = false
self.scrollView.alwaysBounceVertical = false
self.scrollView.scrollsToTop = false
self.scrollView.clipsToBounds = false
self.scrollView.delegate = self
self.addSubview(self.lensView)
self.lensView.contentView.addSubview(self.scrollView)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return self.scrollView.hitTest(self.convert(point, to: self.scrollView), with: event)
}
public func updateTabSwitchFraction(fraction: CGFloat, transition: ComponentTransition) {
self.tabSwitchFraction = -fraction
self.state?.updated(transition: transition, isLocal: true)
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.ignoreScrolling {
return
}
self.updateScrolling(transition: .immediate)
}
private func updateScrolling(transition: ComponentTransition) {
guard let component = self.component, let layoutData = self.layoutData else {
return
}
self.lensView.update(size: layoutData.size, selectionX: -self.scrollView.contentOffset.x + layoutData.selectedItemFrame.minX, selectionWidth: layoutData.selectedItemFrame.width, isDark: component.theme.overallDarkAppearance, isLifted: self.temporaryLiftTimer != nil, transition: transition)
}
func update(component: ChatListTabsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
if self.component?.selectedTab != component.selectedTab {
self.tabSwitchFraction = 0.0
self.temporaryLiftTimer?.invalidate()
self.temporaryLiftTimer = nil
if !transition.animation.isImmediate {
self.temporaryLiftTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false, block: { [weak self] timer in
guard let self else {
return
}
if self.temporaryLiftTimer === timer {
self.temporaryLiftTimer = nil
self.state?.updated(transition: .spring(duration: 0.5))
}
})
}
}
self.component = component
self.state = state
let size = CGSize(width: availableSize.width, height: 40.0)
let sideInset: CGFloat = 3.0
var contentWidth: CGFloat = sideInset
var validIds: [Tab.Id] = []
for tab in component.tabs {
let tabId = tab.id
validIds.append(tabId)
var itemTransition = transition
let itemView: ComponentView<Empty>
if let current = self.itemViews[tabId] {
itemView = current
} else {
itemTransition = itemTransition.withAnimation(.none)
itemView = ComponentView()
self.itemViews[tabId] = itemView
}
let itemSize = itemView.update(
transition: itemTransition,
component: AnyComponent(ItemComponent(
context: component.context,
theme: component.theme,
strings: component.strings,
tab: tab,
selectAction: { [weak self] in
guard let self, let component = self.component else {
return
}
component.selectTab(tabId)
}
)),
environment: {},
containerSize: CGSize(width: 1000.0, height: size.height)
)
let itemFrame = CGRect(origin: CGPoint(x: contentWidth, y: 0.0), size: itemSize)
if let itemComponentView = itemView.view {
if itemComponentView.superview == nil {
self.scrollView.addSubview(itemComponentView)
transition.animateAlpha(view: itemComponentView, from: 0.0, to: 1.0)
transition.animateScale(view: itemComponentView, from: 0.001, to: 1.0)
}
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
}
contentWidth += itemSize.width
}
contentWidth += sideInset
var removedIds: [Tab.Id] = []
for (id, itemView) in self.itemViews {
if !validIds.contains(id) {
removedIds.append(id)
if let itemComponentView = itemView.view {
transition.setScale(view: itemComponentView, scale: 0.001)
transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in
itemComponentView?.removeFromSuperview()
})
}
}
}
for id in removedIds {
self.itemViews.removeValue(forKey: id)
}
var selectedItemFrame: CGRect?
if let selectedTab = component.selectedTab {
for i in 0 ..< component.tabs.count {
if component.tabs[i].id == selectedTab {
if let itemView = self.itemViews[component.tabs[i].id]?.view {
var selectedItemFrameValue = itemView.frame
var pendingItemFrame: CGRect?
if self.tabSwitchFraction != 0.0 {
if self.tabSwitchFraction > 0.0 && i != component.tabs.count - 1 {
if let nextItemView = self.itemViews[component.tabs[i + 1].id]?.view {
pendingItemFrame = nextItemView.frame
}
} else if self.tabSwitchFraction < 0.0 && i != 0 {
if let previousItemView = self.itemViews[component.tabs[i - 1].id]?.view {
pendingItemFrame = previousItemView.frame
}
}
}
if let pendingItemFrame {
let fraction = abs(self.tabSwitchFraction)
selectedItemFrameValue.origin.x = selectedItemFrameValue.minX * (1.0 - fraction) + pendingItemFrame.minX * fraction
selectedItemFrameValue.size.width = selectedItemFrameValue.width * (1.0 - fraction) + pendingItemFrame.width * fraction
}
selectedItemFrame = selectedItemFrameValue
}
break
}
}
}
if let selectedItemFrame {
var selectionTransition = transition
if self.selectionView.isHidden {
self.selectionView.isHidden = false
selectionTransition = selectionTransition.withAnimation(.none)
}
let selectionFrame = CGRect(origin: CGPoint(x: selectedItemFrame.minX, y: 3.0), size: CGSize(width: selectedItemFrame.width, height: size.height - 3.0 * 2.0))
if self.selectionView.image?.size.height != selectionFrame.height {
self.selectionView.image = generateStretchableFilledCircleImage(diameter: selectionFrame.height, color: .white)?.withRenderingMode(.alwaysTemplate)
}
self.selectionView.tintColor = component.theme.chat.inputPanel.panelControlColor.withAlphaComponent(0.1)
selectionTransition.setFrame(view: self.selectionView, frame: selectionFrame)
} else {
self.selectionView.isHidden = true
}
self.layoutData = LayoutData(
size: size,
selectedItemFrame: selectedItemFrame ?? CGRect()
)
self.ignoreScrolling = true
let contentSize = CGSize(width: contentWidth, height: size.height)
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: size))
if self.scrollView.contentSize != contentSize {
self.scrollView.contentSize = contentSize
}
transition.setFrame(view: self.lensView, frame: CGRect(origin: CGPoint(), size: size))
self.ignoreScrolling = false
self.updateScrolling(transition: transition)
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
private final class ItemComponent: Component {
let context: AccountContext
let theme: PresentationTheme
let strings: PresentationStrings
let tab: ChatListTabsComponent.Tab
let selectAction: () -> Void
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, tab: ChatListTabsComponent.Tab, selectAction: @escaping () -> Void) {
self.context = context
self.theme = theme
self.strings = strings
self.tab = tab
self.selectAction = selectAction
}
static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.tab != rhs.tab {
return false
}
return true
}
final class View: UIView {
let title = ComponentView<Empty>()
var badge: ComponentView<Empty>?
var component: ItemComponent?
override init(frame: CGRect) {
super.init(frame: frame)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.component?.selectAction()
}
}
func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.component = component
let sideInset: CGFloat = 16.0
let badgeSpacing: CGFloat = 5.0
let font = Font.medium(15.0)
let titleString: NSAttributedString
var badgeData: (title: String, isActive: Bool)?
switch component.tab {
case .all:
titleString = NSAttributedString(string: component.strings.ChatList_Tabs_All, font: font, textColor: component.theme.chat.inputPanel.panelControlColor)
case let .filter(_, text, unread):
titleString = text.attributedString(font: font, textColor: component.theme.chat.inputPanel.panelControlColor)
if unread.value != 0 {
badgeData = ("\(unread.value)", unread.hasUnmuted)
}
}
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextWithEntitiesComponent(
context: component.context,
animationCache: component.context.animationCache,
animationRenderer: component.context.animationRenderer,
placeholderColor: component.theme.chat.inputPanel.panelControlColor.withMultipliedAlpha(0.1),
text: .plain(titleString),
displaysAsynchronously: false
)),
environment: {},
containerSize: CGSize(width: 300.0, height: 100.0)
)
var size = CGSize(width: sideInset + titleSize.width, height: availableSize.height)
if let badgeData {
let badge: ComponentView<Empty>
var badgeTransition = transition
if let current = self.badge {
badge = current
} else {
badgeTransition = badgeTransition.withAnimation(.none)
badge = ComponentView()
self.badge = badge
}
let badgeSize = badge.update(
transition: badgeTransition,
component: AnyComponent(TextBadgeComponent(
text: badgeData.title,
font: Font.medium(12.0),
background: badgeData.isActive ? component.theme.list.itemCheckColors.fillColor : component.theme.chatList.unreadBadgeInactiveBackgroundColor,
foreground: component.theme.list.itemCheckColors.foregroundColor,
insets: UIEdgeInsets(top: 1.0, left: 5.0, bottom: 2.0, right: 5.0)
)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)
)
size.width += badgeSpacing
let badgeFrame = CGRect(origin: CGPoint(x: size.width, y: floorToScreenPixels((size.height - badgeSize.height) * 0.5)), size: badgeSize)
if let badgeView = badge.view {
if badgeView.superview == nil {
self.addSubview(badgeView)
transition.animateAlpha(view: badgeView, from: 0.0, to: 1.0)
transition.animateScale(view: badgeView, from: 0.001, to: 1.0)
}
badgeTransition.setFrame(view: badgeView, frame: badgeFrame)
}
size.width += badgeSize.width
} else if let badge = self.badge {
self.badge = nil
if let badgeView = badge.view {
transition.setFrame(view: badgeView, frame: badgeView.bounds.size.centered(around: CGPoint(x: size.width + sideInset - badgeView.bounds.width * 0.5, y: size.height * 0.5)))
transition.setScale(view: badgeView, scale: 0.001)
transition.setAlpha(view: badgeView, alpha: 0.0, completion: { [weak badgeView] _ in
badgeView?.removeFromSuperview()
})
}
}
size.width += sideInset
let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floorToScreenPixels((size.height - titleSize.height) * 0.5)), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.layer.anchorPoint = CGPoint()
self.addSubview(titleView)
}
transition.setPosition(view: titleView, position: titleFrame.origin)
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
}
return size
}
}
func makeView() -> View {
return View(frame: CGRect())
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View file

@ -670,7 +670,7 @@ public final class ChatListHeaderComponent: Component {
chatListTitleView.theme = theme
chatListTitleView.strings = strings
chatListTitleView.setTitle(chatListTitle, animated: false)
let titleContentRect = chatListTitleView.updateLayoutInternal(size: chatListTitleContentSize, clearBounds: CGRect(origin: CGPoint(), size: chatListTitleContentSize), transition: transition.containedViewLayoutTransition)
let titleContentRect = chatListTitleView.updateLayoutInternal(size: chatListTitleContentSize, transition: transition.containedViewLayoutTransition)
centerContentWidth = floor((chatListTitleContentSize.width * 0.5 - titleContentRect.minX) * 2.0)
let centerOffset = sideContentWidth * 0.5

View file

@ -11,6 +11,14 @@ import TelegramCore
import StoryPeerListComponent
import EdgeEffect
private func searchScrollHeightValue() -> CGFloat {
return 54.0
}
private func storiesHeightValue() -> CGFloat {
return 96.0
}
public final class ChatListNavigationBar: Component {
public final class AnimationHint {
let disableStoriesAnimations: Bool
@ -172,10 +180,8 @@ public final class ChatListNavigationBar: Component {
}
}
public static let searchScrollHeight: CGFloat = 52.0
public static let storiesScrollHeight: CGFloat = {
return 83.0
}()
public static let searchScrollHeight: CGFloat = searchScrollHeightValue()
public static let storiesScrollHeight: CGFloat = storiesHeightValue()
public final class View: UIView {
private let edgeEffectView: EdgeEffectView
@ -340,7 +346,7 @@ public final class ChatListNavigationBar: Component {
}
let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight)
var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height - self.bottomContentsContainer.bounds.height), size: searchSize)
var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height - self.bottomContentsContainer.bounds.height - 2.0), size: searchSize)
if let activeSearch = component.activeSearch, !activeSearch.isExternal {
searchFrame.origin.y = component.statusBarHeight + 8.0
}
@ -366,7 +372,7 @@ public final class ChatListNavigationBar: Component {
searchFrameValue = searchFrame
transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame)
searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition)
let _ = searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition)
var searchAlpha: CGFloat = search.isEnabled ? 1.0 : 0.5
if let activeSearch = component.activeSearch, activeSearch.isExternal {
@ -686,10 +692,10 @@ public final class ChatListNavigationBar: Component {
}
} else {
contentHeight += 44.0
contentHeight += 7.0
contentHeight += 9.0
if component.search != nil {
contentHeight += navigationBarSearchContentHeight
contentHeight += navigationBarSearchContentHeight + 2.0
}
}

View file

@ -60,9 +60,10 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation
private let animationCache: AnimationCache
private let animationRenderer: MultiAnimationRenderer
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
public var openStatusSetup: ((UIView) -> Void)?
private var validLayout: (CGSize, CGRect)?
private var validLayout: CGSize?
public var manualLayout: Bool = false
@ -321,17 +322,18 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation
override public func layoutSubviews() {
super.layoutSubviews()
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
let _ = self.updateLayoutInternal(size: size, clearBounds: clearBounds, transition: transition)
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let _ = self.updateLayoutInternal(size: availableSize, transition: transition)
return availableSize
}
public func updateLayoutInternal(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect {
self.validLayout = (size, clearBounds)
public func updateLayoutInternal(size: CGSize, transition: ContainedViewLayoutTransition) -> CGRect {
self.validLayout = size
var indicatorPadding: CGFloat = 0.0
let indicatorSize = self.activityIndicator.bounds.size
@ -339,7 +341,7 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation
if !self.activityIndicator.isHidden {
indicatorPadding = indicatorSize.width + 6.0
}
var maxTitleWidth = clearBounds.size.width - indicatorPadding
var maxTitleWidth = size.width - indicatorPadding
var proxyPadding: CGFloat = 0.0
if !self.proxyNode.isHidden {
maxTitleWidth -= 25.0
@ -357,7 +359,7 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation
var titleContentRect = CGRect(origin: CGPoint(x: indicatorPadding + floor((size.width - combinedWidth - indicatorPadding) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
titleContentRect.origin.x = min(titleContentRect.origin.x, clearBounds.maxX - proxyPadding - titleContentRect.width)
titleContentRect.origin.x = min(titleContentRect.origin.x, size.width - proxyPadding - titleContentRect.width)
let titleFrame = titleContentRect
var titleTransition = transition
@ -366,7 +368,7 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation
}
titleTransition.updateFrame(node: self.titleNode, frame: titleFrame)
let proxyFrame = CGRect(origin: CGPoint(x: clearBounds.maxX - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - self.proxyNode.bounds.height) / 2.0)), size: self.proxyNode.bounds.size)
let proxyFrame = CGRect(origin: CGPoint(x: size.width - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - self.proxyNode.bounds.height) / 2.0)), size: self.proxyNode.bounds.size)
self.proxyNode.frame = proxyFrame
self.proxyButton.frame = proxyFrame.insetBy(dx: -2.0, dy: -2.0)

View file

@ -16,16 +16,6 @@ import EmojiStatusComponent
import GlassBackgroundComponent
public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
private struct Params: Equatable {
let size: CGSize
let clearBounds: CGRect
init(size: CGSize, clearBounds: CGRect) {
self.size = size
self.clearBounds = clearBounds
}
}
private final class ContentData {
let context: AccountContext
let theme: PresentationTheme
@ -44,13 +34,14 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
}
}
private let parentTitleState = ComponentState()
private let title = ComponentView<Empty>()
private var contentData: ContentData?
private var params: Params?
private var activities: ChatTitleComponent.Activities?
private var networkState: AccountNetworkState?
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
public var tapAction: (() -> Void)?
public var longTapAction: (() -> Void)?
@ -113,56 +104,57 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
}
private func update(transition: ComponentTransition) {
guard let contentData, let params else {
return
}
self.update(params: params, contentData: contentData, transition: transition)
self.requestUpdate?(transition.containedViewLayoutTransition)
}
private func update(params: Params, contentData: ContentData, transition: ComponentTransition) {
let _ = self.title.update(
transition: transition,
component: AnyComponent(ChatTitleComponent(
context: contentData.context,
theme: contentData.theme,
strings: contentData.strings,
dateTimeFormat: contentData.dateTimeFormat,
nameDisplayOrder: contentData.nameDisplayOrder,
displayBackground: true,
content: contentData.content,
activities: self.activities,
networkState: self.networkState,
tapped: { [weak self] in
guard let self else {
return
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let transition = ComponentTransition(transition)
if let contentData = self.contentData {
let titleSize = self.title.update(
transition: transition,
component: AnyComponent(ChatTitleComponent(
context: contentData.context,
theme: contentData.theme,
strings: contentData.strings,
dateTimeFormat: contentData.dateTimeFormat,
nameDisplayOrder: contentData.nameDisplayOrder,
displayBackground: true,
content: contentData.content,
activities: self.activities,
networkState: self.networkState,
tapped: { [weak self] in
guard let self else {
return
}
self.tapAction?()
},
longTapped: { [weak self] in
guard let self else {
return
}
self.longTapAction?()
}
self.tapAction?()
},
longTapped: { [weak self] in
guard let self else {
return
)),
environment: {},
containerSize: availableSize
)
if let titleView = self.title.view {
if titleView.superview == nil {
self.title.parentState = self.parentTitleState
self.parentTitleState._updated = { [weak self] transition, _ in
guard let self else {
return
}
self.requestUpdate?(transition.containedViewLayoutTransition)
}
self.longTapAction?()
self.addSubview(titleView)
}
)),
environment: {},
containerSize: params.size
)
if let titleView = self.title.view {
if titleView.superview == nil {
self.addSubview(titleView)
}
transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(), size: params.size))
}
}
public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
let params = Params(size: size, clearBounds: clearBounds)
if self.params != params {
self.params = params
if let contentData {
self.update(params: params, contentData: contentData, transition: ComponentTransition(transition))
transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(), size: titleSize))
}
return titleSize
} else {
return availableSize
}
}
}
@ -1008,7 +1000,7 @@ public final class ChatTitleComponent: Component {
contentSize.height += subtitleSize.height
let containerSize = CGSize(width: contentSize.width + containerSideInset * 2.0, height: 44.0)
let containerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - containerSize.width) * 0.5), y: floorToScreenPixels((availableSize.height - containerSize.height) * 0.5)), size: containerSize)
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((availableSize.height - containerSize.height) * 0.5)), size: containerSize)
let titleFrame = CGRect(origin: CGPoint(x: titleLeftIconsWidth + floor((containerFrame.width - titleSize.width - titleLeftIconsWidth - titleRightIconsWidth) * 0.5), y: floor((containerFrame.height - contentSize.height) * 0.5)), size: titleSize)
if let titleView = self.title.view {
@ -1132,7 +1124,7 @@ public final class ChatTitleComponent: Component {
self.contentContainer.layer.cornerRadius = 0.0
}
return availableSize
return CGSize(width: containerSize.width, height: availableSize.height)
}
}

View file

@ -193,7 +193,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
public var disableAnimations: Bool = false
var manualLayout: Bool = false
private var validLayout: (CGSize, CGRect)?
private var validLayout: CGSize?
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
private var titleLeftIcon: ChatTitleIcon = .none
private var titleRightIcon: ChatTitleIcon = .none
@ -462,8 +464,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
if !self.updateStatus(enableAnimation: enableAnimation) {
if updated {
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: (self.disableAnimations || !enableAnimation) ? .immediate : .animated(duration: 0.2, curve: .easeInOut))
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: (self.disableAnimations || !enableAnimation) ? .immediate : .animated(duration: 0.2, curve: .easeInOut))
}
}
}
@ -720,8 +722,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
if self.activityNode.transitionToState(state, animation: enableAnimation ? .slide : .none) {
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: enableAnimation ? .animated(duration: 0.3, curve: .spring) : .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: enableAnimation ? .animated(duration: 0.3, curve: .spring) : .immediate)
}
return true
} else {
@ -817,8 +819,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
override public func layoutSubviews() {
super.layoutSubviews()
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
@ -833,17 +835,19 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleContent = titleContent
let _ = self.updateStatus()
if !self.manualLayout, let (size, clearBounds) = self.validLayout {
let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
if !self.manualLayout, let size = self.validLayout {
let _ = self.updateLayout(availableSize: size, transition: .immediate)
}
}
}
public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, clearBounds)
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let size = availableSize
self.button.frame = clearBounds
self.contentContainer.frame = clearBounds
self.validLayout = size
self.button.frame = CGRect(origin: CGPoint(), size: size)
self.contentContainer.frame = CGRect(origin: CGPoint(), size: size)
var leftIconWidth: CGFloat = 0.0
var rightIconWidth: CGFloat = 0.0
@ -997,7 +1001,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
titleInsets.left = verifiedIconWidth
}
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated)
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: size.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated)
titleSize.width += credibilityIconWidth
titleSize.width += verifiedIconWidth
if statusIconWidth > 0.0 {
@ -1007,15 +1011,15 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
}
let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center)
let activitySize = self.activityNode.updateLayout(CGSize(width: size.width - titleSideInset * 2.0, height: size.height), alignment: .center)
let titleInfoSpacing: CGFloat = 0.0
var activityFrame = CGRect()
if activitySize.height.isZero {
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
if titleFrame.size.width < size.width {
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
titleFrame.origin.x = floor((size.width - titleFrame.width) / 2.0)
}
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
@ -1023,12 +1027,12 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
let contentWidth = max(titleSize.width + rightIconWidth, activitySize.width)
var contentX = floor((clearBounds.width - contentWidth) / 2.0)
contentX = max(contentX, clearBounds.minX + 20.0)
var contentX = floor((size.width - contentWidth) / 2.0)
contentX = max(contentX, 20.0)
titleFrame = CGRect(origin: CGPoint(x: contentX + floor((contentWidth - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth)
titleFrame.origin.x = max(titleFrame.origin.x, leftIconWidth)
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
@ -1068,6 +1072,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
let componentTransition = ComponentTransition(transition)
componentTransition.setFrame(view: self.backgroundView, frame: backgroundFrame)
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: false, transition: componentTransition)
return availableSize
}
@objc private func buttonPressed() {

View file

@ -44,7 +44,6 @@ swift_library(
"//submodules/TelegramUI/Components/ListComposePollOptionComponent",
"//submodules/ComposePollUI",
"//submodules/Markdown",
"//submodules/TelegramUI/Components/EdgeEffect",
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
],
visibility = [

View file

@ -29,24 +29,26 @@ import TextFieldComponent
import ListComposePollOptionComponent
import Markdown
import PresentationDataUtils
import EdgeEffect
import GlassBarButtonComponent
final class ComposeTodoScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let peer: EnginePeer
let initialData: ComposeTodoScreen.InitialData
let completion: (TelegramMediaTodo) -> Void
init(
context: AccountContext,
overNavigationContainer: UIView,
peer: EnginePeer,
initialData: ComposeTodoScreen.InitialData,
completion: @escaping (TelegramMediaTodo) -> Void
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.peer = peer
self.initialData = initialData
self.completion = completion
@ -69,7 +71,6 @@ final class ComposeTodoScreenComponent: Component {
final class View: UIView, UIScrollViewDelegate {
private let scrollView: UIScrollView
private let edgeEffectView: EdgeEffectView
private let todoTextSection = ComponentView<Empty>()
@ -131,8 +132,6 @@ final class ComposeTodoScreenComponent: Component {
self.scrollView.contentInsetAdjustmentBehavior = .never
self.scrollView.alwaysBounceVertical = true
self.edgeEffectView = EdgeEffectView()
self.todoItemsSectionContainer = ListSectionContentView(frame: CGRect())
self.todoItemsSectionContainer.automaticallyLayoutExternalContentBackgroundView = false
@ -141,8 +140,6 @@ final class ComposeTodoScreenComponent: Component {
self.scrollView.delegate = self
self.addSubview(self.scrollView)
self.addSubview(self.edgeEffectView)
let reorderRecognizer = ReorderGestureRecognizer(
shouldBegin: { [weak self] point in
guard let self, let (id, item) = self.item(at: point) else {
@ -1538,11 +1535,6 @@ final class ComposeTodoScreenComponent: Component {
}
}
}
let edgeEffectHeight: CGFloat = 80.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition)
let title: String
if !component.initialData.canEdit && component.initialData.existingTodo != nil {
@ -1570,19 +1562,19 @@ final class ComposeTodoScreenComponent: Component {
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((environment.navigationHeight - titleSize.height) / 2.0) + 3.0), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
self.addSubview(titleView)
component.overNavigationContainer.addSubview(titleView)
}
transition.setFrame(view: titleView, frame: titleFrame)
}
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
let cancelButtonSize = self.cancelButton.update(
transition: transition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: environment.theme.overallDarkAppearance,
state: .generic,
state: .glass,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",
@ -1602,7 +1594,7 @@ final class ComposeTodoScreenComponent: Component {
let cancelButtonFrame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: 16.0), size: cancelButtonSize)
if let cancelButtonView = self.cancelButton.view {
if cancelButtonView.superview == nil {
self.addSubview(cancelButtonView)
component.overNavigationContainer.addSubview(cancelButtonView)
}
transition.setFrame(view: cancelButtonView, frame: cancelButtonFrame)
}
@ -1638,7 +1630,7 @@ final class ComposeTodoScreenComponent: Component {
let doneButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - doneButtonSize.width, y: 16.0), size: doneButtonSize)
if let doneButtonView = self.doneButton.view {
if doneButtonView.superview == nil {
self.addSubview(doneButtonView)
component.overNavigationContainer.addSubview(doneButtonView)
}
transition.setFrame(view: doneButtonView, frame: doneButtonFrame)
}
@ -1703,6 +1695,8 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont
fileprivate let completion: (TelegramMediaTodo) -> Void
private var isDismissed: Bool = false
private let overNavigationContainer: UIView
fileprivate private(set) var sendButtonItem: UIBarButtonItem?
public var isMinimized: Bool = false
@ -1746,12 +1740,15 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont
self.context = context
self.completion = completion
self.overNavigationContainer = SparseContainerView()
super.init(context: context, component: ComposeTodoScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
peer: peer,
initialData: initialData,
completion: completion
), navigationBarAppearance: .transparent, theme: .default)
), navigationBarAppearance: .default, theme: .default)
self._hasGlassStyle = true
@ -1786,6 +1783,10 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont
return componentView.attemptNavigation(complete: complete)
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {

View file

@ -28,7 +28,7 @@ public class EdgeEffectView: UIView {
fatalError("init(coder:) has not been implemented")
}
public func update(content: UIColor, blur: Bool = false, alpha: CGFloat = 0.65, rect: CGRect, edge: Edge, edgeSize: CGFloat, transition: ComponentTransition) {
public func update(content: UIColor, blur: Bool = false, alpha: CGFloat = 0.75, rect: CGRect, edge: Edge, edgeSize: CGFloat, transition: ComponentTransition) {
transition.setBackgroundColor(view: self.contentView, color: content)
switch edge {

View file

@ -1012,7 +1012,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
isHiddenUpdatedImpl?(isHidden)
}, openPremium: {
openPremiumImpl?()
}), navigationBarAppearance: .transparent)
}), navigationBarAppearance: .default)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let title: String

View file

@ -34,6 +34,7 @@ final class GiftOptionsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let starsContext: StarsContext
let peerId: EnginePeer.Id
let premiumOptions: [CachedPremiumGiftOption]
@ -42,6 +43,7 @@ final class GiftOptionsScreenComponent: Component {
init(
context: AccountContext,
overNavigationContainer: UIView,
starsContext: StarsContext,
peerId: EnginePeer.Id,
premiumOptions: [CachedPremiumGiftOption],
@ -49,6 +51,7 @@ final class GiftOptionsScreenComponent: Component {
completion: (() -> Void)?
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.starsContext = starsContext
self.peerId = peerId
self.premiumOptions = premiumOptions
@ -1144,14 +1147,14 @@ final class GiftOptionsScreenComponent: Component {
}
if isGlass {
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
let cancelButtonSize = self.cancelButton.update(
transition: transition,
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: theme.overallDarkAppearance,
state: .generic,
state: .glass,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",
@ -1922,6 +1925,8 @@ final class GiftOptionsScreenComponent: Component {
open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScreenProtocol {
private let context: AccountContext
private let overNavigationContainer: UIView
public var parentController: () -> ViewController? = {
return nil
}
@ -1936,8 +1941,11 @@ open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScree
) {
self.context = context
self.overNavigationContainer = SparseContainerView()
super.init(context: context, component: GiftOptionsScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
starsContext: starsContext,
peerId: peerId,
premiumOptions: premiumOptions,
@ -1954,6 +1962,10 @@ open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScree
}
componentView.scrollToTop()
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {

View file

@ -46,6 +46,7 @@ swift_library(
"//submodules/TelegramUI/Components/LottieComponent",
"//submodules/TelegramUI/Components/TextFieldComponent",
"//submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView",
"//submodules/TelegramUI/Components/EdgeEffect",
],
visibility = [
"//visibility:public",

View file

@ -27,6 +27,7 @@ import UndoUI
import ContextUI
import LottieComponent
import GiftLoadingShimmerView
import EdgeEffect
private let minimumCountToDisplayFilters = 18
@ -34,17 +35,20 @@ final class GiftStoreScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let starsContext: StarsContext
let peerId: EnginePeer.Id
let gift: StarGift.Gift
init(
context: AccountContext,
overNavigationContainer: UIView,
starsContext: StarsContext,
peerId: EnginePeer.Id,
gift: StarGift.Gift
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.starsContext = starsContext
self.peerId = peerId
self.gift = gift
@ -77,8 +81,7 @@ final class GiftStoreScreenComponent: Component {
private let emptyResultsTitle = ComponentView<Empty>()
private let clearFilters = ComponentView<Empty>()
private let topPanel = ComponentView<Empty>()
private let topSeparator = ComponentView<Empty>()
private let edgeEffectView: EdgeEffectView
private let cancelButton = ComponentView<Empty>()
private let sortButton = ComponentView<Empty>()
@ -120,12 +123,16 @@ final class GiftStoreScreenComponent: Component {
self.loadingView = GiftLoadingShimmerView()
self.edgeEffectView = EdgeEffectView()
super.init(frame: frame)
self.scrollView.delegate = self
self.addSubview(self.scrollView)
self.addSubview(self.loadingView)
self.addSubview(self.edgeEffectView)
self.scrollView.layer.addSublayer(self.topOverscrollLayer)
}
@ -169,13 +176,6 @@ final class GiftStoreScreenComponent: Component {
let availableWidth = self.scrollView.bounds.width
let availableHeight = self.scrollView.bounds.height
let contentOffset = self.scrollView.contentOffset.y
let topPanelAlpha = min(20.0, max(0.0, contentOffset)) / 20.0
if let topPanelView = self.topPanel.view, let topSeparator = self.topSeparator.view {
transition.setAlpha(view: topPanelView, alpha: topPanelAlpha)
transition.setAlpha(view: topSeparator, alpha: topPanelAlpha)
}
var topInset = environment.navigationHeight + 39.0
if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters {
@ -893,37 +893,12 @@ final class GiftStoreScreenComponent: Component {
if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters {
topPanelHeight = environment.navigationHeight
}
let topPanelSize = self.topPanel.update(
transition: transition,
component: AnyComponent(BlurredBackgroundComponent(
color: theme.rootController.navigationBar.blurredBackgroundColor
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: topPanelHeight)
)
let topSeparatorSize = self.topSeparator.update(
transition: transition,
component: AnyComponent(Rectangle(
color: theme.rootController.navigationBar.separatorColor
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: UIScreenPixel)
)
let topPanelFrame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: topPanelSize.height))
let topSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelSize.height), size: CGSize(width: topSeparatorSize.width, height: topSeparatorSize.height))
if let topPanelView = self.topPanel.view, let topSeparatorView = self.topSeparator.view {
if topPanelView.superview == nil {
topPanelView.alpha = 0.0
topSeparatorView.alpha = 0.0
self.addSubview(topPanelView)
self.addSubview(topSeparatorView)
}
transition.setFrame(view: topPanelView, frame: topPanelFrame)
transition.setFrame(view: topSeparatorView, frame: topSeparatorFrame)
}
let edgeEffectHeight: CGFloat = environment.navigationHeight + 56.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: min(30, edgeEffectFrame.height), transition: transition)
let balanceTitleSize = self.balanceTitle.update(
transition: .immediate,
@ -962,12 +937,12 @@ final class GiftStoreScreenComponent: Component {
if let balanceTitleView = self.balanceTitle.view, let balanceValueView = self.balanceValue.view, let balanceIconView = self.balanceIcon.view {
if balanceTitleView.superview == nil {
self.addSubview(balanceTitleView)
self.addSubview(balanceValueView)
self.addSubview(balanceIconView)
component.overNavigationContainer.addSubview(balanceTitleView)
component.overNavigationContainer.addSubview(balanceValueView)
component.overNavigationContainer.addSubview(balanceIconView)
}
let navigationHeight = environment.navigationHeight - environment.statusBarHeight
let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - balanceTitleSize.height - balanceValueSize.height) / 2.0
let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - balanceTitleSize.height - balanceValueSize.height) / 2.0 + 3.0
balanceTitleView.center = CGPoint(x: availableSize.width - 16.0 - environment.safeInsets.right - balanceTitleSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height / 2.0)
balanceTitleView.bounds = CGRect(origin: .zero, size: balanceTitleSize)
balanceValueView.center = CGPoint(x: availableSize.width - 16.0 - environment.safeInsets.right - balanceValueSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0)
@ -991,9 +966,9 @@ final class GiftStoreScreenComponent: Component {
)
if let titleView = self.title.view {
if titleView.superview == nil {
self.addSubview(titleView)
component.overNavigationContainer.addSubview(titleView)
}
transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: topInset + 10.0), size: titleSize))
transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: topInset + 22.0), size: titleSize))
}
let effectiveCount: Int32
@ -1018,10 +993,10 @@ final class GiftStoreScreenComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: 100.0)
)
let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) / 2.0), y: topInset + 31.0), size: subtitleSize)
let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) / 2.0), y: topInset + 43.0), size: subtitleSize)
if let subtitleView = self.subtitle.view {
if subtitleView.superview == nil {
self.addSubview(subtitleView)
component.overNavigationContainer.addSubview(subtitleView)
}
transition.setFrame(view: subtitleView, frame: subtitleFrame)
}
@ -1157,9 +1132,9 @@ final class GiftStoreScreenComponent: Component {
if let filterSelectorView = self.filterSelector.view {
if filterSelectorView.superview == nil {
filterSelectorView.alpha = 0.0
self.addSubview(filterSelectorView)
component.overNavigationContainer.addSubview(filterSelectorView)
}
transition.setFrame(view: filterSelectorView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - filterSize.width) / 2.0), y: topInset + 56.0), size: filterSize))
transition.setFrame(view: filterSelectorView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - filterSize.width) / 2.0), y: topInset + 60.0 + 12.0), size: filterSize))
if let initialCount = self.initialCount, initialCount >= minimumCountToDisplayFilters {
loadingTransition.setAlpha(view: filterSelectorView, alpha: 1.0)
@ -1281,6 +1256,8 @@ final class GiftStoreScreenComponent: Component {
public class GiftStoreScreen: ViewControllerComponentContainer {
private let context: AccountContext
private let overNavigationContainer: UIView
public var parentController: () -> ViewController? = {
return nil
}
@ -1293,8 +1270,11 @@ public class GiftStoreScreen: ViewControllerComponentContainer {
) {
self.context = context
self.overNavigationContainer = SparseContainerView()
super.init(context: context, component: GiftStoreScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
starsContext: starsContext,
peerId: peerId,
gift: gift
@ -1308,6 +1288,10 @@ public class GiftStoreScreen: ViewControllerComponentContainer {
}
componentView.scrollToTop()
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {

View file

@ -504,7 +504,11 @@ public class GlassBackgroundView: UIView {
let glassEffectValue = UIGlassEffect(style: .regular)
switch tintColor.kind {
case .panel:
glassEffectValue.tintColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.1)
if isDark {
glassEffectValue.tintColor = UIColor(white: 1.0, alpha: 0.1)
} else {
glassEffectValue.tintColor = UIColor(white: 1.0, alpha: 0.1)
}
case .custom:
glassEffectValue.tintColor = tintColor.color
}

View file

@ -60,8 +60,8 @@ public final class GlassBarButtonComponent: Component {
return true
}
public final class View: HighlightTrackingButton {
private let containerView: UIView
public final class View: UIView {
private let containerView: HighlightTrackingButton
private let genericContainerView: UIView
private let genericBackgroundView: SimpleGlassView
private let glassContainerView: UIView
@ -71,25 +71,23 @@ public final class GlassBarButtonComponent: Component {
private var component: GlassBarButtonComponent?
public override init(frame: CGRect) {
self.containerView = UIView()
self.containerView = HighlightTrackingButton()
self.genericContainerView = UIView()
self.genericBackgroundView = SimpleGlassView()
self.glassContainerView = UIView()
super.init(frame: frame)
self.containerView.isUserInteractionEnabled = false
self.containerView.layer.rasterizationScale = UIScreenScale
self.addSubview(self.containerView)
self.containerView.addSubview(self.genericContainerView)
self.containerView.addSubview(self.glassContainerView)
self.addSubview(self.genericContainerView)
self.addSubview(self.glassContainerView)
self.genericContainerView.addSubview(self.genericBackgroundView)
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.containerView.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.highligthedChanged = { [weak self] highlighted in
self.containerView.highligthedChanged = { [weak self] highlighted in
guard let self else {
return
}
@ -112,11 +110,27 @@ public final class GlassBarButtonComponent: Component {
action(self)
}
@objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
guard let component = self.component, let action = component.action else {
return
}
action(self)
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let result = super.hitTest(point, with: event) else {
return nil
}
return result
}
func update(component: GlassBarButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
let previousComponent = self.component
self.component = component
self.isEnabled = component.isEnabled
self.containerView.isEnabled = component.isEnabled
var componentView: ComponentView<Empty>
var animateAppearance = false
@ -159,6 +173,7 @@ public final class GlassBarButtonComponent: Component {
let componentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((containerSize.width - componentSize.width) / 2.0), y: floorToScreenPixels((containerSize.height - componentSize.height) / 2.0)), size: componentSize)
if let view = componentView.view {
if view.superview == nil {
view.isUserInteractionEnabled = false
self.containerView.addSubview(view)
if animateAppearance {
transition.animateScale(view: view, from: 0.01, to: 1.0)
@ -213,9 +228,29 @@ public final class GlassBarButtonComponent: Component {
self.glassContainerView.addSubview(glassBackgroundView)
self.glassBackgroundView = glassBackgroundView
glassBackgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
transition.animateAlpha(view: glassBackgroundView, from: 0.0, to: 1.0)
}
glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: effectiveState == .tintedGlass ? .custom : .panel , color: backgroundColor.withMultipliedAlpha(effectiveState == .tintedGlass ? 1.0 : 0.7)), transition: glassBackgroundTransition)
glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: effectiveState == .tintedGlass ? .custom : .panel , color: backgroundColor.withMultipliedAlpha(effectiveState == .tintedGlass ? 1.0 : 0.7)), isInteractive: true, transition: glassBackgroundTransition)
glassBackgroundTransition.setFrame(view: glassBackgroundView, frame: bounds)
} else if case .glass = component.state {
let glassBackgroundView: GlassBackgroundView
var glassBackgroundTransition = transition
if let current = self.glassBackgroundView {
glassBackgroundView = current
} else {
glassBackgroundTransition = .immediate
glassBackgroundView = GlassBackgroundView()
glassBackgroundView.isUserInteractionEnabled = false
self.glassContainerView.addSubview(glassBackgroundView)
self.glassBackgroundView = glassBackgroundView
glassBackgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
transition.animateAlpha(view: glassBackgroundView, from: 0.0, to: 1.0)
}
glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: .panel, color: UIColor(white: component.isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: glassBackgroundTransition)
glassBackgroundTransition.setFrame(view: glassBackgroundView, frame: bounds)
} else if let glassBackgroundView = self.glassBackgroundView {
self.glassBackgroundView = nil
@ -224,6 +259,15 @@ public final class GlassBarButtonComponent: Component {
})
}
if let glassBackgroundView = self.glassBackgroundView {
self.containerView.isUserInteractionEnabled = false
if self.containerView.superview !== glassBackgroundView.contentView {
glassBackgroundView.contentView.addSubview(self.containerView)
}
} else if self.containerView.superview !== self {
self.addSubview(self.containerView)
}
return containerSize
}
}

View file

@ -48,7 +48,7 @@ final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, I
didSet {
if self.activity != oldValue {
if let params = self.params {
self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate)
}
}
}
@ -140,7 +140,7 @@ final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, I
return 60.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
self.params = Params(size: size, leftInset: leftInset, rightInset: rightInset)
let transition = ComponentTransition(transition)
@ -216,6 +216,8 @@ final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, I
transition.setFrame(view: self.close.background, frame: closeFrame)
self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
return size
}
func activate() {

View file

@ -5,6 +5,10 @@ import ComponentFlow
import TelegramPresentationData
import GlassBackgroundComponent
public protocol HeaderPanelContainerChildView: UIView {
func setOverlayContainerView(overlayContainerView: UIView)
}
public final class HeaderPanelContainerComponent: Component {
public final class Panel: Equatable {
public let key: AnyHashable
@ -94,6 +98,7 @@ public final class HeaderPanelContainerComponent: Component {
self.backgroundContainer = GlassBackgroundContainerView()
self.backgroundView = GlassBackgroundView()
self.contentContainer = UIView()
self.contentContainer.clipsToBounds = true
super.init(frame: frame)
@ -142,6 +147,9 @@ public final class HeaderPanelContainerComponent: Component {
if let tabsComponentView = tabsView.view {
if tabsComponentView.superview == nil {
self.contentContainer.addSubview(tabsComponentView)
if let tabsComponentView = tabsComponentView as? HeaderPanelContainerChildView {
tabsComponentView.setOverlayContainerView(overlayContainerView: self.backgroundContainer.contentView)
}
transition.animateAlpha(view: tabsComponentView, from: 0.0, to: 1.0)
}
tabsTransition.setFrame(view: tabsComponentView, frame: tabsFrame)

View file

@ -1,8 +1,8 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatListTabsComponent",
module_name = "ChatListTabsComponent",
name = "HorizontalTabsComponent",
module_name = "HorizontalTabsComponent",
srcs = glob([
"Sources/**/*.swift",
]),
@ -20,7 +20,8 @@ swift_library(
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/Components/MultilineTextWithEntitiesComponent",
"//submodules/TelegramUI/Components/LiquidLens",
"//submodules/TelegramUI/Components/TextBadgeComponent"
"//submodules/TelegramUI/Components/TextBadgeComponent",
"//submodules/TelegramUI/Components/HeaderPanelContainerComponent",
],
visibility = [
"//visibility:public",

View file

@ -68,14 +68,16 @@ public final class LiquidLensView: UIView {
var size: CGSize
var selectionX: CGFloat
var selectionWidth: CGFloat
var inset: CGFloat
var isDark: Bool
var isLifted: Bool
var isCollapsed: Bool
init(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool) {
init(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, inset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool) {
self.size = size
self.selectionX = selectionX
self.selectionWidth = selectionWidth
self.inset = inset
self.isLifted = isLifted
self.isDark = isDark
self.isCollapsed = isCollapsed
@ -84,10 +86,12 @@ public final class LiquidLensView: UIView {
private struct LensParams: Equatable {
var baseFrame: CGRect
var inset: CGFloat
var isLifted: Bool
init(baseFrame: CGRect, isLifted: Bool) {
init(baseFrame: CGRect, inset: CGFloat, isLifted: Bool) {
self.baseFrame = baseFrame
self.inset = inset
self.isLifted = isLifted
}
}
@ -124,6 +128,15 @@ public final class LiquidLensView: UIView {
public var selectionWidth: CGFloat? {
return self.params?.selectionWidth
}
public private(set) var isAnimating: Bool = false {
didSet {
if self.isAnimating != oldValue {
self.onUpdatedIsAnimating?(self.isAnimating)
}
}
}
public var onUpdatedIsAnimating: ((Bool) -> Void)?
public init(kind: Kind) {
self.containerView = UIView()
@ -185,10 +198,7 @@ public final class LiquidLensView: UIView {
}
lensView.layer.zPosition = 10.0
if case .noContainer = kind {
} else {
self.liftedContainerView.addSubview(self.restingBackgroundView)
}
self.liftedContainerView.addSubview(self.restingBackgroundView)
self.containerView.addSubview(self.liftedContainerView)
self.containerView.addSubview(lensView)
@ -199,11 +209,8 @@ public final class LiquidLensView: UIView {
} else if let genericBackgroundContainer = self.genericBackgroundContainer {
lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: genericBackgroundContainer)
}
if case .noContainer = kind {
} else {
lensView.perform(NSSelectorFromString("setLiftedContentView:"), with: self.liftedContainerView)
lensView.perform(NSSelectorFromString("setOverridePunchoutView:"), with: self.contentView)
}
lensView.perform(NSSelectorFromString("setLiftedContentView:"), with: self.liftedContainerView)
lensView.perform(NSSelectorFromString("setOverridePunchoutView:"), with: self.contentView)
do {
let selector = NSSelectorFromString("setLiftedContentMode:")
@ -271,9 +278,16 @@ public final class LiquidLensView: UIView {
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setLiftedContainer(view: UIView) {
guard let lensView = self.lensView else {
return
}
lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: view)
}
public func update(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool = false, transition: ComponentTransition) {
let params = Params(size: size, selectionX: selectionX, selectionWidth: selectionWidth, isDark: isDark, isLifted: isLifted, isCollapsed: isCollapsed)
public func update(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, inset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool = false, transition: ComponentTransition) {
let params = Params(size: size, selectionX: selectionX, selectionWidth: selectionWidth, inset: inset, isDark: isDark, isLifted: isLifted, isCollapsed: isCollapsed)
if self.params == params {
return
}
@ -287,7 +301,7 @@ public final class LiquidLensView: UIView {
self.update(params: params, transition: transition)
}
private func updateLens(params: LensParams, animated: Bool) {
private func updateLens(params: LensParams, transition: ComponentTransition) {
guard let lensView = self.lensView else {
return
}
@ -298,22 +312,23 @@ public final class LiquidLensView: UIView {
}
self.isApplyingLensParams = true
let previousParams = self.appliedLensParams
let transition: ComponentTransition = animated ? .easeInOut(duration: 0.3) : .immediate
self.appliedLensParams = params
if previousParams?.isLifted != params.isLifted {
self.isAnimating = true
let selector = NSSelectorFromString("setLifted:animated:alongsideAnimations:completion:")
var shouldScheduleUpdate = false
var didProcessUpdate = false
self.pendingLensParams = params
if let lensView = self.lensView, let method = lensView.method(for: selector) {
typealias ObjCMethod = @convention(c) (AnyObject, Selector, Bool, Bool, @escaping () -> Void, AnyObject?) -> Void
typealias ObjCMethod = @convention(c) (AnyObject, Selector, Bool, Bool, @escaping () -> Void, (() -> Void)?) -> Void
let function = unsafeBitCast(method, to: ObjCMethod.self)
function(lensView, selector, params.isLifted, !transition.animation.isImmediate, { [weak self] in
guard let self else {
return
}
let liftedInset: CGFloat = params.isLifted ? 4.0 : -4.0
let liftedInset: CGFloat = params.isLifted ? 4.0 : -params.inset
lensView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: params.baseFrame.width + liftedInset * 2.0, height: params.baseFrame.height + liftedInset * 2.0))
didProcessUpdate = true
if shouldScheduleUpdate {
@ -323,10 +338,17 @@ public final class LiquidLensView: UIView {
}
self.isApplyingLensParams = false
self.pendingLensParams = nil
self.updateLens(params: pendingLensParams, animated: !transition.animation.isImmediate)
self.updateLens(params: pendingLensParams, transition: transition)
}
}
}, nil)
}, { [weak self] in
guard let self else {
return
}
if !self.isApplyingLensParams {
self.isAnimating = false
}
})
}
if didProcessUpdate {
transition.animateView {
@ -338,11 +360,32 @@ public final class LiquidLensView: UIView {
shouldScheduleUpdate = true
}
} else {
let liftedInset: CGFloat = params.isLifted ? 4.0 : -params.inset
let lensBounds = CGRect(origin: CGPoint(), size: CGSize(width: params.baseFrame.width + liftedInset * 2.0, height: params.baseFrame.height + liftedInset * 2.0))
let lensCenter = CGPoint(x: params.baseFrame.midX, y: params.baseFrame.midY)
let previousBounds: CGRect = lensView.bounds
transition.animateView {
let liftedInset: CGFloat = params.isLifted ? 4.0 : -4.0
lensView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: params.baseFrame.width + liftedInset * 2.0, height: params.baseFrame.height + liftedInset * 2.0))
lensView.center = CGPoint(x: params.baseFrame.midX, y: params.baseFrame.midY)
lensView.bounds = lensBounds
}
lensView.layer.removeAllAnimations()
lensView.bounds = lensBounds
if !transition.animation.isImmediate {
self.isAnimating = true
}
transition.setPosition(view: lensView, position: lensCenter, completion: { [weak self] flag in
guard let self, flag else {
return
}
if !self.isApplyingLensParams {
self.isAnimating = false
}
})
// No idea why
transition.animatePosition(layer: lensView.layer, from: CGPoint(x: (lensBounds.width - previousBounds.width) * 0.5, y: 0.0), to: CGPoint(), additive: true)
self.isApplyingLensParams = false
}
}
@ -402,13 +445,13 @@ public final class LiquidLensView: UIView {
}
let baseLensFrame = CGRect(origin: CGPoint(x: params.selectionX, y: 0.0), size: CGSize(width: params.selectionWidth, height: params.size.height))
self.updateLens(params: LensParams(baseFrame: baseLensFrame, isLifted: params.isLifted), animated: !transition.animation.isImmediate)
self.updateLens(params: LensParams(baseFrame: baseLensFrame, inset: params.inset, isLifted: params.isLifted), transition: transition)
if let legacyContentMaskView = self.legacyContentMaskView {
transition.setFrame(view: legacyContentMaskView, frame: CGRect(origin: CGPoint(), size: params.size))
}
if let legacyContentMaskBlobView = self.legacyContentMaskBlobView, let legacyLiftedContentBlobMaskView = self.legacyLiftedContentBlobMaskView, let legacySelectionView = self.legacySelectionView {
let lensFrame = baseLensFrame.insetBy(dx: 4.0, dy: 4.0)
let lensFrame = baseLensFrame.insetBy(dx: params.inset, dy: params.inset)
let effectiveLensFrame = lensFrame.insetBy(dx: params.isLifted ? -2.0 : 0.0, dy: params.isLifted ? -2.0 : 0.0)
if legacyContentMaskBlobView.image?.size.height != lensFrame.height {

View file

@ -20,7 +20,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)?
private var requestedLayout: Bool = false
public var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
public var requestContainerLayout: ((ContainedViewLayoutTransition) -> Void)?
public var backPressed: () -> () = { }
@ -102,10 +102,45 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
self.titleView = item.titleView
self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] titleView in
if let strongSelf = self {
strongSelf.titleView = titleView
let itemTitleView = item.titleView
if self.titleView !== itemTitleView {
if let oldTitleView = self.titleView as? NavigationBarTitleView {
oldTitleView.requestUpdate = nil
}
self.titleView = itemTitleView
if let titleView = self.titleView as? NavigationBarTitleView {
titleView.requestUpdate = { [weak self, weak titleView] transition in
guard let self, let titleView, self.titleView === titleView else {
return
}
if let requestContainerLayout = self.requestContainerLayout {
requestContainerLayout(transition)
} else {
self.requestLayout()
}
}
}
}
self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] itemTitleView in
guard let self else {
return
}
if let oldTitleView = self.titleView as? NavigationBarTitleView {
oldTitleView.requestUpdate = nil
}
self.titleView = itemTitleView
if let titleView = self.titleView as? NavigationBarTitleView {
titleView.requestUpdate = { [weak self, weak titleView] transition in
guard let self, let titleView, self.titleView === titleView else {
return
}
if let requestContainerLayout = self.requestContainerLayout {
requestContainerLayout(transition)
} else {
self.requestLayout()
}
}
}
}
@ -470,8 +505,12 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
self.rightButtonNodeUpdated = true
if !items.isEmpty {
self.rightButtonNodeImpl.updateItems([], animated: animated)
self.rightButtonNodeImpl.updateItems(items, animated: animated)
if self.rightButtonNodeImpl.isEmpty {
self.rightButtonNodeImpl.updateItems(items, animated: false)
} else {
self.rightButtonNodeImpl.updateItems([], animated: animated)
self.rightButtonNodeImpl.updateItems(items, animated: animated)
}
if self.rightButtonNodeImpl.view.superview == nil {
if let rightButtonsBackgroundView = self.rightButtonsBackgroundView {
rightButtonsBackgroundView.contentView.addSubview(self.rightButtonNodeImpl.view)
@ -490,6 +529,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
self.rightButtonNodeImpl.view.removeFromSuperview()
self.rightButtonNodeImpl.updateItems([], animated: false)
}
} else {
if animated, self.rightButtonNodeImpl.view.superview != nil {
@ -502,6 +542,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
self.rightButtonNodeImpl.view.removeFromSuperview()
self.rightButtonNodeImpl.updateItems([], animated: false)
}
self.updateAccessibilityElements()
@ -548,7 +589,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
public var customOverBackgroundContentSubview: UIView?
public let customOverBackgroundContentView: UIView
public init(presentationData: NavigationBarPresentationData) {
self.presentationData = presentationData
@ -605,6 +646,8 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
self.secondaryContentHeight = NavigationBarImpl.defaultSecondaryContentHeight
self.customOverBackgroundContentView = SparseContainerView()
super.init()
if case .glass = presentationData.theme.style {
@ -616,6 +659,8 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
self.backgroundContainer = backgroundContainer
self.view.addSubview(backgroundContainer)
backgroundContainer.contentView.addSubview(self.customOverBackgroundContentView)
let leftButtonsBackgroundView = GlassBackgroundView()
self.leftButtonsBackgroundView = leftButtonsBackgroundView
backgroundContainer.contentView.addSubview(leftButtonsBackgroundView)
@ -625,6 +670,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
backgroundContainer.contentView.addSubview(rightButtonsBackgroundView)
} else {
self.addSubnode(self.backgroundNode)
self.view.addSubview(self.customOverBackgroundContentView)
}
self.addSubnode(self.buttonsContainerNode)
self.addSubnode(self.clippingNode)
@ -786,10 +832,13 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
case .replacement:
expansionHeight = contentNode.height - defaultHeight
if case .glass = self.presentationData.theme.style {
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOrigin), size: CGSize(width: size.width, height: defaultHeight))
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOrigin), size: CGSize(width: size.width, height: contentNode.nominalHeight))
} else {
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - additionalContentHeight))
}
transition.updateFrame(node: contentNode, frame: contentNodeFrame)
let _ = contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
case .expansion:
expansionHeight = contentNode.height
@ -803,22 +852,23 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
contentNodeFrame.origin.y += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction
}
}
transition.updateFrame(node: contentNode, frame: contentNodeFrame)
let _ = contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
}
transition.updateFrame(node: contentNode, frame: contentNodeFrame)
contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
}
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel))
let nominalHeight: CGFloat = 60.0
var leftTitleInset: CGFloat = leftInset + 1.0
var rightTitleInset: CGFloat = rightInset + 1.0
var leftTitleInset: CGFloat = leftInset
var rightTitleInset: CGFloat = rightInset
var leftButtonsWidth: CGFloat = 0.0
if self.backButtonNodeImpl.view.superview != nil {
let backButtonSize = self.backButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: 44.0), isLandscape: isLandscape, isLeftAligned: true)
leftTitleInset = backButtonSize.width + backButtonInset + 1.0
leftTitleInset = backButtonSize.width + backButtonInset
if case .glass = self.presentationData.theme.style {
} else {
@ -892,7 +942,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
if case .glass = self.presentationData.theme.style {
transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((44.0 - rightButtonSize.height) / 2.0)), size: rightButtonSize))
} else {
rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0
rightTitleInset = rightButtonSize.width + leftButtonInset + 4.0
transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize))
}
}
@ -900,7 +950,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
if let leftButtonsBackgroundView = self.leftButtonsBackgroundView {
if leftButtonsWidth != 0.0 {
leftTitleInset = leftInset + 16.0 + leftButtonsWidth + 4.0
leftTitleInset = leftInset + 16.0 + leftButtonsWidth + 10.0
}
if self.backButtonNodeImpl.view.superview != nil {
@ -921,7 +971,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
if let rightButtonsBackgroundView = self.rightButtonsBackgroundView {
if rightButtonsWidth != 0.0 {
rightTitleInset = rightInset + 16.0 + rightButtonsWidth + 4.0
rightTitleInset = rightInset + 16.0 + rightButtonsWidth + 10.0
let rightButtonsBackgroundFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 16.0 - rightButtonsWidth, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: rightButtonsWidth, height: 44.0))
var rightButtonsBackgroundTransition = ComponentTransition(transition)
@ -937,11 +987,6 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
}
leftTitleInset = floor(leftTitleInset)
if Int(leftTitleInset) % 2 != 0 {
leftTitleInset -= 1.0
}
if self.titleNode.view.superview != nil {
let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight))
@ -958,31 +1003,30 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
if let titleView = self.titleView {
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
var titleViewTransition = transition
if titleView.frame.isEmpty {
titleViewTransition = .immediate
titleView.frame = titleFrame
}
titleViewTransition.updateFrame(view: titleView, frame: titleFrame)
if let titleView = titleView as? NavigationBarTitleView {
let titleWidth = size.width - (leftTitleInset > 0.0 ? leftTitleInset : rightTitleInset) - (rightTitleInset > 0.0 ? rightTitleInset : leftTitleInset)
titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: titleViewTransition)
}
do {
if self.hintAnimateTitleNodeOnNextLayout {
self.hintAnimateTitleNodeOnNextLayout = false
if let titleView = titleView as? NavigationBarTitleView {
titleView.animateLayoutTransition()
}
var titleViewTransition = transition
if titleView.frame.isEmpty {
titleViewTransition = .immediate
}
titleView.alpha = 1.0
transition.updateFrame(view: titleView, frame: titleFrame)
let titleSize = titleView.updateLayout(availableSize: CGSize(width: size.width - leftTitleInset - rightTitleInset, height: nominalHeight), transition: titleViewTransition)
var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) * 0.5), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
if titleFrame.origin.x < leftTitleInset {
titleFrame.origin.x = leftTitleInset + floorToScreenPixels((size.width - leftTitleInset - rightTitleInset - titleFrame.width) * 0.5)
}
titleViewTransition.updateFrame(view: titleView, frame: titleFrame)
} else {
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
var titleViewTransition = transition
if titleView.frame.isEmpty {
titleViewTransition = .immediate
titleView.frame = titleFrame
}
titleViewTransition.updateFrame(view: titleView, frame: titleFrame)
}
}
}
@ -1038,7 +1082,10 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
self.contentNode = contentNode
self.contentNode?.requestContainerLayout = { [weak self] transition in
self?.requestContainerLayout(transition)
guard let self else {
return
}
self.requestContainerLayout?(transition)
}
if let contentNode {
contentNode.layer.removeAnimation(forKey: "opacity")
@ -1144,6 +1191,13 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
}
guard let result = super.hitTest(point, with: event) else {
if !self.bounds.contains(point) {
if let result = self.customOverBackgroundContentView.hitTest(self.view.convert(point, to: self.customOverBackgroundContentView), with: event) {
if result !== self.backgroundContainer?.contentView {
return result
}
}
}
return nil
}

View file

@ -64,13 +64,16 @@ final class AffiliateProgramSetupScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let initialContent: AffiliateProgramSetupScreen.Content
init(
context: AccountContext,
overNavigationContainer: UIView,
initialContent: AffiliateProgramSetupScreen.Content
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.initialContent = initialContent
}
@ -93,6 +96,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
private let coinIcon = ComponentView<Empty>()
private let title = ComponentView<Empty>()
private let titleContainer: UIView
private let titleTransformContainer: UIView
private var titleNeutralScale: CGFloat = 1.0
private let subtitle = ComponentView<Empty>()
@ -157,6 +161,8 @@ final class AffiliateProgramSetupScreenComponent: Component {
self.scrollView.contentInsetAdjustmentBehavior = .never
self.scrollView.alwaysBounceVertical = true
self.titleContainer = SparseContainerView()
self.titleTransformContainer = UIView()
self.titleTransformContainer.isUserInteractionEnabled = false
@ -337,6 +343,8 @@ final class AffiliateProgramSetupScreenComponent: Component {
transition.setSublayerTransform(view: self.titleTransformContainer, transform: CATransform3DMakeTranslation(0.0, titleY - self.titleTransformContainer.center.y, 0.0))
transition.setSublayerTransform(view: self.titleContainer, transform: CATransform3DMakeTranslation(0.0, -self.scrollView.contentOffset.y, 0.0))
let titleYDistance: CGFloat = titleY - titleCenterY
let titleTransformFraction: CGFloat = 1.0 - max(0.0, min(1.0, titleYDistance / titleTransformDistance))
let titleMinScale: CGFloat = 17.0 / 30.0
@ -625,6 +633,10 @@ final class AffiliateProgramSetupScreenComponent: Component {
self.component = component
self.state = state
if self.titleContainer.superview == nil {
component.overNavigationContainer.addSubview(self.titleContainer)
}
let topInset: CGFloat = environment.navigationHeight + 90.0
let bottomInset: CGFloat = 8.0
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
@ -648,7 +660,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
let coinIconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - coinIconSize.width) * 0.5), y: contentHeight - coinIconSize.height + 30.0), size: coinIconSize)
if let coinIconView = self.coinIcon.view {
if coinIconView.superview == nil {
self.scrollView.addSubview(coinIconView)
self.titleContainer.addSubview(coinIconView)
}
transition.setFrame(view: coinIconView, frame: coinIconFrame)
}
@ -1620,16 +1632,21 @@ public class AffiliateProgramSetupScreen: ViewControllerComponentContainer {
private let context: AccountContext
private var isDismissed: Bool = false
private let overNavigationContainer: UIView
public init(
context: AccountContext,
initialContent: AffiliateProgramSetupScreenInitialData
) {
self.context = context
self.overNavigationContainer = SparseContainerView()
let initialContent = initialContent as! AffiliateProgramSetupScreen.Content
super.init(context: context, component: AffiliateProgramSetupScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
initialContent: initialContent
), navigationBarAppearance: .default, theme: .default)
@ -1647,6 +1664,10 @@ public class AffiliateProgramSetupScreen: ViewControllerComponentContainer {
return componentView.attemptNavigation(complete: complete)
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {

View file

@ -63,8 +63,8 @@ public final class CollectionTabItemComponent: Component {
let iconSpacing: CGFloat = 3.0
let normalColor = component.theme.list.itemSecondaryTextColor
let selectedColor = component.theme.list.freeTextColor
let normalColor = component.theme.list.itemPrimaryTextColor
let selectedColor = component.theme.list.itemPrimaryTextColor
let effectiveColor = normalColor.mixedWith(selectedColor, alpha: environment.selectionFraction)
let titleSize = self.title.update(
@ -109,7 +109,7 @@ public final class CollectionTabItemComponent: Component {
transition: .immediate,
component: AnyComponent(BundleIconComponent(
name: "Chat/Input/Media/PanelBadgeAdd",
tintColor: component.theme.list.itemSecondaryTextColor
tintColor: component.theme.list.itemPrimaryTextColor
)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)

View file

@ -60,7 +60,7 @@ private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNod
let size = CGSize(width: width, height: defaultHeight)
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: size))
self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition)
let _ = self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition)
var contentHeight: CGFloat = size.height + 10.0

View file

@ -54,7 +54,7 @@ private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNod
let size = CGSize(width: width, height: defaultHeight)
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: size))
self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition)
let _ = self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition)
var contentHeight: CGFloat = size.height + 10.0

View file

@ -169,6 +169,7 @@ swift_library(
"//submodules/TelegramUI/Components/TextFieldComponent",
"//submodules/TelegramUI/Components/EdgeEffect",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
],
visibility = [
"//visibility:public",

View file

@ -153,7 +153,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let usernameNode: MultiScaleTextNode
var actionButtonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderActionButtonNode] = [:]
var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:]
let panelsExpansionBackgroundView: UIView
let headerEdgeEffectView: EdgeEffectView
var navigationTitle: String?
let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode
@ -170,6 +169,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var cancelUpload: (() -> Void)?
var requestUpdateLayout: ((Bool) -> Void)?
var animateOverlaysFadeIn: (() -> Void)?
var updateUnderHeaderContentsAlpha: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
@ -289,7 +289,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.searchBarContainer = SparseNode()
self.searchContainer = ASDisplayNode()
self.panelsExpansionBackgroundView = UIView()
self.headerEdgeEffectView = EdgeEffectView()
self.headerEdgeEffectView.isUserInteractionEnabled = false
@ -305,7 +304,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self?.requestUpdateLayout?(false)
}
self.view.addSubview(self.panelsExpansionBackgroundView)
self.addSubnode(self.searchContainer)
self.view.addSubview(self.headerEdgeEffectView)
self.view.addSubview(self.backgroundBannerView)
@ -2507,20 +2505,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let edgeEffectHeight: CGFloat = 40.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: backgroundFrame.width, height: navigationHeight + 18.0))
let panelsExpansionBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + paneContainerY - contentOffset), size: CGSize(width: width, height: 2000.0))
if additive {
transition.updateFrameAdditive(layer: self.headerEdgeEffectView.layer, frame: edgeEffectFrame)
transition.updateFrameAdditive(view: self.panelsExpansionBackgroundView, frame: panelsExpansionBackgroundFrame)
} else {
transition.updateFrame(view: self.headerEdgeEffectView, frame: edgeEffectFrame)
transition.updateFrame(view: self.panelsExpansionBackgroundView, frame: panelsExpansionBackgroundFrame)
}
self.panelsExpansionBackgroundView.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
transition.updateAlpha(layer: self.panelsExpansionBackgroundView.layer, alpha: realAreaExpansionFraction)
if isSettings {
self.panelsExpansionBackgroundView.isHidden = true
if !isSettings {
self.updateUnderHeaderContentsAlpha?(1.0 - realAreaExpansionFraction, transition)
}
self.headerEdgeEffectView.update(content: presentationData.theme.rootController.navigationBar.opaqueBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectHeight, transition: ComponentTransition(transition))

View file

@ -16,10 +16,14 @@ import PeerInfoChatPaneNode
import TextFormat
import EmojiTextAttachmentView
import ComponentFlow
import ComponentDisplayAdapters
import TabSelectorComponent
import MultilineTextComponent
import BottomButtonPanelComponent
import UndoUI
import HorizontalTabsComponent
import GlassBackgroundComponent
import EdgeEffect
final class PeerInfoPaneWrapper {
let key: PeerInfoPaneKey
@ -44,8 +48,6 @@ final class PeerInfoPaneWrapper {
}
private final class GiftsTabItemComponent: Component {
typealias EnvironmentType = TabSelectorComponent.ItemEnvironment
let context: AccountContext
let icons: [ProfileGiftsContext.State.StarGift]
let title: String
@ -83,22 +85,19 @@ private final class GiftsTabItemComponent: Component {
private var component: GiftsTabItemComponent?
func update(component: GiftsTabItemComponent, availableSize: CGSize, state: State, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
func update(component: GiftsTabItemComponent, availableSize: CGSize, state: State, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
self.component = component
let environment = environment[EnvironmentType.self].value
let textSpacing: CGFloat = 2.0
let iconSpacing: CGFloat = 1.0
let normalColor = component.theme.list.itemSecondaryTextColor
let selectedColor = component.theme.list.itemAccentColor
let effectiveColor = normalColor.mixedWith(selectedColor, alpha: environment.selectionFraction)
let normalColor = component.theme.chat.inputPanel.panelControlColor
let effectiveColor = normalColor
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.title, font: Font.medium(14.0), textColor: effectiveColor))
text: .plain(NSAttributedString(string: component.title, font: Font.medium(15.0), textColor: effectiveColor))
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: 100.0)
@ -399,292 +398,6 @@ private func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGF
return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t)))
}
final class PeerInfoPaneTabsContainerNode: ASDisplayNode {
private let context: AccountContext
private let scrollNode: ASScrollNode
private var paneNodes: [PeerInfoPaneKey: PeerInfoPaneTabsContainerPaneNode] = [:]
private let selectedLineNode: ASImageNode
private var currentParams: ([PeerInfoPaneSpecifier], PeerInfoPaneKey?, Bool, PresentationData)?
var requestSelectPane: ((PeerInfoPaneKey) -> Void)?
init(context: AccountContext) {
self.context = context
self.scrollNode = ASScrollNode()
self.selectedLineNode = ASImageNode()
self.selectedLineNode.displaysAsynchronously = false
self.selectedLineNode.displayWithoutProcessing = true
super.init()
self.scrollNode.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
guard let strongSelf = self else {
return false
}
return strongSelf.scrollNode.view.contentOffset.x > .ulpOfOne
}
self.scrollNode.view.showsHorizontalScrollIndicator = false
self.scrollNode.view.scrollsToTop = false
if #available(iOS 11.0, *) {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
self.addSubnode(self.scrollNode)
self.scrollNode.addSubnode(self.selectedLineNode)
}
func update(size: CGSize, presentationData: PresentationData, paneList: [PeerInfoPaneSpecifier], selectedPane: PeerInfoPaneKey?, disableSwitching: Bool, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
let focusOnSelectedPane = self.currentParams?.1 != selectedPane
if self.currentParams?.3.theme !== presentationData.theme {
self.selectedLineNode.image = generateImage(CGSize(width: 7.0, height: 4.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width)))
})?.stretchableImage(withLeftCapWidth: 4, topCapHeight: 1)
}
if self.currentParams?.0 != paneList || self.currentParams?.1 != selectedPane || self.currentParams?.3 !== presentationData {
for specifier in paneList {
let paneNode: PeerInfoPaneTabsContainerPaneNode
if let current = self.paneNodes[specifier.key] {
paneNode = current
} else {
paneNode = PeerInfoPaneTabsContainerPaneNode(pressed: { [weak self] in
self?.paneSelected(specifier.key)
})
self.paneNodes[specifier.key] = paneNode
}
paneNode.updateText(context: self.context, title: specifier.title, icons: specifier.icons, isSelected: selectedPane == specifier.key, presentationData: presentationData)
}
var removeKeys: [PeerInfoPaneKey] = []
for (key, _) in self.paneNodes {
if !paneList.contains(where: { $0.key == key }) {
removeKeys.append(key)
}
}
for key in removeKeys {
if let paneNode = self.paneNodes.removeValue(forKey: key) {
paneNode.removeFromSupernode()
}
}
}
self.currentParams = (paneList, selectedPane, disableSwitching, presentationData)
var tabSizes: [(PeerInfoPaneKey, CGSize, PeerInfoPaneTabsContainerPaneNode, Bool)] = []
var totalRawTabSize: CGFloat = 0.0
var selectionFrames: [CGRect] = []
for specifier in paneList {
guard let paneNode = self.paneNodes[specifier.key] else {
continue
}
let wasAdded = paneNode.supernode == nil
if wasAdded {
self.scrollNode.addSubnode(paneNode)
}
let paneNodeWidth = paneNode.updateLayout(height: size.height)
let paneNodeSize = CGSize(width: paneNodeWidth, height: size.height)
tabSizes.append((specifier.key, paneNodeSize, paneNode, wasAdded))
totalRawTabSize += paneNodeSize.width
}
let minSpacing: CGFloat = 26.0
if tabSizes.count <= 1 {
for i in 0 ..< tabSizes.count {
let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i]
let leftOffset: CGFloat = 16.0
let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize)
let paneAlpha: CGFloat
if disableSwitching {
paneAlpha = paneKey == selectedPane ? 1.0 : 0.5
} else {
paneAlpha = 1.0
}
if wasAdded {
paneNode.frame = paneFrame
paneNode.alpha = 0.0
} else {
transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame)
}
transition.updateAlpha(node: paneNode, alpha: paneAlpha)
let areaSideInset: CGFloat = 16.0
paneNode.updateArea(size: paneFrame.size, sideInset: areaSideInset)
paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -areaSideInset, bottom: 0.0, right: -areaSideInset)
selectionFrames.append(paneFrame)
}
self.scrollNode.view.contentSize = CGSize(width: size.width, height: size.height)
} else if totalRawTabSize + CGFloat(tabSizes.count + 1) * minSpacing <= size.width {
let availableSpace = size.width
let availableSpacing = availableSpace - totalRawTabSize
let perTabSpacing = floor(availableSpacing / CGFloat(tabSizes.count + 1))
let normalizedPerTabWidth = floor(availableSpace / CGFloat(tabSizes.count))
var maxSpacing: CGFloat = 0.0
var minSpacing: CGFloat = .greatestFiniteMagnitude
for i in 0 ..< tabSizes.count - 1 {
let distanceToNextBoundary = (normalizedPerTabWidth - tabSizes[i].1.width) / 2.0
let nextDistanceToBoundary = (normalizedPerTabWidth - tabSizes[i + 1].1.width) / 2.0
let distance = nextDistanceToBoundary + distanceToNextBoundary
maxSpacing = max(distance, maxSpacing)
minSpacing = min(distance, minSpacing)
}
if minSpacing >= 100.0 || (maxSpacing / minSpacing) < 0.2 {
for i in 0 ..< tabSizes.count {
let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i]
let paneFrame = CGRect(origin: CGPoint(x: CGFloat(i) * normalizedPerTabWidth + floor((normalizedPerTabWidth - paneNodeSize.width) / 2.0), y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize)
let paneAlpha: CGFloat
if disableSwitching {
paneAlpha = paneKey == selectedPane ? 1.0 : 0.5
} else {
paneAlpha = 1.0
}
if wasAdded {
paneNode.frame = paneFrame
paneNode.alpha = 0.0
} else {
transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame)
}
transition.updateAlpha(node: paneNode, alpha: paneAlpha)
let areaSideInset = floor((normalizedPerTabWidth - paneNodeSize.width) / 2.0)
paneNode.updateArea(size: paneFrame.size, sideInset: areaSideInset)
paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -areaSideInset, bottom: 0.0, right: -areaSideInset)
selectionFrames.append(paneFrame)
}
} else {
var leftOffset = perTabSpacing
for i in 0 ..< tabSizes.count {
let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i]
let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize)
let paneAlpha: CGFloat
if disableSwitching {
paneAlpha = paneKey == selectedPane ? 1.0 : 0.5
} else {
paneAlpha = 1.0
}
if wasAdded {
paneNode.frame = paneFrame
paneNode.alpha = 0.0
} else {
transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame)
}
transition.updateAlpha(node: paneNode, alpha: paneAlpha)
let areaSideInset = floor(perTabSpacing / 2.0)
paneNode.updateArea(size: paneFrame.size, sideInset: areaSideInset)
paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -areaSideInset, bottom: 0.0, right: -areaSideInset)
leftOffset += paneNodeSize.width + perTabSpacing
selectionFrames.append(paneFrame)
}
}
self.scrollNode.view.contentSize = CGSize(width: size.width, height: size.height)
} else {
let sideInset: CGFloat = 16.0
var leftOffset: CGFloat = sideInset
for i in 0 ..< tabSizes.count {
let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i]
let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize)
let paneAlpha: CGFloat
if disableSwitching {
paneAlpha = paneKey == selectedPane ? 1.0 : 0.5
} else {
paneAlpha = 1.0
}
if wasAdded {
paneNode.frame = paneFrame
paneNode.alpha = 0.0
} else {
transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame)
}
transition.updateAlpha(node: paneNode, alpha: paneAlpha)
paneNode.updateArea(size: paneFrame.size, sideInset: minSpacing)
paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -minSpacing, bottom: 0.0, right: -minSpacing)
selectionFrames.append(paneFrame)
leftOffset += paneNodeSize.width + minSpacing
}
self.scrollNode.view.contentSize = CGSize(width: leftOffset - minSpacing + sideInset, height: size.height)
}
var selectedFrame: CGRect?
if let selectedPane = selectedPane, let currentIndex = paneList.firstIndex(where: { $0.key == selectedPane }) {
if currentIndex != 0 && transitionFraction > 0.0 {
let currentFrame = selectionFrames[currentIndex]
let previousFrame = selectionFrames[currentIndex - 1]
selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction))
} else if currentIndex != paneList.count - 1 && transitionFraction < 0.0 {
let currentFrame = selectionFrames[currentIndex]
let previousFrame = selectionFrames[currentIndex + 1]
selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction))
} else {
selectedFrame = selectionFrames[currentIndex]
}
}
if let selectedFrame = selectedFrame {
let wasAdded = self.selectedLineNode.isHidden
self.selectedLineNode.isHidden = false
let lineFrame = CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 4.0), size: CGSize(width: selectedFrame.width, height: 4.0))
if wasAdded {
self.selectedLineNode.frame = lineFrame
self.selectedLineNode.alpha = 0.0
transition.updateAlpha(node: self.selectedLineNode, alpha: 1.0)
} else {
transition.updateFrame(node: self.selectedLineNode, frame: lineFrame)
}
if focusOnSelectedPane {
if selectedPane == paneList.first?.key {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size))
} else if selectedPane == paneList.last?.key {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: max(0.0, self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width), y: 0.0), size: self.scrollNode.bounds.size))
} else {
let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0)))
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size))
}
}
} else {
self.selectedLineNode.isHidden = true
}
}
private func paneSelected(_ key: PeerInfoPaneKey) {
guard let currentParams = self.currentParams else {
return
}
if currentParams.2 {
return
}
self.requestSelectPane?(key)
}
}
private final class PeerInfoPendingPane {
let pane: PeerInfoPaneWrapper
private var disposable: Disposable?
@ -874,11 +587,10 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
weak var parentController: ViewController?
private let coveringBackgroundNode: NavigationBackgroundNode
private let additionalBackgroundNode: ASDisplayNode
private let separatorNode: ASDisplayNode
private let edgeEffectView: EdgeEffectView
private let tabsBackgroundContainer: GlassBackgroundContainerView
private let tabsBackgroundView: GlassBackgroundView
private let tabsContainer = ComponentView<Empty>()
private let tabsSeparatorNode: ASDisplayNode
private var didJustReorderTabs = false
private var actionPanel: ComponentView<Empty>?
@ -914,6 +626,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
private var initialStoryFolderId: Int64?
private var initialGiftCollectionId: Int64?
private var isDraggingTabs: Bool = false
private var transitionFraction: CGFloat = 0.0
var selectionPanelNode: PeerInfoSelectionPanelNode?
@ -951,22 +664,18 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
self.initialStoryFolderId = initialStoryFolderId
self.initialGiftCollectionId = initialGiftCollectionId
self.additionalBackgroundNode = ASDisplayNode()
self.tabsBackgroundContainer = GlassBackgroundContainerView()
self.tabsBackgroundView = GlassBackgroundView()
self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true
self.coveringBackgroundNode = NavigationBackgroundNode(color: .clear)
self.coveringBackgroundNode.isUserInteractionEnabled = false
self.tabsSeparatorNode = ASDisplayNode()
self.edgeEffectView = EdgeEffectView()
self.edgeEffectView.isUserInteractionEnabled = false
super.init()
// self.addSubnode(self.separatorNode)
self.addSubnode(self.additionalBackgroundNode)
self.addSubnode(self.coveringBackgroundNode)
self.addSubnode(self.tabsSeparatorNode)
self.view.addSubview(self.edgeEffectView)
self.tabsBackgroundContainer.contentView.addSubview(self.tabsBackgroundView)
self.view.addSubview(self.tabsBackgroundContainer)
}
override func didLoad() {
@ -1031,6 +740,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
self.isDraggingTabs = true
func cancelContextGestures(view: UIView) {
if let gestureRecognizers = view.gestureRecognizers {
for gesture in gestureRecognizers {
@ -1065,6 +776,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
self.currentPaneUpdated?(false)
}
case .cancelled, .ended:
self.isDraggingTabs = false
if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
let translation = recognizer.translation(in: self.view)
let velocity = recognizer.velocity(in: self.view)
@ -1158,8 +871,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
}
}
func openTabContextMenu(key: PeerInfoPaneKey, sourceNode: ASDisplayNode, gesture: ContextGesture?) {
guard let params = self.currentParams, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else {
func openTabContextMenu(key: PeerInfoPaneKey, sourceView: UIView, gesture: ContextGesture?) {
guard let params = self.currentParams, let sourceView = sourceView as? ContextExtractedContentContainingView else {
return
}
@ -1190,7 +903,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
let contextController = ContextController(
presentationData: params.presentationData,
source: .extracted(TabsExtractedContentSource(sourceNode: sourceNode)),
source: .reference(TabsReferenceContentSource(sourceView: sourceView)),
items: .single(ContextController.Items(content: .list(items))),
recognizer: nil,
gesture: gesture
@ -1244,20 +957,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
self.currentParams = (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight)
transition.updateAlpha(node: self.coveringBackgroundNode, alpha: expansionFraction)
// transition.updateAlpha(node: self.additionalBackgroundNode, alpha: 1.0 - expansionFraction)
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
self.coveringBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.opaqueBackgroundColor, transition: .immediate)
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
self.additionalBackgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
self.tabsSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
let isScrollingLockedAtTop = expansionFraction < 1.0 - CGFloat.ulpOfOne
let tabsHeight: CGFloat = 48.0
let effectiveTabsHeight: CGFloat = areTabsHidden ? 0.0 : tabsHeight
let tabsHeight: CGFloat = 40.0
let effectiveTabsHeight: CGFloat = areTabsHidden ? 0.0 : (10.0 + tabsHeight + 10.0 + 6.0)
let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
@ -1445,7 +1150,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
if let index = availablePanes.firstIndex(of: key), let updatedCurrentIndex = updatedCurrentIndex {
var paneWasAdded = false
if pane.node.supernode == nil {
self.insertSubnode(pane.node, belowSubnode: self.coveringBackgroundNode)
self.insertSubnode(pane.node, at: 0)
paneWasAdded = true
}
let indexOffset = CGFloat(index - updatedCurrentIndex)
@ -1493,12 +1198,14 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
}
var tabsOffset: CGFloat = 0.0
if let currentPane = self.currentPane {
tabsOffset = currentPane.node.tabBarOffset
}
tabsOffset = max(0.0, min(tabsHeight, tabsOffset))
if isScrollingLockedAtTop || self.isMediaOnly {
tabsOffset = 0.0
if !"".isEmpty {
if let currentPane = self.currentPane {
tabsOffset = currentPane.node.tabBarOffset
}
tabsOffset = max(0.0, min(tabsHeight, tabsOffset))
if isScrollingLockedAtTop || self.isMediaOnly {
tabsOffset = 0.0
}
}
var tabsAlpha: CGFloat
@ -1510,12 +1217,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
}
tabsAlpha *= tabsAlpha
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: UIScreenPixel)))
transition.updateFrame(node: self.coveringBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel)))
transition.updateFrame(node: self.additionalBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel)))
self.coveringBackgroundNode.update(size: self.coveringBackgroundNode.bounds.size, transition: transition)
transition.updateFrame(node: self.tabsSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: tabsHeight - tabsOffset), size: CGSize(width: size.width, height: UIScreenPixel)))
let edgeEffectHeight: CGFloat = 60.0
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: 60.0 + 6.0))
transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
self.edgeEffectView.update(content: presentationData.theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectHeight, transition: ComponentTransition(transition))
ComponentTransition(transition).setAlpha(view: self.edgeEffectView, alpha: tabsAlpha)
var canManageTabs = false
if let peer = data?.peer {
@ -1528,124 +1234,138 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
}
}
let items: [TabSelectorComponent.Item] = availablePanes.map { key in
let content: TabSelectorComponent.Item.Content
var canReorder = false
switch key {
case .stories:
content = .text(presentationData.strings.PeerInfo_PaneStories)
canReorder = true
case .storyArchive:
content = .text(presentationData.strings.PeerInfo_PaneArchivedStories)
case .botPreview:
content = .text(presentationData.strings.PeerInfo_PaneBotPreviews)
case .media:
content = .text(presentationData.strings.PeerInfo_PaneMedia)
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .files:
content = .text(presentationData.strings.PeerInfo_PaneFiles)
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .links:
content = .text(presentationData.strings.PeerInfo_PaneLinks)
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .voice:
content = .text(presentationData.strings.PeerInfo_PaneVoiceAndVideo)
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .gifs:
content = .text(presentationData.strings.PeerInfo_PaneGifs)
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .music:
content = .text(presentationData.strings.PeerInfo_PaneAudio)
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .groupsInCommon:
content = .text(presentationData.strings.PeerInfo_PaneGroups)
case .members:
content = .text(presentationData.strings.PeerInfo_PaneMembers)
case .similarChannels:
content = .text(presentationData.strings.PeerInfo_PaneRecommended)
case .similarBots:
content = .text(presentationData.strings.PeerInfo_PaneRecommendedBots)
case .savedMessagesChats:
content = .text(presentationData.strings.DialogList_TabTitle)
case .savedMessages:
content = .text(presentationData.strings.PeerInfo_SavedMessagesTabTitle)
case .gifts:
var icons: [ProfileGiftsContext.State.StarGift] = []
if let gifts = data?.profileGiftsContext?.currentState?.gifts.prefix(3) {
icons = Array(gifts)
}
content = .component(AnyComponent(
GiftsTabItemComponent(context: self.context, icons: icons, title: presentationData.strings.PeerInfo_PaneGifts, theme: presentationData.theme)
))
canReorder = true
}
return TabSelectorComponent.Item(id: key, content: content, isReorderable: false, contextAction: key != availablePanes.first && canManageTabs && canReorder ? { [weak self] node, gesture in
self?.openTabContextMenu(key: key, sourceNode: node, gesture: gesture)
} : nil)
}
let tabsSideInset: CGFloat = sideInset + 16.0
let tabsContainerSize = CGSize(width: size.width - sideInset * 2.0, height: tabsHeight)
let tabsContainerSize = CGSize(width: size.width - tabsSideInset * 2.0, height: tabsHeight)
let tabsContainerEffectiveSize = self.tabsContainer.update(
transition: ComponentTransition(transition),
component: AnyComponent(TabSelectorComponent(
colors: TabSelectorComponent.Colors(
foreground: presentationData.theme.list.itemSecondaryTextColor,
selection: presentationData.theme.list.itemAccentColor
),
component: AnyComponent(HorizontalTabsComponent(
context: self.context,
theme: presentationData.theme,
customLayout: TabSelectorComponent.CustomLayout(
font: Font.medium(14.0),
spacing: 6.0,
fillWidth: true,
lineSelection: true
),
items: items,
selectedId: self.currentPaneKey,
setSelectedId: { [weak self] id in
guard let strongSelf = self, let key = id.base as? PeerInfoPaneKey else {
return
}
if strongSelf.currentPaneKey == key {
if let requestExpandTabs = strongSelf.requestExpandTabs, requestExpandTabs() {
} else {
let _ = strongSelf.currentPane?.node.scrollToTop()
}
return
}
if strongSelf.currentPanes[key] != nil {
strongSelf.currentPaneKey = key
if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams {
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
strongSelf.currentPaneUpdated?(true)
strongSelf.currentPaneStatusPromise.set(strongSelf.currentPane?.node.status ?? .single(nil))
strongSelf.nextPaneStatusPromise.set(.single(nil))
strongSelf.paneTransitionPromise.set(nil)
}
} else if strongSelf.pendingSwitchToPaneKey != key {
strongSelf.pendingSwitchToPaneKey = key
strongSelf.expandOnSwitch = true
if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams {
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
tabs: availablePanes.map { paneKey -> HorizontalTabsComponent.Tab in
var canReorder = false
let content: HorizontalTabsComponent.Tab.Content
switch paneKey {
case .stories:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneStories, entities: [], enableAnimations: false))
canReorder = true
case .storyArchive:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneArchivedStories, entities: [], enableAnimations: false))
case .botPreview:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneBotPreviews, entities: [], enableAnimations: false))
case .media:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneMedia, entities: [], enableAnimations: false))
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .files:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneFiles, entities: [], enableAnimations: false))
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .links:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneLinks, entities: [], enableAnimations: false))
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .voice:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneVoiceAndVideo, entities: [], enableAnimations: false))
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .gifs:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneGifs, entities: [], enableAnimations: false))
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .music:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneAudio, entities: [], enableAnimations: false))
canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel
case .groupsInCommon:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneGroups, entities: [], enableAnimations: false))
case .members:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneMembers, entities: [], enableAnimations: false))
case .similarChannels:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneRecommended, entities: [], enableAnimations: false))
case .similarBots:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneRecommendedBots, entities: [], enableAnimations: false))
case .savedMessagesChats:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.DialogList_TabTitle, entities: [], enableAnimations: false))
case .savedMessages:
content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_SavedMessagesTabTitle, entities: [], enableAnimations: false))
case .gifts:
var icons: [ProfileGiftsContext.State.StarGift] = []
if let gifts = data?.profileGiftsContext?.currentState?.gifts.prefix(3) {
icons = Array(gifts)
}
content = .custom(AnyComponent(
GiftsTabItemComponent(context: self.context, icons: icons, title: presentationData.strings.PeerInfo_PaneGifts, theme: presentationData.theme)
))
canReorder = true
}
return HorizontalTabsComponent.Tab(
id: AnyHashable(paneKey),
content: content,
badge: nil,
action: { [weak self] in
guard let self else {
return
}
if self.currentPaneKey == paneKey {
if let requestExpandTabs = self.requestExpandTabs, requestExpandTabs() {
} else {
let _ = self.currentPane?.node.scrollToTop()
}
return
}
if self.currentPanes[paneKey] != nil {
self.currentPaneKey = paneKey
if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams {
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
self.currentPaneUpdated?(true)
self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil))
self.nextPaneStatusPromise.set(.single(nil))
self.paneTransitionPromise.set(nil)
}
} else if self.pendingSwitchToPaneKey != paneKey {
self.pendingSwitchToPaneKey = paneKey
self.expandOnSwitch = true
if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams {
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
}
}
},
contextAction: paneKey != availablePanes.first && canManageTabs && canReorder ? { [weak self] sourceView, gesture in
guard let self else {
return
}
self.openTabContextMenu(key: paneKey, sourceView: sourceView, gesture: gesture)
} : nil,
deleteAction: nil
)
},
transitionFraction: -self.transitionFraction
selectedTab: self.currentPaneKey.flatMap { HorizontalTabsComponent.Tab.Id($0) },
isEditing: false,
layout: .fit
)),
environment: {},
containerSize: tabsContainerSize
)
let tabContainerFrameOriginX = items.count == 1 ? sideInset : floorToScreenPixels((size.width - tabsContainerEffectiveSize.width) / 2.0)
let tabContainerFrame = CGRect(origin: CGPoint(x: tabContainerFrameOriginX, y: 10.0 - tabsOffset), size: tabsContainerSize)
if let tabsContainerView = self.tabsContainer.view {
let tabContainerFrameOriginX = floorToScreenPixels((size.width - tabsContainerEffectiveSize.width) / 2.0)
let tabContainerFrame = CGRect(origin: CGPoint(x: tabContainerFrameOriginX, y: 10.0), size: tabsContainerEffectiveSize)
transition.updateFrame(view: self.tabsBackgroundContainer, frame: tabContainerFrame)
self.tabsBackgroundContainer.update(size: tabContainerFrame.size, isDark: presentationData.theme.overallDarkAppearance, transition: ComponentTransition(transition))
transition.updateFrame(view: self.tabsBackgroundView, frame: CGRect(origin: CGPoint(), size: tabContainerFrame.size))
self.tabsBackgroundView.update(size: tabContainerFrame.size, cornerRadius: tabContainerFrame.height * 0.5, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: ComponentTransition(transition))
ComponentTransition(transition).setAlpha(view: self.tabsBackgroundContainer, alpha: tabsAlpha)
if let tabsContainerView = self.tabsContainer.view as? HorizontalTabsComponent.View {
if tabsContainerView.superview == nil {
self.view.insertSubview(tabsContainerView, belowSubview: self.tabsSeparatorNode.view)
self.tabsBackgroundView.contentView.addSubview(tabsContainerView)
tabsContainerView.setOverlayContainerView(overlayContainerView: self.view)
}
transition.updateFrame(view: tabsContainerView, frame: tabContainerFrame)
transition.updateAlpha(layer: tabsContainerView.layer, alpha: tabsAlpha)
transition.updateFrame(view: tabsContainerView, frame: CGRect(origin: CGPoint(), size: tabContainerFrame.size))
tabsContainerView.updateTabSwitchFraction(fraction: self.transitionFraction, isDragging: self.isDraggingTabs, transition: ComponentTransition(transition))
}
for (_, pane) in self.pendingPanes {
@ -1717,3 +1437,22 @@ private final class TabsExtractedContentSource: ContextExtractedContentSource {
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
}
}
private final class TabsReferenceContentSource: ContextReferenceContentSource {
let keepInPlace: Bool = true
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
private let sourceView: ContextExtractedContentContainingView
init(sourceView: ContextExtractedContentContainingView) {
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(
referenceView: self.sourceView.contentView,
contentAreaInScreenSpace: UIScreen.main.bounds,
actionsPosition: .bottom
)
}
}

View file

@ -4840,6 +4840,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
}
self.headerNode.updateUnderHeaderContentsAlpha = { [weak self] alpha, transition in
guard let self else {
return
}
for (_, section) in self.regularSections {
transition.updateAlpha(node: section, alpha: alpha)
}
}
let screenData: Signal<PeerInfoScreenData, NoError>
if self.isSettings {
self.notificationExceptions.set(.single(NotificationExceptionsList(peers: [:], settings: [:]))

View file

@ -58,7 +58,8 @@ swift_library(
"//submodules/TelegramUI/Components/BottomButtonPanelComponent",
"//submodules/PromptUI",
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
"//submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent"
"//submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent",
"//submodules/TelegramUI/Components/EdgeEffect",
],
visibility = [
"//visibility:public",

View file

@ -35,6 +35,7 @@ import EmojiTextAttachmentView
import TextFormat
import PromptUI
import CollectionTabItemComponent
import EdgeEffect
public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
public enum GiftCollection: Equatable {
@ -91,8 +92,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private let tabSelector = ComponentView<Empty>()
public private(set) var currentCollection: GiftCollection = .all
private var panelBackground: NavigationBackgroundNode?
private var panelSeparator: ASDisplayNode?
private var panelEdgeEffectView: EdgeEffectView?
private var panelContentContainer: UIView?
private var panelButton: ComponentView<Empty>?
private var panelCheck: ComponentView<Empty>?
@ -657,13 +658,13 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
component: AnyComponent(TabSelectorComponent(
context: self.context,
colors: TabSelectorComponent.Colors(
foreground: params.presentationData.theme.list.itemSecondaryTextColor,
foreground: params.presentationData.theme.list.itemPrimaryTextColor,
selection: params.presentationData.theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15),
simple: true
),
theme: params.presentationData.theme,
customLayout: TabSelectorComponent.CustomLayout(
font: Font.medium(14.0),
font: Font.medium(15.0),
spacing: 2.0
),
items: tabSelectorItems,
@ -712,9 +713,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
tabSelectorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
}
transition.setFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(x: floor((params.size.width - tabSelectorSize.width) / 2.0), y: 60.0), size: tabSelectorSize))
transition.setFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(x: floor((params.size.width - tabSelectorSize.width) / 2.0), y: 67.0), size: tabSelectorSize))
topInset += tabSelectorSize.height + 14.0
topInset += tabSelectorSize.height + 28.0
}
} else if let tabSelectorView = self.tabSelector.view {
tabSelectorView.alpha = 0.0
@ -731,32 +732,31 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
let bottomInset = params.bottomInset
let presentationData = params.presentationData
let themeUpdated = self.theme !== presentationData.theme
self.theme = presentationData.theme
let panelBackground: NavigationBackgroundNode
let panelSeparator: ASDisplayNode
let panelEdgeEffectView: EdgeEffectView
let panelContentContainer: UIView
var panelVisibility = params.expandProgress < 1.0 ? 0.0 : 1.0
if !self.canGift || self.resultsAreEmpty {
panelVisibility = 0.0
}
let panelTransition: ComponentTransition = .immediate
if let current = self.panelBackground {
panelBackground = current
if let current = self.panelContentContainer {
panelContentContainer = current
} else {
panelBackground = NavigationBackgroundNode(color: presentationData.theme.rootController.tabBar.backgroundColor)
self.addSubnode(panelBackground)
self.panelBackground = panelBackground
panelContentContainer = UIView()
self.view.addSubview(panelContentContainer)
self.panelContentContainer = panelContentContainer
}
if let current = self.panelSeparator {
panelSeparator = current
let panelTransition: ComponentTransition = .immediate
if let current = self.panelEdgeEffectView {
panelEdgeEffectView = current
} else {
panelSeparator = ASDisplayNode()
panelBackground.addSubnode(panelSeparator)
self.panelSeparator = panelSeparator
panelEdgeEffectView = EdgeEffectView()
panelContentContainer.addSubview(panelEdgeEffectView)
self.panelEdgeEffectView = panelEdgeEffectView
}
let panelButton: ComponentView<Empty>
@ -767,7 +767,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.panelButton = panelButton
}
let buttonSideInset = sideInset + 16.0
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: params.bottomInset, innerDiameter: 52.0 * 0.5, sideInset: sideInset + 16.0)
let buttonTitle: String
var buttonIconName: String?
@ -800,6 +800,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
component: AnyComponent(
ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: presentationData.theme.list.itemCheckColors.fillColor,
foreground: presentationData.theme.list.itemCheckColors.foregroundColor,
pressedColor: presentationData.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8)
@ -815,12 +816,12 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
)
),
environment: {},
containerSize: CGSize(width: size.width - buttonSideInset * 2.0, height: 50.0)
containerSize: CGSize(width: size.width - buttonInsets.left * 2.0, height: 52.0)
)
var scrollOffset: CGFloat = max(0.0, size.height - params.visibleHeight)
let effectiveBottomInset = max(8.0, bottomInset)
let effectiveBottomInset = max(buttonInsets.bottom, bottomInset)
var bottomPanelHeight = effectiveBottomInset + panelButtonSize.height + 8.0
if params.visibleHeight < 110.0 {
scrollOffset -= bottomPanelHeight
@ -828,15 +829,12 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
if let panelButtonView = panelButton.view {
if panelButtonView.superview == nil {
panelBackground.view.addSubview(panelButtonView)
panelContentContainer.addSubview(panelButtonView)
}
panelButtonView.frame = CGRect(origin: CGPoint(x: buttonSideInset, y: 8.0), size: panelButtonSize)
panelButtonView.frame = CGRect(origin: CGPoint(x: buttonInsets.left, y: 8.0), size: panelButtonSize)
}
if themeUpdated {
panelBackground.updateColor(color: presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate)
panelSeparator.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor
}
panelTransition.setFrame(view: panelContentContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset), size: CGSize(width: size.width, height: bottomPanelHeight)))
if self.canManage {
bottomPanelHeight -= 9.0
@ -903,7 +901,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
)
if let panelCheckView = panelCheck.view {
if panelCheckView.superview == nil {
panelBackground.view.addSubview(panelCheckView)
panelContentContainer.addSubview(panelCheckView)
}
panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: 16.0), size: panelCheckSize)
}
@ -912,11 +910,11 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
}
panelTransition.setFrame(view: panelBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: bottomPanelHeight))
ComponentTransition.spring(duration: 0.4).setSublayerTransform(view: panelBackground.view, transform: CATransform3DMakeTranslation(0.0, bottomPanelHeight * (1.0 - panelVisibility), 0.0))
let edgeEffectFrame = CGRect(x: 0.0, y: 0.0, width: size.width, height: bottomPanelHeight)
panelTransition.setFrame(view: panelEdgeEffectView, frame: edgeEffectFrame)
panelEdgeEffectView.update(content: presentationData.theme.list.blocksBackgroundColor, blur: false, rect: edgeEffectFrame, edge: .bottom, edgeSize: 40.0, transition: panelTransition)
panelBackground.update(size: CGSize(width: size.width, height: bottomPanelHeight), transition: transition.containedViewLayoutTransition)
panelTransition.setFrame(view: panelSeparator.view, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: UIScreenPixel))
ComponentTransition.spring(duration: 0.4).setSublayerTransform(view: panelContentContainer, transform: CATransform3DMakeTranslation(0.0, bottomPanelHeight * (1.0 - panelVisibility), 0.0))
contentHeight += bottomPanelHeight
bottomScrollInset = bottomPanelHeight - 40.0

View file

@ -1824,6 +1824,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
self.maxStoriesPerFolder = maxStoriesPerFolder
super.init()
self.clipsToBounds = true
if case .peer = self.scope {
let _ = (ApplicationSpecificNotice.getSharedMediaScrollingTooltip(accountManager: context.sharedContext.accountManager)
@ -2112,7 +2114,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
}
if case .botPreview = scope {
let backgroundColor = presentationData.theme.list.plainBackgroundColor
let backgroundColor = presentationData.theme.list.blocksBackgroundColor
let foregroundColor = presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.6)
return SparseItemGrid.ShimmerColors(background: backgroundColor.argb, foreground: foregroundColor.argb)
@ -4024,12 +4026,14 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
transition: folderTabTransition,
component: AnyComponent(TabSelectorComponent(
colors: TabSelectorComponent.Colors(
foreground: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.8),
selection: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05)
foreground: self.presentationData.theme.list.itemPrimaryTextColor,
selection: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05),
normal: self.presentationData.theme.list.itemPrimaryTextColor,
simple: true
),
theme: self.presentationData.theme,
customLayout: TabSelectorComponent.CustomLayout(
font: Font.medium(14.0),
font: Font.medium(15.0),
spacing: 9.0,
verticalInset: 11.0
),
@ -4092,7 +4096,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
environment: {},
containerSize: CGSize(width: size.width, height: 44.0)
)
var folderTabFrame = CGRect(origin: CGPoint(x: floor((size.width - folderTabSize.width) * 0.5), y: topInset - 11.0), size: folderTabSize)
var folderTabFrame = CGRect(origin: CGPoint(x: floor((size.width - folderTabSize.width) * 0.5), y: topInset - 19.0), size: folderTabSize)
let effectiveScrollingOffset: CGFloat
effectiveScrollingOffset = self.itemGrid.scrollingOffset
@ -4236,7 +4240,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
}
var hasBarBackground = false
if self.isProfileEmbedded {
if self.isProfileEmbedded && !"".isEmpty {
if case .botPreview = self.scope {
hasBarBackground = true
} else if case let .peer(_, _, isArchived) = self.scope, ((self.canManageStories && !isArchived) || !self.currentStoryFolders.isEmpty) {
@ -4278,10 +4282,10 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
if case .botPreview = self.scope {
updateBotPreviewFooter(size: size, bottomInset: 0.0, transition: transition)
if let botPreviewFooterView = self.botPreviewFooter?.view {
listBottomInset += 18.0 + botPreviewFooterView.bounds.height
listBottomInset += 18.0 + botPreviewFooterView.bounds.height
}
}
}
}
if self.isProfileEmbedded, let selectedIds = self.itemInteraction.selectedIds, self.canManageStories, case let .peer(peerId, _, isArchived) = self.scope {
let selectionPanel: ComponentView<Empty>
@ -4848,7 +4852,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr
} else if self.isProfileEmbedded, case let .peer(_, _, isArchived) = self.scope, ((self.canManageStories && !isArchived) || !self.currentStoryFolders.isEmpty), self.isProfileEmbedded {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
} else if self.isProfileEmbedded {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.plainBackgroundColor)
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
} else {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
}

View file

@ -1022,7 +1022,7 @@ final class QuickReplySetupScreenComponent: Component {
theme: searchBarTheme,
presentationTheme: environment.theme,
strings: environment.strings,
fieldStyle: .modern,
fieldStyle: .glass,
displayBackground: false
)
searchBarNode.placeholderString = NSAttributedString(string: environment.strings.Common_Search, font: Font.regular(17.0), textColor: searchBarTheme.placeholder)

View file

@ -397,7 +397,7 @@ public final class PasskeysScreen: ViewControllerComponentContainer {
public init(context: AccountContext, displaySkip: Bool, initialPasskeysData: [TelegramPasskey]?, passkeysDataUpdated: @escaping ([TelegramPasskey]) -> Void, completion: @escaping () -> Void, cancel: @escaping () -> Void) {
self.context = context
super.init(context: context, component: PasskeysScreenComponent(context: context, displaySkip: displaySkip, initialPasskeysData: initialPasskeysData, passkeysDataUpdated: passkeysDataUpdated, completion: completion, cancel: cancel), navigationBarAppearance: .transparent)
super.init(context: context, component: PasskeysScreenComponent(context: context, displaySkip: displaySkip, initialPasskeysData: initialPasskeysData, passkeysDataUpdated: passkeysDataUpdated, completion: completion, cancel: cancel), navigationBarAppearance: .default)
}
required public init(coder aDecoder: NSCoder) {

View file

@ -1959,7 +1959,7 @@ public class UserAppearanceScreen: ViewControllerComponentContainer {
}
if let navigationBar = self.navigationBar {
navigationBar.view.insertSubview(self.overNavigationContainer, aboveSubview: navigationBar.backgroundView)
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}

View file

@ -504,6 +504,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let starsContext: StarsContext
let options: [Any]
let purpose: StarsPurchasePurpose
@ -515,6 +516,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
init(
context: AccountContext,
overNavigationContainer: UIView,
starsContext: StarsContext,
options: [Any],
purpose: StarsPurchasePurpose,
@ -525,6 +527,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
completion: @escaping (Int64) -> Void
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.starsContext = starsContext
self.options = options
self.purpose = purpose
@ -759,8 +762,6 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
let scrollContent = Child(ScrollComponent<EnvironmentType>.self)
let star = Child(PremiumStarComponent.self)
let avatar = Child(GiftAvatarComponent.self)
let topPanel = Child(BlurredBackgroundComponent.self)
let topSeparator = Child(Rectangle.self)
let title = Child(MultilineTextComponent.self)
let balanceTitle = Child(MultilineTextComponent.self)
let balanceValue = Child(MultilineTextComponent.self)
@ -816,29 +817,13 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
UIColor(rgb: 0xfdd219)
],
particleColor: UIColor(rgb: 0xf9b004),
backgroundColor: environment.theme.list.blocksBackgroundColor
backgroundColor: nil
),
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
transition: context.transition
)
}
let topPanel = topPanel.update(
component: BlurredBackgroundComponent(
color: environment.theme.rootController.navigationBar.blurredBackgroundColor
),
availableSize: CGSize(width: context.availableSize.width, height: environment.navigationHeight),
transition: context.transition
)
let topSeparator = topSeparator.update(
component: Rectangle(
color: environment.theme.rootController.navigationBar.separatorColor
),
availableSize: CGSize(width: context.availableSize.width, height: UIScreenPixel),
transition: context.transition
)
let titleText: String
switch context.component.purpose {
case .generic:
@ -948,14 +933,12 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
)
let topPanelAlpha: CGFloat
let titleOffset: CGFloat
let titleScale: CGFloat
let titleOffsetDelta = (topInset + 160.0) - (environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)
let titleAlpha: CGFloat
if let topContentOffset = state.topContentOffset {
topPanelAlpha = min(20.0, max(0.0, topContentOffset - 95.0)) / 20.0
let topContentOffset = topContentOffset + max(0.0, min(1.0, topContentOffset / titleOffsetDelta)) * 10.0
titleOffset = topContentOffset
let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta))
@ -963,42 +946,37 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
titleAlpha = 1.0
} else {
topPanelAlpha = 0.0
titleScale = 1.0
titleOffset = 0.0
titleAlpha = 1.0
}
context.add(header
context.addWithExternalContainer(header
.position(CGPoint(x: context.availableSize.width / 2.0, y: topInset + header.size.height / 2.0 - 30.0 - titleOffset * titleScale))
.scale(titleScale)
.scale(titleScale),
container: context.component.overNavigationContainer
)
context.add(topPanel
.position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height / 2.0))
.opacity(topPanelAlpha)
)
context.add(topSeparator
.position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height))
.opacity(topPanelAlpha)
)
context.add(title
context.addWithExternalContainer(title
.position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)))
.scale(titleScale)
.opacity(titleAlpha)
.opacity(titleAlpha),
container: context.component.overNavigationContainer
)
let navigationHeight = environment.navigationHeight - environment.statusBarHeight
let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - balanceTitle.size.height - balanceValue.size.height) / 2.0
context.add(balanceTitle
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceTitle.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height / 2.0))
context.addWithExternalContainer(balanceTitle
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceTitle.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height / 2.0)),
container: context.component.overNavigationContainer
)
context.add(balanceValue
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0))
context.addWithExternalContainer(balanceValue
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0)),
container: context.component.overNavigationContainer
)
context.add(balanceIcon
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width - balanceIcon.size.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0 - UIScreenPixel))
context.addWithExternalContainer(balanceIcon
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width - balanceIcon.size.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0 - UIScreenPixel)),
container: context.component.overNavigationContainer
)
return context.availableSize
@ -1010,6 +988,8 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer {
fileprivate let context: AccountContext
fileprivate let starsContext: StarsContext
private let overNavigationContainer: UIView
private var didSetReady = false
private let _ready = Promise<Bool>()
public override var ready: Promise<Bool> {
@ -1027,6 +1007,8 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer {
) {
self.context = context
self.starsContext = starsContext
self.overNavigationContainer = SparseContainerView()
var openAppExamplesImpl: (() -> Void)?
var updateInProgressImpl: ((Bool) -> Void)?
@ -1034,6 +1016,7 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer {
var completionImpl: ((Int64) -> Void)?
super.init(context: context, component: StarsPurchaseScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
starsContext: starsContext,
options: options,
purpose: purpose,
@ -1050,7 +1033,7 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer {
completion: { stars in
completionImpl?(stars)
}
), navigationBarAppearance: .transparent, presentationMode: .modal, theme: customTheme.flatMap { .custom($0) } ?? .default)
), navigationBarAppearance: .default, presentationMode: .modal, theme: customTheme.flatMap { .custom($0) } ?? .default)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -1090,6 +1073,10 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer {
completion(stars)
}
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {
@ -1129,10 +1116,10 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer {
super.containerLayoutUpdated(layout, transition: transition)
if !self.didSetReady {
if let view = self.node.hostView.findTaggedView(tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View {
if let view = findTaggedComponentViewImpl(view: self.node.view, tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View {
self.didSetReady = true
self._ready.set(view.ready)
} else if let view = self.node.hostView.findTaggedView(tag: GiftAvatarComponent.View.Tag()) as? GiftAvatarComponent.View {
} else if let view = findTaggedComponentViewImpl(view: self.node.view, tag: GiftAvatarComponent.View.Tag()) as? GiftAvatarComponent.View {
self.didSetReady = true
self._ready.set(view.ready)
}

View file

@ -26,6 +26,7 @@ final class StarsStatisticsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let peerId: EnginePeer.Id
let revenueContext: StarsRevenueStatsContext
let openTransaction: (StarsContext.State.Transaction) -> Void
@ -36,6 +37,7 @@ final class StarsStatisticsScreenComponent: Component {
init(
context: AccountContext,
overNavigationContainer: UIView,
peerId: EnginePeer.Id,
revenueContext: StarsRevenueStatsContext,
openTransaction: @escaping (StarsContext.State.Transaction) -> Void,
@ -45,6 +47,7 @@ final class StarsStatisticsScreenComponent: Component {
buyAds: @escaping () -> Void
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.peerId = peerId
self.revenueContext = revenueContext
self.openTransaction = openTransaction
@ -125,10 +128,6 @@ final class StarsStatisticsScreenComponent: Component {
private let scrollView: ScrollViewImpl
private var currentSelectedPanelId: AnyHashable?
private let navigationBackgroundView: BlurredBackgroundView
private let navigationSeparatorLayer: SimpleLayer
private let navigationSeparatorLayerContainer: SimpleLayer
private let headerView = ComponentView<Empty>()
private let headerOffsetContainer: UIView
@ -175,14 +174,6 @@ final class StarsStatisticsScreenComponent: Component {
self.headerOffsetContainer = UIView()
self.headerOffsetContainer.isUserInteractionEnabled = false
self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
self.navigationBackgroundView.alpha = 0.0
self.navigationSeparatorLayer = SimpleLayer()
self.navigationSeparatorLayer.opacity = 0.0
self.navigationSeparatorLayerContainer = SimpleLayer()
self.navigationSeparatorLayerContainer.opacity = 0.0
self.scrollContainerView = UIView()
self.scrollView = ScrollViewImpl()
@ -208,11 +199,6 @@ final class StarsStatisticsScreenComponent: Component {
self.scrollView.addSubview(self.scrollContainerView)
self.scrollContainerView.addSubview(self.transactionsBackground)
self.addSubview(self.navigationBackgroundView)
self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer)
self.layer.addSublayer(self.navigationSeparatorLayerContainer)
self.addSubview(self.headerOffsetContainer)
}
@ -307,18 +293,10 @@ final class StarsStatisticsScreenComponent: Component {
let isLockedAtPanels = scrollBounds.maxY == self.scrollView.contentSize.height
if let _ = self.navigationMetrics {
let topContentOffset = self.scrollView.contentOffset.y
let navigationBackgroundAlpha = min(20.0, max(0.0, topContentOffset)) / 20.0
let animatedTransition = ComponentTransition(animation: .curve(duration: 0.18, curve: .easeInOut))
animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha)
animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha)
let expansionDistance: CGFloat = 32.0
var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance
expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor))
transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor)
if let panelContainerView = self.panelContainer.view as? StarsTransactionsPanelContainerComponent.View {
panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition)
}
@ -417,19 +395,7 @@ final class StarsStatisticsScreenComponent: Component {
self.controller = environment.controller
self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight)
self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight))
self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame)
let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel))
transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame)
transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size))
self.backgroundColor = environment.theme.list.blocksBackgroundColor
var contentHeight: CGFloat = 0.0
@ -454,7 +420,7 @@ final class StarsStatisticsScreenComponent: Component {
)
if let titleView = self.titleView.view {
if titleView.superview == nil {
self.addSubview(titleView)
component.overNavigationContainer.addSubview(titleView)
}
let titlePosition = CGPoint(x: availableSize.width / 2.0, y: environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)
transition.setPosition(view: titleView, position: titlePosition)
@ -847,6 +813,8 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer {
private let peerId: EnginePeer.Id
private let revenueContext: StarsRevenueStatsContext
private let overNavigationContainer: UIView
private weak var tooltipScreen: UndoOverlayController?
private var timer: Foundation.Timer?
@ -857,6 +825,8 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer {
self.peerId = peerId
self.revenueContext = revenueContext
self.overNavigationContainer = SparseContainerView()
var buyImpl: (() -> Void)?
var withdrawImpl: (() -> Void)?
var buyAdsImpl: (() -> Void)?
@ -864,6 +834,7 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer {
var openTransactionImpl: ((StarsContext.State.Transaction) -> Void)?
super.init(context: context, component: StarsStatisticsScreenComponent(
context: context,
overNavigationContainer: self.overNavigationContainer,
peerId: peerId,
revenueContext: revenueContext,
openTransaction: { transaction in
@ -881,7 +852,7 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer {
buyAds: {
buyAdsImpl?()
}
), navigationBarAppearance: .transparent)
), navigationBarAppearance: .default)
self.navigationPresentation = .modalInLargeLayout
@ -1060,6 +1031,10 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer {
}
componentView.scrollToTop()
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
}
required public init(coder aDecoder: NSCoder) {

View file

@ -1252,7 +1252,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
gift: {
giftImpl?()
}
), navigationBarAppearance: .transparent)
), navigationBarAppearance: .default)
self.navigationPresentation = .modalInLargeLayout

View file

@ -45,6 +45,7 @@ swift_library(
"//submodules/GalleryData",
"//submodules/SegmentedControlNode",
"//submodules/TelegramUIPreferences",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
],
visibility = [
"//visibility:public",

View file

@ -24,22 +24,26 @@ import GalleryData
import AnimatedTextComponent
import TelegramUIPreferences
import SegmentControlComponent
import GlassBackgroundComponent
final class DataUsageScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let statsSet: StatsSet
let mediaAutoDownloadSettings: MediaAutoDownloadSettings
let makeAutodownloadSettingsController: (Bool) -> ViewController
init(
context: AccountContext,
overNavigationContainer: UIView,
statsSet: StatsSet,
mediaAutoDownloadSettings: MediaAutoDownloadSettings,
makeAutodownloadSettingsController: @escaping (Bool) -> ViewController
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.statsSet = statsSet
self.mediaAutoDownloadSettings = mediaAutoDownloadSettings
self.makeAutodownloadSettingsController = makeAutodownloadSettingsController
@ -313,12 +317,9 @@ final class DataUsageScreenComponent: Component {
private var mediaAutoDownloadSettings: MediaAutoDownloadSettings = .defaultSettings
private var mediaAutoDownloadSettingsDisposable: Disposable?
private let navigationBackgroundView: BlurredBackgroundView
private let navigationSeparatorLayer: SimpleLayer
private let navigationSeparatorLayerContainer: SimpleLayer
private let headerView = ComponentView<Empty>()
private let headerOffsetContainer: HeaderContainer
private let headerContentContainer: HeaderContainer
private let headerDescriptionView = ComponentView<Empty>()
private var doneLabel: ComponentView<Empty>?
@ -356,14 +357,7 @@ final class DataUsageScreenComponent: Component {
override init(frame: CGRect) {
self.headerOffsetContainer = HeaderContainer()
self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
self.navigationBackgroundView.alpha = 0.0
self.navigationSeparatorLayer = SimpleLayer()
self.navigationSeparatorLayer.opacity = 1.0
self.navigationSeparatorLayerContainer = SimpleLayer()
self.navigationSeparatorLayerContainer.opacity = 0.0
self.headerContentContainer = HeaderContainer()
self.scrollContainerView = UIView()
@ -396,12 +390,7 @@ final class DataUsageScreenComponent: Component {
self.scrollContainerView.addSubview(self.autoDownloadSettingsContainerView)
self.addSubview(self.navigationBackgroundView)
self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer)
self.layer.addSublayer(self.navigationSeparatorLayerContainer)
self.addSubview(self.headerOffsetContainer)
self.headerOffsetContainer.addSubview(self.headerContentContainer)
}
required init?(coder: NSCoder) {
@ -447,41 +436,19 @@ final class DataUsageScreenComponent: Component {
headerOffset = min(headerOffset, minOffset)
let animatedTransition = ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut))
let navigationBackgroundAlpha: CGFloat = abs(headerOffset - minOffset) < 4.0 ? 1.0 : 0.0
let navigationButtonAlpha: CGFloat = scrollBounds.minY >= navigationMetrics.navigationHeight ? 0.0 : 1.0
animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha)
animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha)
/*let expansionDistance: CGFloat = 32.0
var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance
expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor))
transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor)*/
/*var offsetFraction: CGFloat = abs(headerOffset - minOffset) / 60.0
offsetFraction = min(1.0, max(0.0, offsetFraction))
transition.setScale(view: headerView, scale: 1.0 * offsetFraction + 0.8 * (1.0 - offsetFraction))*/
transition.setBounds(view: self.headerOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: headerOffset), size: self.headerOffsetContainer.bounds.size))
let headerContentsAlpha: CGFloat
let offsetFraction = abs(headerOffset - minOffset) / 60.0
headerContentsAlpha = min(1.0, max(0.0, offsetFraction))
transition.setAlpha(view: self.headerContentContainer, alpha: headerContentsAlpha)
if let controller = self.controller?(), let backButtonNode = controller.navigationBar?.backButtonNode {
backButtonNode.updateManualAlpha(alpha: navigationButtonAlpha, transition: animatedTransition.containedViewLayoutTransition)
/*if backButtonNode.alpha != navigationButtonAlpha {
if backButtonNode.isHidden {
backButtonNode.alpha = 0.0
backButtonNode.isHidden = false
}
animatedTransition.setAlpha(layer: backButtonNode.layer, alpha: navigationButtonAlpha, completion: { [weak backButtonNode] completed in
if let backButtonNode, completed {
if navigationButtonAlpha.isZero {
backButtonNode.isHidden = true
}
}
})
}*/
}
}
}
@ -539,17 +506,9 @@ final class DataUsageScreenComponent: Component {
self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight)
self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight))
self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame)
let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel))
transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame)
transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size))
if self.headerOffsetContainer.superview == nil {
component.overNavigationContainer.addSubview(self.headerOffsetContainer)
}
self.backgroundColor = environment.theme.list.blocksBackgroundColor
@ -709,7 +668,7 @@ final class DataUsageScreenComponent: Component {
let pieChartFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: pieChartSize)
if let pieChartComponentView = self.pieChartView.view {
if pieChartComponentView.superview == nil {
self.scrollView.addSubview(pieChartComponentView)
self.headerContentContainer.addSubview(pieChartComponentView)
}
pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame)
@ -740,7 +699,7 @@ final class DataUsageScreenComponent: Component {
if let doneLabelView = doneLabel.view {
var animateIn = false
if doneLabelView.superview == nil {
self.scrollView.addSubview(doneLabelView)
self.headerContentContainer.addSubview(doneLabelView)
animateIn = true
}
doneLabelTransition.setFrame(view: doneLabelView, frame: doneLabelFrame)
@ -755,7 +714,7 @@ final class DataUsageScreenComponent: Component {
if let doneSupLabelView = doneSupLabel.view {
var animateIn = false
if doneSupLabelView.superview == nil {
self.scrollView.addSubview(doneSupLabelView)
self.headerContentContainer.addSubview(doneSupLabelView)
animateIn = true
}
doneLabelTransition.setFrame(view: doneSupLabelView, frame: doneSupLabelFrame)
@ -796,7 +755,7 @@ final class DataUsageScreenComponent: Component {
let headerViewFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerViewSize.width) / 2.0), y: contentHeight), size: headerViewSize)
if let headerComponentView = self.headerView.view {
if headerComponentView.superview == nil {
self.scrollContainerView.addSubview(headerComponentView)
self.headerOffsetContainer.addSubview(headerComponentView)
}
transition.setPosition(view: headerComponentView, position: headerViewFrame.center)
headerComponentView.bounds = CGRect(origin: CGPoint(), size: headerViewFrame.size)
@ -838,7 +797,7 @@ final class DataUsageScreenComponent: Component {
let headerDescriptionFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerDescriptionSize.width) / 2.0), y: contentHeight), size: headerDescriptionSize)
if let headerDescriptionComponentView = self.headerDescriptionView.view {
if headerDescriptionComponentView.superview == nil {
self.scrollContainerView.addSubview(headerDescriptionComponentView)
self.headerContentContainer.addSubview(headerDescriptionComponentView)
}
transition.setPosition(view: headerDescriptionComponentView, position: headerDescriptionFrame.center)
headerDescriptionComponentView.bounds = CGRect(origin: CGPoint(), size: headerDescriptionFrame.size)
@ -892,7 +851,7 @@ final class DataUsageScreenComponent: Component {
)
if let chartTotalLabelView = self.chartTotalLabel.view {
if chartTotalLabelView.superview == nil {
self.scrollContainerView.addSubview(chartTotalLabelView)
self.headerContentContainer.addSubview(chartTotalLabelView)
}
let chartAreaHeight: CGFloat
@ -926,7 +885,7 @@ final class DataUsageScreenComponent: Component {
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(value: .modeChanged)))
})),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
containerSize: CGSize(width: availableSize.width - (environment.safeInsets.left + 16.0 + 44.0 + 10.0) * 2.0, height: 100.0)
)
let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - segmentedSize.width) * 0.5), y: contentHeight), size: segmentedSize)
if let segmentedControlComponentView = self.segmentedControlView.view {
@ -1252,6 +1211,8 @@ final class DataUsageScreenComponent: Component {
public final class DataUsageScreen: ViewControllerComponentContainer {
private let context: AccountContext
private let overNavigationContainer: UIView
private let readyValue = Promise<Bool>()
override public var ready: Promise<Bool> {
return self.readyValue
@ -1260,8 +1221,14 @@ public final class DataUsageScreen: ViewControllerComponentContainer {
public init(context: AccountContext, stats: NetworkUsageStats, mediaAutoDownloadSettings: MediaAutoDownloadSettings, makeAutodownloadSettingsController: @escaping (Bool) -> ViewController) {
self.context = context
self.overNavigationContainer = SparseContainerView()
//let componentReady = Promise<Bool>()
super.init(context: context, component: DataUsageScreenComponent(context: context, statsSet: DataUsageScreenComponent.StatsSet(stats: stats), mediaAutoDownloadSettings: mediaAutoDownloadSettings, makeAutodownloadSettingsController: makeAutodownloadSettingsController), navigationBarAppearance: .transparent)
super.init(context: context, component: DataUsageScreenComponent(context: context, overNavigationContainer: self.overNavigationContainer, statsSet: DataUsageScreenComponent.StatsSet(stats: stats), mediaAutoDownloadSettings: mediaAutoDownloadSettings, makeAutodownloadSettingsController: makeAutodownloadSettingsController), navigationBarAppearance: .default)
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
//self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true)))
self.readyValue.set(.single(true))

View file

@ -24,6 +24,7 @@ import TelegramStringFormatting
import GalleryData
import AnimatedTextComponent
import BottomButtonPanelComponent
import GlassBackgroundComponent
#if DEBUG
import os.signpost
@ -116,17 +117,20 @@ final class StorageUsageScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let overNavigationContainer: UIView
let makeStorageUsageExceptionsScreen: (CacheStorageSettings.PeerStorageCategory) -> ViewController?
let peer: EnginePeer?
let ready: Promise<Bool>
init(
context: AccountContext,
overNavigationContainer: UIView,
makeStorageUsageExceptionsScreen: @escaping (CacheStorageSettings.PeerStorageCategory) -> ViewController?,
peer: EnginePeer?,
ready: Promise<Bool>
) {
self.context = context
self.overNavigationContainer = overNavigationContainer
self.makeStorageUsageExceptionsScreen = makeStorageUsageExceptionsScreen
self.peer = peer
self.ready = ready
@ -740,9 +744,7 @@ final class StorageUsageScreenComponent: Component {
private var isOtherCategoryExpanded: Bool = false
private let navigationBackgroundView: BlurredBackgroundView
private let navigationSeparatorLayer: SimpleLayer
private let navigationSeparatorLayerContainer: SimpleLayer
private let navigationRightButtonsBackground: GlassBackgroundView
private let navigationEditButton = ComponentView<Empty>()
private let navigationDoneButton = ComponentView<Empty>()
@ -799,16 +801,7 @@ final class StorageUsageScreenComponent: Component {
private var keepScreenActiveDisposable: Disposable?
override init(frame: CGRect) {
self.headerOffsetContainer = UIView()
self.headerOffsetContainer.isUserInteractionEnabled = false
self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true)
self.navigationBackgroundView.alpha = 0.0
self.navigationSeparatorLayer = SimpleLayer()
self.navigationSeparatorLayer.opacity = 0.0
self.navigationSeparatorLayerContainer = SimpleLayer()
self.navigationSeparatorLayerContainer.opacity = 0.0
self.headerOffsetContainer = SparseContainerView()
self.scrollContainerView = UIView()
@ -821,6 +814,8 @@ final class StorageUsageScreenComponent: Component {
self.headerProgressBackgroundLayer = SimpleLayer()
self.headerProgressForegroundLayer = SimpleLayer()
self.navigationRightButtonsBackground = GlassBackgroundView()
super.init(frame: frame)
self.scrollView.delaysContentTouches = true
@ -846,13 +841,6 @@ final class StorageUsageScreenComponent: Component {
self.scrollView.layer.addSublayer(self.headerProgressBackgroundLayer)
self.scrollView.layer.addSublayer(self.headerProgressForegroundLayer)
self.addSubview(self.navigationBackgroundView)
self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer)
self.layer.addSublayer(self.navigationSeparatorLayerContainer)
self.addSubview(self.headerOffsetContainer)
}
required init?(coder: NSCoder) {
@ -946,9 +934,6 @@ final class StorageUsageScreenComponent: Component {
let animatedTransition = ComponentTransition(animation: .curve(duration: 0.18, curve: .easeInOut))
let navigationBackgroundAlpha: CGFloat = abs(headerOffset - minOffset) < 4.0 ? 1.0 : 0.0
animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha)
animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha)
var buttonsMasterAlpha: CGFloat = 1.0
if let component = self.component, component.peer != nil {
buttonsMasterAlpha = 0.0
@ -960,20 +945,12 @@ final class StorageUsageScreenComponent: Component {
}
}
let isSelectingPeers = self.aggregatedData?.isSelectingPeers ?? false
if let navigationEditButtonView = self.navigationEditButton.view {
animatedTransition.setAlpha(view: navigationEditButtonView, alpha: (isSelectingPeers ? 0.0 : 1.0) * buttonsMasterAlpha * navigationBackgroundAlpha)
}
if let navigationDoneButtonView = self.navigationDoneButton.view {
animatedTransition.setAlpha(view: navigationDoneButtonView, alpha: (isSelectingPeers ? 1.0 : 0.0) * buttonsMasterAlpha * navigationBackgroundAlpha)
}
animatedTransition.setAlpha(view: self.navigationRightButtonsBackground, alpha: buttonsMasterAlpha * navigationBackgroundAlpha)
let expansionDistance: CGFloat = 32.0
var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance
expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor))
transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor)
if let panelContainerView = self.panelContainer.view as? StorageUsagePanelContainerComponent.View {
panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition)
}
@ -983,6 +960,17 @@ final class StorageUsageScreenComponent: Component {
transition.setScale(view: headerView, scale: 1.0 * offsetFraction + 0.8 * (1.0 - offsetFraction))
transition.setBounds(view: self.headerOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: headerOffset), size: self.headerOffsetContainer.bounds.size))
let headerContentsAlpha = offsetFraction
if let chartAvatarNode = self.chartAvatarNode {
transition.setAlpha(view: chartAvatarNode.view, alpha: headerContentsAlpha)
}
if let pieChartComponentView = self.pieChartView.view {
transition.setAlpha(view: pieChartComponentView, alpha: headerContentsAlpha)
}
if let chartTotalLabelView = self.chartTotalLabel.view {
transition.setAlpha(view: chartTotalLabelView, alpha: headerContentsAlpha)
}
}
let _ = self.panelContainer.updateEnvironment(
@ -1109,6 +1097,13 @@ final class StorageUsageScreenComponent: Component {
self.reloadStats(firstTime: true, completion: {})
}
if self.headerOffsetContainer.superview == nil {
component.overNavigationContainer.addSubview(self.headerOffsetContainer)
}
if self.navigationRightButtonsBackground.superview == nil {
component.overNavigationContainer.addSubview(self.navigationRightButtonsBackground)
}
var wasLockedAtPanels = false
if let panelContainerView = self.panelContainer.view, let navigationMetrics = self.navigationMetrics {
if self.scrollView.bounds.minY > 0.0 && abs(self.scrollView.bounds.minY - (panelContainerView.frame.minY - navigationMetrics.navigationHeight)) <= UIScreenPixel {
@ -1147,22 +1142,11 @@ final class StorageUsageScreenComponent: Component {
self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight)
self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight))
self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition)
transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame)
let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel))
transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame)
transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size))
let navigationEditButtonSize = self.navigationEditButton.update(
transition: transition,
component: AnyComponent(Button(
content: AnyComponent(Text(text: environment.strings.Common_Edit, font: Font.regular(17.0), color: environment.theme.rootController.navigationBar.accentTextColor)),
content: AnyComponent(Text(text: environment.strings.Common_Edit, font: Font.regular(17.0), color: environment.theme.chat.inputPanel.panelControlColor)),
contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0),
action: { [weak self] in
guard let self else {
return
@ -1172,21 +1156,22 @@ final class StorageUsageScreenComponent: Component {
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)))
}
}
).minSize(CGSize(width: 16.0, height: environment.navigationHeight - environment.statusBarHeight))),
).minSize(CGSize(width: 44.0, height: 44.0))),
environment: {},
containerSize: CGSize(width: 150.0, height: environment.navigationHeight - environment.statusBarHeight)
containerSize: CGSize(width: 150.0, height: 44.0)
)
if let navigationEditButtonView = self.navigationEditButton.view {
if navigationEditButtonView.superview == nil {
self.addSubview(navigationEditButtonView)
self.navigationRightButtonsBackground.contentView.addSubview(navigationEditButtonView)
}
transition.setFrame(view: navigationEditButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - 12.0 - environment.safeInsets.right - navigationEditButtonSize.width, y: environment.statusBarHeight), size: navigationEditButtonSize))
transition.setFrame(view: navigationEditButtonView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: navigationEditButtonSize))
}
let navigationDoneButtonSize = self.navigationDoneButton.update(
transition: transition,
component: AnyComponent(Button(
content: AnyComponent(Text(text: environment.strings.Common_Done, font: Font.semibold(17.0), color: environment.theme.rootController.navigationBar.accentTextColor)),
content: AnyComponent(Text(text: environment.strings.Common_Done, font: Font.semibold(17.0), color: environment.theme.chat.inputPanel.panelControlColor)),
contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0),
action: { [weak self] in
guard let self, let aggregatedData = self.aggregatedData else {
return
@ -1195,19 +1180,39 @@ final class StorageUsageScreenComponent: Component {
aggregatedData.clearPeerSelection()
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)))
}
).minSize(CGSize(width: 16.0, height: environment.navigationHeight - environment.statusBarHeight))),
).minSize(CGSize(width: 44.0, height: 44.0))),
environment: {},
containerSize: CGSize(width: 150.0, height: environment.navigationHeight - environment.statusBarHeight)
containerSize: CGSize(width: 150.0, height: 44.0)
)
if let navigationDoneButtonView = self.navigationDoneButton.view {
if navigationDoneButtonView.superview == nil {
self.addSubview(navigationDoneButtonView)
self.navigationRightButtonsBackground.contentView.addSubview(navigationDoneButtonView)
}
transition.setFrame(view: navigationDoneButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - 12.0 - environment.safeInsets.right - navigationDoneButtonSize.width, y: environment.statusBarHeight), size: navigationDoneButtonSize))
transition.setFrame(view: navigationDoneButtonView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: navigationDoneButtonSize))
}
let navigationRightButtonMaxWidth: CGFloat = max(navigationEditButtonSize.width, navigationDoneButtonSize.width)
var rightButtonsWidth: CGFloat = 0.0
let isSelectingPeers = self.aggregatedData?.isSelectingPeers ?? false
if let navigationEditButtonView = self.navigationEditButton.view {
if !isSelectingPeers {
rightButtonsWidth += navigationEditButtonSize.width
}
transition.setAlpha(view: navigationEditButtonView, alpha: isSelectingPeers ? 0.0 : 1.0)
}
if let navigationDoneButtonView = self.navigationDoneButton.view {
if isSelectingPeers {
rightButtonsWidth += navigationDoneButtonSize.width
}
transition.setAlpha(view: navigationDoneButtonView, alpha: isSelectingPeers ? 1.0 : 0.0)
}
let navigationRightButtonsBackgroundSize = CGSize(width: max(44.0, rightButtonsWidth), height: 44.0)
self.navigationRightButtonsBackground.update(size: navigationRightButtonsBackgroundSize, cornerRadius: 44.0 * 0.5, isDark: environment.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: environment.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
let navigationRightButtonsBackgroundFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - navigationRightButtonsBackgroundSize.width, y: environment.statusBarHeight + 2.0 + floor((environment.navigationHeight - environment.statusBarHeight - 44.0) * 0.5)), size: navigationRightButtonsBackgroundSize)
transition.setFrame(view: self.navigationRightButtonsBackground, frame: navigationRightButtonsBackgroundFrame)
self.backgroundColor = environment.theme.list.blocksBackgroundColor
var contentHeight: CGFloat = 0.0
@ -1436,7 +1441,7 @@ final class StorageUsageScreenComponent: Component {
let pieChartFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: pieChartSize)
if let pieChartComponentView = self.pieChartView.view {
if pieChartComponentView.superview == nil {
self.scrollView.addSubview(pieChartComponentView)
self.headerOffsetContainer.addSubview(pieChartComponentView)
}
pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame)
@ -1637,7 +1642,7 @@ final class StorageUsageScreenComponent: Component {
} else {
chartAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 17.0))
self.chartAvatarNode = chartAvatarNode
self.scrollContainerView.addSubview(chartAvatarNode.view)
self.headerOffsetContainer.addSubview(chartAvatarNode.view)
chartAvatarNode.frame = avatarFrame
if peer.id == component.context.account.peerId {
@ -1681,7 +1686,7 @@ final class StorageUsageScreenComponent: Component {
)
if let chartTotalLabelView = self.chartTotalLabel.view {
if chartTotalLabelView.superview == nil {
self.scrollContainerView.addSubview(chartTotalLabelView)
self.headerOffsetContainer.addSubview(chartTotalLabelView)
}
let totalLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - chartTotalLabelSize.width) / 2.0), y: pieChartFrame.minY + floor((pieChartFrame.height - chartTotalLabelSize.height) / 2.0)), size: chartTotalLabelSize)
transition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame)
@ -3303,6 +3308,8 @@ final class StorageUsageScreenComponent: Component {
public final class StorageUsageScreen: ViewControllerComponentContainer {
private let context: AccountContext
private let overNavigationContainer: UIView
private let readyValue = Promise<Bool>()
override public var ready: Promise<Bool> {
return self.readyValue
@ -3313,13 +3320,19 @@ public final class StorageUsageScreen: ViewControllerComponentContainer {
public init(context: AccountContext, makeStorageUsageExceptionsScreen: @escaping (CacheStorageSettings.PeerStorageCategory) -> ViewController?, peer: EnginePeer? = nil) {
self.context = context
self.overNavigationContainer = SparseContainerView()
let componentReady = Promise<Bool>()
super.init(context: context, component: StorageUsageScreenComponent(context: context, makeStorageUsageExceptionsScreen: makeStorageUsageExceptionsScreen, peer: peer, ready: componentReady), navigationBarAppearance: .transparent)
super.init(context: context, component: StorageUsageScreenComponent(context: context, overNavigationContainer: self.overNavigationContainer, makeStorageUsageExceptionsScreen: makeStorageUsageExceptionsScreen, peer: peer, ready: componentReady), navigationBarAppearance: .default)
if peer != nil {
self.navigationPresentation = .modal
}
if let navigationBar = self.navigationBar {
navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
}
self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true)))
}

View file

@ -761,7 +761,7 @@ public final class StoryPeerListComponent: Component {
let collapsedItemWidth: CGFloat = 24.0
let collapsedItemDistance: CGFloat = 14.0
let collapsedItemOffsetY: CGFloat = -54.0
let collapsedItemOffsetY: CGFloat = -66.0
let titleContentSpacing: CGFloat = 8.0
let collapsedItemCount: CGFloat = CGFloat(min(self.sortedItems.count - collapseStartIndex, 3))
@ -1409,7 +1409,7 @@ public final class StoryPeerListComponent: Component {
}
if let titleIndicatorSize, let titleIndicatorView = self.titleIndicatorView?.view {
let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset - titleIndicatorSize.width - 9.0, y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize)
let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset - titleIndicatorSize.width - 9.0, y: collapsedItemOffsetY + 14.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize)
if titleIndicatorView.superview == nil {
self.addSubview(titleIndicatorView)
}
@ -1427,7 +1427,7 @@ public final class StoryPeerListComponent: Component {
titleIndicatorView.alpha = indicatorAlpha
}
let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset + titleLockOffset, y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize)
let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset + titleLockOffset, y: collapsedItemOffsetY + 14.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize)
if let image = self.titleView.image {
self.titleView.center = CGPoint(x: titleFrame.minX, y: titleFrame.midY)
self.titleView.bounds = CGRect(origin: CGPoint(), size: image.size)
@ -1494,7 +1494,7 @@ public final class StoryPeerListComponent: Component {
if let titleIconSize, let titleIconView = self.titleIconView?.view {
titleContentOffset += titleIconSpacing
let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset - 3.0 + titleIconSpacing + (collapsedState.titleWidth - (titleIconSpacing + titleIconSize.width)) * (1.0 - collapsedState.activityFraction), y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize)
let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset - 3.0 + titleIconSpacing + (collapsedState.titleWidth - (titleIconSpacing + titleIconSize.width)) * (1.0 - collapsedState.activityFraction), y: collapsedItemOffsetY + 14.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize)
if titleIconView.superview == nil {
self.addSubview(titleIconView)
@ -1729,7 +1729,7 @@ public final class StoryPeerListComponent: Component {
let itemLayout = ItemLayout(
containerSize: availableSize,
containerInsets: UIEdgeInsets(top: 4.0, left: component.sideInset - 4.0, bottom: 0.0, right: component.sideInset - 4.0),
containerInsets: UIEdgeInsets(top: 16.0, left: component.sideInset - 4.0, bottom: 0.0, right: component.sideInset - 4.0),
itemSize: CGSize(width: 60.0, height: 77.0),
itemSpacing: 14.0,
itemCount: self.sortedItems.count

View file

@ -771,7 +771,10 @@ public final class TabBarComponent: Component {
lensSize = CGSize(width: 48.0, height: 48.0)
lensSelection = (0.0, 48.0)
}
self.liquidLensView.update(size: lensSize, selectionX: lensSelection.x, selectionWidth: lensSelection.width, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: isLensCollapsed, transition: transition)
lensSelection.x = max(0.0, min(lensSelection.x, lensSize.width - lensSelection.width))
self.liquidLensView.update(size: lensSize, selectionX: lensSelection.x, selectionWidth: lensSelection.width, inset: 4.0, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: isLensCollapsed, transition: transition)
var size = tabsSize

View file

@ -632,7 +632,7 @@ public final class TabSelectorComponent: Component {
if case .component = item.content {
useSelectionFraction = true
}
if let _ = component.colors.normal {
if let normal = component.colors.normal, normal != component.colors.foreground {
useSelectionFraction = true
}

View file

@ -238,12 +238,12 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
guard case .glass = self.style else {
return
}
let barButtonSize = CGSize(width: 40.0, height: 40.0)
let barButtonSize = CGSize(width: 44.0, height: 44.0)
let closeComponent: AnyComponentWithIdentity<Empty> = AnyComponentWithIdentity(
id: "close",
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: self.presentationData.theme.overallDarkAppearance,
state: .generic,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
@ -264,7 +264,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
id: "search",
component: AnyComponent(GlassBarButtonComponent(
size: barButtonSize,
backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
backgroundColor: nil,
isDark: self.presentationData.theme.overallDarkAppearance,
state: .generic,
component: AnyComponentWithIdentity(id: "search", component: AnyComponent(
@ -291,15 +291,19 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
self.closeButtonNode = closeButtonNode
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: closeButtonNode)
}
let searchButtonNode: BarComponentHostNode
if let current = self.searchButtonNode {
searchButtonNode = current
searchButtonNode.component = searchComponent
if searchComponent != nil {
let searchButtonNode: BarComponentHostNode
if let current = self.searchButtonNode {
searchButtonNode = current
searchButtonNode.component = searchComponent
} else {
searchButtonNode = BarComponentHostNode(component: searchComponent, size: barButtonSize)
self.searchButtonNode = searchButtonNode
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: searchButtonNode)
}
} else {
searchButtonNode = BarComponentHostNode(component: searchComponent, size: barButtonSize)
self.searchButtonNode = searchButtonNode
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: searchButtonNode)
self.navigationItem.rightBarButtonItem = nil
}
}
@ -593,10 +597,12 @@ final class ContactsSearchNavigationContentNode: NavigationBarContentNode {
return 56.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate() {

View file

@ -54,10 +54,12 @@ final class WebSearchNavigationContentNode: NavigationBarContentNode {
return 56.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
return size
}
func activate(select: Bool = false) {