mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Forum improvements
This commit is contained in:
parent
dc085a2fe9
commit
80a7324668
@ -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 %@";
|
||||
|
@ -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:
|
||||
return strings.DialogList_SearchSectionMessages
|
||||
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:
|
||||
return .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
|
||||
|
@ -172,6 +172,11 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
||||
if readCounters.isUnread {
|
||||
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 {
|
||||
@ -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(
|
||||
TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId)
|
||||
)
|
||||
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
@ -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)
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0)
|
||||
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 {
|
||||
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: {})
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
context.fillEllipse(in: itemNode.avatarNode.frame.offsetBy(dx: 0.0, dy: currentY))
|
||||
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)
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0)
|
||||
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)
|
||||
|
@ -58,32 +58,31 @@ 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))
|
||||
)
|
||||
}
|
||||
|
||||
return combineLatest(threadSignals)
|
||||
|> map { threadDatas -> ChatListSelectionOptions in
|
||||
if threadIds.isEmpty {
|
||||
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)
|
||||
} else {
|
||||
var hasUnread = false
|
||||
for thread in threadDatas {
|
||||
guard let thread = thread else {
|
||||
continue
|
||||
}
|
||||
if thread.incomingUnreadCount > 0 {
|
||||
hasUnread = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return ChatListSelectionOptions(read: .selective(enabled: hasUnread), delete: canDelete)
|
||||
}
|
||||
|
||||
var canDelete = !threadIds.contains(1)
|
||||
if !channel.hasPermission(.deleteAllMessages) {
|
||||
canDelete = false
|
||||
}
|
||||
|
||||
var hasUnread = false
|
||||
for thread in threadDatas {
|
||||
guard let thread = thread else {
|
||||
continue
|
||||
}
|
||||
if thread.incomingUnreadCount > 0 {
|
||||
hasUnread = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return ChatListSelectionOptions(read: .selective(enabled: hasUnread), delete: canDelete)
|
||||
}
|
||||
}
|
||||
|
@ -104,8 +104,12 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
||||
switch self.index {
|
||||
case let .chatList(index):
|
||||
return index.pinningIndex != nil
|
||||
case .forum:
|
||||
return false
|
||||
case let .forum(pinnedIndex, _, _, _, _):
|
||||
if case .index = pinnedIndex {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
@ -469,10 +469,17 @@ 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),
|
||||
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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?)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -32,6 +32,10 @@ public extension TelegramEngine {
|
||||
public func updateAccountRemovalTimeout(timeout: Int32) -> Signal<Void, NoError> {
|
||||
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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -17,12 +17,28 @@ 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 {
|
||||
let _ = params.navigationController.popToViewController(controller, animated: params.animated)
|
||||
if let activateMessageSearch = params.activateMessageSearch {
|
||||
controller.activateSearch(query: activateMessageSearch.1)
|
||||
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 {
|
||||
chatListController.activateSearch(query: activateMessageSearch.1)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,18 +2670,17 @@ 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 {
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -591,12 +591,21 @@ 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))
|
||||
|> map { info -> ResolvedUrl? in
|
||||
if let _ = info {
|
||||
return .replyThread(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: 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 .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 .peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user