This commit is contained in:
Ali 2023-06-19 00:36:06 +03:00
parent cf0cfb5c1e
commit e5d948d90a
21 changed files with 394 additions and 32 deletions

View File

@ -9366,3 +9366,8 @@ Sorry for the inconvenience.";
"Paint.Flip" = "Flip";
"Message.ForwardedStoryShort" = "Forwarded Story\nFrom: %@";
"Conversation.StoryForwardTooltip.Chat.One" = "Story forwarded to **%@**";
"Conversation.StoryForwardTooltip.TwoChats.One" = "Story forwarded to to **%@** and **%@**";
"Conversation.StoryForwardTooltip.ManyChats.One" = "Story forwarded to to **%@** and %@ others";
"Conversation.StoryForwardTooltip.SavedMessages.One" = "Story forwarded to to **Saved Messages**";

View File

@ -723,6 +723,19 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
}
let updatePeerStoryNotifications: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
var isMuted: Bool?
switch storyNotifications {
case .default:
isMuted = nil
case .show:
isMuted = false
case .hide:
isMuted = true
}
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
}
let defaultSound: PeerMessageSound
if case .broadcast = channel.info {
@ -756,6 +769,9 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|> deliverOnMainQueue).start(next: { _ in
})
}, updatePeerStoryNotifications: { peerId, storyNotifications in
let _ = (updatePeerStoryNotifications(peerId, storyNotifications)
|> deliverOnMainQueue).start()
}, removePeerFromExceptions: {
}, modifiedPeer: {
})

View File

@ -341,6 +341,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
strongSelf.selectTab(id: targetTab)
} else {
if let componentView = strongSelf.chatListHeaderView(), let storyPeerListView = componentView.storyPeerListView() {
storyPeerListView.scrollToTop()
}
strongSelf.chatListDisplayNode.willScrollToTop()
if let inlineStackContainerNode = strongSelf.chatListDisplayNode.inlineStackContainerNode {
inlineStackContainerNode.currentItemNode.scrollToPosition(.top)
@ -1726,12 +1730,37 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.displayNodeDidLoad()
if case .chatList(.root) = self.location {
self.preloadStorySubscriptionsDisposable = (self.context.engine.messages.preloadStorySubscriptions(includeHidden: false)
|> deliverOnMainQueue).start(next: { [weak self] resources in
let automaticDownloadNetworkType = context.account.networkType
|> map { type -> MediaAutoDownloadNetworkType in
switch type {
case .none, .wifi:
return .wifi
case .cellular:
return .cellular
}
}
|> distinctUntilChanged
self.preloadStorySubscriptionsDisposable = (combineLatest(queue: .mainQueue(),
self.context.engine.messages.preloadStorySubscriptions(includeHidden: false),
self.context.sharedContext.automaticMediaDownloadSettings,
automaticDownloadNetworkType
)
|> deliverOnMainQueue).start(next: { [weak self] resources, automaticMediaDownloadSettings, automaticDownloadNetworkType in
guard let self else {
return
}
var autodownloadEnabled = true
if !shouldDownloadMediaAutomatically(settings: automaticMediaDownloadSettings, peerType: .contact, networkType: automaticDownloadNetworkType, authorPeerId: nil, contactsPeerIds: [], media: nil) {
autodownloadEnabled = false
}
var resources = resources
if !autodownloadEnabled {
resources.removeAll()
}
var validIds: [MediaResourceId] = []
for (_, info) in resources.sorted(by: { $0.value.priority < $1.value.priority }) {
let resource = info.resource

View File

@ -2011,6 +2011,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
targetView.layer.removeAnimation(forKey: "opacity")
}
if strongSelf.hapticFeedback == nil {
strongSelf.hapticFeedback = HapticFeedback()
}
strongSelf.hapticFeedback?.tap()
guard let targetView = targetView as? ReactionIconView else {
return
}
@ -2024,11 +2029,6 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
itemNode.frame = selfTargetBounds
}
if strongSelf.hapticFeedback == nil {
strongSelf.hapticFeedback = HapticFeedback()
}
strongSelf.hapticFeedback?.tap()
if switchToInlineImmediately {
mainAnimationCompleted = true
intermediateCompletion()

View File

@ -62,6 +62,10 @@ private final class NotificationExceptionState : Equatable {
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
}
func withUpdatedPeerStoryNotifications(_ peer: EnginePeer, _ storyNotifications: PeerNotificationDisplayPreviews) -> NotificationExceptionState {
return NotificationExceptionState(mode: mode.withUpdatedPeerStoryNotifications(peer, storyNotifications), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
}
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
return lhs.mode == rhs.mode && lhs.isSearchMode == rhs.isSearchMode && lhs.revealedPeerId == rhs.revealedPeerId && lhs.editing == rhs.editing
}
@ -573,6 +577,19 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
}
let updatePeerStoryNotifications: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
var isMuted: Bool?
switch storyNotifications {
case .default:
isMuted = nil
case .show:
isMuted = false
case .hide:
isMuted = true
}
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
}
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
self.addSubnode(self.listNode)
@ -642,6 +659,16 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
}
updateNotificationsView({})
})
}, updatePeerStoryNotifications: { peerId, displayPreviews in
updateNotificationsDisposable.set(nil)
_ = combineLatest(updatePeerStoryNotifications(peerId, displayPreviews), 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.withUpdatedPeerStoryNotifications(peer, displayPreviews)
}
}
updateNotificationsView({})
})
}, removePeerFromExceptions: {
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])
|> map { _ -> EnginePeer? in }

