mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 11:50:56 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
ae1fd7df68
@ -1,5 +1,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@objc(Application) class Application: UIApplication {
|
@objc(Application) class Application: UIApplication {
|
||||||
|
override func sendEvent(_ event: UIEvent) {
|
||||||
|
super.sendEvent(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -723,17 +723,18 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
|||||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatePeerStoryNotifications: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
|
let updatePeerStoriesMuted: (PeerId, PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> = {
|
||||||
var isMuted: Bool?
|
peerId, mute in
|
||||||
switch storyNotifications {
|
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue
|
||||||
case .default:
|
}
|
||||||
isMuted = nil
|
|
||||||
case .show:
|
let updatePeerStoriesHideSender: (PeerId, PeerStoryNotificationSettings.HideSender) -> Signal<Void, NoError> = {
|
||||||
isMuted = false
|
peerId, hideSender in
|
||||||
case .hide:
|
return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue
|
||||||
isMuted = true
|
}
|
||||||
}
|
|
||||||
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
|
let updatePeerStorySound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||||
|
return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
let defaultSound: PeerMessageSound
|
let defaultSound: PeerMessageSound
|
||||||
@ -769,8 +770,14 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
|||||||
|> deliverOnMainQueue).start(next: { _ in
|
|> deliverOnMainQueue).start(next: { _ in
|
||||||
|
|
||||||
})
|
})
|
||||||
}, updatePeerStoryNotifications: { peerId, storyNotifications in
|
}, updatePeerStoriesMuted: { peerId, mute in
|
||||||
let _ = (updatePeerStoryNotifications(peerId, storyNotifications)
|
let _ = (updatePeerStoriesMuted(peerId, mute)
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
}, updatePeerStoriesHideSender: { peerId, hideSender in
|
||||||
|
let _ = (updatePeerStoriesHideSender(peerId, hideSender)
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
}, updatePeerStorySound: { peerId, sound in
|
||||||
|
let _ = (updatePeerStorySound(peerId, sound)
|
||||||
|> deliverOnMainQueue).start()
|
|> deliverOnMainQueue).start()
|
||||||
}, removePeerFromExceptions: {
|
}, removePeerFromExceptions: {
|
||||||
}, modifiedPeer: {
|
}, modifiedPeer: {
|
||||||
|
|||||||
@ -2560,7 +2560,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
if let rawStorySubscriptions = self.rawStorySubscriptions {
|
if let rawStorySubscriptions = self.rawStorySubscriptions {
|
||||||
var openCamera = false
|
var openCamera = false
|
||||||
if let accountItem = rawStorySubscriptions.accountItem {
|
if let accountItem = rawStorySubscriptions.accountItem {
|
||||||
openCamera = accountItem.storyCount == 0
|
openCamera = accountItem.storyCount == 0 && !accountItem.hasPending
|
||||||
} else {
|
} else {
|
||||||
openCamera = true
|
openCamera = true
|
||||||
}
|
}
|
||||||
@ -2581,9 +2581,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = (self.context.engine.data.get(
|
let _ = (self.context.engine.data.get(
|
||||||
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id)
|
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id),
|
||||||
|
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] notificationSettings in
|
|> deliverOnMainQueue).start(next: { [weak self] notificationSettings, globalSettings in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2639,7 +2640,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
let isMuted = notificationSettings.storiesMuted == true
|
let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings())
|
||||||
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
@ -5338,11 +5339,20 @@ private final class ChatListLocationContext {
|
|||||||
peerStatus = .single(nil)
|
peerStatus = .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let networkState: Signal<AccountNetworkState, NoError>
|
||||||
|
#if DEBUG && false
|
||||||
|
networkState = .single(AccountNetworkState.connecting(proxy: nil)) |> then(.single(AccountNetworkState.updating(proxy: nil)) |> delay(2.0, queue: .mainQueue())) |> then(.single(AccountNetworkState.online(proxy: nil)) |> delay(2.0, queue: .mainQueue())) |> then(.complete() |> delay(2.0, queue: .mainQueue())) |> restart
|
||||||
|
#elseif DEBUG && false
|
||||||
|
networkState = .single(AccountNetworkState.connecting(proxy: nil))
|
||||||
|
#else
|
||||||
|
networkState = context.account.networkState
|
||||||
|
#endif
|
||||||
|
|
||||||
switch location {
|
switch location {
|
||||||
case .chatList:
|
case .chatList:
|
||||||
if !hideNetworkActivityStatus {
|
if !hideNetworkActivityStatus {
|
||||||
self.titleDisposable = combineLatest(queue: .mainQueue(),
|
self.titleDisposable = combineLatest(queue: .mainQueue(),
|
||||||
context.account.networkState,
|
networkState,
|
||||||
hasProxy,
|
hasProxy,
|
||||||
passcode,
|
passcode,
|
||||||
containerNode.currentItemState,
|
containerNode.currentItemState,
|
||||||
@ -5354,6 +5364,7 @@ private final class ChatListLocationContext {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateChatList(
|
self.updateChatList(
|
||||||
networkState: networkState,
|
networkState: networkState,
|
||||||
proxy: proxy,
|
proxy: proxy,
|
||||||
|
|||||||
@ -3843,6 +3843,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
guard let item = self.item else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if let compoundTextButtonNode = self.compoundTextButtonNode, let compoundHighlightingNode = self.compoundHighlightingNode, compoundHighlightingNode.alpha != 0.0 {
|
if let compoundTextButtonNode = self.compoundTextButtonNode, let compoundHighlightingNode = self.compoundHighlightingNode, compoundHighlightingNode.alpha != 0.0 {
|
||||||
let localPoint = self.view.convert(point, to: compoundHighlightingNode.view)
|
let localPoint = self.view.convert(point, to: compoundHighlightingNode.view)
|
||||||
var matches = false
|
var matches = false
|
||||||
@ -3857,8 +3861,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let avatarStoryIndicatorView = self.avatarStoryIndicator?.view, let result = avatarStoryIndicatorView.hitTest(self.view.convert(point, to: avatarStoryIndicatorView), with: event) {
|
if let _ = item.interaction.inlineNavigationLocation {
|
||||||
return result
|
} else {
|
||||||
|
if let avatarStoryIndicatorView = self.avatarStoryIndicator?.view, let result = avatarStoryIndicatorView.hitTest(self.view.convert(point, to: avatarStoryIndicatorView), with: event) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.hitTest(point, with: event)
|
return super.hitTest(point, with: event)
|
||||||
|
|||||||
@ -22,11 +22,11 @@ open class HierarchyTrackingLayer: CALayer {
|
|||||||
|
|
||||||
override open func action(forKey event: String) -> CAAction? {
|
override open func action(forKey event: String) -> CAAction? {
|
||||||
if event == kCAOnOrderIn {
|
if event == kCAOnOrderIn {
|
||||||
self.didEnterHierarchy?()
|
|
||||||
self.isInHierarchy = true
|
self.isInHierarchy = true
|
||||||
|
self.didEnterHierarchy?()
|
||||||
} else if event == kCAOnOrderOut {
|
} else if event == kCAOnOrderOut {
|
||||||
self.didExitHierarchy?()
|
|
||||||
self.isInHierarchy = false
|
self.isInHierarchy = false
|
||||||
|
self.didExitHierarchy?()
|
||||||
}
|
}
|
||||||
return nullAction
|
return nullAction
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,9 +19,14 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con
|
|||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.AreVoiceCallsAvailable(id: peerId),
|
TelegramEngine.EngineData.Item.Peer.AreVoiceCallsAvailable(id: peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.AreVideoCallsAvailable(id: peerId),
|
TelegramEngine.EngineData.Item.Peer.AreVideoCallsAvailable(id: peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId)
|
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId),
|
||||||
|
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
||||||
)
|
)
|
||||||
|> map { [weak contactsController] peer, areVoiceCallsAvailable, areVideoCallsAvailable, notificationSettings -> [ContextMenuItem] in
|
|> map { [weak contactsController] peer, areVoiceCallsAvailable, areVideoCallsAvailable, notificationSettings, globalSettings -> [ContextMenuItem] in
|
||||||
|
guard let peer else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
if isStories {
|
if isStories {
|
||||||
@ -42,7 +47,8 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con
|
|||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
let isMuted = notificationSettings.storiesMuted == true
|
let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings())
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
@ -50,7 +56,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con
|
|||||||
|
|
||||||
let _ = context.engine.peers.togglePeerStoriesMuted(peerId: peerId).start()
|
let _ = context.engine.peers.togglePeerStoriesMuted(peerId: peerId).start()
|
||||||
|
|
||||||
if let peer {
|
do {
|
||||||
let iconColor = UIColor.white
|
let iconColor = UIColor.white
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
if isMuted {
|
if isMuted {
|
||||||
@ -96,7 +102,7 @@ func contactContextMenuItems(context: AccountContext, peerId: EnginePeer.Id, con
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let peer {
|
do {
|
||||||
let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.minY + 1.0), size: CGSize())
|
let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.minY + 1.0), size: CGSize())
|
||||||
let tooltipController = TooltipScreen(
|
let tooltipController = TooltipScreen(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@ -62,8 +62,16 @@ private final class NotificationExceptionState : Equatable {
|
|||||||
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withUpdatedPeerStoryNotifications(_ peer: EnginePeer, _ storyNotifications: PeerNotificationDisplayPreviews) -> NotificationExceptionState {
|
func withUpdatedPeerStoriesMuted(_ peer: EnginePeer, _ mute: PeerStoryNotificationSettings.Mute) -> NotificationExceptionState {
|
||||||
return NotificationExceptionState(mode: mode.withUpdatedPeerStoryNotifications(peer, storyNotifications), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStoriesMuted(peer, mute), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withUpdatedPeerStoriesHideSender(_ peer: EnginePeer, _ hideSender: PeerStoryNotificationSettings.HideSender) -> NotificationExceptionState {
|
||||||
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStoriesHideSender(peer, hideSender), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withUpdatedPeerStorySound(_ peer: EnginePeer, _ sound: PeerMessageSound) -> NotificationExceptionState {
|
||||||
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStorySound(peer, sound), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
|
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
|
||||||
@ -584,17 +592,18 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
|
|||||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatePeerStoryNotifications: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
|
let updatePeerStoriesMuted: (PeerId, PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> = {
|
||||||
var isMuted: Bool?
|
peerId, mute in
|
||||||
switch storyNotifications {
|
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue
|
||||||
case .default:
|
}
|
||||||
isMuted = nil
|
|
||||||
case .show:
|
let updatePeerStoriesHideSender: (PeerId, PeerStoryNotificationSettings.HideSender) -> Signal<Void, NoError> = {
|
||||||
isMuted = false
|
peerId, hideSender in
|
||||||
case .hide:
|
return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue
|
||||||
isMuted = true
|
}
|
||||||
}
|
|
||||||
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
|
let updatePeerStorySound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||||
|
return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||||
@ -670,12 +679,32 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
updateNotificationsView({})
|
updateNotificationsView({})
|
||||||
})
|
})
|
||||||
}, updatePeerStoryNotifications: { peerId, displayPreviews in
|
}, updatePeerStoriesMuted: { peerId, mute in
|
||||||
updateNotificationsDisposable.set(nil)
|
updateNotificationsDisposable.set(nil)
|
||||||
_ = combineLatest(updatePeerStoryNotifications(peerId, displayPreviews), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
let _ = combineLatest(updatePeerStoriesMuted(peerId, mute), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
updateState { value in
|
updateState { value in
|
||||||
return value.withUpdatedPeerStoryNotifications(peer, displayPreviews)
|
return value.withUpdatedPeerStoriesMuted(peer, mute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
}, updatePeerStoriesHideSender: { peerId, hideSender in
|
||||||
|
updateNotificationsDisposable.set(nil)
|
||||||
|
let _ = combineLatest(updatePeerStoriesHideSender(peerId, hideSender), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
||||||
|
if let peer = peer {
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerStoriesHideSender(peer, hideSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
}, updatePeerStorySound: { peerId, sound in
|
||||||
|
updateNotificationsDisposable.set(nil)
|
||||||
|
let _ = combineLatest(updatePeerStorySound(peerId, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
||||||
|
if let peer = peer {
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerStorySound(peer, sound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateNotificationsView({})
|
updateNotificationsView({})
|
||||||
|
|||||||
@ -551,8 +551,19 @@ private func notificationsAndSoundsEntries(authorizationStatus: AccessType, warn
|
|||||||
entries.append(.privateChats(presentationData.theme, presentationData.strings.Notifications_PrivateChats, !exceptions.users.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.users.peerIds.count)) : "", globalSettings.privateChats.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
entries.append(.privateChats(presentationData.theme, presentationData.strings.Notifications_PrivateChats, !exceptions.users.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.users.peerIds.count)) : "", globalSettings.privateChats.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
||||||
entries.append(.groupChats(presentationData.theme, presentationData.strings.Notifications_GroupChats, !exceptions.groups.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.groups.peerIds.count)) : "", globalSettings.groupChats.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
entries.append(.groupChats(presentationData.theme, presentationData.strings.Notifications_GroupChats, !exceptions.groups.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.groups.peerIds.count)) : "", globalSettings.groupChats.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
||||||
entries.append(.channels(presentationData.theme, presentationData.strings.Notifications_Channels, !exceptions.channels.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.channels.peerIds.count)) : "", globalSettings.channels.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
entries.append(.channels(presentationData.theme, presentationData.strings.Notifications_Channels, !exceptions.channels.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.channels.peerIds.count)) : "", globalSettings.channels.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
||||||
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
entries.append(.stories(presentationData.theme, "Stories", !exceptions.stories.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.stories.peerIds.count)) : "", globalSettings.privateChats.storiesMuted != true ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
let storiesValue: String
|
||||||
|
switch globalSettings.privateChats.storySettings.mute {
|
||||||
|
case .default:
|
||||||
|
storiesValue = "Top 5"
|
||||||
|
case .muted:
|
||||||
|
storiesValue = presentationData.strings.Notifications_Off
|
||||||
|
case .unmuted:
|
||||||
|
storiesValue = presentationData.strings.Notifications_On
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.stories(presentationData.theme, "Stories", !exceptions.stories.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.stories.peerIds.count)) : "", storiesValue))
|
||||||
|
|
||||||
entries.append(.inAppHeader(presentationData.theme, presentationData.strings.Notifications_InAppNotifications.uppercased()))
|
entries.append(.inAppHeader(presentationData.theme, presentationData.strings.Notifications_InAppNotifications.uppercased()))
|
||||||
entries.append(.inAppSounds(presentationData.theme, presentationData.strings.Notifications_InAppNotificationsSounds, inAppSettings.playSounds))
|
entries.append(.inAppSounds(presentationData.theme, presentationData.strings.Notifications_InAppNotificationsSounds, inAppSettings.playSounds))
|
||||||
@ -730,6 +741,8 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
|
|||||||
|
|
||||||
let exceptionsSignal = Signal<NotificationExceptionsList?, NoError>.single(exceptionsList) |> then(context.engine.peers.notificationExceptionsList() |> map(Optional.init))
|
let exceptionsSignal = Signal<NotificationExceptionsList?, NoError>.single(exceptionsList) |> then(context.engine.peers.notificationExceptionsList() |> map(Optional.init))
|
||||||
|
|
||||||
|
let defaultStorySettings = PeerStoryNotificationSettings.default
|
||||||
|
|
||||||
notificationExceptions.set(exceptionsSignal |> map { list -> (NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode) in
|
notificationExceptions.set(exceptionsSignal |> map { list -> (NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode) in
|
||||||
var users:[PeerId : NotificationExceptionWrapper] = [:]
|
var users:[PeerId : NotificationExceptionWrapper] = [:]
|
||||||
var groups: [PeerId : NotificationExceptionWrapper] = [:]
|
var groups: [PeerId : NotificationExceptionWrapper] = [:]
|
||||||
@ -738,7 +751,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
|
|||||||
if let list = list {
|
if let list = list {
|
||||||
for (key, value) in list.settings {
|
for (key, value) in list.settings {
|
||||||
if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != context.account.peerId {
|
if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != context.account.peerId {
|
||||||
if value.storiesMuted != nil {
|
if value.storySettings != defaultStorySettings {
|
||||||
stories[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer))
|
stories[key] = NotificationExceptionWrapper(settings: value, peer: EnginePeer(peer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,7 @@ private final class NotificationsPeerCategoryControllerArguments {
|
|||||||
let soundSelectionDisposable: MetaDisposable
|
let soundSelectionDisposable: MetaDisposable
|
||||||
|
|
||||||
let updateEnabled: (Bool) -> Void
|
let updateEnabled: (Bool) -> Void
|
||||||
|
let updateEnabledImportant: (Bool) -> Void
|
||||||
let updatePreviews: (Bool) -> Void
|
let updatePreviews: (Bool) -> Void
|
||||||
|
|
||||||
let openSound: (PeerMessageSound) -> Void
|
let openSound: (PeerMessageSound) -> Void
|
||||||
@ -49,11 +50,12 @@ private final class NotificationsPeerCategoryControllerArguments {
|
|||||||
|
|
||||||
let updatedExceptionMode: (NotificationExceptionMode) -> Void
|
let updatedExceptionMode: (NotificationExceptionMode) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, openSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (EnginePeer) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedPeerId: @escaping (EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer) -> Void, updatedExceptionMode: @escaping (NotificationExceptionMode) -> Void) {
|
init(context: AccountContext, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updateEnabledImportant: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, openSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (EnginePeer) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedPeerId: @escaping (EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer) -> Void, updatedExceptionMode: @escaping (NotificationExceptionMode) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.soundSelectionDisposable = soundSelectionDisposable
|
self.soundSelectionDisposable = soundSelectionDisposable
|
||||||
|
|
||||||
self.updateEnabled = updateEnabled
|
self.updateEnabled = updateEnabled
|
||||||
|
self.updateEnabledImportant = updateEnabledImportant
|
||||||
self.updatePreviews = updatePreviews
|
self.updatePreviews = updatePreviews
|
||||||
self.openSound = openSound
|
self.openSound = openSound
|
||||||
|
|
||||||
@ -90,6 +92,8 @@ public enum NotificationsPeerCategoryEntryTag: ItemListItemTag {
|
|||||||
|
|
||||||
private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||||
case enable(PresentationTheme, String, Bool)
|
case enable(PresentationTheme, String, Bool)
|
||||||
|
case enableImportant(PresentationTheme, String, Bool)
|
||||||
|
case importantInfo(PresentationTheme, String)
|
||||||
case optionsHeader(PresentationTheme, String)
|
case optionsHeader(PresentationTheme, String)
|
||||||
case previews(PresentationTheme, String, Bool)
|
case previews(PresentationTheme, String, Bool)
|
||||||
case sound(PresentationTheme, String, String, PeerMessageSound)
|
case sound(PresentationTheme, String, String, PeerMessageSound)
|
||||||
@ -101,7 +105,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
case .enable:
|
case .enable, .enableImportant, .importantInfo:
|
||||||
return NotificationsPeerCategorySection.enable.rawValue
|
return NotificationsPeerCategorySection.enable.rawValue
|
||||||
case .optionsHeader, .previews, .sound:
|
case .optionsHeader, .previews, .sound:
|
||||||
return NotificationsPeerCategorySection.options.rawValue
|
return NotificationsPeerCategorySection.options.rawValue
|
||||||
@ -114,18 +118,22 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
|||||||
switch self {
|
switch self {
|
||||||
case .enable:
|
case .enable:
|
||||||
return 0
|
return 0
|
||||||
case .optionsHeader:
|
case .enableImportant:
|
||||||
return 1
|
return 1
|
||||||
case .previews:
|
case .importantInfo:
|
||||||
return 2
|
return 2
|
||||||
case .sound:
|
case .optionsHeader:
|
||||||
return 3
|
return 3
|
||||||
case .exceptionsHeader:
|
case .previews:
|
||||||
return 4
|
return 4
|
||||||
case .addException:
|
case .sound:
|
||||||
return 5
|
return 5
|
||||||
|
case .exceptionsHeader:
|
||||||
|
return 6
|
||||||
|
case .addException:
|
||||||
|
return 7
|
||||||
case let .exception(index, _, _, _, _, _, _, _, _, _):
|
case let .exception(index, _, _, _, _, _, _, _, _, _):
|
||||||
return 6 + index
|
return 8 + index
|
||||||
case .removeAllExceptions:
|
case .removeAllExceptions:
|
||||||
return 100000
|
return 100000
|
||||||
}
|
}
|
||||||
@ -152,6 +160,18 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .enableImportant(lhsTheme, lhsText, lhsValue):
|
||||||
|
if case let .enableImportant(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .importantInfo(lhsTheme, lhsText):
|
||||||
|
if case let .importantInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
case let .optionsHeader(lhsTheme, lhsText):
|
case let .optionsHeader(lhsTheme, lhsText):
|
||||||
if case let .optionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .optionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
@ -209,6 +229,12 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
|||||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||||
arguments.updateEnabled(updatedValue)
|
arguments.updateEnabled(updatedValue)
|
||||||
}, tag: self.tag)
|
}, tag: self.tag)
|
||||||
|
case let .enableImportant(_, text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||||
|
arguments.updateEnabledImportant(updatedValue)
|
||||||
|
}, tag: self.tag)
|
||||||
|
case let .importantInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .optionsHeader(_, text):
|
case let .optionsHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .previews(_, text, value):
|
case let .previews(_, text, value):
|
||||||
@ -266,7 +292,34 @@ private func notificationsPeerCategoryEntries(category: NotificationsPeerCategor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if case .stories = category {
|
if case .stories = category {
|
||||||
entries.append(.enable(presentationData.theme, "Show All Notifications", notificationSettings.storiesMuted == nil || notificationSettings.storiesMuted == false))
|
var allEnabled = false
|
||||||
|
var importantEnabled = false
|
||||||
|
|
||||||
|
switch notificationSettings.storySettings.mute {
|
||||||
|
case .muted:
|
||||||
|
allEnabled = false
|
||||||
|
importantEnabled = false
|
||||||
|
case .unmuted:
|
||||||
|
allEnabled = true
|
||||||
|
importantEnabled = true
|
||||||
|
case .default:
|
||||||
|
allEnabled = false
|
||||||
|
importantEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
entries.append(.enable(presentationData.theme, "Show All Notifications", allEnabled))
|
||||||
|
if !allEnabled {
|
||||||
|
entries.append(.enableImportant(presentationData.theme, "Show Important Notifications", importantEnabled))
|
||||||
|
entries.append(.importantInfo(presentationData.theme, "Always on for top 5 contacts."))
|
||||||
|
}
|
||||||
|
|
||||||
|
if notificationSettings.enabled || !notificationExceptions.isEmpty {
|
||||||
|
entries.append(.optionsHeader(presentationData.theme, presentationData.strings.Notifications_Options.uppercased()))
|
||||||
|
|
||||||
|
entries.append(.previews(presentationData.theme, "Display Author Name", notificationSettings.storySettings.hideSender != .hide))
|
||||||
|
entries.append(.sound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: filteredGlobalSound(notificationSettings.storySettings.sound)), filteredGlobalSound(notificationSettings.storySettings.sound)))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
entries.append(.enable(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, notificationSettings.enabled))
|
entries.append(.enable(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, notificationSettings.enabled))
|
||||||
|
|
||||||
@ -280,7 +333,6 @@ private func notificationsPeerCategoryEntries(category: NotificationsPeerCategor
|
|||||||
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsExceptions.uppercased()))
|
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsExceptions.uppercased()))
|
||||||
entries.append(.addException(presentationData.theme, presentationData.strings.Notification_Exceptions_AddException))
|
entries.append(.addException(presentationData.theme, presentationData.strings.Notification_Exceptions_AddException))
|
||||||
|
|
||||||
|
|
||||||
let sortedExceptions = notificationExceptions.settings.sorted(by: { lhs, rhs in
|
let sortedExceptions = notificationExceptions.settings.sorted(by: { lhs, rhs in
|
||||||
let lhsName = lhs.value.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let lhsName = lhs.value.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
let rhsName = rhs.value.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let rhsName = rhs.value.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
@ -312,11 +364,39 @@ private func notificationsPeerCategoryEntries(category: NotificationsPeerCategor
|
|||||||
var title: String
|
var title: String
|
||||||
|
|
||||||
if case .stories = category {
|
if case .stories = category {
|
||||||
if value.settings.storiesMuted == true {
|
var muted = false
|
||||||
|
if value.settings.storySettings.mute == .muted {
|
||||||
|
muted = true
|
||||||
title = presentationData.strings.Notification_Exceptions_AlwaysOff
|
title = presentationData.strings.Notification_Exceptions_AlwaysOff
|
||||||
} else {
|
} else {
|
||||||
title = presentationData.strings.Notification_Exceptions_AlwaysOn
|
title = presentationData.strings.Notification_Exceptions_AlwaysOn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !muted {
|
||||||
|
switch value.settings.storySettings.sound {
|
||||||
|
case .default:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
if !title.isEmpty {
|
||||||
|
title.append(", ")
|
||||||
|
}
|
||||||
|
title.append(presentationData.strings.Notification_Exceptions_SoundCustom)
|
||||||
|
}
|
||||||
|
switch value.settings.storySettings.hideSender {
|
||||||
|
case .default:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
if !title.isEmpty {
|
||||||
|
title += ", "
|
||||||
|
}
|
||||||
|
//TODO:localize
|
||||||
|
if case .show = value.settings.displayPreviews {
|
||||||
|
title += "Show Names"
|
||||||
|
} else {
|
||||||
|
title += "Hide Names"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var muted = false
|
var muted = false
|
||||||
switch value.settings.muteState {
|
switch value.settings.muteState {
|
||||||
@ -427,8 +507,16 @@ private final class NotificationExceptionState : Equatable {
|
|||||||
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withUpdatedPeerStoryNotifications(_ peer: EnginePeer, _ storyNotifications: PeerNotificationDisplayPreviews) -> NotificationExceptionState {
|
func withUpdatedPeerStoriesMuted(_ peer: EnginePeer, _ mute: PeerStoryNotificationSettings.Mute) -> NotificationExceptionState {
|
||||||
return NotificationExceptionState(mode: mode.withUpdatedPeerStoryNotifications(peer, storyNotifications), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStoriesMuted(peer, mute), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withUpdatedPeerStoriesHideSender(_ peer: EnginePeer, _ hideSender: PeerStoryNotificationSettings.HideSender) -> NotificationExceptionState {
|
||||||
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStoriesHideSender(peer, hideSender), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withUpdatedPeerStorySound(_ peer: EnginePeer, _ sound: PeerMessageSound) -> NotificationExceptionState {
|
||||||
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStorySound(peer, sound), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
|
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
|
||||||
@ -470,17 +558,18 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatePeerStoryNotifications: (EnginePeer.Id, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
|
let updatePeerStoriesMuted: (EnginePeer.Id, PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> = {
|
||||||
var isMuted: Bool?
|
peerId, mute in
|
||||||
switch storyNotifications {
|
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue
|
||||||
case .default:
|
}
|
||||||
isMuted = nil
|
|
||||||
case .show:
|
let updatePeerStoriesHideSender: (EnginePeer.Id, PeerStoryNotificationSettings.HideSender) -> Signal<Void, NoError> = {
|
||||||
isMuted = false
|
peerId, hideSender in
|
||||||
case .hide:
|
return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue
|
||||||
isMuted = true
|
}
|
||||||
}
|
|
||||||
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
|
let updatePeerStorySound: (EnginePeer.Id, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||||
|
return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
var peerIds: Set<EnginePeer.Id> = Set(mode.peerIds)
|
var peerIds: Set<EnginePeer.Id> = Set(mode.peerIds)
|
||||||
@ -587,12 +676,32 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
}
|
}
|
||||||
updateNotificationsView({})
|
updateNotificationsView({})
|
||||||
})
|
})
|
||||||
}, updatePeerStoryNotifications: { peerId, storyNotifications in
|
}, updatePeerStoriesMuted: { peerId, mute in
|
||||||
updateNotificationsDisposable.set(nil)
|
updateNotificationsDisposable.set(nil)
|
||||||
_ = combineLatest(updatePeerStoryNotifications(peerId, storyNotifications), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
let _ = combineLatest(updatePeerStoriesMuted(peerId, mute), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
updateState { value in
|
updateState { value in
|
||||||
return value.withUpdatedPeerStoryNotifications(peer, storyNotifications)
|
return value.withUpdatedPeerStoriesMuted(peer, mute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
}, updatePeerStoriesHideSender: { peerId, hideSender in
|
||||||
|
updateNotificationsDisposable.set(nil)
|
||||||
|
let _ = combineLatest(updatePeerStoriesHideSender(peerId, hideSender), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
||||||
|
if let peer = peer {
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerStoriesHideSender(peer, hideSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
}, updatePeerStorySound: { peerId, sound in
|
||||||
|
updateNotificationsDisposable.set(nil)
|
||||||
|
let _ = combineLatest(updatePeerStorySound(peerId, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
||||||
|
if let peer = peer {
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerStorySound(peer, sound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateNotificationsView({})
|
updateNotificationsView({})
|
||||||
@ -608,7 +717,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateState { value in
|
updateState { value in
|
||||||
return value.withUpdatedPeerStoryNotifications(peer, .default)
|
return value.withUpdatedPeerStorySound(peer, .default).withUpdatedPeerStoriesMuted(peer, .default).withUpdatedPeerStoriesHideSender(peer, .default)
|
||||||
}
|
}
|
||||||
updateNotificationsView({})
|
updateNotificationsView({})
|
||||||
})
|
})
|
||||||
@ -637,14 +746,25 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
var settings = settings
|
var settings = settings
|
||||||
switch category {
|
switch category {
|
||||||
case .privateChat:
|
case .privateChat:
|
||||||
settings.privateChats.enabled = value
|
settings.privateChats.enabled = value
|
||||||
case .group:
|
case .group:
|
||||||
settings.groupChats.enabled = value
|
settings.groupChats.enabled = value
|
||||||
case .channel:
|
case .channel:
|
||||||
settings.channels.enabled = value
|
settings.channels.enabled = value
|
||||||
case .stories:
|
case .stories:
|
||||||
settings.privateChats.storiesMuted = !value
|
settings.privateChats.storySettings.mute = value ? .unmuted : .default
|
||||||
|
}
|
||||||
|
return settings
|
||||||
|
}).start()
|
||||||
|
}, updateEnabledImportant: { value in
|
||||||
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||||
|
var settings = settings
|
||||||
|
switch category {
|
||||||
|
case .stories:
|
||||||
|
settings.privateChats.storySettings.mute = value ? .default : .muted
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
}).start()
|
}).start()
|
||||||
@ -659,7 +779,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
case .channel:
|
case .channel:
|
||||||
settings.channels.displayPreviews = value
|
settings.channels.displayPreviews = value
|
||||||
case .stories:
|
case .stories:
|
||||||
break
|
settings.privateChats.storySettings.hideSender = value ? .show : .hide
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
}).start()
|
}).start()
|
||||||
@ -675,7 +795,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
case .channel:
|
case .channel:
|
||||||
settings.channels.sound = value
|
settings.channels.sound = value
|
||||||
case .stories:
|
case .stories:
|
||||||
break
|
settings.privateChats.storySettings.sound = value
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
}).start()
|
}).start()
|
||||||
@ -722,7 +842,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
for value in values {
|
for value in values {
|
||||||
state = state.withUpdatedPeerStoryNotifications(value.peer, .default)
|
state = state.withUpdatedPeerStorySound(value.peer, .default).withUpdatedPeerStoriesMuted(value.peer, .default).withUpdatedPeerStoriesHideSender(value.peer, .default)
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@ -765,7 +885,7 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
updateNotificationsDisposable.set(nil)
|
updateNotificationsDisposable.set(nil)
|
||||||
updateState { value in
|
updateState { value in
|
||||||
return value.withUpdatedPeerStoryNotifications(peer, .default)
|
return value.withUpdatedPeerStorySound(peer, .default).withUpdatedPeerStoriesMuted(peer, .default).withUpdatedPeerStoriesHideSender(peer, .default)
|
||||||
}
|
}
|
||||||
let _ = (context.engine.peers.removeCustomStoryNotificationSettings(peerIds: [peer.id])
|
let _ = (context.engine.peers.removeCustomStoryNotificationSettings(peerIds: [peer.id])
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
|||||||
@ -6,12 +6,15 @@ import TelegramApi
|
|||||||
extension TelegramPeerNotificationSettings {
|
extension TelegramPeerNotificationSettings {
|
||||||
convenience init(apiSettings: Api.PeerNotifySettings) {
|
convenience init(apiSettings: Api.PeerNotifySettings) {
|
||||||
switch apiSettings {
|
switch apiSettings {
|
||||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, _, _, _, _):
|
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, storiesHideSender, storiesIosSound, _, storiesDesktopSound):
|
||||||
let sound: Api.NotificationSound?
|
let sound: Api.NotificationSound?
|
||||||
|
let storiesSound: Api.NotificationSound?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
sound = iosSound
|
sound = iosSound
|
||||||
|
storiesSound = storiesIosSound
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
sound = desktopSound
|
sound = desktopSound
|
||||||
|
storiesSound = storiesDesktopSound
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let muteState: PeerMuteState
|
let muteState: PeerMuteState
|
||||||
@ -35,12 +38,25 @@ extension TelegramPeerNotificationSettings {
|
|||||||
displayPreviews = .default
|
displayPreviews = .default
|
||||||
}
|
}
|
||||||
|
|
||||||
var storiesMutedValue: Bool?
|
let storiesMutedValue: PeerStoryNotificationSettings.Mute
|
||||||
if let storiesMuted = storiesMuted {
|
if let storiesMuted = storiesMuted {
|
||||||
storiesMutedValue = storiesMuted == .boolTrue
|
storiesMutedValue = storiesMuted == .boolTrue ? .muted : .unmuted
|
||||||
|
} else {
|
||||||
|
storiesMutedValue = .default
|
||||||
}
|
}
|
||||||
|
|
||||||
self.init(muteState: muteState, messageSound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault), displayPreviews: displayPreviews, storiesMuted: storiesMutedValue)
|
var storiesHideSenderValue: PeerStoryNotificationSettings.HideSender
|
||||||
|
if let storiesHideSender = storiesHideSender {
|
||||||
|
storiesHideSenderValue = storiesHideSender == .boolTrue ? .hide : .show
|
||||||
|
} else {
|
||||||
|
storiesHideSenderValue = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init(muteState: muteState, messageSound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault), displayPreviews: displayPreviews, storySettings: PeerStoryNotificationSettings(
|
||||||
|
mute: storiesMutedValue,
|
||||||
|
hideSender: storiesHideSenderValue,
|
||||||
|
sound: PeerMessageSound(apiSound: storiesSound ?? .notificationSoundDefault)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,24 +6,48 @@ import TelegramApi
|
|||||||
extension MessageNotificationSettings {
|
extension MessageNotificationSettings {
|
||||||
init(apiSettings: Api.PeerNotifySettings) {
|
init(apiSettings: Api.PeerNotifySettings) {
|
||||||
switch apiSettings {
|
switch apiSettings {
|
||||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, _, _, _, _):
|
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, storiesHideSender, storiesIosSound, _, storiesDesktopSound):
|
||||||
let sound: Api.NotificationSound?
|
let sound: Api.NotificationSound?
|
||||||
|
let storiesSound: Api.NotificationSound?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
sound = iosSound
|
sound = iosSound
|
||||||
|
storiesSound = storiesIosSound
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
sound = desktopSound
|
sound = desktopSound
|
||||||
|
storiesSound = storiesDesktopSound
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let displayPreviews: Bool
|
let displayPreviews: Bool
|
||||||
if let showPreviews = showPreviews, case .boolFalse = showPreviews {
|
if let showPreviews = showPreviews, case .boolFalse = showPreviews {
|
||||||
displayPreviews = false
|
displayPreviews = false
|
||||||
} else {
|
} else {
|
||||||
displayPreviews = true
|
displayPreviews = true
|
||||||
}
|
}
|
||||||
var storiesMutedValue: Bool?
|
|
||||||
|
let storiesMutedValue: PeerStoryNotificationSettings.Mute
|
||||||
if let storiesMuted = storiesMuted {
|
if let storiesMuted = storiesMuted {
|
||||||
storiesMutedValue = storiesMuted == .boolTrue
|
storiesMutedValue = storiesMuted == .boolTrue ? .muted : .unmuted
|
||||||
|
} else {
|
||||||
|
storiesMutedValue = .default
|
||||||
}
|
}
|
||||||
self = MessageNotificationSettings(enabled: muteUntil == 0, displayPreviews: displayPreviews, sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault), storiesMuted: storiesMutedValue)
|
|
||||||
|
var storiesHideSenderValue: PeerStoryNotificationSettings.HideSender
|
||||||
|
if let storiesHideSender = storiesHideSender {
|
||||||
|
storiesHideSenderValue = storiesHideSender == .boolTrue ? .hide : .show
|
||||||
|
} else {
|
||||||
|
storiesHideSenderValue = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
self = MessageNotificationSettings(
|
||||||
|
enabled: muteUntil == 0,
|
||||||
|
displayPreviews: displayPreviews,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault),
|
||||||
|
storySettings: PeerStoryNotificationSettings(
|
||||||
|
mute: storiesMutedValue,
|
||||||
|
hideSender: storiesHideSenderValue,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,12 +116,15 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
|||||||
|> map { chats, users, channels, contactsJoinedMuted in
|
|> map { chats, users, channels, contactsJoinedMuted in
|
||||||
let chatsSettings: MessageNotificationSettings
|
let chatsSettings: MessageNotificationSettings
|
||||||
switch chats {
|
switch chats {
|
||||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, _, _, _, _):
|
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, storiesHideSender, storiesIosSound, _, storiesDesktopSound):
|
||||||
let sound: Api.NotificationSound?
|
let sound: Api.NotificationSound?
|
||||||
|
let storiesSound: Api.NotificationSound?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
sound = iosSound
|
sound = iosSound
|
||||||
|
storiesSound = storiesIosSound
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
sound = desktopSound
|
sound = desktopSound
|
||||||
|
storiesSound = storiesDesktopSound
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
@ -137,22 +140,43 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
|||||||
displayPreviews = true
|
displayPreviews = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var storiesMutedValue: Bool?
|
let storiesMutedValue: PeerStoryNotificationSettings.Mute
|
||||||
if let storiesMuted = storiesMuted {
|
if let storiesMuted = storiesMuted {
|
||||||
storiesMutedValue = storiesMuted == .boolTrue
|
storiesMutedValue = storiesMuted == .boolTrue ? .muted : .unmuted
|
||||||
|
} else {
|
||||||
|
storiesMutedValue = .default
|
||||||
}
|
}
|
||||||
|
|
||||||
chatsSettings = MessageNotificationSettings(enabled: enabled, displayPreviews: displayPreviews, sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault), storiesMuted: storiesMutedValue)
|
var storiesHideSenderValue: PeerStoryNotificationSettings.HideSender
|
||||||
|
if let storiesHideSender = storiesHideSender {
|
||||||
|
storiesHideSenderValue = storiesHideSender == .boolTrue ? .hide : .show
|
||||||
|
} else {
|
||||||
|
storiesHideSenderValue = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
chatsSettings = MessageNotificationSettings(
|
||||||
|
enabled: enabled,
|
||||||
|
displayPreviews: displayPreviews,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault),
|
||||||
|
storySettings: PeerStoryNotificationSettings(
|
||||||
|
mute: storiesMutedValue,
|
||||||
|
hideSender: storiesHideSenderValue,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let userSettings: MessageNotificationSettings
|
let userSettings: MessageNotificationSettings
|
||||||
switch users {
|
switch users {
|
||||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, _, _, _, _):
|
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, storiesHideSender, storiesIosSound, _, storiesDesktopSound):
|
||||||
let sound: Api.NotificationSound?
|
let sound: Api.NotificationSound?
|
||||||
|
let storiesSound: Api.NotificationSound?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
sound = iosSound
|
sound = iosSound
|
||||||
|
storiesSound = storiesIosSound
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
sound = desktopSound
|
sound = desktopSound
|
||||||
|
storiesSound = storiesDesktopSound
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
@ -168,22 +192,43 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
|||||||
displayPreviews = true
|
displayPreviews = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var storiesMutedValue: Bool?
|
let storiesMutedValue: PeerStoryNotificationSettings.Mute
|
||||||
if let storiesMuted = storiesMuted {
|
if let storiesMuted = storiesMuted {
|
||||||
storiesMutedValue = storiesMuted == .boolTrue
|
storiesMutedValue = storiesMuted == .boolTrue ? .muted : .unmuted
|
||||||
|
} else {
|
||||||
|
storiesMutedValue = .default
|
||||||
}
|
}
|
||||||
|
|
||||||
userSettings = MessageNotificationSettings(enabled: enabled, displayPreviews: displayPreviews, sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault), storiesMuted: storiesMutedValue)
|
var storiesHideSenderValue: PeerStoryNotificationSettings.HideSender
|
||||||
|
if let storiesHideSender = storiesHideSender {
|
||||||
|
storiesHideSenderValue = storiesHideSender == .boolTrue ? .hide : .show
|
||||||
|
} else {
|
||||||
|
storiesHideSenderValue = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
userSettings = MessageNotificationSettings(
|
||||||
|
enabled: enabled,
|
||||||
|
displayPreviews: displayPreviews,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault),
|
||||||
|
storySettings: PeerStoryNotificationSettings(
|
||||||
|
mute: storiesMutedValue,
|
||||||
|
hideSender: storiesHideSenderValue,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let channelSettings: MessageNotificationSettings
|
let channelSettings: MessageNotificationSettings
|
||||||
switch channels {
|
switch channels {
|
||||||
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, _, _, _, _):
|
case let .peerNotifySettings(_, showPreviews, _, muteUntil, iosSound, _, desktopSound, storiesMuted, storiesHideSender, storiesIosSound, _, storiesDesktopSound):
|
||||||
let sound: Api.NotificationSound?
|
let sound: Api.NotificationSound?
|
||||||
|
let storiesSound: Api.NotificationSound?
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
sound = iosSound
|
sound = iosSound
|
||||||
|
storiesSound = storiesIosSound
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
sound = desktopSound
|
sound = desktopSound
|
||||||
|
storiesSound = storiesDesktopSound
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
@ -199,12 +244,30 @@ private func fetchedNotificationSettings(network: Network) -> Signal<GlobalNotif
|
|||||||
displayPreviews = true
|
displayPreviews = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var storiesMutedValue: Bool?
|
let storiesMutedValue: PeerStoryNotificationSettings.Mute
|
||||||
if let storiesMuted = storiesMuted {
|
if let storiesMuted = storiesMuted {
|
||||||
storiesMutedValue = storiesMuted == .boolTrue
|
storiesMutedValue = storiesMuted == .boolTrue ? .muted : .unmuted
|
||||||
|
} else {
|
||||||
|
storiesMutedValue = .default
|
||||||
}
|
}
|
||||||
|
|
||||||
channelSettings = MessageNotificationSettings(enabled: enabled, displayPreviews: displayPreviews, sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault), storiesMuted: storiesMutedValue)
|
var storiesHideSenderValue: PeerStoryNotificationSettings.HideSender
|
||||||
|
if let storiesHideSender = storiesHideSender {
|
||||||
|
storiesHideSenderValue = storiesHideSender == .boolTrue ? .hide : .show
|
||||||
|
} else {
|
||||||
|
storiesHideSenderValue = .default
|
||||||
|
}
|
||||||
|
|
||||||
|
channelSettings = MessageNotificationSettings(
|
||||||
|
enabled: enabled,
|
||||||
|
displayPreviews: displayPreviews,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault),
|
||||||
|
storySettings: PeerStoryNotificationSettings(
|
||||||
|
mute: storiesMutedValue,
|
||||||
|
hideSender: storiesHideSenderValue,
|
||||||
|
sound: PeerMessageSound(apiSound: sound ?? .notificationSoundDefault)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return GlobalNotificationSettingsSet(privateChats: userSettings, groupChats: chatsSettings, channels: channelSettings, contactsJoined: contactsJoinedMuted == .boolFalse)
|
return GlobalNotificationSettingsSet(privateChats: userSettings, groupChats: chatsSettings, channels: channelSettings, contactsJoined: contactsJoinedMuted == .boolFalse)
|
||||||
@ -228,13 +291,38 @@ private func apiInputPeerNotifySettings(_ settings: MessageNotificationSettings)
|
|||||||
flags |= (1 << 3)
|
flags |= (1 << 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
var storiesMuted: Api.Bool?
|
let storiesMuted: Api.Bool?
|
||||||
if let storiesMutedValue = settings.storiesMuted {
|
switch settings.storySettings.mute {
|
||||||
|
case .default:
|
||||||
|
storiesMuted = nil
|
||||||
|
case .muted:
|
||||||
|
storiesMuted = .boolTrue
|
||||||
|
case .unmuted:
|
||||||
|
storiesMuted = .boolFalse
|
||||||
|
}
|
||||||
|
if storiesMuted != nil {
|
||||||
flags |= (1 << 6)
|
flags |= (1 << 6)
|
||||||
storiesMuted = storiesMutedValue ? .boolTrue : .boolFalse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return .inputPeerNotifySettings(flags: flags, showPreviews: settings.displayPreviews ? .boolTrue : .boolFalse, silent: nil, muteUntil: muteUntil, sound: sound, storiesMuted: storiesMuted, storiesHideSender: nil, storiesSound: nil)
|
let storiesHideSender: Api.Bool?
|
||||||
|
switch settings.storySettings.hideSender {
|
||||||
|
case .default:
|
||||||
|
storiesHideSender = nil
|
||||||
|
case .hide:
|
||||||
|
storiesHideSender = .boolTrue
|
||||||
|
case .show:
|
||||||
|
storiesHideSender = .boolFalse
|
||||||
|
}
|
||||||
|
if storiesHideSender != nil {
|
||||||
|
flags |= (1 << 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
let storiesSound: Api.NotificationSound? = settings.storySettings.sound.apiSound
|
||||||
|
if storiesSound != nil {
|
||||||
|
flags |= (1 << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .inputPeerNotifySettings(flags: flags, showPreviews: settings.displayPreviews ? .boolTrue : .boolFalse, silent: nil, muteUntil: muteUntil, sound: sound, storiesMuted: storiesMuted, storiesHideSender: storiesHideSender, storiesSound: storiesSound)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pushedNotificationSettings(network: Network, settings: GlobalNotificationSettingsSet) -> Signal<Void, NoError> {
|
private func pushedNotificationSettings(network: Network, settings: GlobalNotificationSettingsSet) -> Signal<Void, NoError> {
|
||||||
|
|||||||
@ -130,13 +130,39 @@ func pushPeerNotificationSettings(postbox: Postbox, network: Network, peerId: Pe
|
|||||||
if sound != nil {
|
if sound != nil {
|
||||||
flags |= (1 << 3)
|
flags |= (1 << 3)
|
||||||
}
|
}
|
||||||
var storiesMuted: Api.Bool?
|
|
||||||
if let storiesMutedValue = settings.storiesMuted {
|
let storiesMuted: Api.Bool?
|
||||||
|
switch settings.storySettings.mute {
|
||||||
|
case .default:
|
||||||
|
storiesMuted = nil
|
||||||
|
case .muted:
|
||||||
|
storiesMuted = .boolTrue
|
||||||
|
case .unmuted:
|
||||||
|
storiesMuted = .boolFalse
|
||||||
|
}
|
||||||
|
if storiesMuted != nil {
|
||||||
flags |= (1 << 6)
|
flags |= (1 << 6)
|
||||||
storiesMuted = storiesMutedValue ? .boolTrue : .boolFalse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound, storiesMuted: storiesMuted, storiesHideSender: nil, storiesSound: nil)
|
let storiesHideSender: Api.Bool?
|
||||||
|
switch settings.storySettings.hideSender {
|
||||||
|
case .default:
|
||||||
|
storiesHideSender = nil
|
||||||
|
case .hide:
|
||||||
|
storiesHideSender = .boolTrue
|
||||||
|
case .show:
|
||||||
|
storiesHideSender = .boolFalse
|
||||||
|
}
|
||||||
|
if storiesHideSender != nil {
|
||||||
|
flags |= (1 << 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
let storiesSound: Api.NotificationSound? = settings.storySettings.sound.apiSound
|
||||||
|
if storiesSound != nil {
|
||||||
|
flags |= (1 << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound, storiesMuted: storiesMuted, storiesHideSender: storiesHideSender, storiesSound: storiesSound)
|
||||||
return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyForumTopic(peer: inputPeer, topMsgId: Int32(clamping: threadId)), settings: inputSettings))
|
return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyForumTopic(peer: inputPeer, topMsgId: Int32(clamping: threadId)), settings: inputSettings))
|
||||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||||
return .single(.boolFalse)
|
return .single(.boolFalse)
|
||||||
@ -179,12 +205,39 @@ func pushPeerNotificationSettings(postbox: Postbox, network: Network, peerId: Pe
|
|||||||
if sound != nil {
|
if sound != nil {
|
||||||
flags |= (1 << 3)
|
flags |= (1 << 3)
|
||||||
}
|
}
|
||||||
var storiesMuted: Api.Bool?
|
|
||||||
if let storiesMutedValue = settings.storiesMuted {
|
let storiesMuted: Api.Bool?
|
||||||
flags |= (1 << 6)
|
switch settings.storySettings.mute {
|
||||||
storiesMuted = storiesMutedValue ? .boolTrue : .boolFalse
|
case .default:
|
||||||
|
storiesMuted = nil
|
||||||
|
case .muted:
|
||||||
|
storiesMuted = .boolTrue
|
||||||
|
case .unmuted:
|
||||||
|
storiesMuted = .boolFalse
|
||||||
}
|
}
|
||||||
let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound, storiesMuted: storiesMuted, storiesHideSender: nil, storiesSound: nil)
|
if storiesMuted != nil {
|
||||||
|
flags |= (1 << 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
let storiesHideSender: Api.Bool?
|
||||||
|
switch settings.storySettings.hideSender {
|
||||||
|
case .default:
|
||||||
|
storiesHideSender = nil
|
||||||
|
case .hide:
|
||||||
|
storiesHideSender = .boolTrue
|
||||||
|
case .show:
|
||||||
|
storiesHideSender = .boolFalse
|
||||||
|
}
|
||||||
|
if storiesHideSender != nil {
|
||||||
|
flags |= (1 << 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
let storiesSound: Api.NotificationSound? = settings.storySettings.sound.apiSound
|
||||||
|
if storiesSound != nil {
|
||||||
|
flags |= (1 << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound, storiesMuted: storiesMuted, storiesHideSender: storiesHideSender, storiesSound: storiesSound)
|
||||||
return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyPeer(peer: inputPeer), settings: inputSettings))
|
return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyPeer(peer: inputPeer), settings: inputSettings))
|
||||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||||
return .single(.boolFalse)
|
return .single(.boolFalse)
|
||||||
|
|||||||
@ -4,17 +4,17 @@ public struct MessageNotificationSettings: Codable, Equatable {
|
|||||||
public var enabled: Bool
|
public var enabled: Bool
|
||||||
public var displayPreviews: Bool
|
public var displayPreviews: Bool
|
||||||
public var sound: PeerMessageSound
|
public var sound: PeerMessageSound
|
||||||
public var storiesMuted: Bool?
|
public var storySettings: PeerStoryNotificationSettings
|
||||||
|
|
||||||
public static var defaultSettings: MessageNotificationSettings {
|
public static var defaultSettings: MessageNotificationSettings {
|
||||||
return MessageNotificationSettings(enabled: true, displayPreviews: true, sound: defaultCloudPeerNotificationSound, storiesMuted: nil)
|
return MessageNotificationSettings(enabled: true, displayPreviews: true, sound: defaultCloudPeerNotificationSound, storySettings: PeerStoryNotificationSettings.default)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(enabled: Bool, displayPreviews: Bool, sound: PeerMessageSound, storiesMuted: Bool?) {
|
public init(enabled: Bool, displayPreviews: Bool, sound: PeerMessageSound, storySettings: PeerStoryNotificationSettings) {
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.displayPreviews = displayPreviews
|
self.displayPreviews = displayPreviews
|
||||||
self.sound = sound
|
self.sound = sound
|
||||||
self.storiesMuted = storiesMuted
|
self.storySettings = storySettings
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -25,7 +25,7 @@ public struct MessageNotificationSettings: Codable, Equatable {
|
|||||||
|
|
||||||
self.sound = try PeerMessageSound.decodeInline(container)
|
self.sound = try PeerMessageSound.decodeInline(container)
|
||||||
|
|
||||||
self.storiesMuted = try? container.decodeIfPresent(Bool.self, forKey: "st")
|
self.storySettings = try container.decodeIfPresent(PeerStoryNotificationSettings.self, forKey: "stor") ?? PeerStoryNotificationSettings(mute: .unmuted, hideSender: .show, sound: defaultCloudPeerNotificationSound)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -34,7 +34,7 @@ public struct MessageNotificationSettings: Codable, Equatable {
|
|||||||
try container.encode((self.enabled ? 1 : 0) as Int32, forKey: "e")
|
try container.encode((self.enabled ? 1 : 0) as Int32, forKey: "e")
|
||||||
try container.encode((self.displayPreviews ? 1 : 0) as Int32, forKey: "p")
|
try container.encode((self.displayPreviews ? 1 : 0) as Int32, forKey: "p")
|
||||||
try self.sound.encodeInline(&container)
|
try self.sound.encodeInline(&container)
|
||||||
try container.encodeIfPresent(self.storiesMuted, forKey: "st")
|
try container.encode(self.storySettings, forKey: "stor")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -388,24 +388,90 @@ public enum PeerNotificationDisplayPreviews: Equatable, Codable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct PeerStoryNotificationSettings: Codable, Equatable {
|
||||||
|
public enum CodingError: Error {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var `default`: PeerStoryNotificationSettings {
|
||||||
|
return PeerStoryNotificationSettings(mute: .default, hideSender: .default, sound: .default)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case mute = "m"
|
||||||
|
case hideSender = "hs"
|
||||||
|
case sound = "s"
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Mute: Int32, Codable {
|
||||||
|
case `default` = 0
|
||||||
|
case unmuted = 1
|
||||||
|
case muted = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HideSender: Int32, Codable {
|
||||||
|
case `default` = 0
|
||||||
|
case hide = 1
|
||||||
|
case show = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public var mute: Mute
|
||||||
|
public var hideSender: HideSender
|
||||||
|
public var sound: PeerMessageSound
|
||||||
|
|
||||||
|
public init(
|
||||||
|
mute: Mute,
|
||||||
|
hideSender: HideSender,
|
||||||
|
sound: PeerMessageSound
|
||||||
|
) {
|
||||||
|
self.mute = mute
|
||||||
|
self.hideSender = hideSender
|
||||||
|
self.sound = sound
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
if let mute = Mute(rawValue: try container.decode(Int32.self, forKey: .mute)) {
|
||||||
|
self.mute = mute
|
||||||
|
} else {
|
||||||
|
throw CodingError.generic
|
||||||
|
}
|
||||||
|
if let hideSender = HideSender(rawValue: try container.decode(Int32.self, forKey: .hideSender)) {
|
||||||
|
self.hideSender = hideSender
|
||||||
|
} else {
|
||||||
|
throw CodingError.generic
|
||||||
|
}
|
||||||
|
self.sound = try container.decode(PeerMessageSound.self, forKey: .sound)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
try container.encode(self.mute.rawValue, forKey: .mute)
|
||||||
|
try container.encode(self.hideSender.rawValue, forKey: .hideSender)
|
||||||
|
try container.encode(self.sound, forKey: .sound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class TelegramPeerNotificationSettings: PeerNotificationSettings, Codable, Equatable {
|
public final class TelegramPeerNotificationSettings: PeerNotificationSettings, Codable, Equatable {
|
||||||
public let muteState: PeerMuteState
|
public let muteState: PeerMuteState
|
||||||
public let messageSound: PeerMessageSound
|
public let messageSound: PeerMessageSound
|
||||||
public let displayPreviews: PeerNotificationDisplayPreviews
|
public let displayPreviews: PeerNotificationDisplayPreviews
|
||||||
public let storiesMuted: Bool?
|
public let storySettings: PeerStoryNotificationSettings
|
||||||
|
|
||||||
public static var defaultSettings: TelegramPeerNotificationSettings {
|
public static var defaultSettings: TelegramPeerNotificationSettings {
|
||||||
return TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storiesMuted: nil)
|
return TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storySettings: PeerStoryNotificationSettings.default)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isRemovedFromTotalUnreadCount(`default`: Bool) -> Bool {
|
public func isRemovedFromTotalUnreadCount(`default`: Bool) -> Bool {
|
||||||
switch self.muteState {
|
switch self.muteState {
|
||||||
case .unmuted:
|
case .unmuted:
|
||||||
return false
|
return false
|
||||||
case .muted:
|
case .muted:
|
||||||
return true
|
return true
|
||||||
case .default:
|
case .default:
|
||||||
return `default`
|
return `default`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,18 +483,18 @@ public final class TelegramPeerNotificationSettings: PeerNotificationSettings, C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(muteState: PeerMuteState, messageSound: PeerMessageSound, displayPreviews: PeerNotificationDisplayPreviews, storiesMuted: Bool?) {
|
public init(muteState: PeerMuteState, messageSound: PeerMessageSound, displayPreviews: PeerNotificationDisplayPreviews, storySettings: PeerStoryNotificationSettings) {
|
||||||
self.muteState = muteState
|
self.muteState = muteState
|
||||||
self.messageSound = messageSound
|
self.messageSound = messageSound
|
||||||
self.displayPreviews = displayPreviews
|
self.displayPreviews = displayPreviews
|
||||||
self.storiesMuted = storiesMuted
|
self.storySettings = storySettings
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
self.muteState = PeerMuteState.decodeInline(decoder)
|
self.muteState = PeerMuteState.decodeInline(decoder)
|
||||||
self.messageSound = PeerMessageSound.decodeInline(decoder)
|
self.messageSound = PeerMessageSound.decodeInline(decoder)
|
||||||
self.displayPreviews = PeerNotificationDisplayPreviews.decodeInline(decoder)
|
self.displayPreviews = PeerNotificationDisplayPreviews.decodeInline(decoder)
|
||||||
self.storiesMuted = decoder.decodeOptionalBoolForKey("stm")
|
self.storySettings = decoder.decode(PeerStoryNotificationSettings.self, forKey: "stor") ?? PeerStoryNotificationSettings.default
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -437,7 +503,7 @@ public final class TelegramPeerNotificationSettings: PeerNotificationSettings, C
|
|||||||
self.muteState = try container.decode(PeerMuteState.self, forKey: "muteState")
|
self.muteState = try container.decode(PeerMuteState.self, forKey: "muteState")
|
||||||
self.messageSound = try container.decode(PeerMessageSound.self, forKey: "messageSound")
|
self.messageSound = try container.decode(PeerMessageSound.self, forKey: "messageSound")
|
||||||
self.displayPreviews = try container.decode(PeerNotificationDisplayPreviews.self, forKey: "displayPreviews")
|
self.displayPreviews = try container.decode(PeerNotificationDisplayPreviews.self, forKey: "displayPreviews")
|
||||||
self.storiesMuted = try? container.decodeIfPresent(Bool.self, forKey: "stm")
|
self.storySettings = try container.decodeIfPresent(PeerStoryNotificationSettings.self, forKey: "stor") ?? PeerStoryNotificationSettings.default
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -446,18 +512,14 @@ public final class TelegramPeerNotificationSettings: PeerNotificationSettings, C
|
|||||||
try container.encode(self.muteState, forKey: "muteState")
|
try container.encode(self.muteState, forKey: "muteState")
|
||||||
try container.encode(self.messageSound, forKey: "messageSound")
|
try container.encode(self.messageSound, forKey: "messageSound")
|
||||||
try container.encode(self.displayPreviews, forKey: "displayPreviews")
|
try container.encode(self.displayPreviews, forKey: "displayPreviews")
|
||||||
try container.encodeIfPresent(self.storiesMuted, forKey: "stm")
|
try container.encode(self.storySettings, forKey: "stor")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
self.muteState.encodeInline(encoder)
|
self.muteState.encodeInline(encoder)
|
||||||
self.messageSound.encodeInline(encoder)
|
self.messageSound.encodeInline(encoder)
|
||||||
self.displayPreviews.encodeInline(encoder)
|
self.displayPreviews.encodeInline(encoder)
|
||||||
if let storiesMuted = self.storiesMuted {
|
encoder.encode(self.storySettings, forKey: "stor")
|
||||||
encoder.encodeBool(storiesMuted, forKey: "stm")
|
|
||||||
} else {
|
|
||||||
encoder.encodeNil(forKey: "stm")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: PeerNotificationSettings) -> Bool {
|
public func isEqual(to: PeerNotificationSettings) -> Bool {
|
||||||
@ -469,22 +531,22 @@ public final class TelegramPeerNotificationSettings: PeerNotificationSettings, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedMuteState(_ muteState: PeerMuteState) -> TelegramPeerNotificationSettings {
|
public func withUpdatedMuteState(_ muteState: PeerMuteState) -> TelegramPeerNotificationSettings {
|
||||||
return TelegramPeerNotificationSettings(muteState: muteState, messageSound: self.messageSound, displayPreviews: self.displayPreviews, storiesMuted: self.storiesMuted)
|
return TelegramPeerNotificationSettings(muteState: muteState, messageSound: self.messageSound, displayPreviews: self.displayPreviews, storySettings: self.storySettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedMessageSound(_ messageSound: PeerMessageSound) -> TelegramPeerNotificationSettings {
|
public func withUpdatedMessageSound(_ messageSound: PeerMessageSound) -> TelegramPeerNotificationSettings {
|
||||||
return TelegramPeerNotificationSettings(muteState: self.muteState, messageSound: messageSound, displayPreviews: self.displayPreviews, storiesMuted: self.storiesMuted)
|
return TelegramPeerNotificationSettings(muteState: self.muteState, messageSound: messageSound, displayPreviews: self.displayPreviews, storySettings: self.storySettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedDisplayPreviews(_ displayPreviews: PeerNotificationDisplayPreviews) -> TelegramPeerNotificationSettings {
|
public func withUpdatedDisplayPreviews(_ displayPreviews: PeerNotificationDisplayPreviews) -> TelegramPeerNotificationSettings {
|
||||||
return TelegramPeerNotificationSettings(muteState: self.muteState, messageSound: self.messageSound, displayPreviews: displayPreviews, storiesMuted: self.storiesMuted)
|
return TelegramPeerNotificationSettings(muteState: self.muteState, messageSound: self.messageSound, displayPreviews: displayPreviews, storySettings: self.storySettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedStoriesMuted(_ storiesMuted: Bool?) -> TelegramPeerNotificationSettings {
|
public func withUpdatedStorySettings(_ storySettings: PeerStoryNotificationSettings) -> TelegramPeerNotificationSettings {
|
||||||
return TelegramPeerNotificationSettings(muteState: self.muteState, messageSound: self.messageSound, displayPreviews: self.displayPreviews, storiesMuted: storiesMuted)
|
return TelegramPeerNotificationSettings(muteState: self.muteState, messageSound: self.messageSound, displayPreviews: self.displayPreviews, storySettings: storySettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: TelegramPeerNotificationSettings, rhs: TelegramPeerNotificationSettings) -> Bool {
|
public static func ==(lhs: TelegramPeerNotificationSettings, rhs: TelegramPeerNotificationSettings) -> Bool {
|
||||||
return lhs.muteState == rhs.muteState && lhs.messageSound == rhs.messageSound && lhs.displayPreviews == rhs.displayPreviews && lhs.storiesMuted == rhs.storiesMuted
|
return lhs.muteState == rhs.muteState && lhs.messageSound == rhs.messageSound && lhs.displayPreviews == rhs.displayPreviews && lhs.storySettings == rhs.storySettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -254,6 +254,44 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func subscribe<
|
||||||
|
T0: TelegramEngineDataItem,
|
||||||
|
T1: TelegramEngineDataItem,
|
||||||
|
T2: TelegramEngineDataItem,
|
||||||
|
T3: TelegramEngineDataItem,
|
||||||
|
T4: TelegramEngineDataItem
|
||||||
|
>(
|
||||||
|
_ t0: T0,
|
||||||
|
_ t1: T1,
|
||||||
|
_ t2: T2,
|
||||||
|
_ t3: T3,
|
||||||
|
_ t4: T4
|
||||||
|
) -> Signal<
|
||||||
|
(
|
||||||
|
T0.Result,
|
||||||
|
T1.Result,
|
||||||
|
T2.Result,
|
||||||
|
T3.Result,
|
||||||
|
T4.Result
|
||||||
|
),
|
||||||
|
NoError> {
|
||||||
|
return self._subscribe(items: [
|
||||||
|
t0 as! AnyPostboxViewDataItem,
|
||||||
|
t1 as! AnyPostboxViewDataItem,
|
||||||
|
t2 as! AnyPostboxViewDataItem,
|
||||||
|
t3 as! AnyPostboxViewDataItem,
|
||||||
|
t4 as! AnyPostboxViewDataItem
|
||||||
|
])
|
||||||
|
|> map { results -> (T0.Result, T1.Result, T2.Result, T3.Result, T4.Result) in
|
||||||
|
return (
|
||||||
|
results[0] as! T0.Result,
|
||||||
|
results[1] as! T1.Result,
|
||||||
|
results[2] as! T2.Result,
|
||||||
|
results[3] as! T3.Result,
|
||||||
|
results[4] as! T4.Result
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func get<
|
public func get<
|
||||||
T0: TelegramEngineDataItem,
|
T0: TelegramEngineDataItem,
|
||||||
@ -308,5 +346,29 @@ public extension TelegramEngine {
|
|||||||
NoError> {
|
NoError> {
|
||||||
return self.subscribe(t0, t1, t2, t3) |> take(1)
|
return self.subscribe(t0, t1, t2, t3) |> take(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func get<
|
||||||
|
T0: TelegramEngineDataItem,
|
||||||
|
T1: TelegramEngineDataItem,
|
||||||
|
T2: TelegramEngineDataItem,
|
||||||
|
T3: TelegramEngineDataItem,
|
||||||
|
T4: TelegramEngineDataItem
|
||||||
|
>(
|
||||||
|
_ t0: T0,
|
||||||
|
_ t1: T1,
|
||||||
|
_ t2: T2,
|
||||||
|
_ t3: T3,
|
||||||
|
_ t4: T4
|
||||||
|
) -> Signal<
|
||||||
|
(
|
||||||
|
T0.Result,
|
||||||
|
T1.Result,
|
||||||
|
T2.Result,
|
||||||
|
T3.Result,
|
||||||
|
T4.Result
|
||||||
|
),
|
||||||
|
NoError> {
|
||||||
|
return self.subscribe(t0, t1, t2, t3, t4) |> take(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -677,12 +677,17 @@ public extension TelegramEngine {
|
|||||||
hasMoreToken = ""
|
hasMoreToken = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var accountPendingItemCount = 0
|
||||||
|
if let view = views.views[PostboxViewKey.storiesState(key: .local)] as? StoryStatesView, let localState = view.value?.get(Stories.LocalState.self) {
|
||||||
|
accountPendingItemCount = localState.items.count
|
||||||
|
}
|
||||||
|
|
||||||
var accountItem: EngineStorySubscriptions.Item = EngineStorySubscriptions.Item(
|
var accountItem: EngineStorySubscriptions.Item = EngineStorySubscriptions.Item(
|
||||||
peer: EnginePeer(accountPeer),
|
peer: EnginePeer(accountPeer),
|
||||||
hasUnseen: false,
|
hasUnseen: false,
|
||||||
hasUnseenCloseFriends: false,
|
hasUnseenCloseFriends: false,
|
||||||
hasPending: false,
|
hasPending: accountPendingItemCount != 0,
|
||||||
storyCount: 0,
|
storyCount: accountPendingItemCount,
|
||||||
unseenCount: 0,
|
unseenCount: 0,
|
||||||
lastTimestamp: 0
|
lastTimestamp: 0
|
||||||
)
|
)
|
||||||
@ -698,7 +703,6 @@ public extension TelegramEngine {
|
|||||||
var hasUnseen = false
|
var hasUnseen = false
|
||||||
var hasUnseenCloseFriends = false
|
var hasUnseenCloseFriends = false
|
||||||
var unseenCount = 0
|
var unseenCount = 0
|
||||||
var hasPending = false
|
|
||||||
if let peerState = peerState {
|
if let peerState = peerState {
|
||||||
hasUnseen = peerState.maxReadId < lastEntry.id
|
hasUnseen = peerState.maxReadId < lastEntry.id
|
||||||
|
|
||||||
@ -717,18 +721,12 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let view = views.views[PostboxViewKey.storiesState(key: .local)] as? StoryStatesView, let localState = view.value?.get(Stories.LocalState.self) {
|
|
||||||
if !localState.items.isEmpty {
|
|
||||||
hasPending = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let item = EngineStorySubscriptions.Item(
|
let item = EngineStorySubscriptions.Item(
|
||||||
peer: EnginePeer(accountPeer),
|
peer: EnginePeer(accountPeer),
|
||||||
hasUnseen: hasUnseen,
|
hasUnseen: hasUnseen,
|
||||||
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
hasUnseenCloseFriends: hasUnseenCloseFriends,
|
||||||
hasPending: hasPending,
|
hasPending: accountPendingItemCount != 0,
|
||||||
storyCount: itemsView.items.count,
|
storyCount: itemsView.items.count + accountPendingItemCount,
|
||||||
unseenCount: unseenCount,
|
unseenCount: unseenCount,
|
||||||
lastTimestamp: lastEntry.timestamp
|
lastTimestamp: lastEntry.timestamp
|
||||||
)
|
)
|
||||||
|
|||||||
@ -62,6 +62,20 @@ func _internal_togglePeerMuted(account: Account, peerId: PeerId, threadId: Int64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func resolvedAreStoriesMuted(globalSettings: GlobalNotificationSettingsSet, peer: Peer, peerSettings: TelegramPeerNotificationSettings?) -> Bool {
|
||||||
|
let defaultIsMuted = globalSettings.privateChats.storySettings.mute == .muted
|
||||||
|
switch peerSettings?.storySettings.mute {
|
||||||
|
case .none:
|
||||||
|
return defaultIsMuted
|
||||||
|
case .muted:
|
||||||
|
return true
|
||||||
|
case .unmuted:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return defaultIsMuted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func _internal_togglePeerStoriesMuted(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
|
func _internal_togglePeerStoriesMuted(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Void in
|
return account.postbox.transaction { transaction -> Void in
|
||||||
guard let peer = transaction.getPeer(peerId) else {
|
guard let peer = transaction.getPeer(peerId) else {
|
||||||
@ -82,12 +96,22 @@ func _internal_togglePeerStoriesMuted(account: Account, peerId: PeerId) -> Signa
|
|||||||
}
|
}
|
||||||
|
|
||||||
let updatedSettings: TelegramPeerNotificationSettings
|
let updatedSettings: TelegramPeerNotificationSettings
|
||||||
if let previousStoriesMuted = previousSettings.storiesMuted {
|
var storySettings = previousSettings.storySettings
|
||||||
updatedSettings = previousSettings.withUpdatedStoriesMuted(!previousStoriesMuted)
|
switch previousSettings.storySettings.mute {
|
||||||
} else {
|
case .default:
|
||||||
updatedSettings = previousSettings.withUpdatedStoriesMuted(true)
|
let globalNotificationSettings = transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications)?.get(GlobalNotificationSettings.self) ?? GlobalNotificationSettings.defaultSettings
|
||||||
|
|
||||||
|
if resolvedAreStoriesMuted(globalSettings: globalNotificationSettings.effective, peer: peer, peerSettings: previousSettings) {
|
||||||
|
storySettings.mute = .unmuted
|
||||||
|
} else {
|
||||||
|
storySettings.mute = .muted
|
||||||
|
}
|
||||||
|
case .unmuted:
|
||||||
|
storySettings.mute = .muted
|
||||||
|
case .muted:
|
||||||
|
storySettings.mute = .unmuted
|
||||||
}
|
}
|
||||||
|
updatedSettings = previousSettings.withUpdatedStorySettings(storySettings)
|
||||||
transaction.updatePendingPeerNotificationSettings(peerId: notificationPeerId, settings: updatedSettings)
|
transaction.updatePendingPeerNotificationSettings(peerId: notificationPeerId, settings: updatedSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,13 +237,13 @@ func _internal_updatePeerDisplayPreviewsSetting(account: Account, transaction: T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_updatePeerStoriesMutedSetting(account: Account, peerId: PeerId, isMuted: Bool?) -> Signal<Void, NoError> {
|
func _internal_updatePeerStoriesMutedSetting(account: Account, peerId: PeerId, mute: PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Void in
|
return account.postbox.transaction { transaction -> Void in
|
||||||
_internal_updatePeerStoriesMutedSetting(account: account, transaction: transaction, peerId: peerId, isMuted: isMuted)
|
_internal_updatePeerStoriesMutedSetting(account: account, transaction: transaction, peerId: peerId, mute: mute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_updatePeerStoriesMutedSetting(account: Account, transaction: Transaction, peerId: PeerId, isMuted: Bool?) {
|
func _internal_updatePeerStoriesMutedSetting(account: Account, transaction: Transaction, peerId: PeerId, mute: PeerStoryNotificationSettings.Mute) {
|
||||||
if let peer = transaction.getPeer(peerId) {
|
if let peer = transaction.getPeer(peerId) {
|
||||||
var notificationPeerId = peerId
|
var notificationPeerId = peerId
|
||||||
if let associatedPeerId = peer.associatedPeerId {
|
if let associatedPeerId = peer.associatedPeerId {
|
||||||
@ -234,7 +258,31 @@ func _internal_updatePeerStoriesMutedSetting(account: Account, transaction: Tran
|
|||||||
previousSettings = TelegramPeerNotificationSettings.defaultSettings
|
previousSettings = TelegramPeerNotificationSettings.defaultSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedSettings = previousSettings.withUpdatedStoriesMuted(isMuted)
|
var storySettings = previousSettings.storySettings
|
||||||
|
storySettings.mute = mute
|
||||||
|
let updatedSettings = previousSettings.withUpdatedStorySettings(storySettings)
|
||||||
|
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_updatePeerStoriesHideSenderSetting(account: Account, transaction: Transaction, peerId: PeerId, hideSender: PeerStoryNotificationSettings.HideSender) {
|
||||||
|
if let peer = transaction.getPeer(peerId) {
|
||||||
|
var notificationPeerId = peerId
|
||||||
|
if let associatedPeerId = peer.associatedPeerId {
|
||||||
|
notificationPeerId = associatedPeerId
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings
|
||||||
|
let previousSettings: TelegramPeerNotificationSettings
|
||||||
|
if let currentSettings = currentSettings {
|
||||||
|
previousSettings = currentSettings
|
||||||
|
} else {
|
||||||
|
previousSettings = TelegramPeerNotificationSettings.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
var storySettings = previousSettings.storySettings
|
||||||
|
storySettings.hideSender = hideSender
|
||||||
|
let updatedSettings = previousSettings.withUpdatedStorySettings(storySettings)
|
||||||
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
|
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,3 +327,25 @@ func _internal_updatePeerNotificationSoundInteractive(account: Account, transact
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _internal_updatePeerStoryNotificationSoundInteractive(account: Account, transaction: Transaction, peerId: PeerId, sound: PeerMessageSound) {
|
||||||
|
if let peer = transaction.getPeer(peerId) {
|
||||||
|
var notificationPeerId = peerId
|
||||||
|
if let associatedPeerId = peer.associatedPeerId {
|
||||||
|
notificationPeerId = associatedPeerId
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentSettings = transaction.getPeerNotificationSettings(id: notificationPeerId) as? TelegramPeerNotificationSettings
|
||||||
|
let previousSettings: TelegramPeerNotificationSettings
|
||||||
|
if let currentSettings = currentSettings {
|
||||||
|
previousSettings = currentSettings
|
||||||
|
} else {
|
||||||
|
previousSettings = TelegramPeerNotificationSettings.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
var storySettings = previousSettings.storySettings
|
||||||
|
storySettings.sound = sound
|
||||||
|
let updatedSettings = previousSettings.withUpdatedStorySettings(storySettings)
|
||||||
|
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -19,7 +19,11 @@ public final class NotificationExceptionsList: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_notificationExceptionsList(postbox: Postbox, network: Network) -> Signal<NotificationExceptionsList, NoError> {
|
func _internal_notificationExceptionsList(postbox: Postbox, network: Network) -> Signal<NotificationExceptionsList, NoError> {
|
||||||
return network.request(Api.functions.account.getNotifyExceptions(flags: 1 << 1, peer: nil))
|
var flags: Int32 = 0
|
||||||
|
flags |= 1 << 1
|
||||||
|
flags |= 1 << 2
|
||||||
|
|
||||||
|
return network.request(Api.functions.account.getNotifyExceptions(flags: flags, peer: nil))
|
||||||
|> retryRequest
|
|> retryRequest
|
||||||
|> mapToSignal { result -> Signal<NotificationExceptionsList, NoError> in
|
|> mapToSignal { result -> Signal<NotificationExceptionsList, NoError> in
|
||||||
return postbox.transaction { transaction -> NotificationExceptionsList in
|
return postbox.transaction { transaction -> NotificationExceptionsList in
|
||||||
|
|||||||
@ -84,22 +84,24 @@ public enum EnginePeer: Equatable {
|
|||||||
case show
|
case show
|
||||||
case hide
|
case hide
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public typealias StorySettigs = PeerStoryNotificationSettings
|
||||||
|
|
||||||
public var muteState: MuteState
|
public var muteState: MuteState
|
||||||
public var messageSound: MessageSound
|
public var messageSound: MessageSound
|
||||||
public var displayPreviews: DisplayPreviews
|
public var displayPreviews: DisplayPreviews
|
||||||
public var storiesMuted: Bool?
|
public var storySettings: StorySettigs
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
muteState: MuteState,
|
muteState: MuteState,
|
||||||
messageSound: MessageSound,
|
messageSound: MessageSound,
|
||||||
displayPreviews: DisplayPreviews,
|
displayPreviews: DisplayPreviews,
|
||||||
storiesMuted: Bool?
|
storySettings: StorySettigs
|
||||||
) {
|
) {
|
||||||
self.muteState = muteState
|
self.muteState = muteState
|
||||||
self.messageSound = messageSound
|
self.messageSound = messageSound
|
||||||
self.displayPreviews = displayPreviews
|
self.displayPreviews = displayPreviews
|
||||||
self.storiesMuted = storiesMuted
|
self.storySettings = storySettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,13 +221,13 @@ public struct EngineGlobalNotificationSettings: Equatable {
|
|||||||
public var enabled: Bool
|
public var enabled: Bool
|
||||||
public var displayPreviews: Bool
|
public var displayPreviews: Bool
|
||||||
public var sound: EnginePeer.NotificationSettings.MessageSound
|
public var sound: EnginePeer.NotificationSettings.MessageSound
|
||||||
public var storiesMuted: Bool
|
public var storySettings: EnginePeer.NotificationSettings.StorySettigs
|
||||||
|
|
||||||
public init(enabled: Bool, displayPreviews: Bool, sound: EnginePeer.NotificationSettings.MessageSound, storiesMuted: Bool) {
|
public init(enabled: Bool, displayPreviews: Bool, sound: EnginePeer.NotificationSettings.MessageSound, storySettings: EnginePeer.NotificationSettings.StorySettigs) {
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.displayPreviews = displayPreviews
|
self.displayPreviews = displayPreviews
|
||||||
self.sound = sound
|
self.sound = sound
|
||||||
self.storiesMuted = storiesMuted
|
self.storySettings = storySettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +335,7 @@ public extension EnginePeer.NotificationSettings {
|
|||||||
muteState: MuteState(notificationSettings.muteState),
|
muteState: MuteState(notificationSettings.muteState),
|
||||||
messageSound: MessageSound(notificationSettings.messageSound),
|
messageSound: MessageSound(notificationSettings.messageSound),
|
||||||
displayPreviews: DisplayPreviews(notificationSettings.displayPreviews),
|
displayPreviews: DisplayPreviews(notificationSettings.displayPreviews),
|
||||||
storiesMuted: notificationSettings.storiesMuted
|
storySettings: notificationSettings.storySettings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +344,7 @@ public extension EnginePeer.NotificationSettings {
|
|||||||
muteState: self.muteState._asMuteState(),
|
muteState: self.muteState._asMuteState(),
|
||||||
messageSound: self.messageSound._asMessageSound(),
|
messageSound: self.messageSound._asMessageSound(),
|
||||||
displayPreviews: self.displayPreviews._asDisplayPreviews(),
|
displayPreviews: self.displayPreviews._asDisplayPreviews(),
|
||||||
storiesMuted: self.storiesMuted
|
storySettings: self.storySettings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -602,7 +604,7 @@ public extension EngineGlobalNotificationSettings.CategorySettings {
|
|||||||
enabled: categorySettings.enabled,
|
enabled: categorySettings.enabled,
|
||||||
displayPreviews: categorySettings.displayPreviews,
|
displayPreviews: categorySettings.displayPreviews,
|
||||||
sound: EnginePeer.NotificationSettings.MessageSound(categorySettings.sound),
|
sound: EnginePeer.NotificationSettings.MessageSound(categorySettings.sound),
|
||||||
storiesMuted: categorySettings.storiesMuted ?? false
|
storySettings: categorySettings.storySettings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +613,7 @@ public extension EngineGlobalNotificationSettings.CategorySettings {
|
|||||||
enabled: self.enabled,
|
enabled: self.enabled,
|
||||||
displayPreviews: self.displayPreviews,
|
displayPreviews: self.displayPreviews,
|
||||||
sound: self.sound._asMessageSound(),
|
sound: self.sound._asMessageSound(),
|
||||||
storiesMuted: self.storiesMuted
|
storySettings: self.storySettings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,4 +627,13 @@ public extension EngineGlobalNotificationSettings {
|
|||||||
contactsJoined: globalNotificationSettings.contactsJoined
|
contactsJoined: globalNotificationSettings.contactsJoined
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _asGlobalNotificationSettings() -> GlobalNotificationSettingsSet {
|
||||||
|
return GlobalNotificationSettingsSet(
|
||||||
|
privateChats: self.privateChats._asMessageNotificationSettings(),
|
||||||
|
groupChats: self.groupChats._asMessageNotificationSettings(),
|
||||||
|
channels: self.channels._asMessageNotificationSettings(),
|
||||||
|
contactsJoined: self.contactsJoined
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -273,8 +273,20 @@ public extension TelegramEngine {
|
|||||||
return _internal_updatePeerDisplayPreviewsSetting(account: self.account, peerId: peerId, threadId: threadId, displayPreviews: displayPreviews)
|
return _internal_updatePeerDisplayPreviewsSetting(account: self.account, peerId: peerId, threadId: threadId, displayPreviews: displayPreviews)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updatePeerStoriesMutedSetting(peerId: PeerId, isMuted: Bool?) -> Signal<Void, NoError> {
|
public func updatePeerStoriesMutedSetting(peerId: PeerId, mute: PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> {
|
||||||
return _internal_updatePeerStoriesMutedSetting(account: self.account, peerId: peerId, isMuted: isMuted)
|
return _internal_updatePeerStoriesMutedSetting(account: self.account, peerId: peerId, mute: mute)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updatePeerStoriesHideSenderSetting(peerId: PeerId, hideSender: PeerStoryNotificationSettings.HideSender) -> Signal<Void, NoError> {
|
||||||
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
_internal_updatePeerStoriesHideSenderSetting(account: self.account, transaction: transaction, peerId: peerId, hideSender: hideSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updatePeerStorySoundInteractive(peerId: PeerId, sound: PeerMessageSound) -> Signal<Void, NoError> {
|
||||||
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
_internal_updatePeerStoryNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, sound: sound)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updatePeerNotificationSoundInteractive(peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal<Void, NoError> {
|
public func updatePeerNotificationSoundInteractive(peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal<Void, NoError> {
|
||||||
@ -306,7 +318,9 @@ public extension TelegramEngine {
|
|||||||
public func removeCustomStoryNotificationSettings(peerIds: [PeerId]) -> Signal<Never, NoError> {
|
public func removeCustomStoryNotificationSettings(peerIds: [PeerId]) -> Signal<Never, NoError> {
|
||||||
return self.account.postbox.transaction { transaction -> Void in
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
_internal_updatePeerStoriesMutedSetting(account: self.account, transaction: transaction, peerId: peerId, isMuted: nil)
|
_internal_updatePeerStoriesMutedSetting(account: self.account, transaction: transaction, peerId: peerId, mute: .default)
|
||||||
|
_internal_updatePeerStoriesHideSenderSetting(account: self.account, transaction: transaction, peerId: peerId, hideSender: .default)
|
||||||
|
_internal_updatePeerStoryNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, sound: .default)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
|
|||||||
@ -496,7 +496,9 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
|
|||||||
}
|
}
|
||||||
updated(stateValue.with({ $0 }).notificationExceptions)
|
updated(stateValue.with({ $0 }).notificationExceptions)
|
||||||
})
|
})
|
||||||
}, updatePeerStoryNotifications: { _, _ in
|
}, updatePeerStoriesMuted: { _, _ in
|
||||||
|
}, updatePeerStoriesHideSender: { _, _ in
|
||||||
|
}, updatePeerStorySound: { _, _ in
|
||||||
}, removePeerFromExceptions: {
|
}, removePeerFromExceptions: {
|
||||||
let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: [item.threadId]).start()
|
let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: [item.threadId]).start()
|
||||||
updateState { current in
|
updateState { current in
|
||||||
|
|||||||
@ -123,7 +123,7 @@ public enum NotificationExceptionMode : Equatable {
|
|||||||
case .default:
|
case .default:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .default, messageSound: sound, displayPreviews: .default, storiesMuted: nil), peer: peer, date: Date().timeIntervalSince1970)
|
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .default, messageSound: sound, displayPreviews: .default, storySettings: .default), peer: peer, date: Date().timeIntervalSince1970)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
@ -161,7 +161,7 @@ public enum NotificationExceptionMode : Equatable {
|
|||||||
case .default:
|
case .default:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: muteState, messageSound: .default, displayPreviews: .default, storiesMuted: nil), peer: peer, date: Date().timeIntervalSince1970)
|
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: muteState, messageSound: .default, displayPreviews: .default, storySettings: .default), peer: peer, date: Date().timeIntervalSince1970)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
@ -215,7 +215,7 @@ public enum NotificationExceptionMode : Equatable {
|
|||||||
case .default:
|
case .default:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: displayPreviews, storiesMuted: nil), peer: peer, date: Date().timeIntervalSince1970)
|
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: displayPreviews, storySettings: .default), peer: peer, date: Date().timeIntervalSince1970)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
@ -233,32 +233,38 @@ public enum NotificationExceptionMode : Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPeerStoryNotifications(_ peer: EnginePeer, _ storyNotifications: PeerNotificationDisplayPreviews) -> NotificationExceptionMode {
|
public func withUpdatedPeerStoriesMuted(_ peer: EnginePeer, _ mute: PeerStoryNotificationSettings.Mute) -> NotificationExceptionMode {
|
||||||
let apply:([EnginePeer.Id : NotificationExceptionWrapper], EnginePeer.Id, PeerNotificationDisplayPreviews) -> [EnginePeer.Id : NotificationExceptionWrapper] = { values, peerId, storyNotifications in
|
let apply:([EnginePeer.Id : NotificationExceptionWrapper], EnginePeer.Id, PeerStoryNotificationSettings.Mute) -> [EnginePeer.Id : NotificationExceptionWrapper] = { values, peerId, mute in
|
||||||
let storiesMuted: Bool?
|
|
||||||
switch storyNotifications {
|
|
||||||
case .default:
|
|
||||||
storiesMuted = nil
|
|
||||||
case .show:
|
|
||||||
storiesMuted = false
|
|
||||||
case .hide:
|
|
||||||
storiesMuted = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var values = values
|
var values = values
|
||||||
if let value = values[peerId] {
|
if let value = values[peerId] {
|
||||||
switch storyNotifications {
|
switch mute {
|
||||||
case .default:
|
case .default:
|
||||||
values.removeValue(forKey: peerId)
|
switch value.settings.storySettings.mute {
|
||||||
|
case .default:
|
||||||
|
//values.removeValue(forKey: peerId)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
values[peerId] = value.updateSettings({
|
||||||
|
var updatedSettings = $0.storySettings
|
||||||
|
updatedSettings.mute = mute
|
||||||
|
return $0.withUpdatedStorySettings(updatedSettings)
|
||||||
|
}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
values[peerId] = value.updateSettings({$0.withUpdatedStoriesMuted(storiesMuted)}).withUpdatedDate(Date().timeIntervalSince1970)
|
values[peerId] = value.updateSettings({
|
||||||
|
var updatedSettings = $0.storySettings
|
||||||
|
updatedSettings.mute = mute
|
||||||
|
return $0.withUpdatedStorySettings(updatedSettings)
|
||||||
|
}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch storyNotifications {
|
switch mute {
|
||||||
case .default:
|
case .default:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storiesMuted: storiesMuted), peer: peer, date: Date().timeIntervalSince1970)
|
var updatedSettings = PeerStoryNotificationSettings.default
|
||||||
|
updatedSettings.mute = mute
|
||||||
|
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storySettings: updatedSettings), peer: peer, date: Date().timeIntervalSince1970)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
@ -266,7 +272,97 @@ public enum NotificationExceptionMode : Equatable {
|
|||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
case let .stories(values):
|
case let .stories(values):
|
||||||
return .stories(apply(values, peer.id, storyNotifications))
|
return .stories(apply(values, peer.id, mute))
|
||||||
|
default:
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedPeerStoriesHideSender(_ peer: EnginePeer, _ hideSender: PeerStoryNotificationSettings.HideSender) -> NotificationExceptionMode {
|
||||||
|
let apply:([EnginePeer.Id : NotificationExceptionWrapper], EnginePeer.Id, PeerStoryNotificationSettings.HideSender) -> [EnginePeer.Id : NotificationExceptionWrapper] = { values, peerId, hideSender in
|
||||||
|
var values = values
|
||||||
|
if let value = values[peerId] {
|
||||||
|
switch hideSender {
|
||||||
|
case .default:
|
||||||
|
switch value.settings.storySettings.hideSender {
|
||||||
|
case .default:
|
||||||
|
//values.removeValue(forKey: peerId)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
values[peerId] = value.updateSettings({
|
||||||
|
var updatedSettings = $0.storySettings
|
||||||
|
updatedSettings.hideSender = hideSender
|
||||||
|
return $0.withUpdatedStorySettings(updatedSettings)
|
||||||
|
}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
values[peerId] = value.updateSettings({
|
||||||
|
var updatedSettings = $0.storySettings
|
||||||
|
updatedSettings.hideSender = hideSender
|
||||||
|
return $0.withUpdatedStorySettings(updatedSettings)
|
||||||
|
}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch hideSender {
|
||||||
|
case .default:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
var updatedSettings = PeerStoryNotificationSettings.default
|
||||||
|
updatedSettings.hideSender = hideSender
|
||||||
|
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storySettings: updatedSettings), peer: peer, date: Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .stories(values):
|
||||||
|
return .stories(apply(values, peer.id, hideSender))
|
||||||
|
default:
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedPeerStorySound(_ peer: EnginePeer, _ sound: PeerMessageSound) -> NotificationExceptionMode {
|
||||||
|
let apply:([EnginePeer.Id : NotificationExceptionWrapper], EnginePeer.Id, PeerMessageSound) -> [EnginePeer.Id : NotificationExceptionWrapper] = { values, peerId, sound in
|
||||||
|
var values = values
|
||||||
|
if let value = values[peerId] {
|
||||||
|
switch sound {
|
||||||
|
case .default:
|
||||||
|
switch value.settings.storySettings.sound {
|
||||||
|
case .default:
|
||||||
|
//values.removeValue(forKey: peerId)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
values[peerId] = value.updateSettings({
|
||||||
|
var updatedSettings = $0.storySettings
|
||||||
|
updatedSettings.sound = sound
|
||||||
|
return $0.withUpdatedStorySettings(updatedSettings)
|
||||||
|
}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
values[peerId] = value.updateSettings({
|
||||||
|
var updatedSettings = $0.storySettings
|
||||||
|
updatedSettings.sound = sound
|
||||||
|
return $0.withUpdatedStorySettings(updatedSettings)
|
||||||
|
}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch sound {
|
||||||
|
case .default:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
var updatedSettings = PeerStoryNotificationSettings.default
|
||||||
|
updatedSettings.sound = sound
|
||||||
|
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storySettings: updatedSettings), peer: peer, date: Date().timeIntervalSince1970)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .stories(values):
|
||||||
|
return .stories(apply(values, peer.id, sound))
|
||||||
default:
|
default:
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
@ -308,6 +404,7 @@ private enum NotificationPeerExceptionEntryId: Hashable {
|
|||||||
case sound(PeerMessageSound.Id)
|
case sound(PeerMessageSound.Id)
|
||||||
case switcherHeader
|
case switcherHeader
|
||||||
case displayPreviews(NotificationPeerExceptionSwitcher)
|
case displayPreviews(NotificationPeerExceptionSwitcher)
|
||||||
|
case showSender(NotificationPeerExceptionSwitcher)
|
||||||
case displayPreviewsHeader
|
case displayPreviewsHeader
|
||||||
case storyNotifications(NotificationPeerExceptionSwitcher)
|
case storyNotifications(NotificationPeerExceptionSwitcher)
|
||||||
case storyNotificationsHeader
|
case storyNotificationsHeader
|
||||||
@ -326,19 +423,36 @@ private final class NotificationPeerExceptionArguments {
|
|||||||
let selectSound: (PeerMessageSound) -> Void
|
let selectSound: (PeerMessageSound) -> Void
|
||||||
let selectMode: (NotificationPeerExceptionSwitcher) -> Void
|
let selectMode: (NotificationPeerExceptionSwitcher) -> Void
|
||||||
let selectDisplayPreviews: (NotificationPeerExceptionSwitcher) -> Void
|
let selectDisplayPreviews: (NotificationPeerExceptionSwitcher) -> Void
|
||||||
let selectStoryNotifications: (NotificationPeerExceptionSwitcher) -> Void
|
let selectStoriesMuted: (NotificationPeerExceptionSwitcher) -> Void
|
||||||
|
let selectHideStoriesSender: (NotificationPeerExceptionSwitcher) -> Void
|
||||||
|
let selectStorySound: (PeerMessageSound) -> Void
|
||||||
let removeFromExceptions: () -> Void
|
let removeFromExceptions: () -> Void
|
||||||
let complete: () -> Void
|
let complete: () -> Void
|
||||||
let cancel: () -> Void
|
let cancel: () -> Void
|
||||||
let upload: () -> Void
|
let upload: () -> Void
|
||||||
let deleteSound: (PeerMessageSound, String) -> Void
|
let deleteSound: (PeerMessageSound, String) -> Void
|
||||||
|
|
||||||
init(account: Account, selectSound: @escaping(PeerMessageSound) -> Void, selectMode: @escaping(NotificationPeerExceptionSwitcher) -> Void, selectDisplayPreviews: @escaping (NotificationPeerExceptionSwitcher) -> Void, selectStoryNotifications: @escaping (NotificationPeerExceptionSwitcher) -> Void, removeFromExceptions: @escaping () -> Void, complete: @escaping()->Void, cancel: @escaping() -> Void, upload: @escaping () -> Void, deleteSound: @escaping (PeerMessageSound, String) -> Void) {
|
init(
|
||||||
|
account: Account,
|
||||||
|
selectSound: @escaping(PeerMessageSound) -> Void,
|
||||||
|
selectMode: @escaping(NotificationPeerExceptionSwitcher) -> Void,
|
||||||
|
selectDisplayPreviews: @escaping (NotificationPeerExceptionSwitcher) -> Void,
|
||||||
|
selectStoriesMuted: @escaping (NotificationPeerExceptionSwitcher) -> Void,
|
||||||
|
selectHideStoriesSender: @escaping (NotificationPeerExceptionSwitcher) -> Void,
|
||||||
|
selectStorySound: @escaping (PeerMessageSound) -> Void,
|
||||||
|
removeFromExceptions: @escaping () -> Void,
|
||||||
|
complete: @escaping() -> Void,
|
||||||
|
cancel: @escaping() -> Void,
|
||||||
|
upload: @escaping () -> Void,
|
||||||
|
deleteSound: @escaping (PeerMessageSound, String) -> Void
|
||||||
|
) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.selectSound = selectSound
|
self.selectSound = selectSound
|
||||||
self.selectMode = selectMode
|
self.selectMode = selectMode
|
||||||
self.selectDisplayPreviews = selectDisplayPreviews
|
self.selectDisplayPreviews = selectDisplayPreviews
|
||||||
self.selectStoryNotifications = selectStoryNotifications
|
self.selectStoriesMuted = selectStoriesMuted
|
||||||
|
self.selectHideStoriesSender = selectHideStoriesSender
|
||||||
|
self.selectStorySound = selectStorySound
|
||||||
self.removeFromExceptions = removeFromExceptions
|
self.removeFromExceptions = removeFromExceptions
|
||||||
self.complete = complete
|
self.complete = complete
|
||||||
self.cancel = cancel
|
self.cancel = cancel
|
||||||
@ -355,6 +469,7 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
case switcher(index:Int32, theme: PresentationTheme, strings: PresentationStrings, mode: NotificationPeerExceptionSwitcher, selected: Bool)
|
case switcher(index:Int32, theme: PresentationTheme, strings: PresentationStrings, mode: NotificationPeerExceptionSwitcher, selected: Bool)
|
||||||
case switcherHeader(index:Int32, theme: PresentationTheme, title: String)
|
case switcherHeader(index:Int32, theme: PresentationTheme, title: String)
|
||||||
case displayPreviews(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
|
case displayPreviews(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
|
||||||
|
case showSender(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
|
||||||
case displayPreviewsHeader(index:Int32, theme: PresentationTheme, title: String)
|
case displayPreviewsHeader(index:Int32, theme: PresentationTheme, title: String)
|
||||||
case storyNotifications(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
|
case storyNotifications(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
|
||||||
case storyNotificationsHeader(index:Int32, theme: PresentationTheme, title: String)
|
case storyNotificationsHeader(index:Int32, theme: PresentationTheme, title: String)
|
||||||
@ -379,6 +494,8 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
return index
|
return index
|
||||||
case let .displayPreviews(index, _, _, _, _):
|
case let .displayPreviews(index, _, _, _, _):
|
||||||
return index
|
return index
|
||||||
|
case let .showSender(index, _, _, _, _):
|
||||||
|
return index
|
||||||
case let .storyNotificationsHeader(index, _, _):
|
case let .storyNotificationsHeader(index, _, _):
|
||||||
return index
|
return index
|
||||||
case let .storyNotifications(index, _, _, _, _):
|
case let .storyNotifications(index, _, _, _, _):
|
||||||
@ -408,7 +525,7 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
return NotificationPeerExceptionSection.remove.rawValue
|
return NotificationPeerExceptionSection.remove.rawValue
|
||||||
case .switcher, .switcherHeader:
|
case .switcher, .switcherHeader:
|
||||||
return NotificationPeerExceptionSection.switcher.rawValue
|
return NotificationPeerExceptionSection.switcher.rawValue
|
||||||
case .displayPreviews, .displayPreviewsHeader:
|
case .displayPreviews, .displayPreviewsHeader, .showSender:
|
||||||
return NotificationPeerExceptionSection.displayPreviews.rawValue
|
return NotificationPeerExceptionSection.displayPreviews.rawValue
|
||||||
case .storyNotifications, .storyNotificationsHeader:
|
case .storyNotifications, .storyNotificationsHeader:
|
||||||
return NotificationPeerExceptionSection.storyNotifications.rawValue
|
return NotificationPeerExceptionSection.storyNotifications.rawValue
|
||||||
@ -439,6 +556,8 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
return .displayPreviews(mode)
|
return .displayPreviews(mode)
|
||||||
case .displayPreviewsHeader:
|
case .displayPreviewsHeader:
|
||||||
return .displayPreviewsHeader
|
return .displayPreviewsHeader
|
||||||
|
case let .showSender(_, _, _, mode, _):
|
||||||
|
return .showSender(mode)
|
||||||
case let .storyNotifications(_, _, _, mode, _):
|
case let .storyNotifications(_, _, _, mode, _):
|
||||||
return .storyNotifications(mode)
|
return .storyNotifications(mode)
|
||||||
case .storyNotificationsHeader:
|
case .storyNotificationsHeader:
|
||||||
@ -497,6 +616,17 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
arguments.selectDisplayPreviews(value)
|
arguments.selectDisplayPreviews(value)
|
||||||
})
|
})
|
||||||
|
case let .showSender(_, _, strings, value, selected):
|
||||||
|
let title: String
|
||||||
|
switch value {
|
||||||
|
case .alwaysOn:
|
||||||
|
title = strings.Notification_Exceptions_MessagePreviewAlwaysOn
|
||||||
|
case .alwaysOff:
|
||||||
|
title = strings.Notification_Exceptions_MessagePreviewAlwaysOff
|
||||||
|
}
|
||||||
|
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
|
arguments.selectHideStoriesSender(value)
|
||||||
|
})
|
||||||
case let .cloudHeader(_, text):
|
case let .cloudHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .cloudInfo(_, text):
|
case let .cloudInfo(_, text):
|
||||||
@ -517,7 +647,7 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
title = strings.Notification_Exceptions_MessagePreviewAlwaysOff
|
title = strings.Notification_Exceptions_MessagePreviewAlwaysOff
|
||||||
}
|
}
|
||||||
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
arguments.selectStoryNotifications(value)
|
arguments.selectStoriesMuted(value)
|
||||||
})
|
})
|
||||||
case let .storyNotificationsHeader(_, _, text):
|
case let .storyNotificationsHeader(_, _, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
@ -545,7 +675,7 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
|
|
||||||
private func notificationPeerExceptionEntries(presentationData: PresentationData, peer: EnginePeer?, notificationSoundList: NotificationSoundList?, state: NotificationExceptionPeerState, isStories: Bool?) -> [NotificationPeerExceptionEntry] {
|
private func notificationPeerExceptionEntries(presentationData: PresentationData, peer: EnginePeer?, notificationSoundList: NotificationSoundList?, state: NotificationExceptionPeerState, isStories: Bool?) -> [NotificationPeerExceptionEntry] {
|
||||||
let selectedSound = resolvedNotificationSound(sound: state.selectedSound, notificationSoundList: notificationSoundList)
|
let selectedSound = resolvedNotificationSound(sound: isStories == true ? state.selectedStoriesSound : state.selectedSound, notificationSoundList: notificationSoundList)
|
||||||
|
|
||||||
var entries: [NotificationPeerExceptionEntry] = []
|
var entries: [NotificationPeerExceptionEntry] = []
|
||||||
|
|
||||||
@ -580,14 +710,31 @@ private func notificationPeerExceptionEntries(presentationData: PresentationData
|
|||||||
//TODO:localize
|
//TODO:localize
|
||||||
entries.append(.storyNotificationsHeader(index: index, theme: presentationData.theme, title: "STORY NOTIFICATIONS"))
|
entries.append(.storyNotificationsHeader(index: index, theme: presentationData.theme, title: "STORY NOTIFICATIONS"))
|
||||||
index += 1
|
index += 1
|
||||||
entries.append(.storyNotifications(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOn, selected: state.storyNotifications == .alwaysOn))
|
entries.append(.storyNotifications(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOn, selected: state.storiesMuted == .alwaysOn))
|
||||||
index += 1
|
index += 1
|
||||||
entries.append(.storyNotifications(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOff, selected: state.storyNotifications == .alwaysOff))
|
entries.append(.storyNotifications(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOff, selected: state.storiesMuted == .alwaysOff))
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
|
if state.storiesMuted != .alwaysOff {
|
||||||
|
entries.append(.displayPreviewsHeader(index: index, theme: presentationData.theme, title: "Display Author Name"))
|
||||||
|
index += 1
|
||||||
|
entries.append(.showSender(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOn, selected: state.storiesHideSender == .alwaysOn))
|
||||||
|
index += 1
|
||||||
|
entries.append(.showSender(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOff, selected: state.storiesHideSender == .alwaysOff))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isStories == nil || isStories == false {
|
var displaySounds = true
|
||||||
|
|
||||||
|
if isStories == true {
|
||||||
|
if state.storiesMuted == .alwaysOff {
|
||||||
|
displaySounds = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if displaySounds {
|
||||||
entries.append(.cloudHeader(index: index, text: presentationData.strings.Notifications_TelegramTones))
|
entries.append(.cloudHeader(index: index, text: presentationData.strings.Notifications_TelegramTones))
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
@ -650,7 +797,9 @@ private struct NotificationExceptionPeerState : Equatable {
|
|||||||
var mode: NotificationPeerExceptionSwitcher
|
var mode: NotificationPeerExceptionSwitcher
|
||||||
var defaultSound: PeerMessageSound
|
var defaultSound: PeerMessageSound
|
||||||
var displayPreviews: NotificationPeerExceptionSwitcher
|
var displayPreviews: NotificationPeerExceptionSwitcher
|
||||||
var storyNotifications: NotificationPeerExceptionSwitcher
|
var storiesMuted: NotificationPeerExceptionSwitcher
|
||||||
|
var selectedStoriesSound: PeerMessageSound
|
||||||
|
var storiesHideSender: NotificationPeerExceptionSwitcher
|
||||||
var removedSounds: [PeerMessageSound]
|
var removedSounds: [PeerMessageSound]
|
||||||
|
|
||||||
init(canRemove: Bool, notifications: TelegramPeerNotificationSettings? = nil) {
|
init(canRemove: Bool, notifications: TelegramPeerNotificationSettings? = nil) {
|
||||||
@ -665,30 +814,64 @@ private struct NotificationExceptionPeerState : Equatable {
|
|||||||
self.mode = .alwaysOn
|
self.mode = .alwaysOn
|
||||||
}
|
}
|
||||||
self.displayPreviews = notifications.displayPreviews == .hide ? .alwaysOff : .alwaysOn
|
self.displayPreviews = notifications.displayPreviews == .hide ? .alwaysOff : .alwaysOn
|
||||||
self.storyNotifications = notifications.storiesMuted == true ? .alwaysOff : .alwaysOn
|
self.storiesMuted = notifications.storySettings.mute == .muted ? .alwaysOff : .alwaysOn
|
||||||
|
self.selectedStoriesSound = notifications.storySettings.sound
|
||||||
|
self.storiesHideSender = notifications.storySettings.hideSender == .hide ? .alwaysOff : .alwaysOn
|
||||||
} else {
|
} else {
|
||||||
self.selectedSound = .default
|
self.selectedSound = .default
|
||||||
self.mode = .alwaysOn
|
self.mode = .alwaysOn
|
||||||
self.displayPreviews = .alwaysOn
|
self.displayPreviews = .alwaysOn
|
||||||
self.storyNotifications = .alwaysOn
|
self.storiesMuted = PeerStoryNotificationSettings.default.mute == .muted ? .alwaysOff : .alwaysOn
|
||||||
|
self.selectedStoriesSound = PeerStoryNotificationSettings.default.sound
|
||||||
|
self.storiesHideSender = PeerStoryNotificationSettings.default.hideSender == .hide ? .alwaysOff : .alwaysOn
|
||||||
}
|
}
|
||||||
|
|
||||||
self.defaultSound = .default
|
self.defaultSound = .default
|
||||||
self.removedSounds = []
|
self.removedSounds = []
|
||||||
}
|
}
|
||||||
|
|
||||||
init(canRemove: Bool, selectedSound: PeerMessageSound, mode: NotificationPeerExceptionSwitcher, defaultSound: PeerMessageSound, displayPreviews: NotificationPeerExceptionSwitcher, storyNotifications: NotificationPeerExceptionSwitcher, removedSounds: [PeerMessageSound]) {
|
init(
|
||||||
|
canRemove: Bool,
|
||||||
|
selectedSound: PeerMessageSound,
|
||||||
|
mode: NotificationPeerExceptionSwitcher,
|
||||||
|
defaultSound: PeerMessageSound,
|
||||||
|
displayPreviews: NotificationPeerExceptionSwitcher,
|
||||||
|
storiesMuted: NotificationPeerExceptionSwitcher,
|
||||||
|
selectedStoriesSound: PeerMessageSound,
|
||||||
|
storiesHideSender: NotificationPeerExceptionSwitcher,
|
||||||
|
removedSounds: [PeerMessageSound]
|
||||||
|
) {
|
||||||
self.canRemove = canRemove
|
self.canRemove = canRemove
|
||||||
self.selectedSound = selectedSound
|
self.selectedSound = selectedSound
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.defaultSound = defaultSound
|
self.defaultSound = defaultSound
|
||||||
self.displayPreviews = displayPreviews
|
self.displayPreviews = displayPreviews
|
||||||
self.storyNotifications = storyNotifications
|
self.storiesMuted = storiesMuted
|
||||||
|
self.selectedStoriesSound = selectedStoriesSound
|
||||||
|
self.storiesHideSender = storiesHideSender
|
||||||
self.removedSounds = removedSounds
|
self.removedSounds = removedSounds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, customTitle: String? = nil, threadId: Int64?, isStories: Bool?, canRemove: Bool, defaultSound: PeerMessageSound, edit: Bool = false, updatePeerSound: @escaping(EnginePeer.Id, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(EnginePeer.Id, Int32?) -> Void, updatePeerDisplayPreviews: @escaping(EnginePeer.Id, PeerNotificationDisplayPreviews) -> Void, updatePeerStoryNotifications: @escaping(EnginePeer.Id, PeerNotificationDisplayPreviews) -> Void, removePeerFromExceptions: @escaping () -> Void, modifiedPeer: @escaping () -> Void) -> ViewController {
|
public func notificationPeerExceptionController(
|
||||||
|
context: AccountContext,
|
||||||
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||||
|
peer: EnginePeer,
|
||||||
|
customTitle: String? = nil,
|
||||||
|
threadId: Int64?,
|
||||||
|
isStories: Bool?,
|
||||||
|
canRemove: Bool,
|
||||||
|
defaultSound: PeerMessageSound,
|
||||||
|
edit: Bool = false,
|
||||||
|
updatePeerSound: @escaping(EnginePeer.Id, PeerMessageSound) -> Void,
|
||||||
|
updatePeerNotificationInterval: @escaping(EnginePeer.Id, Int32?) -> Void,
|
||||||
|
updatePeerDisplayPreviews: @escaping(EnginePeer.Id, PeerNotificationDisplayPreviews) -> Void,
|
||||||
|
updatePeerStoriesMuted: @escaping(EnginePeer.Id, PeerStoryNotificationSettings.Mute) -> Void,
|
||||||
|
updatePeerStoriesHideSender: @escaping (EnginePeer.Id, PeerStoryNotificationSettings.HideSender) -> Void,
|
||||||
|
updatePeerStorySound: @escaping (EnginePeer.Id, PeerMessageSound) -> Void,
|
||||||
|
removePeerFromExceptions: @escaping () -> Void,
|
||||||
|
modifiedPeer: @escaping () -> Void
|
||||||
|
) -> ViewController {
|
||||||
let initialState = NotificationExceptionPeerState(canRemove: false)
|
let initialState = NotificationExceptionPeerState(canRemove: false)
|
||||||
let statePromise = Promise(initialState)
|
let statePromise = Promise(initialState)
|
||||||
let stateValue = Atomic(value: initialState)
|
let stateValue = Atomic(value: initialState)
|
||||||
@ -706,16 +889,30 @@ public func notificationPeerExceptionController(context: AccountContext, updated
|
|||||||
let soundActionDisposable = MetaDisposable()
|
let soundActionDisposable = MetaDisposable()
|
||||||
|
|
||||||
let arguments = NotificationPeerExceptionArguments(account: context.account, selectSound: { sound in
|
let arguments = NotificationPeerExceptionArguments(account: context.account, selectSound: { sound in
|
||||||
updateState { state in
|
if isStories == true {
|
||||||
let _ = (context.engine.peers.notificationSoundList()
|
updateState { state in
|
||||||
|> take(1)
|
let _ = (context.engine.peers.notificationSoundList()
|
||||||
|> deliverOnMainQueue).start(next: { notificationSoundList in
|
|> take(1)
|
||||||
playSoundDisposable.set(playSound(context: context, notificationSoundList: notificationSoundList, sound: sound, defaultSound: state.defaultSound).start())
|
|> deliverOnMainQueue).start(next: { notificationSoundList in
|
||||||
})
|
playSoundDisposable.set(playSound(context: context, notificationSoundList: notificationSoundList, sound: sound, defaultSound: state.defaultSound).start())
|
||||||
|
})
|
||||||
var state = state
|
|
||||||
state.selectedSound = sound
|
var state = state
|
||||||
return state
|
state.selectedStoriesSound = sound
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateState { state in
|
||||||
|
let _ = (context.engine.peers.notificationSoundList()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { notificationSoundList in
|
||||||
|
playSoundDisposable.set(playSound(context: context, notificationSoundList: notificationSoundList, sound: sound, defaultSound: state.defaultSound).start())
|
||||||
|
})
|
||||||
|
|
||||||
|
var state = state
|
||||||
|
state.selectedSound = sound
|
||||||
|
return state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, selectMode: { mode in
|
}, selectMode: { mode in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
@ -729,10 +926,28 @@ public func notificationPeerExceptionController(context: AccountContext, updated
|
|||||||
state.displayPreviews = value
|
state.displayPreviews = value
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}, selectStoryNotifications: { value in
|
}, selectStoriesMuted: { value in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.storyNotifications = value
|
state.storiesMuted = value
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}, selectHideStoriesSender: { value in
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
state.storiesHideSender = value
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}, selectStorySound: { sound in
|
||||||
|
updateState { state in
|
||||||
|
let _ = (context.engine.peers.notificationSoundList()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { notificationSoundList in
|
||||||
|
playSoundDisposable.set(playSound(context: context, notificationSoundList: notificationSoundList, sound: sound, defaultSound: state.defaultSound).start())
|
||||||
|
})
|
||||||
|
|
||||||
|
var state = state
|
||||||
|
state.selectedStoriesSound = sound
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}, removeFromExceptions: {
|
}, removeFromExceptions: {
|
||||||
@ -816,10 +1031,16 @@ public func notificationPeerExceptionController(context: AccountContext, updated
|
|||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { notificationSoundList in
|
|> deliverOnMainQueue).start(next: { notificationSoundList in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
updatePeerSound(peer.id, resolvedNotificationSound(sound: state.selectedSound, notificationSoundList: notificationSoundList))
|
if isStories == nil || isStories == false {
|
||||||
updatePeerNotificationInterval(peer.id, state.mode == .alwaysOn ? 0 : Int32.max)
|
updatePeerSound(peer.id, resolvedNotificationSound(sound: state.selectedSound, notificationSoundList: notificationSoundList))
|
||||||
updatePeerDisplayPreviews(peer.id, state.displayPreviews == .alwaysOn ? .show : .hide)
|
updatePeerNotificationInterval(peer.id, state.mode == .alwaysOn ? 0 : Int32.max)
|
||||||
updatePeerStoryNotifications(peer.id, state.storyNotifications == .alwaysOn ? .show : .hide)
|
updatePeerDisplayPreviews(peer.id, state.displayPreviews == .alwaysOn ? .show : .hide)
|
||||||
|
}
|
||||||
|
if isStories == nil || isStories == true {
|
||||||
|
updatePeerStoriesMuted(peer.id, state.storiesMuted == .alwaysOn ? .unmuted : .muted)
|
||||||
|
updatePeerStoriesHideSender(peer.id, state.storiesHideSender == .alwaysOn ? .show : .hide)
|
||||||
|
updatePeerStorySound(peer.id, resolvedNotificationSound(sound: state.selectedStoriesSound, notificationSoundList: notificationSoundList))
|
||||||
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -15,7 +15,7 @@ private struct StoryKey: Hashable {
|
|||||||
public final class StoryContentContextImpl: StoryContentContext {
|
public final class StoryContentContextImpl: StoryContentContext {
|
||||||
private final class PeerContext {
|
private final class PeerContext {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: EnginePeer.Id
|
let peerId: EnginePeer.Id
|
||||||
|
|
||||||
private(set) var sliceValue: StoryContentContextState.FocusedSlice?
|
private(set) var sliceValue: StoryContentContextState.FocusedSlice?
|
||||||
fileprivate var nextItems: [EngineStoryItem] = []
|
fileprivate var nextItems: [EngineStoryItem] = []
|
||||||
@ -100,10 +100,10 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
let additionalPeerData: StoryContentContextState.AdditionalPeerData
|
let additionalPeerData: StoryContentContextState.AdditionalPeerData
|
||||||
if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView, let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData {
|
if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView, let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData {
|
||||||
var isMuted = false
|
var isMuted = false
|
||||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, let storiesMuted = notificationSettings.storiesMuted {
|
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||||
isMuted = storiesMuted
|
isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings)
|
||||||
} else {
|
} else {
|
||||||
isMuted = globalNotificationSettings.privateChats.storiesMuted
|
isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil)
|
||||||
}
|
}
|
||||||
additionalPeerData = StoryContentContextState.AdditionalPeerData(isMuted: isMuted, areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable)
|
additionalPeerData = StoryContentContextState.AdditionalPeerData(isMuted: isMuted, areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable)
|
||||||
} else {
|
} else {
|
||||||
@ -513,29 +513,26 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
} else {
|
} else {
|
||||||
var startedWithUnseenValue = false
|
var startedWithUnseenValue = false
|
||||||
|
|
||||||
if let (focusedPeerId, _) = self.focusedItem, focusedPeerId == self.context.account.peerId {
|
var centralIndex: Int?
|
||||||
} else {
|
if let (focusedPeerId, _) = self.focusedItem {
|
||||||
var centralIndex: Int?
|
if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) {
|
||||||
if let (focusedPeerId, _) = self.focusedItem {
|
centralIndex = index
|
||||||
if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) {
|
|
||||||
centralIndex = index
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if centralIndex == nil {
|
}
|
||||||
if let index = storySubscriptions.items.firstIndex(where: { $0.hasUnseen }) {
|
if centralIndex == nil {
|
||||||
centralIndex = index
|
if let index = storySubscriptions.items.firstIndex(where: { $0.hasUnseen }) {
|
||||||
}
|
centralIndex = index
|
||||||
}
|
}
|
||||||
if centralIndex == nil {
|
}
|
||||||
if !storySubscriptions.items.isEmpty {
|
if centralIndex == nil {
|
||||||
centralIndex = 0
|
if !storySubscriptions.items.isEmpty {
|
||||||
}
|
centralIndex = 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if let centralIndex {
|
|
||||||
if storySubscriptions.items[centralIndex].hasUnseen {
|
if let centralIndex {
|
||||||
startedWithUnseenValue = true
|
if storySubscriptions.items[centralIndex].hasUnseen {
|
||||||
}
|
startedWithUnseenValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,9 +582,11 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updatePeerContexts() {
|
private func updatePeerContexts() {
|
||||||
if let currentState = self.currentState {
|
if let currentState = self.currentState, let storySubscriptions = self.storySubscriptions, !storySubscriptions.items.contains(where: { $0.peer.id == currentState.centralPeerContext.peerId }) {
|
||||||
let _ = currentState
|
self.currentState = nil
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if self.currentState == nil {
|
||||||
self.switchToFocusedPeerId()
|
self.switchToFocusedPeerId()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -914,13 +913,12 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data
|
let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data
|
||||||
let (item, peers) = itemAndPeers
|
let (item, peers) = itemAndPeers
|
||||||
|
|
||||||
var isMuted = false
|
guard let peer else {
|
||||||
if let storiesMuted = notificationSettings.storiesMuted {
|
return
|
||||||
isMuted = storiesMuted
|
|
||||||
} else {
|
|
||||||
isMuted = globalNotificationSettings.privateChats.storiesMuted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings())
|
||||||
|
|
||||||
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
isMuted: isMuted,
|
isMuted: isMuted,
|
||||||
areVoiceMessagesAvailable: areVoiceMessagesAvailable
|
areVoiceMessagesAvailable: areVoiceMessagesAvailable
|
||||||
@ -935,7 +933,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let item, case let .item(itemValue) = item, let media = itemValue.media, let peer {
|
if let item, case let .item(itemValue) = item, let media = itemValue.media {
|
||||||
let mappedItem = EngineStoryItem(
|
let mappedItem = EngineStoryItem(
|
||||||
id: itemValue.id,
|
id: itemValue.id,
|
||||||
timestamp: itemValue.timestamp,
|
timestamp: itemValue.timestamp,
|
||||||
@ -1063,13 +1061,12 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
|
|
||||||
let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data
|
let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data
|
||||||
|
|
||||||
var isMuted = false
|
guard let peer else {
|
||||||
if let storiesMuted = notificationSettings.storiesMuted {
|
return
|
||||||
isMuted = storiesMuted
|
|
||||||
} else {
|
|
||||||
isMuted = globalNotificationSettings.privateChats.storiesMuted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings())
|
||||||
|
|
||||||
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
isMuted: isMuted,
|
isMuted: isMuted,
|
||||||
areVoiceMessagesAvailable: areVoiceMessagesAvailable
|
areVoiceMessagesAvailable: areVoiceMessagesAvailable
|
||||||
@ -1105,7 +1102,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let stateValue: StoryContentContextState
|
let stateValue: StoryContentContextState
|
||||||
if let focusedIndex = focusedIndex, let peer = peer {
|
if let focusedIndex = focusedIndex {
|
||||||
let item = state.items[focusedIndex]
|
let item = state.items[focusedIndex]
|
||||||
self.focusedId = item.id
|
self.focusedId = item.id
|
||||||
|
|
||||||
@ -1152,7 +1149,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
var resultResources: [EngineMediaResource.Id: StoryPreloadInfo] = [:]
|
var resultResources: [EngineMediaResource.Id: StoryPreloadInfo] = [:]
|
||||||
var pollItems: [StoryKey] = []
|
var pollItems: [StoryKey] = []
|
||||||
|
|
||||||
if let peer, let focusedIndex, let slice = stateValue.slice {
|
if let focusedIndex, let slice = stateValue.slice {
|
||||||
var possibleItems: [(EnginePeer, EngineStoryItem)] = []
|
var possibleItems: [(EnginePeer, EngineStoryItem)] = []
|
||||||
if peer.id == self.context.account.peerId {
|
if peer.id == self.context.account.peerId {
|
||||||
pollItems.append(StoryKey(peerId: peer.id, id: slice.item.storyItem.id))
|
pollItems.append(StoryKey(peerId: peer.id, id: slice.item.storyItem.id))
|
||||||
|
|||||||
@ -138,7 +138,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
loopVideo: true,
|
loopVideo: true,
|
||||||
enableSound: true,
|
enableSound: true,
|
||||||
beginWithAmbientSound: environment.sharedState.useAmbientMode,
|
beginWithAmbientSound: environment.sharedState.useAmbientMode,
|
||||||
useLargeThumbnail: true,
|
useLargeThumbnail: false,
|
||||||
autoFetchFullSizeThumbnail: true,
|
autoFetchFullSizeThumbnail: true,
|
||||||
tempFilePath: nil,
|
tempFilePath: nil,
|
||||||
captureProtected: component.item.isForwardingDisabled,
|
captureProtected: component.item.isForwardingDisabled,
|
||||||
@ -429,7 +429,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
userLocation: .other,
|
userLocation: .other,
|
||||||
videoReference: .story(peer: peerReference, id: component.item.id, media: file),
|
videoReference: .story(peer: peerReference, id: component.item.id, media: file),
|
||||||
onlyFullSize: false,
|
onlyFullSize: false,
|
||||||
useLargeThumbnail: true,
|
useLargeThumbnail: false,
|
||||||
synchronousLoad: synchronousLoad,
|
synchronousLoad: synchronousLoad,
|
||||||
autoFetchFullSizeThumbnail: true,
|
autoFetchFullSizeThumbnail: true,
|
||||||
overlayColor: nil,
|
overlayColor: nil,
|
||||||
|
|||||||
@ -982,9 +982,18 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
var canReply = true
|
||||||
inputPanelView.activateInput()
|
if component.slice.peer.isService {
|
||||||
return false
|
canReply = false
|
||||||
|
} else if case .unsupported = component.slice.item.storyItem.media {
|
||||||
|
canReply = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if canReply {
|
||||||
|
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||||
|
inputPanelView.activateInput()
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -3136,8 +3145,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: component.slice.peer.id))
|
let _ = (component.context.engine.data.get(
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] settings in
|
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: component.slice.peer.id),
|
||||||
|
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] settings, globalSettings in
|
||||||
guard let self, let component = self.component, let controller = component.controller() else {
|
guard let self, let component = self.component, let controller = component.controller() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3152,7 +3164,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
let isMuted = settings.storiesMuted == true
|
let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.peer._asPeer(), peerSettings: settings._asNotificationSettings())
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Don't Notify", icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: component.slice.additionalPeerData.isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: component.slice.additionalPeerData.isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { [weak self] _, a in
|
}, action: { [weak self] _, a in
|
||||||
|
|||||||
@ -22,8 +22,8 @@ swift_library(
|
|||||||
"//submodules/ContextUI",
|
"//submodules/ContextUI",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||||
"//submodules/Components/MultilineTextComponent",
|
"//submodules/Components/MultilineTextComponent",
|
||||||
"//submodules/ActivityIndicator",
|
|
||||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||||
|
"//submodules/Components/HierarchyTrackingLayer",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -240,10 +240,50 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class TitleAnimationState {
|
||||||
|
let duration: Double
|
||||||
|
let startTime: Double
|
||||||
|
let fromFraction: CGFloat
|
||||||
|
let toFraction: CGFloat
|
||||||
|
let imageView: UIImageView
|
||||||
|
|
||||||
|
init(
|
||||||
|
duration: Double,
|
||||||
|
startTime: Double,
|
||||||
|
fromFraction: CGFloat,
|
||||||
|
toFraction: CGFloat,
|
||||||
|
imageView: UIImageView
|
||||||
|
) {
|
||||||
|
self.duration = duration
|
||||||
|
self.startTime = startTime
|
||||||
|
self.fromFraction = fromFraction
|
||||||
|
self.toFraction = toFraction
|
||||||
|
self.imageView = imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
func interpolatedFraction(at timestamp: Double, effectiveFromFraction: CGFloat, toFraction: CGFloat) -> CGFloat {
|
||||||
|
var rawProgress = CGFloat((timestamp - self.startTime) / self.duration)
|
||||||
|
rawProgress = max(0.0, min(1.0, rawProgress))
|
||||||
|
let progress = listViewAnimationCurveSystem(rawProgress)
|
||||||
|
|
||||||
|
return effectiveFromFraction * (1.0 - progress) + toFraction * progress
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFinished(at timestamp: Double) -> Bool {
|
||||||
|
if timestamp > self.startTime + self.duration {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class AnimationState {
|
private final class AnimationState {
|
||||||
let duration: Double
|
let duration: Double
|
||||||
let fromIsUnlocked: Bool
|
let fromIsUnlocked: Bool
|
||||||
let fromFraction: CGFloat
|
let fromFraction: CGFloat
|
||||||
|
let fromTitleWidth: CGFloat
|
||||||
|
let fromActivityFraction: CGFloat
|
||||||
let startTime: Double
|
let startTime: Double
|
||||||
let bounce: Bool
|
let bounce: Bool
|
||||||
|
|
||||||
@ -251,12 +291,16 @@ public final class StoryPeerListComponent: Component {
|
|||||||
duration: Double,
|
duration: Double,
|
||||||
fromIsUnlocked: Bool,
|
fromIsUnlocked: Bool,
|
||||||
fromFraction: CGFloat,
|
fromFraction: CGFloat,
|
||||||
|
fromTitleWidth: CGFloat,
|
||||||
|
fromActivityFraction: CGFloat,
|
||||||
startTime: Double,
|
startTime: Double,
|
||||||
bounce: Bool
|
bounce: Bool
|
||||||
) {
|
) {
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
self.fromIsUnlocked = fromIsUnlocked
|
self.fromIsUnlocked = fromIsUnlocked
|
||||||
self.fromFraction = fromFraction
|
self.fromFraction = fromFraction
|
||||||
|
self.fromTitleWidth = fromTitleWidth
|
||||||
|
self.fromActivityFraction = fromActivityFraction
|
||||||
self.startTime = startTime
|
self.startTime = startTime
|
||||||
self.bounce = bounce
|
self.bounce = bounce
|
||||||
}
|
}
|
||||||
@ -278,6 +322,14 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct TitleState: Equatable {
|
||||||
|
var text: String
|
||||||
|
|
||||||
|
init(text: String) {
|
||||||
|
self.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class View: UIView, UIScrollViewDelegate {
|
public final class View: UIView, UIScrollViewDelegate {
|
||||||
private let collapsedButton: HighlightableButton
|
private let collapsedButton: HighlightableButton
|
||||||
private let scrollView: ScrollView
|
private let scrollView: ScrollView
|
||||||
@ -292,7 +344,13 @@ public final class StoryPeerListComponent: Component {
|
|||||||
private var visibleCollapsableItems: [EnginePeer.Id: VisibleItem] = [:]
|
private var visibleCollapsableItems: [EnginePeer.Id: VisibleItem] = [:]
|
||||||
|
|
||||||
private var titleIndicatorView: ComponentView<Empty>?
|
private var titleIndicatorView: ComponentView<Empty>?
|
||||||
private let titleView = ComponentView<Empty>()
|
|
||||||
|
private let titleView: UIImageView
|
||||||
|
private var titleState: TitleState?
|
||||||
|
private var titleViewAnimation: TitleAnimationState?
|
||||||
|
|
||||||
|
private var disappearingTitleViews: [TitleAnimationState] = []
|
||||||
|
|
||||||
private var titleIconView: ComponentView<Empty>?
|
private var titleIconView: ComponentView<Empty>?
|
||||||
|
|
||||||
private var component: StoryPeerListComponent?
|
private var component: StoryPeerListComponent?
|
||||||
@ -308,6 +366,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
private var animator: ConstantDisplayLinkAnimator?
|
private var animator: ConstantDisplayLinkAnimator?
|
||||||
|
|
||||||
private var currentFraction: CGFloat = 0.0
|
private var currentFraction: CGFloat = 0.0
|
||||||
|
private var currentTitleWidth: CGFloat = 0.0
|
||||||
|
private var currentActivityFraction: CGFloat = 0.0
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
self.collapsedButton = HighlightableButton()
|
self.collapsedButton = HighlightableButton()
|
||||||
@ -325,6 +385,9 @@ public final class StoryPeerListComponent: Component {
|
|||||||
|
|
||||||
self.scrollContainerView = UIView()
|
self.scrollContainerView = UIView()
|
||||||
|
|
||||||
|
self.titleView = UIImageView()
|
||||||
|
self.titleView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.scrollView.delegate = self
|
self.scrollView.delegate = self
|
||||||
@ -333,6 +396,7 @@ public final class StoryPeerListComponent: Component {
|
|||||||
self.addSubview(self.scrollView)
|
self.addSubview(self.scrollView)
|
||||||
self.addSubview(self.scrollContainerView)
|
self.addSubview(self.scrollContainerView)
|
||||||
self.addSubview(self.collapsedButton)
|
self.addSubview(self.collapsedButton)
|
||||||
|
self.addSubview(self.titleView)
|
||||||
|
|
||||||
self.collapsedButton.highligthedChanged = { [weak self] highlighted in
|
self.collapsedButton.highligthedChanged = { [weak self] highlighted in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -438,41 +502,10 @@ public final class StoryPeerListComponent: Component {
|
|||||||
let titleIconSpacing: CGFloat = 4.0
|
let titleIconSpacing: CGFloat = 4.0
|
||||||
let titleIndicatorSpacing: CGFloat = 8.0
|
let titleIndicatorSpacing: CGFloat = 8.0
|
||||||
|
|
||||||
var titleContentWidth: CGFloat = 0.0
|
var realTitleContentWidth: CGFloat = 0.0
|
||||||
|
|
||||||
var titleIndicatorSize: CGSize?
|
let titleSize = self.titleView.image?.size ?? CGSize()
|
||||||
if component.titleHasActivity {
|
realTitleContentWidth += titleSize.width
|
||||||
let titleIndicatorView: ComponentView<Empty>
|
|
||||||
if let current = self.titleIndicatorView {
|
|
||||||
titleIndicatorView = current
|
|
||||||
} else {
|
|
||||||
titleIndicatorView = ComponentView()
|
|
||||||
self.titleIndicatorView = titleIndicatorView
|
|
||||||
}
|
|
||||||
let titleIndicatorSizeValue = titleIndicatorView.update(
|
|
||||||
transition: .immediate,
|
|
||||||
component: AnyComponent(TitleActivityIndicatorComponent(
|
|
||||||
color: component.theme.rootController.navigationBar.accentTextColor
|
|
||||||
)),
|
|
||||||
environment: {},
|
|
||||||
containerSize: CGSize(width: 22.0, height: 22.0)
|
|
||||||
)
|
|
||||||
titleIndicatorSize = titleIndicatorSizeValue
|
|
||||||
titleContentWidth += titleIndicatorSizeValue.width + titleIndicatorSpacing
|
|
||||||
} else {
|
|
||||||
if let titleIndicatorView = self.titleIndicatorView {
|
|
||||||
self.titleIndicatorView = nil
|
|
||||||
titleIndicatorView.view?.removeFromSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let titleSize = self.titleView.update(
|
|
||||||
transition: .immediate,
|
|
||||||
component: AnyComponent(Text(text: component.title, font: Font.semibold(17.0), color: component.theme.rootController.navigationBar.primaryTextColor)),
|
|
||||||
environment: {},
|
|
||||||
containerSize: CGSize(width: 200.0, height: 100.0)
|
|
||||||
)
|
|
||||||
titleContentWidth += titleSize.width
|
|
||||||
|
|
||||||
var titleIconSize: CGSize?
|
var titleIconSize: CGSize?
|
||||||
if let peerStatus = component.titlePeerStatus {
|
if let peerStatus = component.titlePeerStatus {
|
||||||
@ -523,12 +556,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
|
|
||||||
titleIconSize = titleIconSizeValue
|
titleIconSize = titleIconSizeValue
|
||||||
|
|
||||||
if let titleIconComponentView = titleIconView.view {
|
|
||||||
titleIconComponentView.isHidden = component.titleHasActivity
|
|
||||||
}
|
|
||||||
|
|
||||||
if !component.titleHasActivity {
|
if !component.titleHasActivity {
|
||||||
titleContentWidth += titleIconSpacing + titleIconSizeValue.width
|
realTitleContentWidth += titleIconSpacing + titleIconSizeValue.width
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let titleIconView = self.titleIconView {
|
if let titleIconView = self.titleIconView {
|
||||||
@ -552,50 +581,14 @@ public final class StoryPeerListComponent: Component {
|
|||||||
collapseStartIndex = 1
|
collapseStartIndex = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let collapsedItemWidth: CGFloat = 24.0
|
|
||||||
let collapsedItemDistance: CGFloat = 14.0
|
|
||||||
let collapsedItemCount: CGFloat = CGFloat(min(self.sortedItems.count - collapseStartIndex, 3))
|
|
||||||
var collapsedContentWidth: CGFloat = 0.0
|
|
||||||
if collapsedItemCount > 0 {
|
|
||||||
collapsedContentWidth = 1.0 * collapsedItemWidth + (collapsedItemDistance) * max(0.0, collapsedItemCount - 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
let collapseEndIndex = collapseStartIndex + max(0, Int(collapsedItemCount) - 1)
|
|
||||||
|
|
||||||
var collapsedContentOrigin: CGFloat
|
|
||||||
let collapsedItemOffsetY: CGFloat
|
|
||||||
|
|
||||||
let titleContentSpacing: CGFloat = 8.0
|
|
||||||
var combinedTitleContentWidth = titleContentWidth
|
|
||||||
if !combinedTitleContentWidth.isZero {
|
|
||||||
combinedTitleContentWidth += titleContentSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
let centralContentWidth: CGFloat
|
|
||||||
centralContentWidth = collapsedContentWidth + combinedTitleContentWidth
|
|
||||||
|
|
||||||
collapsedContentOrigin = floor((itemLayout.containerSize.width - centralContentWidth) * 0.5)
|
|
||||||
|
|
||||||
if component.titleHasActivity {
|
|
||||||
collapsedContentOrigin -= (collapsedContentWidth + titleContentSpacing) * 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
collapsedContentOrigin = min(collapsedContentOrigin, component.maxTitleX - centralContentWidth - 4.0)
|
|
||||||
|
|
||||||
var collapsedContentOriginOffset: CGFloat = 0.0
|
|
||||||
|
|
||||||
if itemLayout.itemCount == 1 && collapsedContentWidth <= 0.1 {
|
|
||||||
collapsedContentOriginOffset += 4.0
|
|
||||||
}
|
|
||||||
collapsedContentOrigin -= collapsedContentOriginOffset
|
|
||||||
collapsedItemOffsetY = -59.0
|
|
||||||
|
|
||||||
struct CollapseState {
|
struct CollapseState {
|
||||||
var globalFraction: CGFloat
|
var globalFraction: CGFloat
|
||||||
var scaleFraction: CGFloat
|
var scaleFraction: CGFloat
|
||||||
var minFraction: CGFloat
|
var minFraction: CGFloat
|
||||||
var maxFraction: CGFloat
|
var maxFraction: CGFloat
|
||||||
var sideAlphaFraction: CGFloat
|
var sideAlphaFraction: CGFloat
|
||||||
|
var titleWidth: CGFloat
|
||||||
|
var activityFraction: CGFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetExpandedFraction = component.collapseFraction
|
let targetExpandedFraction = component.collapseFraction
|
||||||
@ -619,6 +612,17 @@ public final class StoryPeerListComponent: Component {
|
|||||||
targetSideAlphaFraction = 0.0
|
targetSideAlphaFraction = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let collapsedItemWidth: CGFloat = 24.0
|
||||||
|
let collapsedItemDistance: CGFloat = 14.0
|
||||||
|
let collapsedItemOffsetY: CGFloat = -60.0
|
||||||
|
let titleContentSpacing: CGFloat = 8.0
|
||||||
|
|
||||||
|
let collapsedItemCount: CGFloat = CGFloat(min(self.sortedItems.count - collapseStartIndex, 3))
|
||||||
|
|
||||||
|
let targetActivityFraction: CGFloat = component.titleHasActivity ? 1.0 : 0.0
|
||||||
|
|
||||||
|
let timestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
let collapsedState: CollapseState
|
let collapsedState: CollapseState
|
||||||
let expandBoundsFraction: CGFloat
|
let expandBoundsFraction: CGFloat
|
||||||
if let animationState = self.animationState {
|
if let animationState = self.animationState {
|
||||||
@ -646,20 +650,22 @@ public final class StoryPeerListComponent: Component {
|
|||||||
effectiveFromSideAlphaFraction = 0.0
|
effectiveFromSideAlphaFraction = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let timestamp = CACurrentMediaTime()
|
|
||||||
|
|
||||||
let animatedGlobalFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: animationState.fromFraction, toFraction: targetFraction)
|
let animatedGlobalFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: animationState.fromFraction, toFraction: targetFraction)
|
||||||
let animatedScaleFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromScaleFraction, toFraction: targetScaleFraction)
|
let animatedScaleFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromScaleFraction, toFraction: targetScaleFraction)
|
||||||
let animatedMinFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromMinFraction, toFraction: targetMinFraction)
|
let animatedMinFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromMinFraction, toFraction: targetMinFraction)
|
||||||
let animatedMaxFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromMaxFraction, toFraction: targetMaxFraction)
|
let animatedMaxFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromMaxFraction, toFraction: targetMaxFraction)
|
||||||
let animatedSideAlphaFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromSideAlphaFraction, toFraction: targetSideAlphaFraction)
|
let animatedSideAlphaFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: effectiveFromSideAlphaFraction, toFraction: targetSideAlphaFraction)
|
||||||
|
let animatedTitleWidth = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: animationState.fromTitleWidth, toFraction: realTitleContentWidth)
|
||||||
|
let animatedActivityFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: animationState.fromActivityFraction, toFraction: targetActivityFraction)
|
||||||
|
|
||||||
collapsedState = CollapseState(
|
collapsedState = CollapseState(
|
||||||
globalFraction: animatedGlobalFraction,
|
globalFraction: animatedGlobalFraction,
|
||||||
scaleFraction: animatedScaleFraction,
|
scaleFraction: animatedScaleFraction,
|
||||||
minFraction: animatedMinFraction,
|
minFraction: animatedMinFraction,
|
||||||
maxFraction: animatedMaxFraction,
|
maxFraction: animatedMaxFraction,
|
||||||
sideAlphaFraction: animatedSideAlphaFraction
|
sideAlphaFraction: animatedSideAlphaFraction,
|
||||||
|
titleWidth: animatedTitleWidth,
|
||||||
|
activityFraction: animatedActivityFraction
|
||||||
)
|
)
|
||||||
|
|
||||||
var rawProgress = CGFloat((timestamp - animationState.startTime) / animationState.duration)
|
var rawProgress = CGFloat((timestamp - animationState.startTime) / animationState.duration)
|
||||||
@ -676,14 +682,36 @@ public final class StoryPeerListComponent: Component {
|
|||||||
scaleFraction: targetScaleFraction,
|
scaleFraction: targetScaleFraction,
|
||||||
minFraction: targetMinFraction,
|
minFraction: targetMinFraction,
|
||||||
maxFraction: targetMaxFraction,
|
maxFraction: targetMaxFraction,
|
||||||
sideAlphaFraction: targetSideAlphaFraction
|
sideAlphaFraction: targetSideAlphaFraction,
|
||||||
|
titleWidth: realTitleContentWidth,
|
||||||
|
activityFraction: targetActivityFraction
|
||||||
)
|
)
|
||||||
expandBoundsFraction = 0.0
|
expandBoundsFraction = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentFraction = collapsedState.globalFraction
|
var targetCollapsedContentWidth: CGFloat = 0.0
|
||||||
|
if collapsedItemCount > 0 {
|
||||||
|
targetCollapsedContentWidth = 1.0 * collapsedItemWidth + (collapsedItemDistance) * max(0.0, collapsedItemCount - 1.0)
|
||||||
|
}
|
||||||
|
let activityCollapsedContentWidth: CGFloat = 16.0 + titleIndicatorSpacing
|
||||||
|
let collapsedContentWidth = activityCollapsedContentWidth * collapsedState.activityFraction + targetCollapsedContentWidth * (1.0 - collapsedState.activityFraction)
|
||||||
|
|
||||||
component.externalState.collapsedWidth = collapsedContentWidth
|
let collapseEndIndex = collapseStartIndex + max(0, Int(collapsedItemCount) - 1)
|
||||||
|
|
||||||
|
var collapsedContentOrigin: CGFloat
|
||||||
|
|
||||||
|
let centralContentWidth: CGFloat = collapsedContentWidth + titleContentSpacing + collapsedState.titleWidth
|
||||||
|
|
||||||
|
collapsedContentOrigin = floor((itemLayout.containerSize.width - centralContentWidth) * 0.5)
|
||||||
|
|
||||||
|
collapsedContentOrigin = min(collapsedContentOrigin, component.maxTitleX - centralContentWidth - 4.0)
|
||||||
|
|
||||||
|
let collapsedContentOriginOffset: CGFloat = 0.0
|
||||||
|
collapsedContentOrigin -= collapsedContentOriginOffset
|
||||||
|
|
||||||
|
self.currentFraction = collapsedState.globalFraction
|
||||||
|
self.currentTitleWidth = collapsedState.titleWidth
|
||||||
|
self.currentActivityFraction = collapsedState.activityFraction
|
||||||
|
|
||||||
let effectiveVisibleBounds = self.scrollView.bounds
|
let effectiveVisibleBounds = self.scrollView.bounds
|
||||||
let visibleBounds = effectiveVisibleBounds.insetBy(dx: -200.0, dy: 0.0)
|
let visibleBounds = effectiveVisibleBounds.insetBy(dx: -200.0, dy: 0.0)
|
||||||
@ -698,6 +726,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let expandedItemWidth: CGFloat = 60.0
|
||||||
|
|
||||||
struct MeasuredItem {
|
struct MeasuredItem {
|
||||||
var itemFrame: CGRect
|
var itemFrame: CGRect
|
||||||
var itemScale: CGFloat
|
var itemScale: CGFloat
|
||||||
@ -712,10 +742,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
let collapsedItemX: CGFloat
|
let collapsedItemX: CGFloat
|
||||||
if collapseIndex < collapseStartIndex {
|
if collapseIndex < collapseStartIndex {
|
||||||
collapsedItemX = collapsedContentOrigin
|
collapsedItemX = collapsedContentOrigin
|
||||||
} else if collapseIndex > collapseEndIndex {
|
|
||||||
collapsedItemX = collapsedContentOrigin + CGFloat(collapseEndIndex) * collapsedItemDistance - collapsedItemWidth * 0.5
|
|
||||||
} else {
|
} else {
|
||||||
collapsedItemX = collapsedContentOrigin + CGFloat(collapseIndex - collapseStartIndex) * collapsedItemDistance
|
collapsedItemX = collapsedContentOrigin + CGFloat(min(collapseIndex - collapseStartIndex, collapseEndIndex - collapseStartIndex)) * collapsedItemDistance * (1.0 - collapsedState.activityFraction) * (1.0 - collapsedState.maxFraction)
|
||||||
}
|
}
|
||||||
let collapsedItemFrame = CGRect(origin: CGPoint(x: collapsedItemX, y: regularItemFrame.minY + collapsedItemOffsetY), size: CGSize(width: collapsedItemWidth, height: regularItemFrame.height))
|
let collapsedItemFrame = CGRect(origin: CGPoint(x: collapsedItemX, y: regularItemFrame.minY + collapsedItemOffsetY), size: CGSize(width: collapsedItemWidth, height: regularItemFrame.height))
|
||||||
|
|
||||||
@ -730,12 +758,15 @@ public final class StoryPeerListComponent: Component {
|
|||||||
collapsedMaxItemFrame.origin.y += collapsedState.minFraction * 10.0
|
collapsedMaxItemFrame.origin.y += collapsedState.minFraction * 10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let minimizedItemScale: CGFloat = 24.0 / 52.0
|
let minimizedDefaultItemScale: CGFloat = 24.0 / 52.0
|
||||||
|
let minimizedItemScale = minimizedDefaultItemScale
|
||||||
|
|
||||||
let minimizedMaxItemScale: CGFloat = (24.0 + 4.0) / 52.0
|
let minimizedMaxItemScale: CGFloat = (24.0 + 4.0) / 52.0
|
||||||
|
|
||||||
let maximizedItemScale: CGFloat = 1.0
|
let maximizedItemScale: CGFloat = 1.0
|
||||||
|
|
||||||
let minItemScale = minimizedItemScale.interpolate(to: minimizedMaxItemScale, amount: collapsedState.minFraction)
|
let minItemScale: CGFloat = minimizedItemScale.interpolate(to: minimizedMaxItemScale, amount: collapsedState.minFraction) * (1.0 - collapsedState.activityFraction) + 0.1 * collapsedState.activityFraction
|
||||||
|
|
||||||
let itemScale: CGFloat = minItemScale.interpolate(to: maximizedItemScale, amount: collapsedState.maxFraction)
|
let itemScale: CGFloat = minItemScale.interpolate(to: maximizedItemScale, amount: collapsedState.maxFraction)
|
||||||
|
|
||||||
let itemFrame: CGRect
|
let itemFrame: CGRect
|
||||||
@ -787,7 +818,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let isReallyVisible = effectiveVisibleBounds.intersects(regularItemFrame)
|
//let isReallyVisible = effectiveVisibleBounds.intersects(regularItemFrame)
|
||||||
|
//let _ = isReallyVisible
|
||||||
|
|
||||||
validIds.append(itemSet.peer.id)
|
validIds.append(itemSet.peer.id)
|
||||||
|
|
||||||
@ -845,11 +877,7 @@ public final class StoryPeerListComponent: Component {
|
|||||||
rightItemFrame = calculateItem(i + 1).itemFrame
|
rightItemFrame = calculateItem(i + 1).itemFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
if effectiveFirstVisibleIndex == 0 && !component.titleHasActivity {
|
itemAlpha = collapsedState.sideAlphaFraction * 1.0 + (1.0 - collapsedState.sideAlphaFraction) * (1.0 - collapsedState.activityFraction)
|
||||||
itemAlpha = 1.0
|
|
||||||
} else {
|
|
||||||
itemAlpha = collapsedState.sideAlphaFraction
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if itemLayout.itemCount == 1 {
|
if itemLayout.itemCount == 1 {
|
||||||
itemAlpha = min(1.0, (collapsedState.minFraction + collapsedState.maxFraction) * 4.0)
|
itemAlpha = min(1.0, (collapsedState.minFraction + collapsedState.maxFraction) * 4.0)
|
||||||
@ -879,9 +907,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
||||||
hasItems: hasItems,
|
hasItems: hasItems,
|
||||||
ringAnimation: itemRingAnimation,
|
ringAnimation: itemRingAnimation,
|
||||||
collapseFraction: isReallyVisible ? (1.0 - collapsedState.maxFraction) : 0.0,
|
|
||||||
scale: itemScale,
|
scale: itemScale,
|
||||||
collapsedWidth: collapsedItemWidth,
|
fullWidth: expandedItemWidth,
|
||||||
expandedAlphaFraction: collapsedState.sideAlphaFraction,
|
expandedAlphaFraction: collapsedState.sideAlphaFraction,
|
||||||
leftNeighborDistance: leftNeighborDistance,
|
leftNeighborDistance: leftNeighborDistance,
|
||||||
rightNeighborDistance: rightNeighborDistance,
|
rightNeighborDistance: rightNeighborDistance,
|
||||||
@ -967,11 +994,7 @@ public final class StoryPeerListComponent: Component {
|
|||||||
|
|
||||||
var itemAlpha: CGFloat = 1.0
|
var itemAlpha: CGFloat = 1.0
|
||||||
var isCollapsable: Bool = false
|
var isCollapsable: Bool = false
|
||||||
var itemScale = measuredItem.itemScale
|
let itemScale = measuredItem.itemScale
|
||||||
if itemLayout.itemCount == 1 {
|
|
||||||
let singleScaleFactor = min(1.0, collapsedState.minFraction + collapsedState.maxFraction)
|
|
||||||
itemScale = 0.001 * (1.0 - singleScaleFactor) + itemScale * singleScaleFactor
|
|
||||||
}
|
|
||||||
|
|
||||||
if i >= collapseStartIndex && i <= collapseEndIndex {
|
if i >= collapseStartIndex && i <= collapseEndIndex {
|
||||||
isCollapsable = true
|
isCollapsable = true
|
||||||
@ -983,21 +1006,9 @@ public final class StoryPeerListComponent: Component {
|
|||||||
rightItemFrame = calculateItem(collapseIndex + 1).itemFrame
|
rightItemFrame = calculateItem(collapseIndex + 1).itemFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
if effectiveFirstVisibleIndex == 0 {
|
itemAlpha = (1.0 - collapsedState.sideAlphaFraction) * (1.0 - collapsedState.activityFraction)
|
||||||
itemAlpha = 0.0
|
|
||||||
} else {
|
|
||||||
itemAlpha = 1.0 - collapsedState.sideAlphaFraction
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if itemLayout.itemCount == 1 {
|
itemAlpha = collapsedState.sideAlphaFraction
|
||||||
itemAlpha = min(1.0, (collapsedState.minFraction + collapsedState.maxFraction) * 4.0)
|
|
||||||
} else {
|
|
||||||
itemAlpha = collapsedState.sideAlphaFraction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if component.titleHasActivity {
|
|
||||||
itemAlpha = 0.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var leftNeighborDistance: CGPoint?
|
var leftNeighborDistance: CGPoint?
|
||||||
@ -1021,9 +1032,8 @@ public final class StoryPeerListComponent: Component {
|
|||||||
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
|
||||||
hasItems: hasItems,
|
hasItems: hasItems,
|
||||||
ringAnimation: itemRingAnimation,
|
ringAnimation: itemRingAnimation,
|
||||||
collapseFraction: 1.0 - collapsedState.maxFraction,
|
|
||||||
scale: itemScale,
|
scale: itemScale,
|
||||||
collapsedWidth: collapsedItemWidth,
|
fullWidth: expandedItemWidth,
|
||||||
expandedAlphaFraction: collapsedState.sideAlphaFraction,
|
expandedAlphaFraction: collapsedState.sideAlphaFraction,
|
||||||
leftNeighborDistance: leftNeighborDistance,
|
leftNeighborDistance: leftNeighborDistance,
|
||||||
rightNeighborDistance: rightNeighborDistance,
|
rightNeighborDistance: rightNeighborDistance,
|
||||||
@ -1093,46 +1103,110 @@ public final class StoryPeerListComponent: Component {
|
|||||||
|
|
||||||
let defaultCollapsedTitleOffset: CGFloat = 0.0
|
let defaultCollapsedTitleOffset: CGFloat = 0.0
|
||||||
|
|
||||||
var targetCollapsedTitleOffset: CGFloat = collapsedContentOrigin + collapsedContentOriginOffset + collapsedContentWidth + titleContentSpacing
|
let targetCollapsedTitleOffset: CGFloat = collapsedContentOrigin + collapsedContentOriginOffset + collapsedContentWidth + titleContentSpacing
|
||||||
if itemLayout.itemCount == 1 && collapsedContentWidth <= 0.1 {
|
|
||||||
let singleScaleFactor = min(1.0, collapsedState.minFraction)
|
|
||||||
targetCollapsedTitleOffset += singleScaleFactor * 4.0
|
|
||||||
}
|
|
||||||
|
|
||||||
let collapsedTitleOffset = targetCollapsedTitleOffset - defaultCollapsedTitleOffset
|
let collapsedTitleOffset = targetCollapsedTitleOffset - defaultCollapsedTitleOffset
|
||||||
|
|
||||||
let titleMinContentOffset: CGFloat = collapsedTitleOffset.interpolate(to: collapsedTitleOffset + 12.0, amount: collapsedState.minFraction)
|
let titleMinContentOffset: CGFloat = collapsedTitleOffset.interpolate(to: collapsedTitleOffset + 12.0, amount: collapsedState.minFraction * (1.0 - collapsedState.activityFraction))
|
||||||
var titleContentOffset: CGFloat = titleMinContentOffset.interpolate(to: floor((itemLayout.containerSize.width - titleContentWidth) * 0.5) as CGFloat, amount: collapsedState.maxFraction)
|
var titleContentOffset: CGFloat = titleMinContentOffset.interpolate(to: floor((itemLayout.containerSize.width - collapsedState.titleWidth) * 0.5) as CGFloat, amount: collapsedState.maxFraction * (1.0 - collapsedState.activityFraction))
|
||||||
|
|
||||||
|
var titleIndicatorSize: CGSize?
|
||||||
|
if collapsedState.activityFraction != 0.0 {
|
||||||
|
let collapsedItemMinX = collapsedContentOrigin - collapsedItemWidth * 0.5
|
||||||
|
let collapsedItemMaxX = collapsedContentOrigin + CGFloat(collapseEndIndex - collapseStartIndex) * collapsedItemDistance * (1.0 - collapsedState.activityFraction) * (1.0 - collapsedState.sideAlphaFraction) + collapsedItemWidth * 0.5
|
||||||
|
let collapsedContentWidth = max(collapsedItemWidth, collapsedItemMaxX - collapsedItemMinX)
|
||||||
|
|
||||||
|
let titleIndicatorView: ComponentView<Empty>
|
||||||
|
if let current = self.titleIndicatorView {
|
||||||
|
titleIndicatorView = current
|
||||||
|
} else {
|
||||||
|
titleIndicatorView = ComponentView()
|
||||||
|
self.titleIndicatorView = titleIndicatorView
|
||||||
|
}
|
||||||
|
let titleIndicatorSizeValue = titleIndicatorView.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(TitleActivityIndicatorComponent(
|
||||||
|
color: component.theme.rootController.navigationBar.accentTextColor
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: collapsedContentWidth - 2.0, height: collapsedItemWidth - 2.0)
|
||||||
|
)
|
||||||
|
titleIndicatorSize = titleIndicatorSizeValue
|
||||||
|
} else if let titleIndicatorView = self.titleIndicatorView {
|
||||||
|
self.titleIndicatorView = nil
|
||||||
|
titleIndicatorView.view?.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
if let titleIndicatorSize, let titleIndicatorView = self.titleIndicatorView?.view {
|
if let titleIndicatorSize, let titleIndicatorView = self.titleIndicatorView?.view {
|
||||||
let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 1.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize)
|
let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset - titleIndicatorSize.width - 9.0, y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize)
|
||||||
if titleIndicatorView.superview == nil {
|
if titleIndicatorView.superview == nil {
|
||||||
self.addSubview(titleIndicatorView)
|
self.addSubview(titleIndicatorView)
|
||||||
}
|
}
|
||||||
titleIndicatorView.frame = titleIndicatorFrame
|
titleIndicatorView.center = titleIndicatorFrame.center
|
||||||
titleContentOffset += titleIndicatorSize.width + titleIndicatorSpacing
|
titleIndicatorView.bounds = CGRect(origin: CGPoint(), size: titleIndicatorFrame.size)
|
||||||
|
|
||||||
|
var indicatorMinScale: CGFloat = collapsedState.sideAlphaFraction * 0.1 + (1.0 - collapsedState.sideAlphaFraction) * 1.0
|
||||||
|
if collapsedItemCount == 0 {
|
||||||
|
indicatorMinScale = 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
let indicatorScale: CGFloat = collapsedState.activityFraction * 1.0 + (1.0 - collapsedState.activityFraction) * indicatorMinScale
|
||||||
|
let indicatorAlpha: CGFloat = collapsedState.activityFraction
|
||||||
|
titleIndicatorView.layer.transform = CATransform3DMakeScale(indicatorScale, indicatorScale, 1.0)
|
||||||
|
titleIndicatorView.alpha = indicatorAlpha
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 1.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize)
|
||||||
if let titleComponentView = self.titleView.view {
|
if let image = self.titleView.image {
|
||||||
if titleComponentView.superview == nil {
|
self.titleView.center = CGPoint(x: titleFrame.minX, y: titleFrame.midY)
|
||||||
titleComponentView.isUserInteractionEnabled = false
|
self.titleView.bounds = CGRect(origin: CGPoint(), size: image.size)
|
||||||
self.addSubview(titleComponentView)
|
|
||||||
|
let titleFraction: CGFloat
|
||||||
|
if let titleViewAnimation = self.titleViewAnimation {
|
||||||
|
titleFraction = titleViewAnimation.interpolatedFraction(at: timestamp, effectiveFromFraction: titleViewAnimation.fromFraction, toFraction: titleViewAnimation.toFraction)
|
||||||
|
} else {
|
||||||
|
titleFraction = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.titleView.alpha = titleFraction
|
||||||
|
|
||||||
|
let titleScale: CGFloat = titleFraction * 1.0 + (1.0 - titleFraction) * 0.3
|
||||||
|
self.titleView.layer.transform = CATransform3DMakeScale(titleScale, titleScale, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for disappearingTitleView in self.disappearingTitleViews {
|
||||||
|
if let image = disappearingTitleView.imageView.image {
|
||||||
|
disappearingTitleView.imageView.center = CGPoint(x: titleFrame.minX, y: titleFrame.midY)
|
||||||
|
disappearingTitleView.imageView.bounds = CGRect(origin: CGPoint(), size: image.size)
|
||||||
|
|
||||||
|
let titleFraction = disappearingTitleView.interpolatedFraction(at: timestamp, effectiveFromFraction: disappearingTitleView.fromFraction, toFraction: disappearingTitleView.toFraction)
|
||||||
|
|
||||||
|
disappearingTitleView.imageView.alpha = titleFraction
|
||||||
|
|
||||||
|
let titleScale: CGFloat = titleFraction * 1.0 + (1.0 - titleFraction) * 0.3
|
||||||
|
disappearingTitleView.imageView.layer.transform = CATransform3DMakeScale(titleScale, titleScale, 1.0)
|
||||||
}
|
}
|
||||||
titleComponentView.frame = titleFrame
|
|
||||||
}
|
}
|
||||||
titleContentOffset += titleSize.width
|
|
||||||
|
|
||||||
if let titleIconSize, let titleIconView = self.titleIconView?.view {
|
if let titleIconSize, let titleIconView = self.titleIconView?.view {
|
||||||
titleContentOffset += titleIconSpacing
|
titleContentOffset += titleIconSpacing
|
||||||
|
|
||||||
let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 1.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize)
|
let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset + titleIconSpacing + (collapsedState.titleWidth - (titleIconSpacing + titleIconSize.width)) * (1.0 - collapsedState.activityFraction), y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize)
|
||||||
|
|
||||||
if titleIconView.superview == nil {
|
if titleIconView.superview == nil {
|
||||||
self.addSubview(titleIconView)
|
self.addSubview(titleIconView)
|
||||||
}
|
}
|
||||||
titleIconView.frame = titleIconFrame
|
titleIconView.center = titleIconFrame.center
|
||||||
|
titleIconView.bounds = CGRect(origin: CGPoint(), size: titleIconFrame.size)
|
||||||
|
|
||||||
|
let titleIconFraction = 1.0 - collapsedState.activityFraction
|
||||||
|
let titleIconAlpha: CGFloat = titleIconFraction
|
||||||
|
let titleIconScale: CGFloat = titleIconFraction * 1.0 + (1.0 - titleIconFraction) * 0.1
|
||||||
|
titleIconView.alpha = titleIconAlpha
|
||||||
|
titleIconView.layer.transform = CATransform3DMakeScale(titleIconScale, titleIconScale, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
titleContentOffset += collapsedState.titleWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
@ -1179,7 +1253,10 @@ public final class StoryPeerListComponent: Component {
|
|||||||
useAnimation = true
|
useAnimation = true
|
||||||
} else if let animationHint, animationHint.allowAvatarsExpansionUpdated {
|
} else if let animationHint, animationHint.allowAvatarsExpansionUpdated {
|
||||||
useAnimation = true
|
useAnimation = true
|
||||||
|
} else if let previousComponent = self.component, (component.title != previousComponent.title || component.titleHasActivity != previousComponent.titleHasActivity) {
|
||||||
|
useAnimation = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let animationHint, animationHint.disableAnimations {
|
if let animationHint, animationHint.disableAnimations {
|
||||||
useAnimation = false
|
useAnimation = false
|
||||||
self.animationState = nil
|
self.animationState = nil
|
||||||
@ -1195,7 +1272,48 @@ public final class StoryPeerListComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
duration = 0.25
|
duration = 0.25
|
||||||
}
|
}
|
||||||
self.animationState = AnimationState(duration: duration * UIView.animationDurationFactor(), fromIsUnlocked: previousComponent.unlocked, fromFraction: self.currentFraction, startTime: timestamp, bounce: animationHint?.bounce ?? true)
|
|
||||||
|
if useAnimation, let previousComponent = self.component, component.title != previousComponent.title, self.titleView.image != nil {
|
||||||
|
var fromFraction: CGFloat = 1.0
|
||||||
|
if let titleViewAnimation = self.titleViewAnimation {
|
||||||
|
fromFraction = titleViewAnimation.interpolatedFraction(
|
||||||
|
at: timestamp,
|
||||||
|
effectiveFromFraction: titleViewAnimation.fromFraction,
|
||||||
|
toFraction: titleViewAnimation.toFraction
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let previousImage = self.titleView.image {
|
||||||
|
let previousImageView = UIImageView(image: previousImage)
|
||||||
|
previousImageView.layer.anchorPoint = self.titleView.layer.anchorPoint
|
||||||
|
self.disappearingTitleViews.append(TitleAnimationState(
|
||||||
|
duration: duration,
|
||||||
|
startTime: timestamp,
|
||||||
|
fromFraction: fromFraction,
|
||||||
|
toFraction: 0.0,
|
||||||
|
imageView: previousImageView
|
||||||
|
))
|
||||||
|
self.insertSubview(previousImageView, belowSubview: self.titleView)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.titleViewAnimation = TitleAnimationState(
|
||||||
|
duration: duration * UIView.animationDurationFactor(),
|
||||||
|
startTime: timestamp,
|
||||||
|
fromFraction: 0.0,
|
||||||
|
toFraction: 1.0,
|
||||||
|
imageView: self.titleView
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.animationState = AnimationState(
|
||||||
|
duration: duration * UIView.animationDurationFactor(),
|
||||||
|
fromIsUnlocked: previousComponent.unlocked,
|
||||||
|
fromFraction: self.currentFraction,
|
||||||
|
fromTitleWidth: self.currentTitleWidth,
|
||||||
|
fromActivityFraction: self.currentActivityFraction,
|
||||||
|
startTime: timestamp,
|
||||||
|
bounce: animationHint?.bounce ?? true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let animationState = self.animationState {
|
if let animationState = self.animationState {
|
||||||
@ -1204,7 +1322,20 @@ public final class StoryPeerListComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = self.animationState {
|
if let titleViewAnimation = self.titleViewAnimation {
|
||||||
|
if titleViewAnimation.isFinished(at: timestamp) {
|
||||||
|
self.titleViewAnimation = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in (0 ..< self.disappearingTitleViews.count).reversed() {
|
||||||
|
if self.disappearingTitleViews[i].isFinished(at: timestamp) {
|
||||||
|
self.disappearingTitleViews[i].imageView.removeFromSuperview()
|
||||||
|
self.disappearingTitleViews.remove(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.animationState != nil || self.titleViewAnimation != nil || !self.disappearingTitleViews.isEmpty {
|
||||||
if self.animator == nil {
|
if self.animator == nil {
|
||||||
let animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
let animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -1223,6 +1354,27 @@ public final class StoryPeerListComponent: Component {
|
|||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
let updatedTitleState = TitleState(text: component.title)
|
||||||
|
if self.titleState != updatedTitleState {
|
||||||
|
self.titleState = updatedTitleState
|
||||||
|
|
||||||
|
let attributedText = NSAttributedString(string: updatedTitleState.text, attributes: [
|
||||||
|
NSAttributedString.Key.font: Font.semibold(17.0),
|
||||||
|
NSAttributedString.Key.foregroundColor: component.theme.rootController.navigationBar.primaryTextColor
|
||||||
|
])
|
||||||
|
var boundingRect = attributedText.boundingRect(with: CGSize(width: max(0.0, component.maxTitleX - component.minTitleX - 30.0), height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
||||||
|
boundingRect.size.width = ceil(boundingRect.size.width)
|
||||||
|
boundingRect.size.height = ceil(boundingRect.size.height)
|
||||||
|
|
||||||
|
let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: CGPoint(), size: boundingRect.size))
|
||||||
|
let image = renderer.image { context in
|
||||||
|
UIGraphicsPushContext(context.cgContext)
|
||||||
|
attributedText.draw(at: CGPoint())
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
}
|
||||||
|
self.titleView.image = image
|
||||||
|
}
|
||||||
|
|
||||||
if let storySubscriptions = component.storySubscriptions, let hasMoreToken = storySubscriptions.hasMoreToken {
|
if let storySubscriptions = component.storySubscriptions, let hasMoreToken = storySubscriptions.hasMoreToken {
|
||||||
if self.requestedLoadMoreToken != hasMoreToken {
|
if self.requestedLoadMoreToken != hasMoreToken {
|
||||||
self.requestedLoadMoreToken = hasMoreToken
|
self.requestedLoadMoreToken = hasMoreToken
|
||||||
|
|||||||
@ -269,9 +269,8 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
public let hasUnseenCloseFriendsItems: Bool
|
public let hasUnseenCloseFriendsItems: Bool
|
||||||
public let hasItems: Bool
|
public let hasItems: Bool
|
||||||
public let ringAnimation: RingAnimation?
|
public let ringAnimation: RingAnimation?
|
||||||
public let collapseFraction: CGFloat
|
|
||||||
public let scale: CGFloat
|
public let scale: CGFloat
|
||||||
public let collapsedWidth: CGFloat
|
public let fullWidth: CGFloat
|
||||||
public let expandedAlphaFraction: CGFloat
|
public let expandedAlphaFraction: CGFloat
|
||||||
public let leftNeighborDistance: CGPoint?
|
public let leftNeighborDistance: CGPoint?
|
||||||
public let rightNeighborDistance: CGPoint?
|
public let rightNeighborDistance: CGPoint?
|
||||||
@ -287,9 +286,8 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
hasUnseenCloseFriendsItems: Bool,
|
hasUnseenCloseFriendsItems: Bool,
|
||||||
hasItems: Bool,
|
hasItems: Bool,
|
||||||
ringAnimation: RingAnimation?,
|
ringAnimation: RingAnimation?,
|
||||||
collapseFraction: CGFloat,
|
|
||||||
scale: CGFloat,
|
scale: CGFloat,
|
||||||
collapsedWidth: CGFloat,
|
fullWidth: CGFloat,
|
||||||
expandedAlphaFraction: CGFloat,
|
expandedAlphaFraction: CGFloat,
|
||||||
leftNeighborDistance: CGPoint?,
|
leftNeighborDistance: CGPoint?,
|
||||||
rightNeighborDistance: CGPoint?,
|
rightNeighborDistance: CGPoint?,
|
||||||
@ -304,9 +302,8 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
|
||||||
self.hasItems = hasItems
|
self.hasItems = hasItems
|
||||||
self.ringAnimation = ringAnimation
|
self.ringAnimation = ringAnimation
|
||||||
self.collapseFraction = collapseFraction
|
|
||||||
self.scale = scale
|
self.scale = scale
|
||||||
self.collapsedWidth = collapsedWidth
|
self.fullWidth = fullWidth
|
||||||
self.expandedAlphaFraction = expandedAlphaFraction
|
self.expandedAlphaFraction = expandedAlphaFraction
|
||||||
self.leftNeighborDistance = leftNeighborDistance
|
self.leftNeighborDistance = leftNeighborDistance
|
||||||
self.rightNeighborDistance = rightNeighborDistance
|
self.rightNeighborDistance = rightNeighborDistance
|
||||||
@ -339,13 +336,10 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
if lhs.ringAnimation != rhs.ringAnimation {
|
if lhs.ringAnimation != rhs.ringAnimation {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.collapseFraction != rhs.collapseFraction {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.scale != rhs.scale {
|
if lhs.scale != rhs.scale {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.collapsedWidth != rhs.collapsedWidth {
|
if lhs.fullWidth != rhs.fullWidth {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.expandedAlphaFraction != rhs.expandedAlphaFraction {
|
if lhs.expandedAlphaFraction != rhs.expandedAlphaFraction {
|
||||||
@ -526,7 +520,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
self.component = component
|
self.component = component
|
||||||
self.componentState = state
|
self.componentState = state
|
||||||
|
|
||||||
let effectiveWidth: CGFloat = (1.0 - component.collapseFraction) * availableSize.width + component.collapseFraction * component.collapsedWidth
|
let effectiveWidth: CGFloat = component.scale * component.fullWidth
|
||||||
|
|
||||||
let effectiveScale: CGFloat = component.scale
|
let effectiveScale: CGFloat = component.scale
|
||||||
|
|
||||||
@ -571,7 +565,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
baseLineWidth = 1.0 + UIScreenPixel
|
baseLineWidth = 1.0 + UIScreenPixel
|
||||||
}
|
}
|
||||||
let indicatorLineWidth: CGFloat = baseLineWidth * (1.0 - component.collapseFraction) + minimizedLineWidth * component.collapseFraction
|
let indicatorLineWidth: CGFloat = baseLineWidth * component.scale + minimizedLineWidth * (1.0 - component.scale)
|
||||||
|
|
||||||
avatarNode.setPeer(
|
avatarNode.setPeer(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
@ -643,7 +637,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
|
|
||||||
let baseRadius: CGFloat = 30.0
|
let baseRadius: CGFloat = 30.0
|
||||||
let collapsedRadius: CGFloat = 32.0
|
let collapsedRadius: CGFloat = 32.0
|
||||||
let indicatorRadius: CGFloat = baseRadius * (1.0 - component.collapseFraction) + collapsedRadius * component.collapseFraction
|
let indicatorRadius: CGFloat = baseRadius * component.scale + collapsedRadius * (1.0 - component.scale)
|
||||||
|
|
||||||
self.indicatorShapeLayer.lineWidth = indicatorLineWidth
|
self.indicatorShapeLayer.lineWidth = indicatorLineWidth
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import ActivityIndicator
|
import HierarchyTrackingLayer
|
||||||
|
|
||||||
public final class TitleActivityIndicatorComponent: Component {
|
public final class TitleActivityIndicatorComponent: Component {
|
||||||
let color: UIColor
|
let color: UIColor
|
||||||
@ -21,10 +21,38 @@ public final class TitleActivityIndicatorComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class View: UIView {
|
public final class View: UIView {
|
||||||
private var activityIndicator: ActivityIndicator?
|
private let shapeLayer: SimpleShapeLayer
|
||||||
|
private let hierarchyTrackingLayer: HierarchyTrackingLayer
|
||||||
|
|
||||||
|
private var component: TitleActivityIndicatorComponent?
|
||||||
|
private var animator: ConstantDisplayLinkAnimator?
|
||||||
|
private var animationPhase: CGFloat = 0.0
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
|
self.shapeLayer = SimpleShapeLayer()
|
||||||
|
self.hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||||
|
self.hierarchyTrackingLayer.didExitHierarchy = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.refreshAnimation()
|
||||||
|
}
|
||||||
|
self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.refreshAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.shapeLayer)
|
||||||
|
|
||||||
|
self.shapeLayer.lineCap = .round
|
||||||
|
self.shapeLayer.lineWidth = 1.5
|
||||||
|
self.shapeLayer.fillColor = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init?(coder: NSCoder) {
|
required public init?(coder: NSCoder) {
|
||||||
@ -34,18 +62,154 @@ public final class TitleActivityIndicatorComponent: Component {
|
|||||||
deinit {
|
deinit {
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(component: TitleActivityIndicatorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
private func refreshAnimation() {
|
||||||
let activityIndicator: ActivityIndicator
|
if self.hierarchyTrackingLayer.isInHierarchy {
|
||||||
if let current = self.activityIndicator {
|
if self.animator == nil {
|
||||||
activityIndicator = current
|
let animationStartTime = CACurrentMediaTime()
|
||||||
|
self.animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let duration: Double = 0.5
|
||||||
|
self.animationPhase = (CACurrentMediaTime() - animationStartTime).truncatingRemainder(dividingBy: duration) / duration
|
||||||
|
self.updateAnimation()
|
||||||
|
})
|
||||||
|
self.animator?.isPaused = false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
activityIndicator = ActivityIndicator(type: .custom(component.color, availableSize.width, 2.0, true))
|
if let animator = self.animator {
|
||||||
self.activityIndicator = activityIndicator
|
self.animator = nil
|
||||||
self.addSubview(activityIndicator.view)
|
animator.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateAnimation() {
|
||||||
|
let size = self.shapeLayer.bounds
|
||||||
|
let path = CGMutablePath()
|
||||||
|
|
||||||
|
let radius = size.height * 0.5
|
||||||
|
|
||||||
|
enum Segment {
|
||||||
|
case line(start: CGPoint, end: CGPoint)
|
||||||
|
case halfCircle(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
|
||||||
|
|
||||||
|
func length(radius: CGFloat) -> CGFloat {
|
||||||
|
switch self {
|
||||||
|
case let .line(start, end):
|
||||||
|
return abs(start.x - end.x)
|
||||||
|
case let .halfCircle(_, radius, startAngle, endAngle):
|
||||||
|
return (endAngle - startAngle) * radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPath(into path: CGMutablePath, fromFraction: CGFloat, toFraction: CGFloat) {
|
||||||
|
switch self {
|
||||||
|
case let .line(start, end):
|
||||||
|
if fromFraction != 0.0 {
|
||||||
|
path.move(to: CGPoint(
|
||||||
|
x: start.x * (1.0 - fromFraction) + end.x * fromFraction,
|
||||||
|
y: start.y * (1.0 - fromFraction) + end.y * fromFraction
|
||||||
|
))
|
||||||
|
}
|
||||||
|
path.addLine(to: CGPoint(
|
||||||
|
x: start.x * (1.0 - toFraction) + end.x * toFraction,
|
||||||
|
y: start.y * (1.0 - toFraction) + end.y * toFraction
|
||||||
|
))
|
||||||
|
case let .halfCircle(center, radius, startAngle, endAngle):
|
||||||
|
path.addArc(center: center, radius: radius, startAngle: startAngle + fromFraction * (endAngle - startAngle), endAngle: startAngle + toFraction * (endAngle - startAngle), clockwise: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activityIndicator.frame = CGRect(origin: CGPoint(), size: availableSize)
|
let segments: [Segment] = [
|
||||||
activityIndicator.isHidden = false
|
.halfCircle(center: CGPoint(x: size.width - radius, y: radius), radius: radius, startAngle: -CGFloat.pi * 0.5, endAngle: CGFloat.pi * 0.5),
|
||||||
|
.line(start: CGPoint(x: size.width - radius, y: size.height), end: CGPoint(x: radius, y: size.height)),
|
||||||
|
.halfCircle(center: CGPoint(x: radius, y: radius), radius: radius, startAngle: CGFloat.pi * 0.5, endAngle: CGFloat.pi * 1.5),
|
||||||
|
.line(start: CGPoint(x: radius, y: 0.0), end: CGPoint(x: size.width - radius, y: 0.0)),
|
||||||
|
]
|
||||||
|
|
||||||
|
var totalLength: CGFloat = 0.0
|
||||||
|
for segment in segments {
|
||||||
|
totalLength += segment.length(radius: radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
let startOffset: CGFloat = self.animationPhase
|
||||||
|
let endOffset: CGFloat = startOffset + 0.8
|
||||||
|
|
||||||
|
var startLength = startOffset * totalLength
|
||||||
|
|
||||||
|
var startSegment: (Int, CGFloat)?
|
||||||
|
while startSegment == nil {
|
||||||
|
for i in 0 ..< segments.count {
|
||||||
|
let segment = segments[i]
|
||||||
|
let segmentLength = segment.length(radius: radius)
|
||||||
|
if segmentLength <= startLength {
|
||||||
|
startLength -= segmentLength
|
||||||
|
} else {
|
||||||
|
let subOffset = startLength
|
||||||
|
startSegment = (i, subOffset)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isFirst = true
|
||||||
|
var pathLength = (endOffset - startOffset) * totalLength
|
||||||
|
|
||||||
|
if let (startIndex, startOffset) = startSegment {
|
||||||
|
var index = startIndex
|
||||||
|
while pathLength > 0.0 {
|
||||||
|
let segment = segments[index]
|
||||||
|
let segmentOffset: CGFloat = isFirst ? startOffset : 0.0
|
||||||
|
let segmentLength = segment.length(radius: radius)
|
||||||
|
|
||||||
|
let segmentSubLength = segmentLength - segmentOffset
|
||||||
|
if segmentSubLength > 0.0 {
|
||||||
|
//remainingLength <= segmentRemainingLength -> take remainingLength
|
||||||
|
//remainingLength > segmentRemainingLength -> take segmentRemainingLength
|
||||||
|
|
||||||
|
let pathPart: CGFloat
|
||||||
|
if pathLength <= segmentSubLength {
|
||||||
|
pathPart = pathLength
|
||||||
|
} else {
|
||||||
|
pathPart = segmentSubLength
|
||||||
|
}
|
||||||
|
pathLength -= pathPart
|
||||||
|
|
||||||
|
segment.addPath(into: path, fromFraction: segmentOffset / segmentLength, toFraction: (segmentOffset + pathPart) / segmentLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
index = (index + 1) % segments.count
|
||||||
|
isFirst = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*for segment in segments {
|
||||||
|
segment.addPath(into: path, fromFraction: 0.0, toFraction: 1.0)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if let currentPath = self.shapeLayer.path {
|
||||||
|
if currentPath != path {
|
||||||
|
self.shapeLayer.path = path
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.shapeLayer.path = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: TitleActivityIndicatorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
let isFirstTime = self.component == nil
|
||||||
|
self.component = component
|
||||||
|
let _ = isFirstTime
|
||||||
|
|
||||||
|
transition.setFrame(layer: self.shapeLayer, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
transition.setShapeLayerPath(layer: self.shapeLayer, path: UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: availableSize), cornerRadius: availableSize.height * 0.5).cgPath)
|
||||||
|
|
||||||
|
self.shapeLayer.strokeColor = component.color.cgColor
|
||||||
|
|
||||||
|
self.refreshAnimation()
|
||||||
|
self.updateAnimation()
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4712,17 +4712,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatePeerStoryNotifications: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
|
let updatePeerStoriesMuted: (PeerId, PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> = {
|
||||||
var isMuted: Bool?
|
peerId, mute in
|
||||||
switch storyNotifications {
|
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue
|
||||||
case .default:
|
}
|
||||||
isMuted = nil
|
|
||||||
case .show:
|
let updatePeerStoriesHideSender: (PeerId, PeerStoryNotificationSettings.HideSender) -> Signal<Void, NoError> = {
|
||||||
isMuted = false
|
peerId, hideSender in
|
||||||
case .hide:
|
return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue
|
||||||
isMuted = true
|
}
|
||||||
}
|
|
||||||
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
|
let updatePeerStorySound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||||
|
return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
let mode: NotificationExceptionMode
|
let mode: NotificationExceptionMode
|
||||||
@ -4775,11 +4776,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
|> deliverOnMainQueue).start(next: { _ in
|
|> deliverOnMainQueue).start(next: { _ in
|
||||||
|
|
||||||
})
|
})
|
||||||
}, updatePeerStoryNotifications: { peerId, storyNotifications in
|
}, updatePeerStoriesMuted: { peerId, mute in
|
||||||
let _ = (updatePeerStoryNotifications(peerId, storyNotifications)
|
let _ = (updatePeerStoriesMuted(peerId, mute)
|
||||||
|> deliverOnMainQueue).start(next: { _ in
|
|> deliverOnMainQueue).start()
|
||||||
|
}, updatePeerStoriesHideSender: { peerId, hideSender in
|
||||||
})
|
let _ = (updatePeerStoriesHideSender(peerId, hideSender)
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
}, updatePeerStorySound: { peerId, sound in
|
||||||
|
let _ = (updatePeerStorySound(peer.id, sound)
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
}, removePeerFromExceptions: {
|
}, removePeerFromExceptions: {
|
||||||
}, modifiedPeer: {
|
}, modifiedPeer: {
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user