Forum improvements

This commit is contained in:
Ali 2022-11-22 19:50:50 +04:00
parent dc085a2fe9
commit 80a7324668
30 changed files with 1076 additions and 443 deletions

View File

@ -8302,3 +8302,10 @@ Sorry for the inconvenience.";
"CreateTopic.ShowGeneral" = "Show in Topics";
"CreateTopic.ShowGeneralInfo" = "If the 'General' topic is hidden, group members can pull down in the topic list to view it.";
"ChatList.DeleteThreadsConfirmation_1" = "Delete";
"ChatList.DeleteThreadsConfirmation_any" = "Delete %@ Threads";
"ChatList.DeletedThreads_1" = "Deleted 1 thread";
"ChatList.DeletedThreads_any" = "Deleted %@ threads";
"DialogList.SearchSectionMessagesIn" = "Messages in %@";

View File

@ -21,7 +21,7 @@ public enum ChatListSearchItemHeaderType {
case chats
case chatTypes
case faq
case messages
case messages(location: String?)
case groupMembers
case activeVoiceChats
case recentCalls
@ -65,8 +65,12 @@ public enum ChatListSearchItemHeaderType {
return strings.ChatList_ChatTypesSection
case .faq:
return strings.Settings_FrequentlyAskedQuestions
case .messages:
case let .messages(location):
if let location {
return strings.DialogList_SearchSectionMessagesIn(location).string
} else {
return strings.DialogList_SearchSectionMessages
}
case .groupMembers:
return strings.Group_GroupMembersHeader
case .activeVoiceChats:
@ -120,8 +124,12 @@ public enum ChatListSearchItemHeaderType {
return .chatTypes
case .faq:
return .faq
case .messages:
case let .messages(location):
if let _ = location {
return .messagesWithLocation
} else {
return .messages
}
case .groupMembers:
return .groupMembers
case .activeVoiceChats:
@ -160,6 +168,7 @@ private enum ChatListSearchItemHeaderId: Int32 {
case chatTypes
case faq
case messages
case messagesWithLocation
case photos
case links
case files

View File

@ -173,6 +173,11 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
isUnread = true
}
var isForum = false
if case let .channel(channel) = peer, channel.flags.contains(.isForum) {
isForum = true
}
if case let .chatList(currentFilter) = source {
if let currentFilter = currentFilter, case let .filter(id, title, emoticon, data) = currentFilter {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromFolder, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/RemoveFromFolder"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
@ -317,7 +322,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
let _ = context.engine.messages.togglePeersUnreadMarkInteractively(peerIds: [peerId], setToValue: nil).start()
f(.default)
})))
} else {
} else if !isForum {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsUnread, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsUnread"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = context.engine.messages.togglePeersUnreadMarkInteractively(peerIds: [peerId], setToValue: nil).start()
f(.default)
@ -494,15 +499,12 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let strings = presentationData.strings
return combineLatest(
context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
),
context.engine.data.get(
return context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId),
TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId)
)
)
|> mapToSignal { peer, threadData -> Signal<[ContextMenuItem], NoError> in
|> mapToSignal { peer, peerNotificationSettings, threadData -> Signal<[ContextMenuItem], NoError> in
guard case let .channel(channel) = peer else {
return .single([])
}
@ -548,12 +550,19 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
}
var isMuted = false
if case .muted = threadData.notificationSettings.muteState {
switch threadData.notificationSettings.muteState {
case .muted:
isMuted = true
case .unmuted:
isMuted = false
case .default:
if case .muted = peerNotificationSettings.muteState {
isMuted = true
}
}
items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] c, f in
if isMuted {
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: threadId)
let _ = (context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: 0)
|> deliverOnMainQueue).start(completed: {
f(.default)
})
@ -776,7 +785,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
}
private func openCustomMute(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, baseController: ViewController) {
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, peerId: peerId, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak baseController] value in
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak baseController] value in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
if value <= 0 {

File diff suppressed because it is too large Load Diff

View File

@ -253,13 +253,17 @@ private final class ChatListShimmerNode: ASDisplayNode {
context.setFillColor(UIColor.clear.cgColor)
if !isInlineMode {
if itemNodes[sampleIndex].avatarNode.isHidden {
if !itemNodes[sampleIndex].avatarNode.isHidden {
context.fillEllipse(in: itemNodes[sampleIndex].avatarNode.frame.offsetBy(dx: 0.0, dy: currentY))
}
}
let titleFrame = itemNodes[sampleIndex].titleNode.frame.offsetBy(dx: 0.0, dy: currentY)
if isInlineMode {
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX + 22.0, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0 - 22.0)
} else {
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0)
}
let textFrame = itemNodes[sampleIndex].textNode.textNode.frame.offsetBy(dx: 0.0, dy: currentY)
@ -1157,7 +1161,15 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
private let animationCache: AnimationCache
private let animationRenderer: MultiAnimationRenderer
let containerNode: ChatListContainerNode
let mainContainerNode: ChatListContainerNode
var effectiveContainerNode: ChatListContainerNode {
if let inlineStackContainerNode = self.inlineStackContainerNode {
return inlineStackContainerNode
} else {
return self.mainContainerNode
}
}
private(set) var inlineStackContainerTransitionFraction: CGFloat = 0.0
private(set) var inlineStackContainerNode: ChatListContainerNode?
@ -1206,7 +1218,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
var filterBecameEmpty: ((ChatListFilter?) -> Void)?
var filterEmptyAction: ((ChatListFilter?) -> Void)?
var secondaryEmptyAction: (() -> Void)?
self.containerNode = ChatListContainerNode(context: context, location: location, previewing: previewing, controlsHistoryPreload: controlsHistoryPreload, isInlineMode: false, presentationData: presentationData, animationCache: animationCache, animationRenderer: animationRenderer, filterBecameEmpty: { filter in
self.mainContainerNode = ChatListContainerNode(context: context, location: location, previewing: previewing, controlsHistoryPreload: controlsHistoryPreload, isInlineMode: false, presentationData: presentationData, animationCache: animationCache, animationRenderer: animationRenderer, filterBecameEmpty: { filter in
filterBecameEmpty?(filter)
}, filterEmptyAction: { filter in
filterEmptyAction?(filter)
@ -1224,12 +1236,12 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.backgroundColor = presentationData.theme.chatList.backgroundColor
self.addSubnode(self.containerNode)
self.addSubnode(self.mainContainerNode)
self.containerNode.contentOffsetChanged = { [weak self] offset in
self.mainContainerNode.contentOffsetChanged = { [weak self] offset in
self?.contentOffsetChanged(offset: offset, isPrimary: true)
}
self.containerNode.contentScrollingEnded = { [weak self] listView in
self.mainContainerNode.contentScrollingEnded = { [weak self] listView in
return self?.contentScrollingEnded(listView: listView, isPrimary: true) ?? false
}
@ -1259,7 +1271,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
(controller.navigationController as? NavigationController)?.replaceController(controller, with: chatController, animated: false)
}
self.containerNode.onFilterSwitch = { [weak self] in
self.mainContainerNode.onFilterSwitch = { [weak self] in
if let strongSelf = self {
strongSelf.controller?.dismissAllUndoControllers()
}
@ -1362,7 +1374,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
self.containerNode.updatePresentationData(presentationData)
self.mainContainerNode.updatePresentationData(presentationData)
self.inlineStackContainerNode?.updatePresentationData(presentationData)
self.searchDisplayController?.updatePresentationData(presentationData)
@ -1432,7 +1444,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
childrenLayout.intrinsicInsets = UIEdgeInsets(top: visualNavigationHeight, left: childrenLayout.intrinsicInsets.left, bottom: childrenLayout.intrinsicInsets.bottom, right: childrenLayout.intrinsicInsets.right)
self.controller?.presentationContext.containerLayoutUpdated(childrenLayout, transition: transition)
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.mainContainerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
var mainNavigationBarHeight = navigationBarHeight
var cleanMainNavigationBarHeight = cleanNavigationBarHeight
var mainInsets = insets
@ -1441,13 +1453,13 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
cleanMainNavigationBarHeight = visualNavigationHeight
mainInsets.top = visualNavigationHeight
}
self.containerNode.update(layout: layout, navigationBarHeight: mainNavigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: cleanMainNavigationBarHeight, insets: mainInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: self.inlineStackContainerNode?.location, inlineNavigationTransitionFraction: self.inlineStackContainerTransitionFraction, transition: transition)
self.mainContainerNode.update(layout: layout, navigationBarHeight: mainNavigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: cleanMainNavigationBarHeight, insets: mainInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: self.inlineStackContainerNode?.location, inlineNavigationTransitionFraction: self.inlineStackContainerTransitionFraction, transition: transition)
if let inlineStackContainerNode = self.inlineStackContainerNode {
var inlineStackContainerNodeTransition = transition
var animateIn = false
if inlineStackContainerNode.supernode == nil {
self.insertSubnode(inlineStackContainerNode, aboveSubnode: self.containerNode)
self.insertSubnode(inlineStackContainerNode, aboveSubnode: self.mainContainerNode)
inlineStackContainerNodeTransition = .immediate
animateIn = true
}
@ -1486,9 +1498,9 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
let effectiveLocation = self.inlineStackContainerNode?.location ?? self.location
var filter: ChatListNodePeersFilter = []
let filter: ChatListNodePeersFilter = []
if case .forum = effectiveLocation {
filter.insert(.excludeRecent)
//filter.insert(.excludeRecent)
}
let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, location: effectiveLocation, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in
@ -1515,7 +1527,8 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
requestDeactivateSearch()
}
})
self.containerNode.accessibilityElementsHidden = true
self.mainContainerNode.accessibilityElementsHidden = true
self.inlineStackContainerNode?.accessibilityElementsHidden = true
return (contentNode.filterContainerNode, { [weak self] focus in
guard let strongSelf = self else {
@ -1538,7 +1551,8 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
if let searchDisplayController = self.searchDisplayController {
searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated)
self.searchDisplayController = nil
self.containerNode.accessibilityElementsHidden = false
self.mainContainerNode.accessibilityElementsHidden = false
self.inlineStackContainerNode?.accessibilityElementsHidden = false
return { [weak self] in
if let strongSelf = self, let (layout, _, _, cleanNavigationBarHeight) = strongSelf.containerLayout {
@ -1551,7 +1565,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
}
func clearHighlightAnimated(_ animated: Bool) {
self.containerNode.currentItemNode.clearHighlightAnimated(true)
self.mainContainerNode.currentItemNode.clearHighlightAnimated(true)
self.inlineStackContainerNode?.currentItemNode.clearHighlightAnimated(true)
}
@ -1581,7 +1595,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
if isPrimary {
targetNode = inlineStackContainerNode
} else {
targetNode = self.containerNode
targetNode = self.mainContainerNode
}
switch offset {
@ -1626,24 +1640,27 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
return self.contentScrollingEnded?(listView) ?? false
}
func setInlineChatList(location: ChatListControllerLocation?) {
if let location = location {
if self.inlineStackContainerNode?.location != location {
func makeInlineChatList(location: ChatListControllerLocation) -> ChatListContainerNode {
let inlineStackContainerNode = ChatListContainerNode(context: self.context, location: location, previewing: false, controlsHistoryPreload: false, isInlineMode: true, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filterBecameEmpty: { _ in }, filterEmptyAction: { _ in }, secondaryEmptyAction: {})
return inlineStackContainerNode
}
func setInlineChatList(inlineStackContainerNode: ChatListContainerNode?) {
if let inlineStackContainerNode = inlineStackContainerNode {
if self.inlineStackContainerNode !== inlineStackContainerNode {
inlineStackContainerNode.leftSeparatorLayer.isHidden = false
inlineStackContainerNode.presentAlert = self.containerNode.presentAlert
inlineStackContainerNode.present = self.containerNode.present
inlineStackContainerNode.push = self.containerNode.push
inlineStackContainerNode.deletePeerChat = self.containerNode.deletePeerChat
inlineStackContainerNode.deletePeerThread = self.containerNode.deletePeerThread
inlineStackContainerNode.setPeerThreadStopped = self.containerNode.setPeerThreadStopped
inlineStackContainerNode.setPeerThreadPinned = self.containerNode.setPeerThreadPinned
inlineStackContainerNode.setPeerThreadHidden = self.containerNode.setPeerThreadHidden
inlineStackContainerNode.peerSelected = self.containerNode.peerSelected
inlineStackContainerNode.groupSelected = self.containerNode.groupSelected
inlineStackContainerNode.updatePeerGrouping = self.containerNode.updatePeerGrouping
inlineStackContainerNode.presentAlert = self.mainContainerNode.presentAlert
inlineStackContainerNode.present = self.mainContainerNode.present
inlineStackContainerNode.push = self.mainContainerNode.push
inlineStackContainerNode.deletePeerChat = self.mainContainerNode.deletePeerChat
inlineStackContainerNode.deletePeerThread = self.mainContainerNode.deletePeerThread
inlineStackContainerNode.setPeerThreadStopped = self.mainContainerNode.setPeerThreadStopped
inlineStackContainerNode.setPeerThreadPinned = self.mainContainerNode.setPeerThreadPinned
inlineStackContainerNode.setPeerThreadHidden = self.mainContainerNode.setPeerThreadHidden
inlineStackContainerNode.peerSelected = self.mainContainerNode.peerSelected
inlineStackContainerNode.groupSelected = self.mainContainerNode.groupSelected
inlineStackContainerNode.updatePeerGrouping = self.mainContainerNode.updatePeerGrouping
inlineStackContainerNode.contentOffsetChanged = { [weak self] offset in
self?.contentOffsetChanged(offset: offset, isPrimary: false)
@ -1652,9 +1669,9 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
return self?.contentScrollingEnded(listView: listView, isPrimary: false) ?? false
}
inlineStackContainerNode.activateChatPreview = self.containerNode.activateChatPreview
inlineStackContainerNode.addedVisibleChatsWithPeerIds = self.containerNode.addedVisibleChatsWithPeerIds
inlineStackContainerNode.didBeginSelectingChats = nil
inlineStackContainerNode.activateChatPreview = self.mainContainerNode.activateChatPreview
inlineStackContainerNode.addedVisibleChatsWithPeerIds = self.mainContainerNode.addedVisibleChatsWithPeerIds
inlineStackContainerNode.didBeginSelectingChats = self.mainContainerNode.didBeginSelectingChats
inlineStackContainerNode.displayFilterLimit = nil
let previousInlineStackContainerNode = self.inlineStackContainerNode
@ -1663,7 +1680,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.inlineStackContainerTransitionFraction = 1.0
if let _ = self.containerLayout {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
let transition: ContainedViewLayoutTransition = .animated(duration: 0.5, curve: .spring)
if let previousInlineStackContainerNode {
transition.updatePosition(node: previousInlineStackContainerNode, position: CGPoint(x: previousInlineStackContainerNode.position.x + previousInlineStackContainerNode.bounds.width + UIScreenPixel, y: previousInlineStackContainerNode.position.y), completion: { [weak previousInlineStackContainerNode] _ in
@ -1681,7 +1698,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.inlineStackContainerNode = nil
self.inlineStackContainerTransitionFraction = 0.0
self.containerNode.contentScrollingEnded = self.contentScrollingEnded
self.mainContainerNode.contentScrollingEnded = self.contentScrollingEnded
if let _ = self.containerLayout {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
@ -1701,7 +1718,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
}
func playArchiveAnimation() {
self.containerNode.playArchiveAnimation()
self.mainContainerNode.playArchiveAnimation()
}
func scrollToTop() {
@ -1710,7 +1727,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
} else if let inlineStackContainerNode = self.inlineStackContainerNode {
inlineStackContainerNode.scrollToTop()
} else {
self.containerNode.scrollToTop()
self.mainContainerNode.scrollToTop()
}
}
}

View File

@ -136,6 +136,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var validLayout: (ContainerViewLayout, CGFloat)?
public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, location: ChatListControllerLocation, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
var initialFilter = initialFilter
if case .chats = initialFilter, case .forum = location {
initialFilter = .topics
}
self.context = context
self.peersFilter = filter
self.location = location

View File

@ -720,7 +720,11 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
openClearRecentlyDownloaded()
})
case .index:
header = ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
var headerType: ChatListSearchItemHeaderType = .messages(location: nil)
if case .forum = location, let peer = peer.peer {
headerType = .messages(location: peer.compactDisplayTitle)
}
header = ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
}
let selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none
var isMedia = false
@ -980,7 +984,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
var peersFilter = peersFilter
if case .forum = location {
peersFilter.insert(.excludeRecent)
//peersFilter.insert(.excludeRecent)
} else if case .chatList(.archive) = location {
peersFilter.insert(.excludeRecent)
}
@ -1106,7 +1110,21 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let previousRecentlySearchedPeerOrder = Atomic<[EnginePeer.Id]>(value: [])
let fixedRecentlySearchedPeers: Signal<[RecentlySearchedPeer], NoError>
if case .chats = key, case .chatList(.root) = location {
var enableRecentlySearched = false
if !self.peersFilter.contains(.excludeRecent) {
if case .chats = key {
if case .chatList(.root) = location {
enableRecentlySearched = true
} else if case .forum = location {
enableRecentlySearched = true
}
} else if case .topics = key, case .forum = location {
enableRecentlySearched = true
}
}
if enableRecentlySearched {
fixedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers()
|> map { peers -> [RecentlySearchedPeer] in
var result: [RecentlySearchedPeer] = []
@ -1283,7 +1301,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1)
let foundLocalPeers: Signal<(peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set<EnginePeer.Id>), NoError>
if let query = query, case .chats = key {
if let query = query, (key == .chats || key == .topics) {
let fixedOrRemovedRecentlySearchedPeers = context.engine.peers.recentlySearchedPeers()
|> map { peers -> [RecentlySearchedPeer] in
let allIds = peers.map(\.peer.peerId)
@ -3071,6 +3089,11 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in })
var isInlineMode = false
if case .topics = key {
isInlineMode = true
}
interaction.isInlineMode = isInlineMode
let items = (0 ..< 2).compactMap { _ -> ListViewItem? in
switch key {
@ -3277,17 +3300,36 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
let selectionOffset: CGFloat = hasSelection ? 45.0 : 0.0
if let itemNode = itemNodes[sampleIndex] as? ChatListItemNode {
if !isInlineMode {
if !itemNode.avatarNode.isHidden {
context.fillEllipse(in: itemNode.avatarNode.frame.offsetBy(dx: 0.0, dy: currentY))
}
}
let titleFrame = itemNode.titleNode.frame.offsetBy(dx: 0.0, dy: currentY)
if isInlineMode {
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX + 22.0, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0 - 22.0)
} else {
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0)
}
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: currentY + itemHeight - floor(itemNode.titleNode.frame.midY - fakeLabelPlaceholderHeight / 2.0) - fakeLabelPlaceholderHeight), width: 60.0)
let textFrame = itemNode.textNode.textNode.frame.offsetBy(dx: 0.0, dy: currentY)
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 120.0)
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX + 120.0 + 10.0, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 60.0)
if isInlineMode {
context.fillEllipse(in: CGRect(origin: CGPoint(x: textFrame.minX, y: titleFrame.minY + 2.0), size: CGSize(width: 16.0, height: 16.0)))
}
fillLabelPlaceholderRect(origin: CGPoint(x: textFrame.minX, y: currentY + itemHeight - floor(itemNode.titleNode.frame.midY - fakeLabelPlaceholderHeight / 2.0) - fakeLabelPlaceholderHeight), width: 60.0)
fillLabelPlaceholderRect(origin: CGPoint(x: textFrame.minX, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 120.0)
fillLabelPlaceholderRect(origin: CGPoint(x: textFrame.minX + 120.0 + 10.0, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 60.0)
let dateFrame = itemNode.dateNode.frame.offsetBy(dx: 0.0, dy: currentY)
fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: floor(dateFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 30.0)
fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: dateFrame.minY), width: 30.0)
context.setBlendMode(.normal)
context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor)
context.fill(itemNode.separatorNode.frame.offsetBy(dx: 0.0, dy: currentY))
context.setBlendMode(.normal)
context.setFillColor(presentationData.theme.chatList.itemSeparatorColor.cgColor)

View File

@ -58,21 +58,21 @@ func chatListSelectionOptions(context: AccountContext, peerIds: Set<PeerId>, fil
}
func forumSelectionOptions(context: AccountContext, peerId: PeerId, threadIds: Set<Int64>, canDelete: Bool) -> Signal<ChatListSelectionOptions, NoError> {
let threadIdsArray = Array(threadIds)
var threadSignals: [Signal<MessageHistoryThreadData?, NoError>] = []
for threadId in threadIdsArray {
threadSignals.append(
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId))
func forumSelectionOptions(context: AccountContext, peerId: PeerId, threadIds: Set<Int64>) -> Signal<ChatListSelectionOptions, NoError> {
return context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
EngineDataList(threadIds.map { TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: $0) })
)
|> map { peer, threadDatas -> ChatListSelectionOptions in
guard !threadIds.isEmpty, case let .channel(channel) = peer else {
return ChatListSelectionOptions(read: .selective(enabled: false), delete: false)
}
var canDelete = !threadIds.contains(1)
if !channel.hasPermission(.deleteAllMessages) {
canDelete = false
}
return combineLatest(threadSignals)
|> map { threadDatas -> ChatListSelectionOptions in
if threadIds.isEmpty {
return ChatListSelectionOptions(read: .selective(enabled: false), delete: false)
} else {
var hasUnread = false
for thread in threadDatas {
guard let thread = thread else {
@ -86,4 +86,3 @@ func forumSelectionOptions(context: AccountContext, peerId: PeerId, threadIds: S
return ChatListSelectionOptions(read: .selective(enabled: hasUnread), delete: canDelete)
}
}
}

View File

@ -104,10 +104,14 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
switch self.index {
case let .chatList(index):
return index.pinningIndex != nil
case .forum:
case let .forum(pinnedIndex, _, _, _, _):
if case .index = pinnedIndex {
return true
} else {
return false
}
}
}
public init(presentationData: ChatListPresentationData, context: AccountContext, chatListLocation: ChatListControllerLocation, filterData: ChatListItemFilterData?, index: EngineChatList.Item.Index, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, hiddenOffset: Bool, interaction: ChatListNodeInteraction) {
self.presentationData = presentationData
@ -1349,9 +1353,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateAlpha(layer: compoundHighlightingNode.layer, alpha: self.authorNode.alpha)
}
if let item = self.item, case let .chatList(index) = item.index {
if let item = self.item {
let onlineIcon: UIImage?
if index.pinningIndex != nil {
if item.isPinned {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: self.onlineIsVoiceChat)
} else {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat)
@ -2015,7 +2019,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
currentMentionBadgeImage = PresentationResourcesChatList.badgeBackgroundReactions(item.presentationData.theme, diameter: badgeDiameter)
}
mentionBadgeContent = .mention
} else if case let .chatList(chatListIndex) = item.index, chatListIndex.pinningIndex != nil, promoInfo == nil, currentBadgeBackgroundImage == nil {
} else if item.isPinned, promoInfo == nil, currentBadgeBackgroundImage == nil {
currentPinnedIconImage = PresentationResourcesChatList.badgeBackgroundPinned(item.presentationData.theme, diameter: badgeDiameter)
}
}
@ -2896,6 +2900,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let compoundTextButtonNode = strongSelf.compoundTextButtonNode {
if strongSelf.textNode.textNode.supernode !== compoundTextButtonNode {
compoundTextButtonNode.addSubnode(strongSelf.textNode.textNode)
if let dustNode = strongSelf.dustNode {
compoundTextButtonNode.addSubnode(dustNode)
}
}
strongSelf.textNode.textNode.frame = textNodeFrame.offsetBy(dx: -compoundTextButtonNode.frame.minX, dy: -compoundTextButtonNode.frame.minY)
@ -2903,6 +2910,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
if strongSelf.textNode.textNode.supernode !== strongSelf.mainContentContainerNode {
strongSelf.mainContentContainerNode.addSubnode(strongSelf.textNode.textNode)
if let dustNode = strongSelf.dustNode {
strongSelf.mainContentContainerNode.addSubnode(dustNode)
}
}
strongSelf.textNode.textNode.frame = textNodeFrame
@ -2917,7 +2927,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
dustNode = InvisibleInkDustNode(textNode: nil)
dustNode.isUserInteractionEnabled = false
strongSelf.dustNode = dustNode
strongSelf.mainContentContainerNode.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode.textNode)
strongSelf.textNode.textNode.supernode?.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode.textNode)
}
dustNode.update(size: textNodeFrame.size, color: theme.messageTextColor, textColor: theme.messageTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
dustNode.frame = textNodeFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)

View File

@ -469,11 +469,18 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
var isForum = false
if let peer = chatPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
isForum = true
if editing {
if editing, case .chatList = mode {
enabled = false
}
}
var selectable = editing
if case .chatList = mode {
if isForum {
selectable = false
}
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings),
sortOrder: presentationData.nameSortOrder,
@ -483,7 +490,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
peer: peerContent,
status: status,
enabled: enabled,
selection: editing && !isForum ? .selectable(selected: selected) : .none,
selection: selectable ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,
@ -644,11 +651,18 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
var isForum = false
if let peer = chatPeer, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
isForum = true
if editing {
if editing, case .chatList = mode {
enabled = false
}
}
var selectable = editing
if case .chatList = mode {
if isForum {
selectable = false
}
}
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings),
sortOrder: presentationData.nameSortOrder,
@ -658,7 +672,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
peer: peerContent,
status: status,
enabled: enabled,
selection: editing && !isForum ? .selectable(selected: selected) : .none,
selection: selectable ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,

View File

@ -1631,7 +1631,7 @@ open class NavigationBar: ASDisplayNode {
if self.secondaryContentNode !== secondaryContentNode {
if let previous = self.secondaryContentNode, previous.supernode === self.clippingNode {
if animated {
previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in
previous.layer.animateAlpha(from: previous.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in
if finished {
previous?.removeFromSupernode()
previous?.layer.removeAllAnimations()
@ -1646,7 +1646,7 @@ open class NavigationBar: ASDisplayNode {
self.clippingNode.addSubnode(secondaryContentNode)
if animated {
secondaryContentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
secondaryContentNode.layer.animateAlpha(from: 0.0, to: secondaryContentNode.alpha, duration: 0.3)
}
}
}

View File

@ -107,6 +107,7 @@ swift_library(
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
"//submodules/PersistentStringHash:PersistentStringHash",
"//submodules/TelegramUI/Components/NotificationPeerExceptionController",
"//submodules/TelegramUI/Components/ChatTimerScreen",
],
visibility = [
"//visibility:public",

View File

@ -18,6 +18,7 @@ import UndoUI
import PremiumUI
import AuthorizationUI
import AuthenticationServices
import ChatTimerScreen
private final class PrivacyAndSecurityControllerArguments {
let account: Account
@ -34,10 +35,11 @@ private final class PrivacyAndSecurityControllerArguments {
let openActiveSessions: () -> Void
let toggleArchiveAndMuteNonContacts: (Bool) -> Void
let setupAccountAutoremove: () -> Void
let setupMessageAutoremove: () -> Void
let openDataSettings: () -> Void
let openEmailSettings: (String?) -> Void
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void) {
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, setupMessageAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void) {
self.account = account
self.openBlockedUsers = openBlockedUsers
self.openLastSeenPrivacy = openLastSeenPrivacy
@ -52,6 +54,7 @@ private final class PrivacyAndSecurityControllerArguments {
self.openActiveSessions = openActiveSessions
self.toggleArchiveAndMuteNonContacts = toggleArchiveAndMuteNonContacts
self.setupAccountAutoremove = setupAccountAutoremove
self.setupMessageAutoremove = setupMessageAutoremove
self.openDataSettings = openDataSettings
self.openEmailSettings = openEmailSettings
}
@ -62,11 +65,13 @@ private enum PrivacyAndSecuritySection: Int32 {
case privacy
case autoArchive
case account
case messageAutoremove
case dataSettings
}
public enum PrivacyAndSecurityEntryTag: ItemListItemTag {
case accountTimeout
case messageAutoremoveTimeout
case autoArchive
public func isEqual(to other: ItemListItemTag) -> Bool {
@ -100,6 +105,9 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
case accountHeader(PresentationTheme, String)
case accountTimeout(PresentationTheme, String, String)
case accountInfo(PresentationTheme, String)
case messageAutoremoveHeader(PresentationTheme, String)
case messageAutoremoveTimeout(PresentationTheme, String, String)
case messageAutoremoveInfo(PresentationTheme, String)
case dataSettings(PresentationTheme, String)
case dataSettingsInfo(PresentationTheme, String)
@ -113,6 +121,8 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
return PrivacyAndSecuritySection.autoArchive.rawValue
case .accountHeader, .accountTimeout, .accountInfo:
return PrivacyAndSecuritySection.account.rawValue
case .messageAutoremoveHeader, .messageAutoremoveTimeout, .messageAutoremoveInfo:
return PrivacyAndSecuritySection.messageAutoremove.rawValue
case .dataSettings, .dataSettingsInfo:
return PrivacyAndSecuritySection.dataSettings.rawValue
}
@ -162,10 +172,16 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
return 20
case .accountInfo:
return 21
case .dataSettings:
case .messageAutoremoveHeader:
return 22
case .dataSettingsInfo:
case .messageAutoremoveTimeout:
return 23
case .messageAutoremoveInfo:
return 24
case .dataSettings:
return 25
case .dataSettingsInfo:
return 26
}
}
@ -297,6 +313,24 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
} else {
return false
}
case let .messageAutoremoveHeader(lhsTheme, lhsText):
if case let .messageAutoremoveHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .messageAutoremoveTimeout(lhsTheme, lhsText, lhsValue):
if case let .messageAutoremoveTimeout(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .messageAutoremoveInfo(lhsTheme, lhsText):
if case let .messageAutoremoveInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .dataSettings(lhsTheme, lhsText):
if case let .dataSettings(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
@ -389,6 +423,14 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
}, tag: PrivacyAndSecurityEntryTag.accountTimeout)
case let .accountInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .messageAutoremoveHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .messageAutoremoveTimeout(_, text, value):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.setupMessageAutoremove()
}, tag: PrivacyAndSecurityEntryTag.messageAutoremoveTimeout)
case let .messageAutoremoveInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .dataSettings(_, text):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openDataSettings()
@ -402,6 +444,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
private struct PrivacyAndSecurityControllerState: Equatable {
var updatingAccountTimeoutValue: Int32? = nil
var updatingAutomaticallyArchiveAndMuteNonContacts: Bool? = nil
var updatingMessageAutoremoveTimeoutValue: Int32? = nil
}
private func countForSelectivePeers(_ peers: [PeerId: SelectivePrivacyPeer]) -> Int {
@ -545,6 +588,27 @@ private func privacyAndSecurityControllerEntries(
}
entries.append(.accountInfo(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountHelp))
//TODO:localize
entries.append(.messageAutoremoveHeader(presentationData.theme, "MESSAGE TIMER"))
if let privacySettings = privacySettings {
let value: Int32?
if let updatingMessageAutoremoveTimeoutValue = state.updatingMessageAutoremoveTimeoutValue {
value = updatingMessageAutoremoveTimeoutValue
} else {
value = privacySettings.messageAutoremoveTimeout
}
let valueText: String
if let value {
valueText = timeIntervalString(strings: presentationData.strings, value: value)
} else {
valueText = "Never"
}
entries.append(.messageAutoremoveTimeout(presentationData.theme, "Auto-Delete Messages", valueText))
} else {
entries.append(.messageAutoremoveTimeout(presentationData.theme, "Auto-Delete Messages", presentationData.strings.Channel_NotificationLoading))
}
entries.append(.messageAutoremoveInfo(presentationData.theme, "Set a global message timer."))
entries.append(.dataSettings(presentationData.theme, presentationData.strings.PrivacySettings_DataSettings))
entries.append(.dataSettingsInfo(presentationData.theme, presentationData.strings.PrivacySettings_DataSettingsHelp))
@ -680,7 +744,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -703,7 +767,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -740,7 +804,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -763,7 +827,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -786,7 +850,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -809,7 +873,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: updated, phoneDiscoveryEnabled: updatedDiscoveryEnabled ?? value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: updated, phoneDiscoveryEnabled: updatedDiscoveryEnabled ?? value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -839,7 +903,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: updated, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: updated, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -912,7 +976,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: archiveValue, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: archiveValue, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -951,7 +1015,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: timeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: timeout, messageAutoremoveTimeout: value.messageAutoremoveTimeout)))
}
return .complete()
}
@ -999,6 +1063,85 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
presentControllerImpl?(controller)
}
}))
}, setupMessageAutoremove: {
let signal = privacySettingsPromise.get()
|> take(1)
|> deliverOnMainQueue
updateAccountTimeoutDisposable.set(signal.start(next: { [weak updateAccountTimeoutDisposable] privacySettingsValue in
if let privacySettingsValue = privacySettingsValue {
let timeoutAction: (Int32?) -> Void = { timeout in
if let updateAccountTimeoutDisposable = updateAccountTimeoutDisposable {
updateState { state in
var state = state
state.updatingMessageAutoremoveTimeoutValue = timeout
return state
}
let applyTimeout: Signal<Void, NoError> = privacySettingsPromise.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, phoneDiscoveryEnabled: value.phoneDiscoveryEnabled, voiceMessages: value.voiceMessages, automaticallyArchiveAndMuteNonContacts: value.automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: value.accountRemovalTimeout, messageAutoremoveTimeout: timeout)))
}
return .complete()
}
updateAccountTimeoutDisposable.set((context.engine.privacy.updateGlobalMessageRemovalTimeout(timeout: timeout)
|> then(applyTimeout)
|> deliverOnMainQueue).start(completed: {
updateState { state in
var state = state
state.updatingMessageAutoremoveTimeoutValue = nil
return state
}
}))
}
}
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, style: .default, mode: .autoremove, currentTime: privacySettingsValue.messageAutoremoveTimeout, dismissByTapOutside: true, completion: { value in
timeoutAction(value)
})
presentControllerImpl?(controller)
/*
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
let timeoutValues: [Int32] = [
1 * 30 * 24 * 60 * 60,
3 * 30 * 24 * 60 * 60,
6 * 30 * 24 * 60 * 60,
365 * 24 * 60 * 60
]
var timeoutItems: [ActionSheetItem] = timeoutValues.map { value in
return ActionSheetButtonItem(title: timeIntervalString(strings: presentationData.strings, value: value), action: {
dismissAction()
timeoutAction(value)
})
}
timeoutItems.append(ActionSheetButtonItem(title: presentationData.strings.PrivacySettings_DeleteAccountNow, color: .destructive, action: {
dismissAction()
guard let navigationController = getNavigationControllerImpl?() else {
return
}
let _ = (combineLatest(twoStepAuth.get(), twoStepAuthDataValue.get())
|> take(1)
|> deliverOnMainQueue).start(next: { hasTwoStepAuth, twoStepAuthData in
let optionsController = deleteAccountOptionsController(context: context, navigationController: navigationController, hasTwoStepAuth: hasTwoStepAuth ?? false, twoStepAuthData: twoStepAuthData)
pushControllerImpl?(optionsController, true)
})
}))
controller.setItemGroups([
ActionSheetItemGroup(items: timeoutItems),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
presentControllerImpl?(controller)*/
}
}))
}, openDataSettings: {
pushControllerImpl?(dataPrivacyController(context: context), true)
}, openEmailSettings: { emailPattern in

View File

@ -180,6 +180,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[383348795] = { return Api.ContactStatus.parse_contactStatus($0) }
dict[2104790276] = { return Api.DataJSON.parse_dataJSON($0) }
dict[414687501] = { return Api.DcOption.parse_dcOption($0) }
dict[1135897376] = { return Api.DefaultHistoryTTL.parse_defaultHistoryTTL($0) }
dict[-1460809483] = { return Api.Dialog.parse_dialog($0) }
dict[1908216652] = { return Api.Dialog.parse_dialogFolder($0) }
dict[1949890536] = { return Api.DialogFilter.parse_dialogFilter($0) }
@ -1255,6 +1256,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.DcOption:
_1.serialize(buffer, boxed)
case let _1 as Api.DefaultHistoryTTL:
_1.serialize(buffer, boxed)
case let _1 as Api.Dialog:
_1.serialize(buffer, boxed)
case let _1 as Api.DialogFilter:

View File

@ -1852,15 +1852,16 @@ public extension Api.functions.channels {
}
}
public extension Api.functions.channels {
static func createChannel(flags: Int32, title: String, about: String, geoPoint: Api.InputGeoPoint?, address: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
static func createChannel(flags: Int32, title: String, about: String, geoPoint: Api.InputGeoPoint?, address: String?, ttlPeriod: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1029681423)
buffer.appendInt32(-1862244601)
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(about, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {geoPoint!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(address!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "channels.createChannel", parameters: [("flags", String(describing: flags)), ("title", String(describing: title)), ("about", String(describing: about)), ("geoPoint", String(describing: geoPoint)), ("address", String(describing: address))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "channels.createChannel", parameters: [("flags", String(describing: flags)), ("title", String(describing: title)), ("about", String(describing: about)), ("geoPoint", String(describing: geoPoint)), ("address", String(describing: address)), ("ttlPeriod", String(describing: ttlPeriod))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
@ -3725,16 +3726,18 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func createChat(users: [Api.InputUser], title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
static func createChat(flags: Int32, users: [Api.InputUser], title: String, ttlPeriod: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(164303470)
buffer.appendInt32(3450904)
serializeInt32(flags, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
serializeString(title, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.createChat", parameters: [("users", String(describing: users)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.createChat", parameters: [("flags", String(describing: flags)), ("users", String(describing: users)), ("title", String(describing: title)), ("ttlPeriod", String(describing: ttlPeriod))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
@ -4351,6 +4354,21 @@ public extension Api.functions.messages {
})
}
}
public extension Api.functions.messages {
static func getDefaultHistoryTTL() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.DefaultHistoryTTL>) {
let buffer = Buffer()
buffer.appendInt32(1703637384)
return (FunctionDescription(name: "messages.getDefaultHistoryTTL", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.DefaultHistoryTTL? in
let reader = BufferReader(buffer)
var result: Api.DefaultHistoryTTL?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.DefaultHistoryTTL
}
return result
})
}
}
public extension Api.functions.messages {
static func getDhConfig(version: Int32, randomLength: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.DhConfig>) {
let buffer = Buffer()
@ -6335,6 +6353,21 @@ public extension Api.functions.messages {
})
}
}
public extension Api.functions.messages {
static func setDefaultHistoryTTL(period: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-1632299963)
serializeInt32(period, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.setDefaultHistoryTTL", parameters: [("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Bool
}
return result
})
}
}
public extension Api.functions.messages {
static func setDefaultReaction(reaction: Api.Reaction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()

View File

@ -836,6 +836,42 @@ public extension Api {
}
}
public extension Api {
enum DefaultHistoryTTL: TypeConstructorDescription {
case defaultHistoryTTL(period: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .defaultHistoryTTL(let period):
if boxed {
buffer.appendInt32(1135897376)
}
serializeInt32(period, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .defaultHistoryTTL(let period):
return ("defaultHistoryTTL", [("period", String(describing: period))])
}
}
public static func parse_defaultHistoryTTL(_ reader: BufferReader) -> DefaultHistoryTTL? {
var _1: Int32?
_1 = reader.readInt32()
let _c1 = _1 != nil
if _c1 {
return Api.DefaultHistoryTTL.defaultHistoryTTL(period: _1!)
}
else {
return nil
}
}
}
}
public extension Api {
enum Dialog: TypeConstructorDescription {
case dialog(flags: Int32, peer: Api.Peer, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, notifySettings: Api.PeerNotifySettings, pts: Int32?, draft: Api.DraftMessage?, folderId: Int32?)

View File

@ -97,8 +97,9 @@ public struct AccountPrivacySettings: Equatable {
public let automaticallyArchiveAndMuteNonContacts: Bool
public let accountRemovalTimeout: Int32
public let messageAutoremoveTimeout: Int32?
public init(presence: SelectivePrivacySettings, groupInvitations: SelectivePrivacySettings, voiceCalls: SelectivePrivacySettings, voiceCallsP2P: SelectivePrivacySettings, profilePhoto: SelectivePrivacySettings, forwards: SelectivePrivacySettings, phoneNumber: SelectivePrivacySettings, phoneDiscoveryEnabled: Bool, voiceMessages: SelectivePrivacySettings, automaticallyArchiveAndMuteNonContacts: Bool, accountRemovalTimeout: Int32) {
public init(presence: SelectivePrivacySettings, groupInvitations: SelectivePrivacySettings, voiceCalls: SelectivePrivacySettings, voiceCallsP2P: SelectivePrivacySettings, profilePhoto: SelectivePrivacySettings, forwards: SelectivePrivacySettings, phoneNumber: SelectivePrivacySettings, phoneDiscoveryEnabled: Bool, voiceMessages: SelectivePrivacySettings, automaticallyArchiveAndMuteNonContacts: Bool, accountRemovalTimeout: Int32, messageAutoremoveTimeout: Int32?) {
self.presence = presence
self.groupInvitations = groupInvitations
self.voiceCalls = voiceCalls
@ -110,6 +111,7 @@ public struct AccountPrivacySettings: Equatable {
self.voiceMessages = voiceMessages
self.automaticallyArchiveAndMuteNonContacts = automaticallyArchiveAndMuteNonContacts
self.accountRemovalTimeout = accountRemovalTimeout
self.messageAutoremoveTimeout = messageAutoremoveTimeout
}
public static func ==(lhs: AccountPrivacySettings, rhs: AccountPrivacySettings) -> Bool {
@ -146,6 +148,9 @@ public struct AccountPrivacySettings: Equatable {
if lhs.accountRemovalTimeout != rhs.accountRemovalTimeout {
return false
}
if lhs.messageAutoremoveTimeout != rhs.messageAutoremoveTimeout {
return false
}
return true
}

View File

@ -34,7 +34,7 @@ private func createChannel(account: Account, title: String, description: String?
transaction.clearItemCacheCollection(collectionId: Namespaces.CachedItemCollection.cachedGroupCallDisplayAsPeers)
return account.network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address), automaticFloodWait: false)
return account.network.request(Api.functions.channels.createChannel(flags: flags, title: title, about: description ?? "", geoPoint: geoPoint, address: address, ttlPeriod: nil), automaticFloodWait: false)
|> mapError { error -> CreateChannelError in
if error.errorCode == 406 {
return .serverProvided(error.errorDescription)

View File

@ -23,7 +23,7 @@ func _internal_createGroup(account: Account, title: String, peerIds: [PeerId]) -
return .single(nil)
}
}
return account.network.request(Api.functions.messages.createChat(users: inputUsers, title: title))
return account.network.request(Api.functions.messages.createChat(flags: 0, users: inputUsers, title: title, ttlPeriod: nil))
|> mapError { error -> CreateGroupError in
if error.errorDescription == "USERS_TOO_FEW" {
return .privacy

View File

@ -866,6 +866,19 @@ public extension TelegramEngine {
|> ignoreValues
}
public func removeForumChannelThreads(id: EnginePeer.Id, threadIds: [Int64]) -> Signal<Never, NoError> {
return self.account.postbox.transaction { transaction -> Void in
for threadId in threadIds {
cloudChatAddClearHistoryOperation(transaction: transaction, peerId: id, threadId: threadId, explicitTopMessageId: nil, minTimestamp: nil, maxTimestamp: nil, type: CloudChatClearHistoryType(.forEveryone))
transaction.setMessageHistoryThreadInfo(peerId: id, threadId: threadId, info: nil)
_internal_clearHistory(transaction: transaction, mediaBox: self.account.postbox.mediaBox, peerId: id, threadId: threadId, namespaces: .not(Namespaces.Message.allScheduled))
}
}
|> ignoreValues
}
public func toggleForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64) -> Signal<Never, SetForumChannelTopicPinnedError> {
return self.account.postbox.transaction { transaction -> ([Int64], Int) in
var limit = 5

View File

@ -33,6 +33,10 @@ public extension TelegramEngine {
return _internal_updateAccountRemovalTimeout(account: self.account, timeout: timeout)
}
public func updateGlobalMessageRemovalTimeout(timeout: Int32?) -> Signal<Void, NoError> {
return _internal_updateMessageRemovalTimeout(account: self.account, timeout: timeout)
}
public func updatePhoneNumberDiscovery(value: Bool) -> Signal<Void, NoError> {
return _internal_updatePhoneNumberDiscovery(account: self.account, value: value)
}

View File

@ -16,17 +16,29 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal<Account
let voiceMessagesPrivacy = account.network.request(Api.functions.account.getPrivacy(key: .inputPrivacyKeyVoiceMessages))
let autoremoveTimeout = account.network.request(Api.functions.account.getAccountTTL())
let globalPrivacySettings = account.network.request(Api.functions.account.getGlobalPrivacySettings())
return combineLatest(lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, voiceCallP2P, profilePhotoPrivacy, forwardPrivacy, phoneNumberPrivacy, phoneDiscoveryPrivacy, voiceMessagesPrivacy, autoremoveTimeout, globalPrivacySettings)
let messageAutoremoveTimeout = account.network.request(Api.functions.messages.getDefaultHistoryTTL())
return combineLatest(lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, voiceCallP2P, profilePhotoPrivacy, forwardPrivacy, phoneNumberPrivacy, phoneDiscoveryPrivacy, voiceMessagesPrivacy, autoremoveTimeout, globalPrivacySettings, messageAutoremoveTimeout)
|> `catch` { _ in
return .complete()
}
|> mapToSignal { lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, voiceCallP2P, profilePhotoPrivacy, forwardPrivacy, phoneNumberPrivacy, phoneDiscoveryPrivacy, voiceMessagesPrivacy, autoremoveTimeout, globalPrivacySettings -> Signal<AccountPrivacySettings, NoError> in
|> mapToSignal { lastSeenPrivacy, groupPrivacy, voiceCallPrivacy, voiceCallP2P, profilePhotoPrivacy, forwardPrivacy, phoneNumberPrivacy, phoneDiscoveryPrivacy, voiceMessagesPrivacy, autoremoveTimeout, globalPrivacySettings, messageAutoremoveTimeout -> Signal<AccountPrivacySettings, NoError> in
let accountTimeoutSeconds: Int32
switch autoremoveTimeout {
case let .accountDaysTTL(days):
accountTimeoutSeconds = days * 24 * 60 * 60
}
let messageAutoremoveSeconds: Int32?
switch messageAutoremoveTimeout {
case let .defaultHistoryTTL(period):
if period != 0 {
messageAutoremoveSeconds = period
} else {
messageAutoremoveSeconds = nil
}
}
let lastSeenRules: [Api.PrivacyRule]
let groupRules: [Api.PrivacyRule]
let voiceRules: [Api.PrivacyRule]
@ -143,7 +155,7 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal<Account
return updated
})
return AccountPrivacySettings(presence: SelectivePrivacySettings(apiRules: lastSeenRules, peers: peerMap), groupInvitations: SelectivePrivacySettings(apiRules: groupRules, peers: peerMap), voiceCalls: SelectivePrivacySettings(apiRules: voiceRules, peers: peerMap), voiceCallsP2P: SelectivePrivacySettings(apiRules: voiceP2PRules, peers: peerMap), profilePhoto: SelectivePrivacySettings(apiRules: profilePhotoRules, peers: peerMap), forwards: SelectivePrivacySettings(apiRules: forwardRules, peers: peerMap), phoneNumber: SelectivePrivacySettings(apiRules: phoneNumberRules, peers: peerMap), phoneDiscoveryEnabled: phoneDiscoveryValue, voiceMessages: SelectivePrivacySettings(apiRules: voiceMessagesRules, peers: peerMap), automaticallyArchiveAndMuteNonContacts: automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: accountTimeoutSeconds)
return AccountPrivacySettings(presence: SelectivePrivacySettings(apiRules: lastSeenRules, peers: peerMap), groupInvitations: SelectivePrivacySettings(apiRules: groupRules, peers: peerMap), voiceCalls: SelectivePrivacySettings(apiRules: voiceRules, peers: peerMap), voiceCallsP2P: SelectivePrivacySettings(apiRules: voiceP2PRules, peers: peerMap), profilePhoto: SelectivePrivacySettings(apiRules: profilePhotoRules, peers: peerMap), forwards: SelectivePrivacySettings(apiRules: forwardRules, peers: peerMap), phoneNumber: SelectivePrivacySettings(apiRules: phoneNumberRules, peers: peerMap), phoneDiscoveryEnabled: phoneDiscoveryValue, voiceMessages: SelectivePrivacySettings(apiRules: voiceMessagesRules, peers: peerMap), automaticallyArchiveAndMuteNonContacts: automaticallyArchiveAndMuteNonContacts, accountRemovalTimeout: accountTimeoutSeconds, messageAutoremoveTimeout: messageAutoremoveSeconds)
}
}
}
@ -164,6 +176,16 @@ func _internal_updateAccountRemovalTimeout(account: Account, timeout: Int32) ->
}
}
func _internal_updateMessageRemovalTimeout(account: Account, timeout: Int32?) -> Signal<Void, NoError> {
return account.network.request(Api.functions.messages.setDefaultHistoryTTL(period: timeout ?? 0))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
}
func _internal_updatePhoneNumberDiscovery(account: Account, value: Bool) -> Signal<Void, NoError> {
var rules: [Api.InputPrivacyRule] = []
if value {

View File

@ -916,12 +916,14 @@ public final class NavigationButtonComponent: Component {
guard let self, let component = self.component else {
return
}
self.moreButton?.play()
component.pressed(self)
}
moreButton.contextAction = { [weak self] sourceNode, gesture in
guard let self, let component = self.component else {
return
}
self.moreButton?.play()
component.contextAction?(self, gesture)
}
self.moreButton = moreButton

View File

@ -30,7 +30,6 @@ public final class ChatTimerScreen: ViewController {
private var animatedIn = false
private let context: AccountContext
private let peerId: PeerId
private let style: ChatTimerScreenStyle
private let mode: ChatTimerScreenMode
private let currentTime: Int32?
@ -40,9 +39,8 @@ public final class ChatTimerScreen: ViewController {
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, style: ChatTimerScreenStyle, mode: ChatTimerScreenMode = .sendTimer, currentTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, style: ChatTimerScreenStyle, mode: ChatTimerScreenMode = .sendTimer, currentTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
self.context = context
self.peerId = peerId
self.style = style
self.mode = mode
self.currentTime = currentTime

View File

@ -14934,7 +14934,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if let navigationController = self.effectiveNavigationController {
var chatLocation: NavigateToChatControllerParams.Location = .peer(peer)
if let message = message, let threadId = message.threadId {
if case let .channel(channel) = peer, channel.flags.contains(.isForum), let message = message, let threadId = message.threadId {
chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
}
@ -17171,10 +17171,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
private func presentTimerPicker(style: ChatTimerScreenStyle = .default, selectedTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
guard case let .peer(peerId) = self.chatLocation else {
guard case .peer = self.chatLocation else {
return
}
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peerId, style: style, currentTime: selectedTime, dismissByTapOutside: dismissByTapOutside, completion: { time in
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, style: style, currentTime: selectedTime, dismissByTapOutside: dismissByTapOutside, completion: { time in
completion(time)
})
self.chatDisplayNode.dismissInput()
@ -17231,7 +17231,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, style: .default, mode: .autoremove, currentTime: self.presentationInterfaceState.autoremoveTimeout, dismissByTapOutside: true, completion: { [weak self] value in
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, style: .default, mode: .autoremove, currentTime: self.presentationInterfaceState.autoremoveTimeout, dismissByTapOutside: true, completion: { [weak self] value in
guard let strongSelf = self else {
return
}

View File

@ -2367,8 +2367,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
return VisibleMessageRange(lowerBound: range.lowerBound, upperBound: range.upperBound)
})
if let loaded = displayedRange.loadedRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last {
if let loaded = displayedRange.visibleRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last {
if loaded.firstIndex < 5 && historyView.originalView.laterId != nil {
if !"".isEmpty {
print("load next")
return
}
let locationInput: ChatHistoryLocation = .Navigation(index: .message(lastEntry.index), anchorIndex: .message(lastEntry.index), count: historyMessageCount, highlight: false)
if self.chatHistoryLocationValue?.content != locationInput {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: locationInput, id: self.takeNextHistoryLocationId())
@ -2378,6 +2382,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .upperBound, anchorIndex: .upperBound, count: historyMessageCount, highlight: false), id: self.takeNextHistoryLocationId())
}
} else if loaded.lastIndex >= historyView.filteredEntries.count - 5 && historyView.originalView.earlierId != nil {
if !"".isEmpty {
print("load previous")
return
}
let locationInput: ChatHistoryLocation = .Navigation(index: .message(firstEntry.index), anchorIndex: .message(firstEntry.index), count: historyMessageCount, highlight: false)
if self.chatHistoryLocationValue?.content != locationInput {
self.chatHistoryLocationValue = ChatHistoryLocationInput(content: locationInput, id: self.takeNextHistoryLocationId())

View File

@ -17,14 +17,30 @@ import ForumCreateTopicScreen
public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParams) {
if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
for controller in params.navigationController.viewControllers.reversed() {
if let controller = controller as? ChatListControllerImpl, case let .forum(peerId) = controller.location, peer.id == peerId {
var chatListController: ChatListControllerImpl?
if let controller = controller as? ChatListControllerImpl {
chatListController = controller
} else if let controller = controller as? TabBarController {
chatListController = controller.currentController as? ChatListControllerImpl
}
if let chatListController = chatListController {
var matches = false
if case let .forum(peerId) = chatListController.location, peer.id == peerId {
matches = true
} else if case let .forum(peerId) = chatListController.effectiveLocation, peer.id == peerId {
matches = true
}
if matches {
let _ = params.navigationController.popToViewController(controller, animated: params.animated)
if let activateMessageSearch = params.activateMessageSearch {
controller.activateSearch(query: activateMessageSearch.1)
chatListController.activateSearch(query: activateMessageSearch.1)
}
return
}
}
}
let controller = ChatListControllerImpl(context: params.context, location: .forum(peerId: peer.id), controlsHistoryPreload: false, enableDebugActions: false)

View File

@ -28,6 +28,7 @@ import AnimationCache
import MultiAnimationRenderer
import ComponentDisplayAdapters
import ChatTitleView
import AppBundle
enum PeerInfoHeaderButtonKey: Hashable {
case message
@ -2083,6 +2084,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let subtitleNodeContainer: ASDisplayNode
let subtitleNodeRawContainer: ASDisplayNode
let subtitleNode: MultiScaleTextNode
var subtitleBackgroundNode: ASDisplayNode?
var subtitleBackgroundButton: HighlightTrackingButtonNode?
var subtitleArrowNode: ASImageNode?
let panelSubtitleNode: MultiScaleTextNode
let nextPanelSubtitleNode: MultiScaleTextNode
let usernameNodeContainer: ASDisplayNode
@ -2111,6 +2115,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)?
var navigateToForum: (() -> Void)?
var navigationTransition: PeerInfoHeaderNavigationTransition?
var backgroundAlpha: CGFloat = 1.0
@ -2161,7 +2167,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.usernameNode.displaysAsynchronously = false
self.buttonsContainerNode = SparseNode()
self.buttonsContainerNode.clipsToBounds = true
self.buttonsContainerNode.clipsToBounds = false
self.regularContentNode = PeerInfoHeaderRegularContentNode()
var requestUpdateLayoutImpl: (() -> Void)?
@ -2300,6 +2306,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
}
@objc private func subtitleBackgroundPressed() {
self.navigateToForum?()
}
func invokeDisplayPremiumIntro() {
self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, .never(), self.isAvatarExpanded)
}
@ -2616,6 +2626,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let titleString: NSAttributedString
let smallSubtitleString: NSAttributedString
let subtitleString: NSAttributedString
var subtitleIsButton: Bool = false
var panelSubtitleString: NSAttributedString?
var nextPanelSubtitleString: NSAttributedString?
let usernameString: NSAttributedString
@ -2659,19 +2670,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
subtitleString = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
} else if let _ = threadData {
let subtitleColor: UIColor = presentationData.theme.list.itemSecondaryTextColor
let subtitleColor: UIColor
subtitleColor = presentationData.theme.list.itemAccentColor
let statusText: String
if let addressName = peer.addressName {
statusText = presentationData.strings.PeerInfo_TopicHeaderLocation("@\(addressName)").string
} else {
statusText = presentationData.strings.PeerInfo_TopicHeaderLocation(peer.debugDisplayTitle).string
}
statusText = peer.debugDisplayTitle
smallSubtitleString = NSAttributedString(string: statusText, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
subtitleString = NSAttributedString(string: statusText, font: Font.regular(17.0), textColor: subtitleColor)
subtitleString = NSAttributedString(string: statusText, font: Font.semibold(15.0), textColor: subtitleColor)
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
subtitleIsButton = true
let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData
if let panelStatusData = maybePanelStatusData {
let subtitleColor: UIColor
@ -2739,6 +2749,81 @@ final class PeerInfoHeaderNode: ASDisplayNode {
], mainState: TitleNodeStateRegular)
self.subtitleNode.accessibilityLabel = subtitleString.string
if subtitleIsButton {
let subtitleBackgroundNode: ASDisplayNode
if let current = self.subtitleBackgroundNode {
subtitleBackgroundNode = current
} else {
subtitleBackgroundNode = ASDisplayNode()
self.subtitleBackgroundNode = subtitleBackgroundNode
self.subtitleNode.insertSubnode(subtitleBackgroundNode, at: 0)
}
let subtitleBackgroundButton: HighlightTrackingButtonNode
if let current = self.subtitleBackgroundButton {
subtitleBackgroundButton = current
} else {
subtitleBackgroundButton = HighlightTrackingButtonNode()
self.subtitleBackgroundButton = subtitleBackgroundButton
self.subtitleNode.addSubnode(subtitleBackgroundButton)
subtitleBackgroundButton.addTarget(self, action: #selector(self.subtitleBackgroundPressed), forControlEvents: .touchUpInside)
subtitleBackgroundButton.highligthedChanged = { [weak self] highlighted in
guard let self else {
return
}
if highlighted {
self.subtitleNode.layer.removeAnimation(forKey: "opacity")
self.subtitleNode.alpha = 0.4
} else {
self.subtitleNode.alpha = 1.0
self.subtitleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
let subtitleArrowNode: ASImageNode
if let current = self.subtitleArrowNode {
subtitleArrowNode = current
if themeUpdated {
subtitleArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.5))
}
} else {
subtitleArrowNode = ASImageNode()
self.subtitleArrowNode = subtitleArrowNode
self.subtitleNode.insertSubnode(subtitleArrowNode, at: 1)
subtitleArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.5))
}
subtitleBackgroundNode.backgroundColor = presentationData.theme.list.itemAccentColor.withMultipliedAlpha(0.1)
let subtitleSize = subtitleNodeLayout[TitleNodeStateRegular]!.size
var subtitleBackgroundFrame = CGRect(origin: CGPoint(), size: subtitleSize).offsetBy(dx: -subtitleSize.width * 0.5, dy: -subtitleSize.height * 0.5).insetBy(dx: -6.0, dy: -4.0)
subtitleBackgroundFrame.size.width += 12.0
transition.updateFrame(node: subtitleBackgroundNode, frame: subtitleBackgroundFrame)
transition.updateCornerRadius(node: subtitleBackgroundNode, cornerRadius: subtitleBackgroundFrame.height * 0.5)
transition.updateFrame(node: subtitleBackgroundButton, frame: subtitleBackgroundFrame)
if let arrowImage = subtitleArrowNode.image {
let scaleFactor: CGFloat = 0.8
let arrowSize = CGSize(width: floorToScreenPixels(arrowImage.size.width * scaleFactor), height: floorToScreenPixels(arrowImage.size.height * scaleFactor))
subtitleArrowNode.frame = CGRect(origin: CGPoint(x: subtitleBackgroundFrame.maxX - arrowSize.width - 1.0, y: subtitleBackgroundFrame.minY + floor((subtitleBackgroundFrame.height - arrowSize.height) / 2.0)), size: arrowSize)
}
} else {
if let subtitleBackgroundNode = self.subtitleBackgroundNode {
self.subtitleBackgroundNode = nil
subtitleBackgroundNode.removeFromSupernode()
}
if let subtitleArrowNode = self.subtitleArrowNode {
self.subtitleArrowNode = nil
subtitleArrowNode.removeFromSupernode()
}
if let subtitleBackgroundButton = self.subtitleBackgroundButton {
self.subtitleBackgroundButton = nil
subtitleBackgroundButton.removeFromSupernode()
}
}
if let previousPanelStatusData = previousPanelStatusData, let currentPanelStatusData = panelStatusData.0, let previousPanelStatusDataKey = previousPanelStatusData.key, let currentPanelStatusDataKey = currentPanelStatusData.key, previousPanelStatusDataKey != currentPanelStatusDataKey {
if let snapshotView = self.panelSubtitleNode.view.snapshotContentTree() {
let direction: CGFloat = previousPanelStatusDataKey.rawValue > currentPanelStatusDataKey.rawValue ? 1.0 : -1.0
@ -2803,7 +2888,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
var titleFrame: CGRect
let subtitleFrame: CGRect
var subtitleFrame: CGRect
let usernameFrame: CGRect
let usernameSpacing: CGFloat = 4.0
@ -2829,6 +2914,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
}
if subtitleIsButton {
subtitleFrame.origin.y += 11.0
}
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
@ -3025,7 +3114,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.avatarListNode.avatarContainerNode.canAttachVideo = false
}
let panelWithAvatarHeight: CGFloat = 35.0 + avatarSize
var panelWithAvatarHeight: CGFloat = 35.0 + avatarSize
if threadData != nil {
panelWithAvatarHeight += 10.0
}
let rawHeight: CGFloat
let height: CGFloat
@ -3307,6 +3399,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
}
if let subtitleBackgroundButton = self.subtitleBackgroundButton, subtitleBackgroundButton.view.convert(subtitleBackgroundButton.bounds, to: self.view).contains(point) {
if let result = subtitleBackgroundButton.view.hitTest(self.view.convert(point, to: subtitleBackgroundButton.view), with: event) {
return result
}
}
if result.isDescendant(of: self.navigationButtonContainer.view) {
return result
}
@ -3314,6 +3412,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if result == self.view || result == self.regularContentNode.view || result == self.editingContentNode.view {
return nil
}
return result
}

View File

@ -3445,6 +3445,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
strongSelf.controller?.presentInGlobalOverlay(contextController)
}
self.headerNode.navigateToForum = { [weak self] in
guard let self, let navigationController = self.controller?.navigationController as? NavigationController, let peer = self.data?.peer else {
return
}
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer))))
}
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peerId.namespace) {
self.displayAsPeersPromise.set(context.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: peerId))
}
@ -4987,7 +4994,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
private func openAutoremove(currentValue: Int32?) {
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId, style: .default, mode: .autoremove, currentTime: currentValue, dismissByTapOutside: true, completion: { [weak self] value in
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, style: .default, mode: .autoremove, currentTime: currentValue, dismissByTapOutside: true, completion: { [weak self] value in
guard let strongSelf = self else {
return
}
@ -5016,7 +5023,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}
private func openCustomMute() {
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak self] value in
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak self] value in
guard let strongSelf = self else {
return
}

View File

@ -591,14 +591,23 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
}
case let .channelMessage(id, timecode):
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: Int64(id))
let messageId = MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: id)
return context.engine.messages.getMessagesLoadIfNecessary([messageId], strategy: .cloud(skipLocal: false))
|> take(1)
|> mapToSignal { messages -> Signal<ResolvedUrl?, NoError> in
if let threadId = messages.first?.threadId {
return context.engine.peers.fetchForumChannelTopic(id: channel.id, threadId: threadId)
|> map { info -> ResolvedUrl? in
if let _ = info {
return .replyThread(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: id))
return .replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)
} else {
return .peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
}
}
} else {
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
}
}
} else {
return .single(.channelMessage(peer: peer, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), timecode: timecode))
}