View File

@ -411,6 +411,10 @@ private final class NotificationExceptionState : Equatable {
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), revealedPeerId: self.revealedPeerId, editing: self.editing)
}
func withUpdatedPeerStoryNotifications(_ peer: EnginePeer, _ storyNotifications: PeerNotificationDisplayPreviews) -> NotificationExceptionState {
return NotificationExceptionState(mode: mode.withUpdatedPeerStoryNotifications(peer, storyNotifications), revealedPeerId: self.revealedPeerId, editing: self.editing)
}
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
return lhs.mode == rhs.mode && lhs.revealedPeerId == rhs.revealedPeerId && lhs.editing == rhs.editing
}
@ -450,6 +454,19 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
}
let updatePeerStoryNotifications: (EnginePeer.Id, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
var isMuted: Bool?
switch storyNotifications {
case .default:
isMuted = nil
case .show:
isMuted = false
case .hide:
isMuted = true
}
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
}
var peerIds: Set<EnginePeer.Id> = Set(mode.peerIds)
let updateNotificationsDisposable = MetaDisposable()
let updateNotificationsView: (@escaping () -> Void) -> Void = { completion in
@ -550,6 +567,16 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
}
updateNotificationsView({})
})
}, updatePeerStoryNotifications: { peerId, storyNotifications in
updateNotificationsDisposable.set(nil)
_ = combineLatest(updatePeerStoryNotifications(peerId, storyNotifications), 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.withUpdatedPeerStoryNotifications(peer, storyNotifications)
}
}
updateNotificationsView({})
})
}, removePeerFromExceptions: {
let _ = (
context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])

View File

