Various fixes

This commit is contained in:
Ilya Laktyushin 2023-03-14 23:25:44 +04:00
parent 85a51978f0
commit 5c906c198d
14 changed files with 353 additions and 75 deletions

View File

@ -9071,3 +9071,5 @@ Sorry for the inconvenience.";
"DataUsage.SettingsHelpWifi" = "You can change your auto-download settings for media to reduce data usage when on wifi."; "DataUsage.SettingsHelpWifi" = "You can change your auto-download settings for media to reduce data usage when on wifi.";
"DataUsage.Reset" = "Reset Statistics"; "DataUsage.Reset" = "Reset Statistics";
"Conversation.SendWhenOnlineTooltip" = "Long tap to send the message later.";

View File

@ -320,7 +320,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
let bold = MarkdownAttributeSet(font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) let bold = MarkdownAttributeSet(font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
if let _ = resetPendingDate { if let _ = resetPendingDate {
self.actionDisposable.set( self.actionDisposable.set(
resetLoginEmail(account: self.account, phoneNumber: number, phoneCodeHash: phoneCodeHash).start(error: { [weak self] error in (resetLoginEmail(account: self.account, phoneNumber: number, phoneCodeHash: phoneCodeHash)
|> deliverOnMainQueue).start(error: { [weak self] error in
if let self, case .alreadyInProgress = error { if let self, case .alreadyInProgress = error {
let formattedNumber = formatPhoneNumber(number) let formattedNumber = formatPhoneNumber(number)
let title = NSAttributedString(string: self.presentationData.strings.Login_Email_PremiumRequiredTitle, font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize), textColor: self.presentationData.theme.actionSheet.primaryTextColor) let title = NSAttributedString(string: self.presentationData.strings.Login_Email_PremiumRequiredTitle, font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize), textColor: self.presentationData.theme.actionSheet.primaryTextColor)

View File

@ -103,9 +103,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
return context.engine.data.get( return context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.IsContact(id: peer.id), TelegramEngine.EngineData.Item.Peer.IsContact(id: peer.id),
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id),
TelegramEngine.EngineData.Item.NotificationSettings.Global(),
TelegramEngine.EngineData.Item.Messages.PeerReadCounters(id: peer.id) TelegramEngine.EngineData.Item.Messages.PeerReadCounters(id: peer.id)
) )
|> map { [weak chatListController] isContact, notificationSettings, readCounters -> [ContextMenuItem] in |> map { [weak chatListController] isContact, notificationSettings, globalNotificationSettings, readCounters -> [ContextMenuItem] in
if promoInfo != nil { if promoInfo != nil {
return [] return []
} }
@ -164,8 +165,21 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
} }
var isMuted = false var isMuted = false
if case .muted = notificationSettings.muteState { if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
isMuted = true isMuted = true
} else if case .default = notificationSettings.muteState {
if case .user = peer {
isMuted = !globalNotificationSettings.privateChats.enabled
} else if case .legacyGroup = peer {
isMuted = !globalNotificationSettings.groupChats.enabled
} else if case let .channel(channel) = peer {
switch channel.info {
case .group:
isMuted = !globalNotificationSettings.groupChats.enabled
case .broadcast:
isMuted = !globalNotificationSettings.channels.enabled
}
}
} }
var isUnread = false var isUnread = false
@ -406,8 +420,21 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
if !isSavedMessages { if !isSavedMessages {
var isMuted = false var isMuted = false
if case .muted = notificationSettings.muteState { if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
isMuted = true isMuted = true
} else if case .default = notificationSettings.muteState {
if case .user = peer {
isMuted = !globalNotificationSettings.privateChats.enabled
} else if case .legacyGroup = peer {
isMuted = !globalNotificationSettings.groupChats.enabled
} else if case let .channel(channel) = peer {
switch channel.info {
case .group:
isMuted = !globalNotificationSettings.groupChats.enabled
case .broadcast:
isMuted = !globalNotificationSettings.channels.enabled
}
}
} }
items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil) let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil)
@ -506,9 +533,10 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
return context.engine.data.get( return context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId),
TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId) TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
) )
|> mapToSignal { peer, peerNotificationSettings, threadData -> Signal<[ContextMenuItem], NoError> in |> mapToSignal { peer, peerNotificationSettings, threadData, globalNotificationSettings -> Signal<[ContextMenuItem], NoError> in
guard case let .channel(channel) = peer else { guard case let .channel(channel) = peer else {
return .single([]) return .single([])
} }
@ -560,10 +588,16 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
case .unmuted: case .unmuted:
isMuted = false isMuted = false
case .default: case .default:
if case .muted = peerNotificationSettings.muteState { var peerIsMuted = false
isMuted = true if case let .muted(until) = peerNotificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
peerIsMuted = true
} else if case .default = peerNotificationSettings.muteState {
if case let .channel(channel) = peer, case .group = channel.info {
peerIsMuted = !globalNotificationSettings.groupChats.enabled
} }
} }
isMuted = peerIsMuted
}
items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] c, f in items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] c, f in
if isMuted { if isMuted {
let _ = (context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: 0) let _ = (context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: 0)

View File

@ -38,13 +38,13 @@ private enum ChatListRecentEntryStableId: Hashable {
private enum ChatListRecentEntry: Comparable, Identifiable { private enum ChatListRecentEntry: Comparable, Identifiable {
case topPeers([EnginePeer], PresentationTheme, PresentationStrings) case topPeers([EnginePeer], PresentationTheme, PresentationStrings)
case peer(index: Int, peer: RecentlySearchedPeer, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder) case peer(index: Int, peer: RecentlySearchedPeer, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, EngineGlobalNotificationSettings)
var stableId: ChatListRecentEntryStableId { var stableId: ChatListRecentEntryStableId {
switch self { switch self {
case .topPeers: case .topPeers:
return .topPeers return .topPeers
case let .peer(_, peer, _, _, _, _, _): case let .peer(_, peer, _, _, _, _, _, _):
return .peerId(peer.peer.peerId) return .peerId(peer.peer.peerId)
} }
} }
@ -66,8 +66,8 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder): case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsGlobalNotificationsSettings):
if case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings && lhsTimeFormat == rhsTimeFormat && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder { if case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsGlobalNotificationsSettings) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings && lhsTimeFormat == rhsTimeFormat && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsGlobalNotificationsSettings == rhsGlobalNotificationsSettings {
return true return true
} else { } else {
return false return false
@ -79,11 +79,11 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
switch lhs { switch lhs {
case .topPeers: case .topPeers:
return true return true
case let .peer(lhsIndex, _, _, _, _, _, _): case let .peer(lhsIndex, _, _, _, _, _, _, _):
switch rhs { switch rhs {
case .topPeers: case .topPeers:
return false return false
case let .peer(rhsIndex, _, _, _, _, _, _): case let .peer(rhsIndex, _, _, _, _, _, _, _):
return lhsIndex <= rhsIndex return lhsIndex <= rhsIndex
} }
} }
@ -101,7 +101,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
gesture?.cancel() gesture?.cancel()
} }
}) })
case let .peer(_, peer, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder): case let .peer(_, peer, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder, globalNotificationSettings):
let primaryPeer: EnginePeer let primaryPeer: EnginePeer
var chatPeer: EnginePeer? var chatPeer: EnginePeer?
let maybeChatPeer = EnginePeer(peer.peer.peers[peer.peer.peerId]!) let maybeChatPeer = EnginePeer(peer.peer.peers[peer.peer.peerId]!)
@ -188,8 +188,24 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
var isMuted = false var isMuted = false
if let notificationSettings = peer.notificationSettings { if let notificationSettings = peer.notificationSettings {
isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
isMuted = true
} else if case .default = notificationSettings.muteState {
if case .user = primaryPeer {
isMuted = !globalNotificationSettings.privateChats.enabled
} else if case .legacyGroup = primaryPeer {
isMuted = !globalNotificationSettings.groupChats.enabled
} else if case let .channel(channel) = primaryPeer {
switch channel.info {
case .group:
isMuted = !globalNotificationSettings.groupChats.enabled
case .broadcast:
isMuted = !globalNotificationSettings.channels.enabled
} }
}
}
}
var badge: ContactsPeerItemBadge? var badge: ContactsPeerItemBadge?
if peer.unreadCount > 0 { if peer.unreadCount > 0 {
badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active) badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active)
@ -1374,7 +1390,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
context.engine.contacts.searchLocalPeers(query: query.lowercased()), context.engine.contacts.searchLocalPeers(query: query.lowercased()),
fixedOrRemovedRecentlySearchedPeers fixedOrRemovedRecentlySearchedPeers
) )
|> mapToSignal { local, allRecentlySearched -> Signal<([EnginePeer.Id: Optional<EnginePeer.NotificationSettings>], [EnginePeer.Id: Int], [EngineRenderedPeer], Set<EnginePeer.Id>), NoError> in |> mapToSignal { local, allRecentlySearched -> Signal<([EnginePeer.Id: Optional<EnginePeer.NotificationSettings>], [EnginePeer.Id: Int], [EngineRenderedPeer], Set<EnginePeer.Id>, EngineGlobalNotificationSettings), NoError> in
let recentlySearched = allRecentlySearched.filter { peer in let recentlySearched = allRecentlySearched.filter { peer in
guard let peer = peer.peer.peer else { guard let peer = peer.peer.peer else {
return false return false
@ -1408,25 +1424,37 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
peerIds.map { peerId -> TelegramEngine.EngineData.Item.Messages.PeerUnreadCount in peerIds.map { peerId -> TelegramEngine.EngineData.Item.Messages.PeerUnreadCount in
return TelegramEngine.EngineData.Item.Messages.PeerUnreadCount(id: peerId) return TelegramEngine.EngineData.Item.Messages.PeerUnreadCount(id: peerId)
} }
),
TelegramEngine.EngineData.Item.NotificationSettings.Global()
) )
) |> map { notificationSettings, unreadCounts, globalNotificationSettings in
|> map { notificationSettings, unreadCounts in return (notificationSettings, unreadCounts, peers, Set(recentlySearched.map(\.peer.peerId)), globalNotificationSettings)
return (notificationSettings, unreadCounts, peers, Set(recentlySearched.map(\.peer.peerId)))
} }
} }
|> map { notificationSettings, unreadCounts, peers, recentlySearchedPeerIds -> (peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set<EnginePeer.Id>) in |> map { notificationSettings, unreadCounts, peers, recentlySearchedPeerIds, globalNotificationSettings -> (peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set<EnginePeer.Id>) in
var unread: [EnginePeer.Id: (Int32, Bool)] = [:] var unread: [EnginePeer.Id: (Int32, Bool)] = [:]
for peer in peers { for peer in peers {
var isMuted: Bool = false var isMuted = false
if let nofiticationSettings = notificationSettings[peer.peerId] { if let peerNotificationSettings = notificationSettings[peer.peerId], let peerNotificationSettings {
switch nofiticationSettings?.muteState { if case let .muted(until) = peerNotificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
case .muted:
isMuted = true isMuted = true
default: } else if case .default = peerNotificationSettings.muteState {
break if let peer = peer.peer {
if case .user = peer {
isMuted = !globalNotificationSettings.privateChats.enabled
} else if case .legacyGroup = peer {
isMuted = !globalNotificationSettings.groupChats.enabled
} else if case let .channel(channel) = peer {
switch channel.info {
case .group:
isMuted = !globalNotificationSettings.groupChats.enabled
case .broadcast:
isMuted = !globalNotificationSettings.channels.enabled
}
}
}
} }
} }
let unreadCount = unreadCounts[peer.peerId] let unreadCount = unreadCounts[peer.peerId]
if let unreadCount = unreadCount, unreadCount > 0 { if let unreadCount = unreadCount, unreadCount > 0 {
unread[peer.peerId] = (Int32(unreadCount), isMuted) unread[peer.peerId] = (Int32(unreadCount), isMuted)
@ -2399,8 +2427,13 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} }
|> distinctUntilChanged |> distinctUntilChanged
var recentItems = combineLatest(hasRecentPeers, fixedRecentlySearchedPeers, presentationDataPromise.get()) var recentItems = combineLatest(
|> mapToSignal { hasRecentPeers, peers, presentationData -> Signal<[ChatListRecentEntry], NoError> in hasRecentPeers,
fixedRecentlySearchedPeers,
presentationDataPromise.get(),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global())
)
|> mapToSignal { hasRecentPeers, peers, presentationData, globalNotificationSettings -> Signal<[ChatListRecentEntry], NoError> in
var entries: [ChatListRecentEntry] = [] var entries: [ChatListRecentEntry] = []
if !peersFilter.contains(.onlyGroups) { if !peersFilter.contains(.onlyGroups) {
if hasRecentPeers { if hasRecentPeers {
@ -2419,7 +2452,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} }
peerIds.insert(peer.id) peerIds.insert(peer.id)
entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder)) entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalNotificationSettings))
index += 1 index += 1
} }
} }

