mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
[WIP] API update
This commit is contained in:
parent
79799804f1
commit
df35dcf3c4
@ -536,17 +536,44 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si
|
||||
return signal
|
||||
}
|
||||
|
||||
func _internal_forumChannelTopicNotificationExceptions(account: Account, id: EnginePeer.Id) -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(id).flatMap(apiInputPeer)
|
||||
public extension EngineMessageHistoryThread {
|
||||
struct NotificationException: Equatable {
|
||||
public var threadId: Int64
|
||||
public var info: EngineMessageHistoryThread.Info
|
||||
public var notificationSettings: EnginePeer.NotificationSettings
|
||||
|
||||
public init(
|
||||
threadId: Int64,
|
||||
info: EngineMessageHistoryThread.Info,
|
||||
notificationSettings: EnginePeer.NotificationSettings
|
||||
) {
|
||||
self.threadId = threadId
|
||||
self.info = info
|
||||
self.notificationSettings = notificationSettings
|
||||
}
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
}
|
||||
|
||||
func _internal_forumChannelTopicNotificationExceptions(account: Account, id: EnginePeer.Id) -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> {
|
||||
return account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(id)
|
||||
}
|
||||
|> mapToSignal { peer -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> in
|
||||
guard let inputPeer = peer.flatMap(apiInputPeer), let inputChannel = peer.flatMap(apiInputChannel) else {
|
||||
return .single([])
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.account.getNotifyExceptions(flags: 1 << 0, peer: Api.InputNotifyPeer.inputNotifyPeer(peer: inputPeer)))
|
||||
|> map { result -> [(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)] in
|
||||
var list: [(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)] = []
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> [(threadId: Int64, notificationSettings: EnginePeer.NotificationSettings)] in
|
||||
guard let result = result else {
|
||||
return []
|
||||
}
|
||||
|
||||
var list: [(threadId: Int64, notificationSettings: EnginePeer.NotificationSettings)] = []
|
||||
for update in result.allUpdates {
|
||||
switch update {
|
||||
case let .updateNotifySettings(peer, notifySettings):
|
||||
@ -562,8 +589,34 @@ func _internal_forumChannelTopicNotificationExceptions(account: Account, id: Eng
|
||||
}
|
||||
return list
|
||||
}
|
||||
|> `catch` { _ -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> in
|
||||
return .single([])
|
||||
|> mapToSignal { list -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> in
|
||||
return account.network.request(Api.functions.channels.getForumTopicsByID(channel: inputChannel, topics: list.map { Int32(clamping: $0.threadId) }))
|
||||
|> map { result -> [EngineMessageHistoryThread.NotificationException] in
|
||||
var infoMapping: [Int64: EngineMessageHistoryThread.Info] = [:]
|
||||
|
||||
switch result {
|
||||
case let .forumTopics(_, _, topics, _, _, _, _):
|
||||
for topic in topics {
|
||||
switch topic {
|
||||
case let .forumTopic(_, id, _, title, iconColor, iconEmojiId, _, _, _, _, _, _, _, _, _):
|
||||
infoMapping[Int64(id)] = EngineMessageHistoryThread.Info(title: title, icon: iconEmojiId, iconColor: iconColor)
|
||||
case .forumTopicDeleted:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.compactMap { item -> EngineMessageHistoryThread.NotificationException? in
|
||||
if let info = infoMapping[item.threadId] {
|
||||
return EngineMessageHistoryThread.NotificationException(threadId: item.threadId, info: info, notificationSettings: item.notificationSettings)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> in
|
||||
return .single([])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -840,7 +840,7 @@ public extension TelegramEngine {
|
||||
return _internal_setForumChannelTopicPinned(account: self.account, id: id, threadId: threadId, isPinned: isPinned)
|
||||
}
|
||||
|
||||
public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> {
|
||||
public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> {
|
||||
return _internal_forumChannelTopicNotificationExceptions(account: self.account, id: id)
|
||||
}
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ swift_library(
|
||||
"//submodules/Media/LocalAudioTranscription:LocalAudioTranscription",
|
||||
"//submodules/Components/PagerComponent:PagerComponent",
|
||||
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
|
||||
"//submodules/TelegramUI/Components/ForumTopicListScreen:ForumTopicListScreen",
|
||||
"//submodules/TelegramUI/Components/NotificationExceptionsScreen:NotificationExceptionsScreen",
|
||||
"//submodules/TelegramUI/Components/ForumCreateTopicScreen:ForumCreateTopicScreen",
|
||||
"//submodules/TelegramUI/Components/ChatTitleView",
|
||||
"//submodules/InviteLinksUI:InviteLinksUI",
|
||||
|
@ -1,494 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import ComponentDisplayAdapters
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
private final class ForumTopicListItemComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let item: ForumChannelTopics.Item
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
item: ForumChannelTopics.Item,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.item = item
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: ForumTopicListItemComponent, rhs: ForumTopicListItemComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.item != rhs.item {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: HighlightTrackingButton {
|
||||
private var highlightedBackgroundLayer: SimpleLayer?
|
||||
private let title: ComponentView<Empty>
|
||||
|
||||
private var component: ForumTopicListItemComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.title = ComponentView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
if let self, let component = self.component {
|
||||
if highlighted {
|
||||
if let superview = self.superview {
|
||||
superview.bringSubviewToFront(self)
|
||||
}
|
||||
let highlightedBackgroundLayer: SimpleLayer
|
||||
if let current = self.highlightedBackgroundLayer {
|
||||
highlightedBackgroundLayer = current
|
||||
} else {
|
||||
highlightedBackgroundLayer = SimpleLayer()
|
||||
self.highlightedBackgroundLayer = highlightedBackgroundLayer
|
||||
highlightedBackgroundLayer.backgroundColor = component.theme.list.itemHighlightedBackgroundColor.cgColor
|
||||
highlightedBackgroundLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: self.bounds.width, height: self.bounds.height + UIScreenPixel))
|
||||
self.layer.insertSublayer(highlightedBackgroundLayer, at: 0)
|
||||
}
|
||||
highlightedBackgroundLayer.removeAllAnimations()
|
||||
highlightedBackgroundLayer.opacity = 1.0
|
||||
} else {
|
||||
if let highlightedBackgroundLayer = self.highlightedBackgroundLayer {
|
||||
self.highlightedBackgroundLayer = nil
|
||||
highlightedBackgroundLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak highlightedBackgroundLayer] _ in
|
||||
highlightedBackgroundLayer?.removeFromSuperlayer()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.component?.action()
|
||||
}
|
||||
|
||||
func update(component: ForumTopicListItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
text: component.item.info.title,
|
||||
font: Font.regular(17.0),
|
||||
color: component.theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
titleView.isUserInteractionEnabled = false
|
||||
}
|
||||
transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: 11.0, y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize))
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ForumTopicListComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let items: [ForumChannelTopics.Item]
|
||||
let navigationHeight: CGFloat
|
||||
let action: (ForumChannelTopics.Item) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
items: [ForumChannelTopics.Item],
|
||||
navigationHeight: CGFloat,
|
||||
action: @escaping (ForumChannelTopics.Item) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.items = items
|
||||
self.navigationHeight = navigationHeight
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: ForumTopicListComponent, rhs: ForumTopicListComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.navigationHeight != rhs.navigationHeight {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView, UIScrollViewDelegate {
|
||||
private struct ItemLayout {
|
||||
let containerSize: CGSize
|
||||
let itemHeight: CGFloat
|
||||
let contentSize: CGSize
|
||||
let itemsInsets: UIEdgeInsets
|
||||
|
||||
init(containerSize: CGSize, navigationHeight: CGFloat, itemCount: Int) {
|
||||
self.itemHeight = 44.0
|
||||
self.containerSize = containerSize
|
||||
self.itemsInsets = UIEdgeInsets(top: navigationHeight, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
self.contentSize = CGSize(width: containerSize.width, height: self.itemsInsets.top + self.itemsInsets.bottom + CGFloat(itemCount) * self.itemHeight)
|
||||
}
|
||||
|
||||
func frame(at index: Int) -> CGRect {
|
||||
return CGRect(origin: CGPoint(x: 0.0, y: self.itemsInsets.top + CGFloat(index) * self.itemHeight), size: CGSize(width: self.containerSize.width, height: self.itemHeight))
|
||||
}
|
||||
}
|
||||
|
||||
private final class ItemView {
|
||||
let host: ComponentView<Empty>
|
||||
let separatorLayer: SimpleLayer
|
||||
|
||||
init() {
|
||||
self.host = ComponentView()
|
||||
self.separatorLayer = SimpleLayer()
|
||||
}
|
||||
}
|
||||
|
||||
private let scrollView: UIScrollView
|
||||
|
||||
private var component: ForumTopicListComponent?
|
||||
private var itemLayout: ItemLayout?
|
||||
|
||||
private var ignoreScrolling: Bool = false
|
||||
private var visibleItemViews: [Int64: ItemView] = [:]
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.scrollView = UIScrollView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.scrollView.layer.anchorPoint = CGPoint()
|
||||
self.scrollView.delaysContentTouches = true
|
||||
self.scrollView.clipsToBounds = false
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
if #available(iOS 13.0, *) {
|
||||
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
||||
}
|
||||
self.scrollView.showsVerticalScrollIndicator = true
|
||||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.alwaysBounceHorizontal = false
|
||||
self.scrollView.alwaysBounceVertical = true
|
||||
self.scrollView.scrollsToTop = false
|
||||
self.scrollView.delegate = self
|
||||
self.scrollView.clipsToBounds = true
|
||||
self.scrollView.canCancelContentTouches = true
|
||||
|
||||
self.addSubview(self.scrollView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if !self.ignoreScrolling {
|
||||
self.updateVisibleItems(transition: .immediate, synchronous: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibleItems(transition: Transition, synchronous: Bool) {
|
||||
guard let component = self.component, let itemLayout = self.itemLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
var validIds = Set<Int64>()
|
||||
let visibleBounds = self.scrollView.bounds
|
||||
for index in 0 ..< component.items.count {
|
||||
let itemFrame = itemLayout.frame(at: index)
|
||||
if !visibleBounds.intersects(itemFrame) {
|
||||
continue
|
||||
}
|
||||
|
||||
let item = component.items[index]
|
||||
validIds.insert(item.id)
|
||||
|
||||
let itemView: ItemView
|
||||
var itemTransition = transition
|
||||
if let current = self.visibleItemViews[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
itemView = ItemView()
|
||||
self.visibleItemViews[item.id] = itemView
|
||||
}
|
||||
|
||||
let id = item.id
|
||||
let _ = itemView.host.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(ForumTopicListItemComponent(
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
item: item,
|
||||
action: { [weak self] in
|
||||
guard let strongSelf = self, let component = strongSelf.component else {
|
||||
return
|
||||
}
|
||||
for item in component.items {
|
||||
if item.id == id {
|
||||
component.action(item)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: itemFrame.size
|
||||
)
|
||||
if let itemComponentView = itemView.host.view {
|
||||
if itemComponentView.superview == nil {
|
||||
self.scrollView.addSubview(itemComponentView)
|
||||
self.scrollView.layer.addSublayer(itemView.separatorLayer)
|
||||
}
|
||||
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
|
||||
let separatorInset: CGFloat
|
||||
if index == component.items.count - 1 {
|
||||
separatorInset = 0.0
|
||||
} else {
|
||||
separatorInset = 16.0
|
||||
}
|
||||
itemView.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
||||
itemTransition.setFrame(layer: itemView.separatorLayer, frame: CGRect(origin: CGPoint(x: separatorInset, y: itemFrame.maxY - UIScreenPixel), size: CGSize(width: itemLayout.contentSize.width - separatorInset, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
|
||||
var removedIds: [Int64] = []
|
||||
for (id, itemView) in self.visibleItemViews {
|
||||
if !validIds.contains(id) {
|
||||
itemView.host.view?.removeFromSuperview()
|
||||
itemView.separatorLayer.removeFromSuperlayer()
|
||||
removedIds.append(id)
|
||||
}
|
||||
}
|
||||
for id in removedIds {
|
||||
self.visibleItemViews.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: ForumTopicListComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let itemLayout = ItemLayout(containerSize: availableSize, navigationHeight: component.navigationHeight, itemCount: component.items.count)
|
||||
self.itemLayout = itemLayout
|
||||
|
||||
self.ignoreScrolling = true
|
||||
self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
|
||||
if self.scrollView.contentSize != itemLayout.contentSize {
|
||||
self.scrollView.contentSize = itemLayout.contentSize
|
||||
}
|
||||
if self.scrollView.scrollIndicatorInsets != itemLayout.itemsInsets {
|
||||
self.scrollView.scrollIndicatorInsets = itemLayout.itemsInsets
|
||||
}
|
||||
self.ignoreScrolling = false
|
||||
|
||||
self.updateVisibleItems(transition: transition, synchronous: false)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class ForumTopicListScreen: ViewController {
|
||||
private final class Node: ViewControllerTracingNode {
|
||||
private weak var controller: ForumTopicListScreen?
|
||||
|
||||
private let context: AccountContext
|
||||
private let id: EnginePeer.Id
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let topicList: ComponentView<Empty>
|
||||
|
||||
private let forumChannelContext: ForumChannelTopics
|
||||
private var stateDisposable: Disposable?
|
||||
private var currentState: ForumChannelTopics.State?
|
||||
|
||||
private var currentLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||
|
||||
init(controller: ForumTopicListScreen, context: AccountContext, id: EnginePeer.Id) {
|
||||
self.controller = controller
|
||||
|
||||
self.context = context
|
||||
self.id = id
|
||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.topicList = ComponentView()
|
||||
|
||||
self.forumChannelContext = ForumChannelTopics(account: self.context.account, peerId: self.id)
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.stateDisposable = (self.forumChannelContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.currentState = state
|
||||
strongSelf.update(transition: .immediate)
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.stateDisposable?.dispose()
|
||||
}
|
||||
|
||||
func createPressed() {
|
||||
}
|
||||
|
||||
private func update(transition: Transition) {
|
||||
if let currentLayout = self.currentLayout {
|
||||
self.containerLayoutUpdated(layout: currentLayout.layout, navigationHeight: currentLayout.navigationHeight, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: Transition) {
|
||||
self.currentLayout = (layout, navigationHeight)
|
||||
|
||||
let _ = self.topicList.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ForumTopicListComponent(
|
||||
context: self.context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
items: self.currentState?.items ?? [],
|
||||
navigationHeight: navigationHeight,
|
||||
action: { [weak self] item in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controller?.openTopic(item)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: layout.size
|
||||
)
|
||||
if let topicListView = self.topicList.view {
|
||||
if topicListView.superview == nil {
|
||||
if let navigationBar = self.controller?.navigationBar {
|
||||
self.view.insertSubview(topicListView, belowSubview: navigationBar.view)
|
||||
} else {
|
||||
self.view.addSubview(topicListView)
|
||||
}
|
||||
}
|
||||
transition.setFrame(view: topicListView, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var node: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let id: EnginePeer.Id
|
||||
private var presentationData: PresentationData
|
||||
private let openTopic: (ForumChannelTopics.Item) -> Void
|
||||
|
||||
public init(context: AccountContext, id: EnginePeer.Id, openTopic: @escaping (ForumChannelTopics.Item) -> Void) {
|
||||
self.context = context
|
||||
self.id = id
|
||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.openTopic = openTopic
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
//TODO:localize
|
||||
self.title = "Forum"
|
||||
|
||||
self.navigationItem.setRightBarButton(UIBarButtonItem(title: "Create", style: .plain, target: self, action: #selector(self.createPressed)), animated: false)
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func createPressed() {
|
||||
self.node.createPressed()
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(controller: self, context: self.context, id: self.id)
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.node.containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: Transition(transition))
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ForumTopicListScreen",
|
||||
module_name = "ForumTopicListScreen",
|
||||
name = "NotificationExceptionsScreen",
|
||||
module_name = "NotificationExceptionsScreen",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
@ -26,6 +26,10 @@ swift_library(
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/ItemListPeerItem",
|
||||
"//submodules/ItemListPeerActionItem",
|
||||
"//submodules/NotificationSoundSelectionUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
@ -0,0 +1,535 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import NotificationSoundSelectionUI
|
||||
import TelegramStringFormatting
|
||||
import ItemListPeerItem
|
||||
import ItemListPeerActionItem
|
||||
|
||||
private extension EnginePeer.NotificationSettings.MuteState {
|
||||
var timeInterval: Int32? {
|
||||
switch self {
|
||||
case .default:
|
||||
return nil
|
||||
case .unmuted:
|
||||
return 0
|
||||
case let .muted(until):
|
||||
return until
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound {
|
||||
if case .default = sound {
|
||||
return defaultCloudPeerNotificationSound
|
||||
} else {
|
||||
return sound
|
||||
}
|
||||
}
|
||||
|
||||
private final class NotificationsPeerCategoryControllerArguments {
|
||||
let context: AccountContext
|
||||
let soundSelectionDisposable: MetaDisposable
|
||||
|
||||
let updateEnabled: (Bool) -> Void
|
||||
let updatePreviews: (Bool) -> Void
|
||||
|
||||
let openSound: (PeerMessageSound) -> Void
|
||||
|
||||
let addException: () -> Void
|
||||
let openException: (Int64) -> Void
|
||||
let removeAllExceptions: () -> Void
|
||||
let updateRevealedThreadId: (Int64?) -> Void
|
||||
let removeThread: (Int64) -> Void
|
||||
|
||||
init(context: AccountContext, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, openSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (Int64) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedThreadId: @escaping (Int64?) -> Void, removeThread: @escaping (Int64) -> Void) {
|
||||
self.context = context
|
||||
self.soundSelectionDisposable = soundSelectionDisposable
|
||||
|
||||
self.updateEnabled = updateEnabled
|
||||
self.updatePreviews = updatePreviews
|
||||
self.openSound = openSound
|
||||
|
||||
self.addException = addException
|
||||
self.openException = openException
|
||||
self.removeAllExceptions = removeAllExceptions
|
||||
|
||||
self.updateRevealedThreadId = updateRevealedThreadId
|
||||
self.removeThread = removeThread
|
||||
}
|
||||
}
|
||||
|
||||
private enum NotificationsPeerCategorySection: Int32 {
|
||||
case enable
|
||||
case options
|
||||
case exceptions
|
||||
}
|
||||
|
||||
private enum NotificationsPeerCategoryEntryTag: ItemListItemTag {
|
||||
case enable
|
||||
case previews
|
||||
case sound
|
||||
|
||||
public func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
if let other = other as? NotificationsPeerCategoryEntryTag, self == other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
case enable(String, Bool)
|
||||
case optionsHeader(String)
|
||||
case previews(String, Bool)
|
||||
case sound(String, String, PeerMessageSound)
|
||||
|
||||
case exceptionsHeader(String)
|
||||
case addException(String)
|
||||
case exception(Int32, PresentationDateTimeFormat, PresentationPersonNameOrder, EngineMessageHistoryThread.Info, String, TelegramPeerNotificationSettings, Bool, Bool)
|
||||
case removeAllExceptions(String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .enable:
|
||||
return NotificationsPeerCategorySection.enable.rawValue
|
||||
case .optionsHeader, .previews, .sound:
|
||||
return NotificationsPeerCategorySection.options.rawValue
|
||||
case .exceptionsHeader, .addException, .exception, .removeAllExceptions:
|
||||
return NotificationsPeerCategorySection.exceptions.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .enable:
|
||||
return 0
|
||||
case .optionsHeader:
|
||||
return 1
|
||||
case .previews:
|
||||
return 2
|
||||
case .sound:
|
||||
return 3
|
||||
case .exceptionsHeader:
|
||||
return 4
|
||||
case .addException:
|
||||
return 5
|
||||
case let .exception(index, _, _, _, _, _, _, _):
|
||||
return 6 + index
|
||||
case .removeAllExceptions:
|
||||
return 100000
|
||||
}
|
||||
}
|
||||
|
||||
var tag: ItemListItemTag? {
|
||||
switch self {
|
||||
case .enable:
|
||||
return NotificationsPeerCategoryEntryTag.enable
|
||||
case .previews:
|
||||
return NotificationsPeerCategoryEntryTag.previews
|
||||
case .sound:
|
||||
return NotificationsPeerCategoryEntryTag.sound
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: NotificationsPeerCategoryEntry, rhs: NotificationsPeerCategoryEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .enable(lhsText, lhsValue):
|
||||
if case let .enable(rhsText, rhsValue) = rhs, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .optionsHeader(lhsText):
|
||||
if case let .optionsHeader(rhsText) = rhs, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .previews(lhsText, lhsValue):
|
||||
if case let .previews(rhsText, rhsValue) = rhs, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .sound(lhsText, lhsValue, lhsSound):
|
||||
if case let .sound(rhsText, rhsValue, rhsSound) = rhs, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .exceptionsHeader(lhsText):
|
||||
if case let .exceptionsHeader(rhsText) = rhs, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .addException(lhsText):
|
||||
if case let .addException(rhsText) = rhs, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .exception(lhsIndex, lhsDateTimeFormat, lhsDisplayNameOrder, lhsInfo, lhsDescription, lhsSettings, lhsEditing, lhsRevealed):
|
||||
if case let .exception(rhsIndex, rhsDateTimeFormat, rhsDisplayNameOrder, rhsInfo, rhsDescription, rhsSettings, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, lhsInfo == rhsInfo, lhsDescription == rhsDescription, lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .removeAllExceptions(lhsText):
|
||||
if case let .removeAllExceptions(rhsText) = rhs, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: NotificationsPeerCategoryEntry, rhs: NotificationsPeerCategoryEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! NotificationsPeerCategoryControllerArguments
|
||||
switch self {
|
||||
case let .enable(text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateEnabled(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .optionsHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .previews(text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updatePreviews(value)
|
||||
})
|
||||
case let .sound(text, value, sound):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openSound(sound)
|
||||
}, tag: self.tag)
|
||||
case let .exceptionsHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .addException(text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), title: text, sectionId: self.section, height: .peerList, color: .accent, editing: false, action: {
|
||||
arguments.addException()
|
||||
})
|
||||
//case let .exception(_, dateTimeFormat, nameDisplayOrder, info, description, _, editing, revealed):
|
||||
case .exception:
|
||||
preconditionFailure()
|
||||
/*return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: nil, text: .text(description, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||
arguments.openException(peer)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.updateRevealedPeerId(peerId)
|
||||
}, removePeer: { peerId in
|
||||
arguments.removePeer(peer)
|
||||
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)*/
|
||||
case let .removeAllExceptions(text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.deleteIconImage(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
||||
arguments.removeAllExceptions()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func notificationsPeerCategoryEntries(notificationSettings: EnginePeer.NotificationSettings, state: NotificationExceptionState, presentationData: PresentationData, notificationSoundList: NotificationSoundList?) -> [NotificationsPeerCategoryEntry] {
|
||||
var entries: [NotificationsPeerCategoryEntry] = []
|
||||
|
||||
var notificationsEnabled = true
|
||||
if case .muted = notificationSettings.muteState {
|
||||
notificationsEnabled = false
|
||||
}
|
||||
var displayPreviews = true
|
||||
switch notificationSettings.displayPreviews {
|
||||
case .hide:
|
||||
displayPreviews = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
entries.append(.enable(presentationData.strings.Notifications_MessageNotificationsAlert, notificationsEnabled))
|
||||
|
||||
if notificationsEnabled || !state.notificationExceptions.isEmpty {
|
||||
entries.append(.optionsHeader(presentationData.strings.Notifications_Options.uppercased()))
|
||||
entries.append(.previews(presentationData.strings.Notifications_MessageNotificationsPreview, displayPreviews))
|
||||
entries.append(.sound(presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: filteredGlobalSound(notificationSettings.messageSound._asMessageSound())), filteredGlobalSound(notificationSettings.messageSound._asMessageSound())))
|
||||
}
|
||||
|
||||
entries.append(.exceptionsHeader(presentationData.strings.Notifications_MessageNotificationsExceptions.uppercased()))
|
||||
entries.append(.addException(presentationData.strings.Notification_Exceptions_AddException))
|
||||
|
||||
var existingThreadIds = Set<Int64>()
|
||||
var index: Int = 0
|
||||
|
||||
for value in state.notificationExceptions {
|
||||
var title: String
|
||||
var muted = false
|
||||
switch value.notificationSettings.muteState {
|
||||
case let .muted(until):
|
||||
if until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||
if until < Int32.max - 1 {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale(identifier: presentationData.strings.baseLanguageCode)
|
||||
|
||||
if Calendar.current.isDateInToday(Date(timeIntervalSince1970: Double(until))) {
|
||||
formatter.dateFormat = "HH:mm"
|
||||
} else {
|
||||
formatter.dateFormat = "E, d MMM HH:mm"
|
||||
}
|
||||
|
||||
let dateString = formatter.string(from: Date(timeIntervalSince1970: Double(until)))
|
||||
|
||||
title = presentationData.strings.Notification_Exceptions_MutedUntil(dateString).string
|
||||
} else {
|
||||
muted = true
|
||||
title = presentationData.strings.Notification_Exceptions_AlwaysOff
|
||||
}
|
||||
} else {
|
||||
title = presentationData.strings.Notification_Exceptions_AlwaysOn
|
||||
}
|
||||
case .unmuted:
|
||||
title = presentationData.strings.Notification_Exceptions_AlwaysOn
|
||||
default:
|
||||
title = ""
|
||||
}
|
||||
if !muted {
|
||||
switch value.notificationSettings.messageSound {
|
||||
case .default:
|
||||
break
|
||||
default:
|
||||
if !title.isEmpty {
|
||||
title.append(", ")
|
||||
}
|
||||
title.append(presentationData.strings.Notification_Exceptions_SoundCustom)
|
||||
}
|
||||
switch value.notificationSettings.displayPreviews {
|
||||
case .default:
|
||||
break
|
||||
default:
|
||||
if !title.isEmpty {
|
||||
title += ", "
|
||||
}
|
||||
if case .show = value.notificationSettings.displayPreviews {
|
||||
title += presentationData.strings.Notification_Exceptions_PreviewAlwaysOn
|
||||
} else {
|
||||
title += presentationData.strings.Notification_Exceptions_PreviewAlwaysOff
|
||||
}
|
||||
}
|
||||
}
|
||||
existingThreadIds.insert(value.threadId)
|
||||
entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId))
|
||||
index += 1
|
||||
}
|
||||
|
||||
if state.notificationExceptions.count > 0 {
|
||||
entries.append(.removeAllExceptions(presentationData.strings.Notifications_DeleteAllExceptions))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
private struct NotificationExceptionState: Equatable {
|
||||
var revealedThreadId: Int64? = nil
|
||||
var editing: Bool = false
|
||||
var notificationExceptions: [EngineMessageHistoryThread.NotificationException] = []
|
||||
}
|
||||
|
||||
public func threadNotificationExceptionsScreen(context: AccountContext, peerId: EnginePeer.Id, notificationExceptions: [EngineMessageHistoryThread.NotificationException], updated: @escaping ([EngineMessageHistoryThread.NotificationException]) -> Void) -> ViewController {
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let initialState = NotificationExceptionState(notificationExceptions: notificationExceptions)
|
||||
let stateValue = Atomic<NotificationExceptionState>(value: initialState)
|
||||
let statePromise: ValuePromise<NotificationExceptionState> = ValuePromise(ignoreRepeated: true)
|
||||
|
||||
statePromise.set(initialState)
|
||||
|
||||
let updateState: ((NotificationExceptionState) -> NotificationExceptionState) -> Void = { f in
|
||||
let result = stateValue.modify { f($0) }
|
||||
statePromise.set(result)
|
||||
//updatedMode(result.mode)
|
||||
}
|
||||
|
||||
/*let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: sound)
|
||||
|> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
|
||||
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: nil, muteInterval: muteInterval)
|
||||
|> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
||||
peerId, displayPreviews in
|
||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||
}*/
|
||||
|
||||
let _ = presentControllerImpl
|
||||
|
||||
let arguments = NotificationsPeerCategoryControllerArguments(context: context, soundSelectionDisposable: MetaDisposable(), updateEnabled: { _ in
|
||||
let _ = context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil).start()
|
||||
}, updatePreviews: { value in
|
||||
let _ = context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: value ? .show : .hide).start()
|
||||
}, openSound: { sound in
|
||||
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: sound, defaultSound: nil, completion: { value in
|
||||
let _ = context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: value).start()
|
||||
})
|
||||
pushControllerImpl?(controller)
|
||||
}, addException: {
|
||||
/*let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
|
||||
switch category {
|
||||
case .privateChat:
|
||||
filter.insert(.onlyPrivateChats)
|
||||
filter.insert(.excludeSavedMessages)
|
||||
filter.insert(.excludeSecretChats)
|
||||
case .group:
|
||||
filter.insert(.onlyGroups)
|
||||
case .channel:
|
||||
filter.insert(.onlyChannels)
|
||||
}
|
||||
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
|
||||
controller.peerSelected = { [weak controller] peer in
|
||||
let peerId = peer.id
|
||||
|
||||
presentPeerSettings(peerId, {
|
||||
controller?.dismiss()
|
||||
})
|
||||
}
|
||||
pushControllerImpl?(controller)*/
|
||||
}, openException: { peer in
|
||||
//presentPeerSettings(peer.id, {})
|
||||
}, removeAllExceptions: {
|
||||
/*let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: presentationData.strings.Notification_Exceptions_DeleteAllConfirmation),
|
||||
ActionSheetButtonItem(title: presentationData.strings.Notification_Exceptions_DeleteAll, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
let values = stateValue.with { $0.mode.settings.values }
|
||||
|
||||
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { EnginePeer($0.peer) })
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsDisposable.set(nil)
|
||||
updateState { state in
|
||||
var state = state
|
||||
for value in values {
|
||||
state = state.withUpdatedPeerMuteInterval(value.peer, nil).withUpdatedPeerSound(value.peer, .default).withUpdatedPeerDisplayPreviews(value.peer, .default)
|
||||
}
|
||||
return state
|
||||
}
|
||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsView({})
|
||||
})
|
||||
})
|
||||
})
|
||||
]), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)*/
|
||||
}, updateRevealedThreadId: { threadId in
|
||||
updateState { current in
|
||||
var current = current
|
||||
current.revealedThreadId = threadId
|
||||
return current
|
||||
}
|
||||
}, removeThread: { threadId in
|
||||
/*let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [EnginePeer(peer)])
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsDisposable.set(nil)
|
||||
updateState { value in
|
||||
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default)
|
||||
}
|
||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id])
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
updateNotificationsView({})
|
||||
})
|
||||
})*/
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(),
|
||||
context.sharedContext.presentationData,
|
||||
context.engine.peers.notificationSoundList(),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId)),
|
||||
statePromise.get()
|
||||
)
|
||||
|> map { presentationData, notificationSoundList, peer, notificationSettings, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let entries = notificationsPeerCategoryEntries(notificationSettings: notificationSettings, state: state, presentationData: presentationData, notificationSoundList: notificationSoundList)
|
||||
|
||||
var scrollToItem: ListViewScrollToItem?
|
||||
scrollToItem = nil
|
||||
/*var index = 0
|
||||
if let focusOnItemTag = focusOnItemTag {
|
||||
for entry in entries {
|
||||
if entry.tag?.isEqual(to: focusOnItemTag) ?? false {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}*/
|
||||
|
||||
let leftNavigationButton: ItemListNavigationButton?
|
||||
let rightNavigationButton: ItemListNavigationButton?
|
||||
if !state.notificationExceptions.isEmpty {
|
||||
if state.editing {
|
||||
leftNavigationButton = ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {})
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
updateState { value in
|
||||
var value = value
|
||||
value.editing = false
|
||||
return value
|
||||
}
|
||||
})
|
||||
} else {
|
||||
leftNavigationButton = nil
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
||||
updateState { value in
|
||||
var value = value
|
||||
value.editing = true
|
||||
return value
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
leftNavigationButton = nil
|
||||
rightNavigationButton = nil
|
||||
}
|
||||
|
||||
let title: String
|
||||
if let peer = peer {
|
||||
title = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
} else {
|
||||
title = ""
|
||||
}
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: nil, initialScrollToItem: scrollToItem)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
return controller
|
||||
}
|
@ -12,7 +12,6 @@ import PeerAvatarGalleryUI
|
||||
import SettingsUI
|
||||
import ChatPresentationInterfaceState
|
||||
import AttachmentUI
|
||||
import ForumTopicListScreen
|
||||
import ForumCreateTopicScreen
|
||||
|
||||
public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParams) {
|
||||
|
@ -78,6 +78,7 @@ import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import ChatTitleView
|
||||
import ForumCreateTopicScreen
|
||||
import NotificationExceptionsScreen
|
||||
|
||||
protocol PeerInfoScreenItem: AnyObject {
|
||||
var id: AnyHashable { get }
|
||||
@ -1874,7 +1875,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
private weak var copyProtectionTooltipController: TooltipController?
|
||||
weak var emojiStatusSelectionController: ViewController?
|
||||
|
||||
private var forumTopicNotificationExceptions: [(threadId: Int64, EnginePeer.NotificationSettings)] = []
|
||||
private var forumTopicNotificationExceptions: [EngineMessageHistoryThread.NotificationException] = []
|
||||
private var forumTopicNotificationExceptionsDisposable: Disposable?
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
@ -4148,7 +4149,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
} else {
|
||||
text = "There are \(self.forumTopicNotificationExceptions.count) topics that are listed as exceptions."
|
||||
}
|
||||
tip = .notificationTopicExceptions(text: text, action: {
|
||||
tip = .notificationTopicExceptions(text: text, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.controller?.present(threadNotificationExceptionsScreen(context: self.context, peerId: self.peerId, notificationExceptions: self.forumTopicNotificationExceptions, updated: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.forumTopicNotificationExceptions = value
|
||||
}), in: .window(.root))
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user