@ -766,8 +766,10 @@ public final class ManagedAudioSession: NSObject {
managedAudioSessionLog("ManagedAudioSession setting category for \(type) (native: \(nativeCategory)) activateNow: \(activateNow)")
var options: AVAudioSession.CategoryOptions = []
switch type {
case .play, .ambient:
case .play:
break
case .ambient:
options.insert(.mixWithOthers)
case .playWithPossiblePortOverride:
if case .playAndRecord = nativeCategory {
options.insert(.allowBluetoothA2DP)

View File

@ -213,6 +213,32 @@ func _internal_updatePeerDisplayPreviewsSetting(account: Account, transaction: T
}
}
func _internal_updatePeerStoriesMutedSetting(account: Account, peerId: PeerId, isMuted: Bool?) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in
_internal_updatePeerStoriesMutedSetting(account: account, transaction: transaction, peerId: peerId, isMuted: isMuted)
}
}
func _internal_updatePeerStoriesMutedSetting(account: Account, transaction: Transaction, peerId: PeerId, isMuted: Bool?) {
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
}
let updatedSettings = previousSettings.withUpdatedStoriesMuted(isMuted)
transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: updatedSettings)
}
}
func _internal_updatePeerNotificationSoundInteractive(account: Account, peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Void in
_internal_updatePeerNotificationSoundInteractive(account: account, transaction: transaction, peerId: peerId, threadId: threadId, sound: sound)

View File

@ -268,6 +268,10 @@ public extension TelegramEngine {
public func updatePeerDisplayPreviewsSetting(peerId: PeerId, threadId: Int64?, displayPreviews: PeerNotificationDisplayPreviews) -> Signal<Void, NoError> {
return _internal_updatePeerDisplayPreviewsSetting(account: self.account, peerId: peerId, threadId: threadId, displayPreviews: displayPreviews)
}
public func updatePeerStoriesMutedSetting(peerId: PeerId, isMuted: Bool?) -> Signal<Void, NoError> {
return _internal_updatePeerStoriesMutedSetting(account: self.account, peerId: peerId, isMuted: isMuted)
}
public func updatePeerNotificationSoundInteractive(peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal<Void, NoError> {
return _internal_updatePeerNotificationSoundInteractive(account: self.account, peerId: peerId, threadId: threadId, sound: sound)

View File

@ -25,6 +25,7 @@ public enum MessageContentKindKey {
case restricted
case dice
case invoice
case story
}
public enum MessageContentKind: Equatable {
@ -46,6 +47,7 @@ public enum MessageContentKind: Equatable {
case restricted(String)
case dice(String)
case invoice(String)
case story
public func isSemanticallyEqual(to other: MessageContentKind) -> Bool {
switch self {
@ -157,6 +159,12 @@ public enum MessageContentKind: Equatable {
} else {
return false
}
case .story:
if case .story = other {
return true
} else {
return false
}
}
}
@ -198,6 +206,8 @@ public enum MessageContentKind: Equatable {
return .dice
case .invoice:
return .invoice
case .story:
return .story
}
}
}
@ -332,6 +342,8 @@ public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil
} else {
return .invoice(invoice.title)
}
case .story:
return .story
default:
return nil
}
@ -383,6 +395,9 @@ public func stringForMediaKind(_ kind: MessageContentKind, strings: Presentation
return (NSAttributedString(string: emoji), true)
case let .invoice(text):
return (NSAttributedString(string: text), true)
case .story:
//TODO:localize
return (NSAttributedString(string: "Story"), true)
}
}

View File

@ -496,6 +496,7 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
}
updated(stateValue.with({ $0 }).notificationExceptions)
})
}, updatePeerStoryNotifications: { _, _ in
}, removePeerFromExceptions: {
let _ = context.engine.peers.removeCustomThreadNotificationSettings(peerId: peerId, threadIds: [item.threadId]).start()
updateState { current in

View File

@ -217,6 +217,52 @@ public enum NotificationExceptionMode : Equatable {
}
}
public func withUpdatedPeerStoryNotifications(_ peer: EnginePeer, _ storyNotifications: PeerNotificationDisplayPreviews) -> NotificationExceptionMode {
let apply:([EnginePeer.Id : NotificationExceptionWrapper], EnginePeer.Id, PeerNotificationDisplayPreviews) -> [EnginePeer.Id : NotificationExceptionWrapper] = { values, peerId, storyNotifications in
let storiesMuted: Bool?
switch storyNotifications {
case .default:
storiesMuted = nil
case .show:
storiesMuted = false
case .hide:
storiesMuted = true
}
var values = values
if let value = values[peerId] {
switch storyNotifications {
case .default:
switch value.settings.storiesMuted {
case .none:
values.removeValue(forKey: peerId)
default:
values[peerId] = value.updateSettings({$0.withUpdatedStoriesMuted(storiesMuted)}).withUpdatedDate(Date().timeIntervalSince1970)
}
default:
values[peerId] = value.updateSettings({$0.withUpdatedStoriesMuted(storiesMuted)}).withUpdatedDate(Date().timeIntervalSince1970)
}
} else {
switch storyNotifications {
case .default:
break
default:
values[peerId] = NotificationExceptionWrapper(settings: TelegramPeerNotificationSettings(muteState: .unmuted, messageSound: .default, displayPreviews: .default, storiesMuted: storiesMuted), peer: peer, date: Date().timeIntervalSince1970)
}
}
return values
}
switch self {
case let .groups(values):
return .groups(apply(values, peer.id, storyNotifications))
case let .users(values):
return .users(apply(values, peer.id, storyNotifications))
case let .channels(values):
return .channels(apply(values, peer.id, storyNotifications))
}
}
public var peerIds: [EnginePeer.Id] {
switch self {
case let .users(settings), let .groups(settings), let .channels(settings):
@ -236,6 +282,7 @@ private enum NotificationPeerExceptionSection: Int32 {
case remove
case switcher
case displayPreviews
case storyNotifications
case soundCloud
case soundModern
case soundClassic
@ -253,6 +300,8 @@ private enum NotificationPeerExceptionEntryId: Hashable {
case switcherHeader
case displayPreviews(NotificationPeerExceptionSwitcher)
case displayPreviewsHeader
case storyNotifications(NotificationPeerExceptionSwitcher)
case storyNotificationsHeader
case soundModernHeader
case soundClassicHeader
case none
@ -268,17 +317,19 @@ private final class NotificationPeerExceptionArguments {
let selectSound: (PeerMessageSound) -> Void
let selectMode: (NotificationPeerExceptionSwitcher) -> Void
let selectDisplayPreviews: (NotificationPeerExceptionSwitcher) -> Void
let selectStoryNotifications: (NotificationPeerExceptionSwitcher) -> Void
let removeFromExceptions: () -> Void
let complete: () -> Void
let cancel: () -> Void
let upload: () -> Void
let deleteSound: (PeerMessageSound, String) -> Void
init(account: Account, selectSound: @escaping(PeerMessageSound) -> Void, selectMode: @escaping(NotificationPeerExceptionSwitcher) -> Void, selectDisplayPreviews: @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, selectStoryNotifications: @escaping (NotificationPeerExceptionSwitcher) -> Void, removeFromExceptions: @escaping () -> Void, complete: @escaping()->Void, cancel: @escaping() -> Void, upload: @escaping () -> Void, deleteSound: @escaping (PeerMessageSound, String) -> Void) {
self.account = account
self.selectSound = selectSound
self.selectMode = selectMode
self.selectDisplayPreviews = selectDisplayPreviews
self.selectStoryNotifications = selectStoryNotifications
self.removeFromExceptions = removeFromExceptions
self.complete = complete
self.cancel = cancel
@ -296,6 +347,8 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
case switcherHeader(index:Int32, theme: PresentationTheme, title: String)
case displayPreviews(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
case displayPreviewsHeader(index:Int32, theme: PresentationTheme, title: String)
case storyNotifications(index:Int32, theme: PresentationTheme, strings: PresentationStrings, value: NotificationPeerExceptionSwitcher, selected: Bool)
case storyNotificationsHeader(index:Int32, theme: PresentationTheme, title: String)
case soundModernHeader(index:Int32, theme: PresentationTheme, title: String)
case soundClassicHeader(index:Int32, theme: PresentationTheme, title: String)
case none(index:Int32, section: NotificationPeerExceptionSection, theme: PresentationTheme, text: String, selected: Bool)
@ -317,6 +370,10 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
return index
case let .displayPreviews(index, _, _, _, _):
return index
case let .storyNotificationsHeader(index, _, _):
return index
case let .storyNotifications(index, _, _, _, _):
return index
case let .soundModernHeader(index, _, _):
return index
case let .soundClassicHeader(index, _, _):
@ -344,6 +401,8 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
return NotificationPeerExceptionSection.switcher.rawValue
case .displayPreviews, .displayPreviewsHeader:
return NotificationPeerExceptionSection.displayPreviews.rawValue
case .storyNotifications, .storyNotificationsHeader:
return NotificationPeerExceptionSection.storyNotifications.rawValue
case .cloudInfo, .cloudHeader, .uploadSound:
return NotificationPeerExceptionSection.soundCloud.rawValue
case .soundModernHeader:
@ -371,6 +430,10 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
return .displayPreviews(mode)
case .displayPreviewsHeader:
return .displayPreviewsHeader
case let .storyNotifications(_, _, _, mode, _):
return .storyNotifications(mode)
case .storyNotificationsHeader:
return .storyNotificationsHeader
case .soundModernHeader:
return .soundModernHeader
case .soundClassicHeader:
@ -436,6 +499,19 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
})
case let .displayPreviewsHeader(_, _, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .storyNotifications(_, _, 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.selectStoryNotifications(value)
})
case let .storyNotificationsHeader(_, _, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .soundModernHeader(_, _, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .soundClassicHeader(_, _, text):
@ -459,7 +535,7 @@ private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
}
private func notificationPeerExceptionEntries(presentationData: PresentationData, notificationSoundList: NotificationSoundList?, state: NotificationExceptionPeerState) -> [NotificationPeerExceptionEntry] {
private func notificationPeerExceptionEntries(presentationData: PresentationData, peer: EnginePeer?, notificationSoundList: NotificationSoundList?, state: NotificationExceptionPeerState) -> [NotificationPeerExceptionEntry] {
let selectedSound = resolvedNotificationSound(sound: state.selectedSound, notificationSoundList: notificationSoundList)
var entries: [NotificationPeerExceptionEntry] = []
@ -488,6 +564,16 @@ private func notificationPeerExceptionEntries(presentationData: PresentationData
entries.append(.displayPreviews(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOff, selected: state.displayPreviews == .alwaysOff))
index += 1
if case .user = peer {
//TODO:localize
entries.append(.storyNotificationsHeader(index: index, theme: presentationData.theme, title: "STORY NOTIFICATIONS"))
index += 1
entries.append(.storyNotifications(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOn, selected: state.storyNotifications == .alwaysOn))
index += 1
entries.append(.storyNotifications(index: index, theme: presentationData.theme, strings: presentationData.strings, value: .alwaysOff, selected: state.storyNotifications == .alwaysOff))
index += 1
}
entries.append(.cloudHeader(index: index, text: presentationData.strings.Notifications_TelegramTones))
index += 1
@ -550,6 +636,7 @@ private struct NotificationExceptionPeerState : Equatable {
var mode: NotificationPeerExceptionSwitcher
var defaultSound: PeerMessageSound
var displayPreviews: NotificationPeerExceptionSwitcher
var storyNotifications: NotificationPeerExceptionSwitcher
var removedSounds: [PeerMessageSound]
init(canRemove: Bool, notifications: TelegramPeerNotificationSettings? = nil) {
@ -564,27 +651,30 @@ private struct NotificationExceptionPeerState : Equatable {
self.mode = .alwaysOn
}
self.displayPreviews = notifications.displayPreviews == .hide ? .alwaysOff : .alwaysOn
self.storyNotifications = notifications.storiesMuted == false ? .alwaysOff : .alwaysOn
} else {
self.selectedSound = .default
self.mode = .alwaysOn
self.displayPreviews = .alwaysOn
self.storyNotifications = .alwaysOn
}
self.defaultSound = .default
self.removedSounds = []
}
init(canRemove: Bool, selectedSound: PeerMessageSound, mode: NotificationPeerExceptionSwitcher, defaultSound: PeerMessageSound, displayPreviews: NotificationPeerExceptionSwitcher, removedSounds: [PeerMessageSound]) {
init(canRemove: Bool, selectedSound: PeerMessageSound, mode: NotificationPeerExceptionSwitcher, defaultSound: PeerMessageSound, displayPreviews: NotificationPeerExceptionSwitcher, storyNotifications: NotificationPeerExceptionSwitcher, removedSounds: [PeerMessageSound]) {
self.canRemove = canRemove
self.selectedSound = selectedSound
self.mode = mode
self.defaultSound = defaultSound
self.displayPreviews = displayPreviews
self.storyNotifications = storyNotifications
self.removedSounds = removedSounds
}
}
public func notificationPeerExceptionController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, customTitle: String? = nil, threadId: Int64?, 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, 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?, 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 {
let initialState = NotificationExceptionPeerState(canRemove: false)
let statePromise = Promise(initialState)
let stateValue = Atomic(value: initialState)
@ -625,6 +715,12 @@ public func notificationPeerExceptionController(context: AccountContext, updated
state.displayPreviews = value
return state
}
}, selectStoryNotifications: { value in
updateState { state in
var state = state
state.storyNotifications = value
return state
}
}, removeFromExceptions: {
removeFromExceptionsImpl?()
}, complete: {
@ -686,7 +782,7 @@ public func notificationPeerExceptionController(context: AccountContext, updated
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(titleString), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: notificationPeerExceptionEntries(presentationData: presentationData, notificationSoundList: notificationSoundList, state: state), style: .blocks, animateChanges: animated)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: notificationPeerExceptionEntries(presentationData: presentationData, peer: peer, notificationSoundList: notificationSoundList, state: state), style: .blocks, animateChanges: animated)
return (controllerState, (listState, arguments))
}
@ -709,6 +805,7 @@ public func notificationPeerExceptionController(context: AccountContext, updated
updatePeerSound(peer.id, resolvedNotificationSound(sound: state.selectedSound, notificationSoundList: notificationSoundList))
updatePeerNotificationInterval(peer.id, state.mode == .alwaysOn ? 0 : Int32.max)
updatePeerDisplayPreviews(peer.id, state.displayPreviews == .alwaysOn ? .show : .hide)
updatePeerStoryNotifications(peer.id, state.storyNotifications == .alwaysOn ? .show : .hide)
return state
}
})

View File

@ -373,6 +373,8 @@ private final class StoryContainerScreenComponent: Component {
case .began:
self.verticalPanState = ItemSetPanState(fraction: 0.0, didBegin: true)
self.state?.updated(transition: .immediate)
self.dismissAllTooltips()
case .changed:
let translation = recognizer.translation(in: self)
self.verticalPanState = ItemSetPanState(fraction: max(-1.0, min(1.0, translation.y / self.bounds.height)), didBegin: true)
@ -480,6 +482,18 @@ private final class StoryContainerScreenComponent: Component {
return nil
}
private func dismissAllTooltips() {
guard let controller = self.environment?.controller() else {
return
}
controller.forEachController { controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()
}
return true
}
}
func animateIn() {
if let component = self.component {
component.focusedItemPromise.set(self.focusedItem.get())

View File

@ -229,7 +229,7 @@ public final class StoryItemSetContainerComponent: Component {
let contentContainerView: UIView
let topContentGradientLayer: SimpleGradientLayer
let bottomContentGradientLayer: SimpleGradientLayer
let contentDimLayer: SimpleLayer
let contentDimView: UIView
let closeButton: HighlightableButton
let closeButtonIconView: UIImageView
@ -287,7 +287,9 @@ public final class StoryItemSetContainerComponent: Component {
self.topContentGradientLayer = SimpleGradientLayer()
self.bottomContentGradientLayer = SimpleGradientLayer()
self.contentDimLayer = SimpleLayer()
self.contentDimView = UIView()
self.contentDimView.isUserInteractionEnabled = false
self.closeButton = HighlightableButton()
self.closeButtonIconView = UIImageView()
@ -312,7 +314,7 @@ public final class StoryItemSetContainerComponent: Component {
self.scrollView.clipsToBounds = true
self.addSubview(self.contentContainerView)
self.contentContainerView.layer.addSublayer(self.contentDimLayer)
self.contentContainerView.addSubview(self.contentDimView)
self.contentContainerView.layer.addSublayer(self.topContentGradientLayer)
self.layer.addSublayer(self.bottomContentGradientLayer)
@ -970,7 +972,7 @@ public final class StoryItemSetContainerComponent: Component {
self.bottomContentGradientLayer.colors = colors
self.bottomContentGradientLayer.type = .axial
self.contentDimLayer.backgroundColor = UIColor(white: 0.0, alpha: 0.3).cgColor
self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.3)
}
//self.updatePreloads()
@ -1767,7 +1769,12 @@ public final class StoryItemSetContainerComponent: Component {
let centerInfoItemSize = currentCenterInfoItem.view.update(
transition: .immediate,
component: currentCenterInfoItem.component,
component: AnyComponent(PlainButtonComponent(content: currentCenterInfoItem.component, effectAlignment: .center, action: { [weak self] in
guard let self, let component = self.component else {
return
}
self.navigateToPeer(peer: component.slice.peer)
})),
environment: {},
containerSize: CGSize(width: contentFrame.width, height: 44.0)
)
@ -2125,12 +2132,12 @@ public final class StoryItemSetContainerComponent: Component {
dimAlpha = 0.0
}
transition.setFrame(layer: self.contentDimLayer, frame: CGRect(origin: CGPoint(), size: contentFrame.size))
transition.setFrame(view: self.contentDimView, frame: CGRect(origin: CGPoint(), size: contentFrame.size))
if transition.animation.isImmediate && forceDimAnimation && self.contentDimLayer.opacity != Float(dimAlpha) {
Transition(animation: .curve(duration: 0.25, curve: .easeInOut)).setAlpha(layer: self.contentDimLayer, alpha: dimAlpha)
if transition.animation.isImmediate && forceDimAnimation && self.contentDimView.alpha != dimAlpha {
Transition(animation: .curve(duration: 0.25, curve: .easeInOut)).setAlpha(view: self.contentDimView, alpha: dimAlpha)
} else {
transition.setAlpha(layer: self.contentDimLayer, alpha: dimAlpha)
transition.setAlpha(view: self.contentDimView, alpha: dimAlpha)
}
self.ignoreScrolling = true

View File

@ -315,6 +315,61 @@ final class StoryItemSetContainerSendMessage {
forceTheme: defaultDarkColorPresentationTheme
)
shareController.completed = { [weak view] peerIds in
guard let view, let component = view.component else {
return
}
let _ = (component.context.engine.data.get(
EngineDataList(
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
)
)
|> deliverOnMainQueue).start(next: { [weak view] peerList in
guard let view, let component = view.component else {
return
}
let peers = peerList.compactMap { $0 }
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == component.context.account.peerId {
text = presentationData.strings.Conversation_StoryForwardTooltip_SavedMessages_One
savedMessages = true
} else {
if peers.count == 1, let peer = peers.first {
var peerName = peer.id == component.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
peerName = peerName.replacingOccurrences(of: "**", with: "")
text = presentationData.strings.Conversation_StoryForwardTooltip_Chat_One(peerName).string
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
var firstPeerName = firstPeer.id == component.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "")
var secondPeerName = secondPeer.id == component.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "")
text = presentationData.strings.Conversation_StoryForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
} else if let peer = peers.first {
var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
peerName = peerName.replacingOccurrences(of: "**", with: "")
text = presentationData.strings.Conversation_StoryForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
} else {
text = ""
}
}
if let controller = component.controller() {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(
presentationData: presentationData,
content: .forward(savedMessages: savedMessages, text: text),
elevatedLayout: false,
animateInAsReplacement: false,
action: { _ in return false }
), in: .current)
}
})
}
self.shareController = shareController
view.updateIsProgressPaused()

View File

@ -247,6 +247,10 @@ public final class StoryPeerListComponent: Component {
return nil
}
public func scrollToTop() {
self.scrollView.setContentOffset(CGPoint(), animated: true)
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !self.ignoreScrolling {
self.updateScrolling(transition: .immediate, keepVisibleUntilCompletion: false)

View File

@ -676,8 +676,8 @@ public final class StoryPeerListItemComponent: Component {
if component.peer.id == component.context.account.peerId && !component.hasItems && component.ringAnimation == nil {
let cutoutSize: CGFloat = 18.0 + UIScreenPixel * 2.0
avatarPath.addEllipse(in: CGRect(origin: CGPoint(x: avatarSize.width - cutoutSize + UIScreenPixel, y: avatarSize.height - 1.0 - cutoutSize + UIScreenPixel), size: CGSize(width: cutoutSize, height: cutoutSize)))
} else if let mappedRightCenter {
avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -indicatorLineWidth, dy: -indicatorLineWidth).offsetBy(dx: abs(mappedRightCenter.x - indicatorCenter.x), dy: 0.0))
} else if let mappedLeftCenter {
avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -indicatorLineWidth, dy: -indicatorLineWidth).offsetBy(dx: -abs(indicatorCenter.x - mappedLeftCenter.x), dy: 0.0))
}
Transition.immediate.setShapeLayerPath(layer: self.avatarShapeLayer, path: avatarPath)

View File

@ -700,6 +700,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
var isSticker = false
var maxDimensions = layoutConstants.image.maxDimensions
var maxHeight = layoutConstants.image.maxDimensions.height
var isStory = false
var unboundSize: CGSize
if let _ = media as? TelegramMediaStory {
@ -987,6 +988,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
if let story = media as? TelegramMediaStory {
isStory = true
if hasCurrentVideoNode {
replaceVideoNode = true
}
@ -1058,7 +1061,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
uploading = true
}
if file.isVideo && !file.isVideoSticker && !isSecretMedia && automaticPlayback && !uploading {
if file.isVideo && !file.isVideoSticker && !isSecretMedia && automaticPlayback && !isStory && !uploading {
updateVideoFile = file
if hasCurrentVideoNode {
if let currentFile = currentMedia as? TelegramMediaFile {
@ -1194,7 +1197,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
uploading = true
}
if file.isVideo && !file.isVideoSticker && !isSecretMedia && automaticPlayback && !uploading {
if file.isVideo && !file.isVideoSticker && !isSecretMedia && automaticPlayback && !isStory && !uploading {
updateVideoFile = file
if hasCurrentVideoNode {
if let currentFile = currentMedia as? TelegramMediaFile {

View File

@ -466,6 +466,13 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
)
|> distinctUntilChanged
let storyListContext = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false)
let hasStories: Signal<Bool, NoError> = storyListContext.state
|> map { state -> Bool in
return !state.items.isEmpty
}
|> distinctUntilChanged
return combineLatest(
context.account.viewTracker.peerView(peerId, updateData: true),
accountsAndPeers,
@ -487,9 +494,10 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|> mapToSignal { settings -> Signal<Bool, NoError> in
return automaticEnergyUsageShouldBeOn(settings: settings)
}
|> distinctUntilChanged
|> distinctUntilChanged,
hasStories
)
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled -> PeerInfoScreenData in
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories -> PeerInfoScreenData in
let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications
let (featuredStickerPacks, archivedStickerPacks) = stickerPacks
@ -546,7 +554,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
groupsInCommon: nil,
linkedDiscussionPeer: nil,
members: nil,
storyListContext: nil,
storyListContext: storyListContext,
encryptionKeyFingerprint: nil,
globalSettings: globalSettings,
invitations: nil,

View File

@ -4685,6 +4685,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue
}
let updatePeerStoryNotifications: (PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = { peerId, storyNotifications in
var isMuted: Bool?
switch storyNotifications {
case .default:
isMuted = nil
case .show:
isMuted = false
case .hide:
isMuted = true
}
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, isMuted: isMuted) |> deliverOnMainQueue
}
let mode: NotificationExceptionMode
let defaultSound: PeerMessageSound
if let _ = peer as? TelegramUser {
@ -4734,6 +4747,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let _ = (updatePeerDisplayPreviews(peerId, displayPreviews)
|> deliverOnMainQueue).start(next: { _ in
})
}, updatePeerStoryNotifications: { peerId, storyNotifications in
let _ = (updatePeerStoryNotifications(peerId, storyNotifications)
|> deliverOnMainQueue).start(next: { _ in
})
}, removePeerFromExceptions: {
}, modifiedPeer: {

View File

@ -479,7 +479,11 @@ public func effectiveAutodownloadCategories(settings: MediaAutoDownloadSettings,
}
}
private func categoryAndSizeForMedia(_ media: Media, categories: MediaAutoDownloadCategories) -> (MediaAutoDownloadCategory, Int32?)? {
private func categoryAndSizeForMedia(_ media: Media?, categories: MediaAutoDownloadCategories) -> (MediaAutoDownloadCategory, Int32?)? {
guard let media = media else {
return (categories.photo, 0)
}
if media is TelegramMediaImage || media is TelegramMediaWebFile {
return (categories.photo, 0)
} else if let file = media as? TelegramMediaFile {
@ -520,7 +524,7 @@ public func isAutodownloadEnabledForAnyPeerType(category: MediaAutoDownloadCateg
return category.contacts || category.otherPrivate || category.groups || category.channels
}
public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings, peerType: MediaAutoDownloadPeerType, networkType: MediaAutoDownloadNetworkType, authorPeerId: PeerId? = nil, contactsPeerIds: Set<PeerId> = Set(), media: Media) -> Bool {
public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings, peerType: MediaAutoDownloadPeerType, networkType: MediaAutoDownloadNetworkType, authorPeerId: PeerId? = nil, contactsPeerIds: Set<PeerId> = Set(), media: Media?) -> Bool {
if (networkType == .cellular && !settings.cellular.enabled) || (networkType == .wifi && !settings.wifi.enabled) {
return false
}
@ -542,7 +546,7 @@ public func shouldDownloadMediaAutomatically(settings: MediaAutoDownloadSettings
return false
}
return size <= sizeLimit
} else if media.id?.namespace == Namespaces.Media.LocalFile {
} else if media?.id?.namespace == Namespaces.Media.LocalFile {
return true
} else if category.sizeLimit == Int32.max {
return true