View File

@ -32,8 +32,11 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL
for groupId in additionalGroupIds { for groupId in additionalGroupIds {
unreadCountItems.append(.totalInGroup(groupId)) unreadCountItems.append(.totalInGroup(groupId))
} }
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.globalNotifications]))
let unreadKey: PostboxViewKey = .unreadCounts(items: unreadCountItems) let unreadKey: PostboxViewKey = .unreadCounts(items: unreadCountItems)
var keys: [PostboxViewKey] = [] var keys: [PostboxViewKey] = []
keys.append(globalNotificationsKey)
keys.append(unreadKey) keys.append(unreadKey)
for peerId in additionalPeerIds { for peerId in additionalPeerIds {
keys.append(.basicPeer(peerId)) keys.append(.basicPeer(peerId))
@ -45,6 +48,13 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL
return (0, []) return (0, [])
} }
var globalNotificationSettings: GlobalNotificationSettingsSet
if let settingsView = view.views[globalNotificationsKey] as? PreferencesView, let settings = settingsView.values[PreferencesKeys.globalNotifications]?.get(GlobalNotificationSettings.self) {
globalNotificationSettings = settings.effective
} else {
globalNotificationSettings = GlobalNotificationSettings.defaultSettings.effective
}
var result: [(ChatListFilter, Int, Bool)] = [] var result: [(ChatListFilter, Int, Bool)] = []
var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int, Bool, PeerGroupId?, Bool)] = [:] var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int, Bool, PeerGroupId?, Bool)] = [:]
@ -66,7 +76,28 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL
peerCount = max(1, peerCount) peerCount = max(1, peerCount)
} }
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, case .muted = notificationSettings.muteState { var isMuted = false
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
if case .muted = notificationSettings.muteState {
isMuted = true
} else if case .default = notificationSettings.muteState {
if let peer = peerView.peer {
if peer is TelegramUser {
isMuted = !globalNotificationSettings.privateChats.enabled
} else if peer is TelegramGroup {
isMuted = !globalNotificationSettings.groupChats.enabled
} else if let channel = peer as? TelegramChannel {
switch channel.info {
case .group:
isMuted = !globalNotificationSettings.groupChats.enabled
case .broadcast:
isMuted = !globalNotificationSettings.channels.enabled
}
}
}
}
}
if isMuted {
peerTagAndCount[peerId] = (tag, peerCount, false, peerView.groupId, true) peerTagAndCount[peerId] = (tag, peerCount, false, peerView.groupId, true)
} else { } else {
peerTagAndCount[peerId] = (tag, peerCount, true, peerView.groupId, false) peerTagAndCount[peerId] = (tag, peerCount, true, peerView.groupId, false)

View File

@ -1328,6 +1328,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
} else if let file = m as? TelegramMediaFile { } else if let file = m as? TelegramMediaFile {
subject = .media(.message(message: MessageReference(messages[0]._asMessage()), media: file)) subject = .media(.message(message: MessageReference(messages[0]._asMessage()), media: file))
if file.isAnimated { if file.isAnimated {
if messages[0].id.peerId.namespace == Namespaces.Peer.SecretChat {
preferredAction = .default
} else {
preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Preview_SaveGif, action: { [weak self] in preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Preview_SaveGif, action: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
let message = messages[0] let message = messages[0]
@ -1360,6 +1363,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
}) })
} }
})) }))
}
} else if file.mimeType.hasPrefix("image/") { } else if file.mimeType.hasPrefix("image/") {
preferredAction = .saveToCameraRoll preferredAction = .saveToCameraRoll
actionCompletionText = strongSelf.presentationData.strings.Gallery_ImageSaved actionCompletionText = strongSelf.presentationData.strings.Gallery_ImageSaved

View File

@ -234,7 +234,7 @@ public final class MediaGroupsScreen: ViewController {
albums.append(collection) albums.append(collection)
} }
} }
state.albums.enumerateObjects { collection, _, _ in state.albums.enumerateObjects(options: [.reverse]) { collection, _, _ in
albums.append(collection) albums.append(collection)
} }
entries.append(.albums(self.presentationData.theme, albums)) entries.append(.albums(self.presentationData.theme, albums))

