mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +00:00
Update next channel navigation UI
This commit is contained in:
parent
321f4002ab
commit
0a0517122c
@ -289,9 +289,10 @@ public final class NavigateToChatControllerParams {
|
||||
public let animated: Bool
|
||||
public let options: NavigationAnimationOptions
|
||||
public let parentGroupId: PeerGroupId?
|
||||
public let chatListFilter: ChatListFilterData?
|
||||
public let completion: (ChatController) -> Void
|
||||
|
||||
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping (ChatController) -> Void = { _ in }) {
|
||||
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: ChatListFilterData? = nil, completion: @escaping (ChatController) -> Void = { _ in }) {
|
||||
self.navigationController = navigationController
|
||||
self.chatController = chatController
|
||||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
@ -312,6 +313,7 @@ public final class NavigateToChatControllerParams {
|
||||
self.animated = animated
|
||||
self.options = options
|
||||
self.parentGroupId = parentGroupId
|
||||
self.chatListFilter = chatListFilter
|
||||
self.completion = completion
|
||||
}
|
||||
}
|
||||
|
||||
@ -612,7 +612,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
scrollToEndIfExists = true
|
||||
}
|
||||
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] controller in
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, chatListFilter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter?.data, completion: { [weak self] controller in
|
||||
self?.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true)
|
||||
if let promoInfo = promoInfo {
|
||||
switch promoInfo {
|
||||
|
||||
@ -28,7 +28,7 @@ struct ChatListNodeViewUpdate {
|
||||
let scrollPosition: ChatListNodeViewScrollPosition?
|
||||
}
|
||||
|
||||
func chatListFilterPredicate(filter: ChatListFilterData) -> ChatListFilterPredicate {
|
||||
public func chatListFilterPredicate(filter: ChatListFilterData) -> ChatListFilterPredicate {
|
||||
var includePeers = Set(filter.includePeers.peers)
|
||||
var excludePeers = Set(filter.excludePeers)
|
||||
|
||||
|
||||
@ -495,16 +495,19 @@ public extension TelegramEngine {
|
||||
return _internal_updatePeerDescription(account: self.account, peerId: peerId, description: description)
|
||||
}
|
||||
|
||||
public func getNextUnreadChannel(peerId: PeerId) -> Signal<EnginePeer?, NoError> {
|
||||
public func getNextUnreadChannel(peerId: PeerId, filter: ChatListFilterPredicate?) -> Signal<EnginePeer?, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> EnginePeer? in
|
||||
var peers: [RenderedPeer] = []
|
||||
peers.append(contentsOf: transaction.getTopChatListEntries(groupId: .root, count: 100))
|
||||
peers.append(contentsOf: transaction.getTopChatListEntries(groupId: Namespaces.PeerGroup.archive, count: 100))
|
||||
|
||||
var results: [(EnginePeer, Int32)] = []
|
||||
|
||||
for peer in peers {
|
||||
guard let channel = peer.chatMainPeer as? TelegramChannel, case .broadcast = channel.info else {
|
||||
var peerIds: [PeerId] = []
|
||||
peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: .root, filterPredicate: filter))
|
||||
peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: Namespaces.PeerGroup.archive, filterPredicate: filter))
|
||||
|
||||
for listId in peerIds {
|
||||
guard let peer = transaction.getPeer(listId) else {
|
||||
continue
|
||||
}
|
||||
guard let channel = peer as? TelegramChannel, case .broadcast = channel.info else {
|
||||
continue
|
||||
}
|
||||
if channel.id == peerId {
|
||||
@ -516,6 +519,7 @@ public extension TelegramEngine {
|
||||
guard let topMessageIndex = transaction.getTopPeerMessageIndex(peerId: channel.id) else {
|
||||
continue
|
||||
}
|
||||
|
||||
results.append((EnginePeer(channel), topMessageIndex.timestamp))
|
||||
}
|
||||
|
||||
|
||||
@ -137,6 +137,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
||||
case callsTabTip = 18
|
||||
case chatFolderTips = 19
|
||||
case locationProximityAlertTip = 20
|
||||
case nextChatSuggestionTip = 21
|
||||
|
||||
var key: ValueBoxKey {
|
||||
let v = ValueBoxKey(length: 4)
|
||||
@ -268,6 +269,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
static func locationProximityAlertTip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.locationProximityAlertTip.key)
|
||||
}
|
||||
|
||||
static func nextChatSuggestionTip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.nextChatSuggestionTip.key)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificNotice {
|
||||
@ -736,6 +741,28 @@ public struct ApplicationSpecificNotice {
|
||||
}
|
||||
}
|
||||
|
||||
public static func getNextChatSuggestionTip(accountManager: AccountManager) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.nextChatSuggestionTip()) as? ApplicationSpecificCounterNotice {
|
||||
return value.value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func incrementNextChatSuggestionTip(accountManager: AccountManager, count: Int32 = 1) -> Signal<Void, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
var currentValue: Int32 = 0
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.nextChatSuggestionTip()) as? ApplicationSpecificCounterNotice {
|
||||
currentValue = value.value
|
||||
}
|
||||
currentValue += count
|
||||
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.nextChatSuggestionTip(), ApplicationSpecificCounterNotice(value: currentValue))
|
||||
}
|
||||
}
|
||||
|
||||
public static func reset(accountManager: AccountManager) -> Signal<Void, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
}
|
||||
|
||||
@ -64,6 +64,7 @@ import TelegramPermissionsUI
|
||||
import Speak
|
||||
import UniversalMediaPlayer
|
||||
import WallpaperBackgroundNode
|
||||
import ChatListUI
|
||||
|
||||
#if DEBUG
|
||||
import os.signpost
|
||||
@ -213,6 +214,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
public weak var parentController: ViewController?
|
||||
|
||||
private let currentChatListFilter: ChatListFilterData?
|
||||
|
||||
public var peekActions: ChatControllerPeekActions = .standard
|
||||
private var didSetup3dTouch: Bool = false
|
||||
|
||||
@ -283,6 +286,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private var preloadHistoryPeerId: PeerId?
|
||||
private let preloadHistoryPeerIdDisposable = MetaDisposable()
|
||||
|
||||
private var preloadNextChatPeerId: PeerId?
|
||||
private let preloadNextChatPeerIdDisposable = MetaDisposable()
|
||||
|
||||
private let botCallbackAlertMessage = Promise<String?>(nil)
|
||||
private var botCallbackAlertMessageDisposable: Disposable?
|
||||
|
||||
@ -459,7 +465,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
private var nextChannelToReadDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil) {
|
||||
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: ChatListFilterData? = nil) {
|
||||
let _ = ChatControllerCount.modify { value in
|
||||
return value + 1
|
||||
}
|
||||
@ -470,6 +476,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.subject = subject
|
||||
self.botStart = botStart
|
||||
self.peekData = peekData
|
||||
self.currentChatListFilter = chatListFilter
|
||||
|
||||
var useSharedAnimationPhase = false
|
||||
switch mode {
|
||||
@ -3306,16 +3313,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
if strongSelf.nextChannelToReadDisposable == nil {
|
||||
strongSelf.nextChannelToReadDisposable = (strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id)
|
||||
|> deliverOnMainQueue
|
||||
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
|
||||
strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id, filter: strongSelf.currentChatListFilter.flatMap(chatListFilterPredicate)),
|
||||
ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager)
|
||||
)
|
||||
|> then(.complete() |> delay(1.0, queue: .mainQueue()))
|
||||
|> restart).start(next: { nextPeer in
|
||||
|> restart).start(next: { nextPeer, nextChatSuggestionTip in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.offerNextChannelToRead = true
|
||||
strongSelf.chatDisplayNode.historyNode.nextChannelToRead = nextPeer
|
||||
strongSelf.chatDisplayNode.historyNode.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
|
||||
|
||||
let nextPeerId = nextPeer?.id
|
||||
|
||||
if strongSelf.preloadNextChatPeerId != nextPeerId {
|
||||
strongSelf.preloadNextChatPeerId = nextPeerId
|
||||
if let nextPeerId = nextPeerId {
|
||||
let combinedDisposable = DisposableSet()
|
||||
strongSelf.preloadNextChatPeerIdDisposable.set(combinedDisposable)
|
||||
combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: nextPeerId).start())
|
||||
combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: nextPeerId))
|
||||
} else {
|
||||
strongSelf.preloadNextChatPeerIdDisposable.set(nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -3878,6 +3902,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.shareStatusDisposable?.dispose()
|
||||
self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeTarget(self)
|
||||
self.preloadHistoryPeerIdDisposable.dispose()
|
||||
self.preloadNextChatPeerIdDisposable.dispose()
|
||||
self.reportIrrelvantGeoDisposable?.dispose()
|
||||
self.reminderActivity?.invalidate()
|
||||
self.updateSlowmodeStatusDisposable.dispose()
|
||||
@ -7053,11 +7078,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.effectiveNavigationController {
|
||||
ApplicationSpecificNotice.incrementNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||
|
||||
let snapshotState = strongSelf.chatDisplayNode.prepareSnapshotState(
|
||||
titleViewSnapshotState: strongSelf.chatTitleView?.prepareSnapshotState(),
|
||||
avatarSnapshotState: (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState()
|
||||
)
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: false, completion: { nextController in
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: false, chatListFilter: strongSelf.currentChatListFilter, completion: { nextController in
|
||||
(nextController as! ChatControllerImpl).animateFromPreviousController(snapshotState: snapshotState)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -551,6 +551,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
private var overscrollView: ComponentHostView<Empty>?
|
||||
var nextChannelToRead: EnginePeer?
|
||||
var offerNextChannelToRead: Bool = false
|
||||
var nextChannelToReadDisplayName: Bool = false
|
||||
private var currentOverscrollExpandProgress: CGFloat = 0.0
|
||||
private var feedback: HapticFeedback?
|
||||
var openNextChannelToRead: ((EnginePeer) -> Void)?
|
||||
@ -1217,16 +1218,26 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self.view.addSubview(overscrollView)
|
||||
}
|
||||
|
||||
let expandProgress: CGFloat = min(1.0, max(-offset, 0.0) / 110.0)
|
||||
let expandDistance = max(-offset - 12.0, 0.0)
|
||||
let expandProgress: CGFloat = min(1.0, expandDistance / 90.0)
|
||||
|
||||
let text: String
|
||||
if self.nextChannelToRead != nil {
|
||||
if let nextChannelToRead = nextChannelToRead {
|
||||
if self.nextChannelToReadDisplayName {
|
||||
if expandProgress >= 0.99 {
|
||||
//TODO:localize
|
||||
text = "Release to go to \(nextChannelToRead.compactDisplayTitle)"
|
||||
} else {
|
||||
text = "Swipe up to go to \(nextChannelToRead.compactDisplayTitle)"
|
||||
}
|
||||
} else {
|
||||
if expandProgress >= 0.99 {
|
||||
//TODO:localize
|
||||
text = "Release to go to the next unread channel"
|
||||
} else {
|
||||
text = "Swipe up to go to the next unread channel"
|
||||
}
|
||||
}
|
||||
|
||||
let previousType = self.currentOverscrollExpandProgress >= 0.99
|
||||
let currentType = expandProgress >= 0.99
|
||||
@ -1251,7 +1262,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
foregroundColor: bubbleVariableColor(variableColor: self.currentPresentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: self.currentPresentationData.theme.wallpaper),
|
||||
peer: self.nextChannelToRead,
|
||||
context: self.context,
|
||||
expandProgress: expandProgress
|
||||
expandDistance: expandDistance
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: self.bounds.width, height: 200.0)
|
||||
|
||||
@ -341,7 +341,7 @@ final class ChatOverscrollControl: CombinedComponent {
|
||||
let foregroundColor: UIColor
|
||||
let peer: EnginePeer?
|
||||
let context: AccountContext
|
||||
let expandProgress: CGFloat
|
||||
let expandDistance: CGFloat
|
||||
|
||||
init(
|
||||
text: String,
|
||||
@ -349,14 +349,14 @@ final class ChatOverscrollControl: CombinedComponent {
|
||||
foregroundColor: UIColor,
|
||||
peer: EnginePeer?,
|
||||
context: AccountContext,
|
||||
expandProgress: CGFloat
|
||||
expandDistance: CGFloat
|
||||
) {
|
||||
self.text = text
|
||||
self.backgroundColor = backgroundColor
|
||||
self.foregroundColor = foregroundColor
|
||||
self.peer = peer
|
||||
self.context = context
|
||||
self.expandProgress = expandProgress
|
||||
self.expandDistance = expandDistance
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatOverscrollControl, rhs: ChatOverscrollControl) -> Bool {
|
||||
@ -375,7 +375,7 @@ final class ChatOverscrollControl: CombinedComponent {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.expandProgress != rhs.expandProgress {
|
||||
if lhs.expandDistance != rhs.expandDistance {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -431,13 +431,32 @@ final class ChatOverscrollControl: CombinedComponent {
|
||||
)
|
||||
})
|
||||
|
||||
let progressSize = avatarBackground.size.width - avatarProgressPadding * 2.0
|
||||
|
||||
let halfDistance = progressSize
|
||||
let quarterDistance = halfDistance / 2.0
|
||||
|
||||
let clippedDistance = max(0.0, min(halfDistance * 2.0, context.component.expandDistance))
|
||||
|
||||
var mappedProgress: CGFloat
|
||||
if clippedDistance <= quarterDistance {
|
||||
mappedProgress = acos(1.0 - clippedDistance / quarterDistance) / (CGFloat.pi * 2.0)
|
||||
} else if clippedDistance <= halfDistance {
|
||||
let sectionDistance = halfDistance - clippedDistance
|
||||
mappedProgress = 0.25 + asin(1.0 - sectionDistance / quarterDistance) / (CGFloat.pi * 2.0)
|
||||
} else {
|
||||
let restDistance = clippedDistance - halfDistance
|
||||
mappedProgress = min(1.0, 0.5 + restDistance / 60.0)
|
||||
}
|
||||
mappedProgress = max(0.01, mappedProgress)
|
||||
|
||||
let avatarExpandProgress = avatarExpandProgress.update(
|
||||
component: RadialProgressComponent(
|
||||
color: context.component.foregroundColor,
|
||||
lineWidth: 2.5,
|
||||
value: context.component.peer == nil ? 0.0 : context.component.expandProgress
|
||||
value: context.component.peer == nil ? 0.0 : mappedProgress
|
||||
),
|
||||
availableSize: CGSize(width: avatarBackground.size.width - avatarProgressPadding * 2.0, height: avatarBackground.size.height - avatarProgressPadding * 2.0),
|
||||
availableSize: CGSize(width: progressSize, height: progressSize),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
||||
})
|
||||
}
|
||||
} else {
|
||||
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData)
|
||||
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter)
|
||||
}
|
||||
controller.purposefulAction = params.purposefulAction
|
||||
if let search = params.activateMessageSearch {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user