View File

@ -220,6 +220,40 @@ public extension TelegramEngine {
} }
} }
public func subscribe<
T0: TelegramEngineDataItem,
T1: TelegramEngineDataItem,
T2: TelegramEngineDataItem,
T3: TelegramEngineDataItem
>(
_ t0: T0,
_ t1: T1,
_ t2: T2,
_ t3: T3
) -> Signal<
(
T0.Result,
T1.Result,
T2.Result,
T3.Result
),
NoError> {
return self._subscribe(items: [
t0 as! AnyPostboxViewDataItem,
t1 as! AnyPostboxViewDataItem,
t2 as! AnyPostboxViewDataItem,
t3 as! AnyPostboxViewDataItem
])
|> map { results -> (T0.Result, T1.Result, T2.Result, T3.Result) in
return (
results[0] as! T0.Result,
results[1] as! T1.Result,
results[2] as! T2.Result,
results[3] as! T3.Result
)
}
}
public func get< public func get<
T0: TelegramEngineDataItem, T0: TelegramEngineDataItem,
@ -253,5 +287,26 @@ public extension TelegramEngine {
NoError> { NoError> {
return self.subscribe(t0, t1, t2) |> take(1) return self.subscribe(t0, t1, t2) |> take(1)
} }
public func get<
T0: TelegramEngineDataItem,
T1: TelegramEngineDataItem,
T2: TelegramEngineDataItem,
T3: TelegramEngineDataItem
>(
_ t0: T0,
_ t1: T1,
_ t2: T2,
_ t3: T3
) -> Signal<
(
T0.Result,
T1.Result,
T2.Result,
T3.Result
),
NoError> {
return self.subscribe(t0, t1, t2, t3) |> take(1)
}
} }
} }

View File

@ -169,6 +169,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case dismissedTrendingEmojiPacks = 35 case dismissedTrendingEmojiPacks = 35
case audioRateOptionsTip = 36 case audioRateOptionsTip = 36
case translationSuggestion = 37 case translationSuggestion = 37
case sendWhenOnlineTip = 38
var key: ValueBoxKey { var key: ValueBoxKey {
let v = ValueBoxKey(length: 4) let v = ValueBoxKey(length: 4)
@ -374,6 +375,10 @@ private struct ApplicationSpecificNoticeKeys {
static func audioRateOptionsTip() -> NoticeEntryKey { static func audioRateOptionsTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.audioRateOptionsTip.key) return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.audioRateOptionsTip.key)
} }
static func sendWhenOnlineTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.sendWhenOnlineTip.key)
}
} }
public struct ApplicationSpecificNotice { public struct ApplicationSpecificNotice {
@ -1327,6 +1332,30 @@ public struct ApplicationSpecificNotice {
} }
} }
public static func getSendWhenOnlineTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.sendWhenOnlineTip())?.get(ApplicationSpecificCounterNotice.self) {
return value.value
} else {
return 0
}
}
}
public static func incrementSendWhenOnlineTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.sendWhenOnlineTip())?.get(ApplicationSpecificCounterNotice.self) {
currentValue = value.value
}
currentValue += count
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.sendWhenOnlineTip(), entry)
}
}
}
public static func reset(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Void, NoError> { public static func reset(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in return accountManager.transaction { transaction -> Void in
} }

View File

@ -368,15 +368,12 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var inputActivitiesAllowed = true var inputActivitiesAllowed = true
if let titleContent = self.titleContent { if let titleContent = self.titleContent {
switch titleContent { switch titleContent {
case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount, _): case let .peer(peerView, _, _, isScheduledMessages, _, _, _):
if let peer = peerViewMainPeer(peerView) { if let peer = peerViewMainPeer(peerView) {
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
inputActivitiesAllowed = false inputActivitiesAllowed = false
} }
} }
if customMessageCount != nil {
inputActivitiesAllowed = false
}
case .replyThread: case .replyThread:
inputActivitiesAllowed = true inputActivitiesAllowed = true
default: default:

View File

@ -431,6 +431,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private weak var messageTooltipController: TooltipController? private weak var messageTooltipController: TooltipController?
private weak var videoUnmuteTooltipController: TooltipController? private weak var videoUnmuteTooltipController: TooltipController?
private var didDisplayVideoUnmuteTooltip = false private var didDisplayVideoUnmuteTooltip = false
private var didDisplaySendWhenOnlineTip = false
private weak var silentPostTooltipController: TooltipController? private weak var silentPostTooltipController: TooltipController?
private weak var mediaRecordingModeTooltipController: TooltipController? private weak var mediaRecordingModeTooltipController: TooltipController?
private weak var mediaRestrictedTooltipController: TooltipController? private weak var mediaRestrictedTooltipController: TooltipController?
@ -3875,16 +3876,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: { let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id)) let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id))
|> mapToSignal { message -> Signal<(EngineMessage.Id, Int32?)?, NoError> in |> mapToSignal { message -> Signal<(EngineMessage.Id, Int32?)?, NoError> in
if let message = message, let sourceMessageId = message.forwardInfo?.sourceMessageId { if let message {
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: sourceMessageId.peerId)) return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: message.id.peerId))
|> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in |> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in
return (sourceMessageId, statsDatacenterId) return (message.id, statsDatacenterId)
} }
} else { } else {
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: id.peerId)) return .complete()
|> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in
return (id, statsDatacenterId)
}
} }
} }
|> deliverOnMainQueue).start(next: { [weak self] messageIdAndStatsDatacenterId in |> deliverOnMainQueue).start(next: { [weak self] messageIdAndStatsDatacenterId in
@ -7572,6 +7570,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
Signal<Bool, NoError>.single(false) Signal<Bool, NoError>.single(false)
|> delay(4.0, queue: Queue.mainQueue()) |> delay(4.0, queue: Queue.mainQueue())
)) ))
if !strongSelf.didDisplaySendWhenOnlineTip {
strongSelf.didDisplaySendWhenOnlineTip = true
Queue.mainQueue().after(2.0) {
strongSelf.displaySendWhenOnlineTooltip()
}
}
} else { } else {
strongSelf.typingActivityPromise.set(.single(false)) strongSelf.typingActivityPromise.set(.single(false))
} }
@ -9878,6 +9883,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
sendWhenOnlineAvailable = false sendWhenOnlineAvailable = false
} }
if sendWhenOnlineAvailable {
let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: strongSelf.context.sharedContext.accountManager, count: 4).start()
}
let controller = ChatSendMessageActionSheetController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak self] in let controller = ChatSendMessageActionSheetController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.supportedOrientations = previousSupportedOrientations strongSelf.supportedOrientations = previousSupportedOrientations
@ -17507,6 +17516,59 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
private func displaySendWhenOnlineTooltip() {
guard let rect = self.chatDisplayNode.frameForInputActionButton(), self.effectiveNavigationController?.topViewController === self, let peerId = self.chatLocation.peerId else {
return
}
let inputText = self.presentationInterfaceState.interfaceState.effectiveInputState.inputText.string
guard !inputText.isEmpty else {
return
}
self.sendingOptionsTooltipController?.dismiss()
let _ = (ApplicationSpecificNotice.getSendWhenOnlineTip(accountManager: self.context.sharedContext.accountManager)
|> deliverOnMainQueue).start(next: { [weak self] counter in
if let strongSelf = self, counter < 3 {
let _ = (strongSelf.context.account.viewTracker.peerView(peerId)
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peerView in
guard let strongSelf = self, let peer = peerViewMainPeer(peerView) else {
return
}
var sendWhenOnlineAvailable = false
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if currentTime > until {
sendWhenOnlineAvailable = true
}
}
if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 {
sendWhenOnlineAvailable = false
}
if sendWhenOnlineAvailable {
let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: strongSelf.context.sharedContext.accountManager).start()
let tooltipController = TooltipController(content: .text(strongSelf.presentationData.strings.Conversation_SendWhenOnlineTooltip), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, timeout: 3.0, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true, padding: 2.0)
strongSelf.sendingOptionsTooltipController = tooltipController
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.sendingOptionsTooltipController === tooltipController {
strongSelf.sendingOptionsTooltipController = nil
}
}
strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
if let strongSelf = self {
return (strongSelf.chatDisplayNode, rect)
}
return nil
}))
}
})
}
})
}
private func displaySendingOptionsTooltip() { private func displaySendingOptionsTooltip() {
guard let rect = self.chatDisplayNode.frameForInputActionButton(), self.effectiveNavigationController?.topViewController === self else { guard let rect = self.chatDisplayNode.frameForInputActionButton(), self.effectiveNavigationController?.topViewController === self else {
return return

View File

@ -1514,7 +1514,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
break break
} }
} }
if let file = media as? TelegramMediaFile, !isCopyProtected { if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
} else if let file = media as? TelegramMediaFile, !isCopyProtected {
if file.isVideo { if file.isVideo {
if file.isAnimated && !file.isVideoSticker { if file.isAnimated && !file.isVideoSticker {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveGif, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveGif, icon: { theme in

View File

@ -3540,11 +3540,35 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
var mediaMessage: Message? var mediaMessage: Message?
var forceOpen = false var forceOpen = false
if let item = self.item { if let item = self.item {
if case .group = item.content {
var message: Message? = item.content.firstMessage
loop: for contentNode in self.contentNodes {
if !(contentNode is ChatMessageTextBubbleContentNode) {
continue loop
}
let convertedNodeFrame = contentNode.view.convert(contentNode.bounds, to: self.view).insetBy(dx: 0.0, dy: -10.0)
if !convertedNodeFrame.contains(location) {
continue loop
}
if contentNode is ChatMessageEventLogPreviousMessageContentNode {
} else {
message = contentNode.item?.message
}
}
if let message {
for media in message.media {
if let file = media as? TelegramMediaFile, file.duration != nil {
mediaMessage = message
}
}
}
} else {
for media in item.message.media { for media in item.message.media {
if let file = media as? TelegramMediaFile, file.duration != nil { if let file = media as? TelegramMediaFile, file.duration != nil {
mediaMessage = item.message mediaMessage = item.message
} }
} }
}
if mediaMessage == nil { if mediaMessage == nil {
for attribute in item.message.attributes { for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute { if let attribute = attribute as? ReplyMessageAttribute {

View File

@ -4908,16 +4908,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}))) })))
} }
let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: user, chatPeer: user, cachedData: strongSelf.data?.cachedData) let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: user, chatPeer: chatPeer, cachedData: strongSelf.data?.cachedData)
if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil {
if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor)
}, action: { c, _ in }, action: { c, _ in
self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user)
}))) })))
} }
}
if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) {
if data.isContact { if data.isContact {
@ -5560,8 +5558,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}))) })))
} }
if let _ = clearPeerHistory.canClearForMyself { if let canClearForMyself = clearPeerHistory.canClearForMyself {
let text: String = self.presentationData.strings.Conversation_DeleteMessagesForMe let text: String
switch canClearForMyself {
case .secretChat:
text = self.presentationData.strings.Conversation_DeleteMessagesFor(EnginePeer(chatPeer).compactDisplayTitle).string
default:
text = self.presentationData.strings.Conversation_DeleteMessagesForMe
}
subItems.append(.action(ContextMenuActionItem(text: text, textColor: .destructive, icon: { _ in subItems.append(.action(ContextMenuActionItem(text: text, textColor: .destructive, icon: { _ in
return nil return nil
@ -6012,7 +6016,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
private func openChatWithClearedHistory(type: InteractiveHistoryClearingType) { private func openChatWithClearedHistory(type: InteractiveHistoryClearingType) {
guard let peer = self.data?.peer, let navigationController = self.controller?.navigationController as? NavigationController else { guard let peer = self.data?.chatPeer, let navigationController = self.controller?.navigationController as? NavigationController else {
return return
} }