Implemented chat-specific themes

This commit is contained in:
Ilya Laktyushin 2021-08-22 18:30:49 +03:00
parent c7fe52fd72
commit d423f90681
63 changed files with 1649 additions and 234 deletions

View File

@ -6,6 +6,8 @@ load("@build_bazel_rules_apple//apple:ios.bzl",
"ios_application", "ios_application",
"ios_extension", "ios_extension",
"ios_framework", "ios_framework",
"ios_unit_test",
"ios_ui_test",
) )
load("@build_bazel_rules_apple//apple:resources.bzl", load("@build_bazel_rules_apple//apple:resources.bzl",
@ -1853,6 +1855,23 @@ ios_application(
], ],
) )
swift_library(
name = "UITestsLib",
module_name = "UITestsLib",
srcs = glob(["UITests/**/*.swift"]),
deps = [
"//submodules/TelegramUI:TelegramUI",
],
)
ios_ui_test(
name = "UITests",
minimum_os_version = "9.0",
deps = [":UITestsLib"],
infoplists = [":UITestsLib/Info.plist"],
test_host = ":Telegram",
)
# Temporary targets used to simplify webrtc build tests # Temporary targets used to simplify webrtc build tests
ios_application( ios_application(

View File

@ -6622,6 +6622,21 @@ Sorry for the inconvenience.";
"Group.Username.InvalidStartsWithUnderscore" = "Group names can't start with an underscore."; "Group.Username.InvalidStartsWithUnderscore" = "Group names can't start with an underscore.";
"Group.Username.InvalidEndsWithUnderscore" = "Group names can't end with an underscore."; "Group.Username.InvalidEndsWithUnderscore" = "Group names can't end with an underscore.";
"UserInfo.ChangeColors" = "Change Colors";
"Conversation.Theme.Title" = "Select Theme";
"Conversation.Theme.Apply" = "Apply Theme";
"Conversation.Theme.NoTheme" = "No\nTheme";
"Conversation.Theme.Reset" = "Reset Theme for This Chat";
"Conversation.Theme.SwitchToDark" = "Switch to dark appearance";
"Conversation.Theme.SwitchToLight" = "Switch to light appearance";
"Notification.ChangedTheme" = "%1$@ changed chat theme to %2$@";
"Notification.DisabledTheme" = "%@ disabled chat theme";
"Notification.YouChangedTheme" = "You changed chat theme to %@";
"Notification.YouDisabledTheme" = "You disabled chat theme";
"Appstore.Cloud" = "**Cloud-based**\nUnlimited storage for chats,\nmedia and documents."; "Appstore.Cloud" = "**Cloud-based**\nUnlimited storage for chats,\nmedia and documents.";
"Appstore.Cloud.Profile" = "**Jennifer**\n23 y.o. designer from San Francisco."; "Appstore.Cloud.Profile" = "**Jennifer**\n23 y.o. designer from San Francisco.";
"Appstore.Creative" = "**Creative**\nColor themes, stickers, GIFs,\nvideo messages and more."; "Appstore.Creative" = "**Creative**\nColor themes, stickers, GIFs,\nvideo messages and more.";

View File

@ -290,9 +290,10 @@ public final class NavigateToChatControllerParams {
public let options: NavigationAnimationOptions public let options: NavigationAnimationOptions
public let parentGroupId: PeerGroupId? public let parentGroupId: PeerGroupId?
public let chatListFilter: Int32? public let chatListFilter: Int32?
public let changeColors: Bool
public let completion: (ChatController) -> Void public let completion: (ChatController) -> Void
public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, completion: @escaping (ChatController) -> Void = { _ in }) { public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: Int32? = nil, changeColors: Bool = false, completion: @escaping (ChatController) -> Void = { _ in }) {
self.navigationController = navigationController self.navigationController = navigationController
self.chatController = chatController self.chatController = chatController
self.chatLocationContextHolder = chatLocationContextHolder self.chatLocationContextHolder = chatLocationContextHolder
@ -314,6 +315,7 @@ public final class NavigateToChatControllerParams {
self.options = options self.options = options
self.parentGroupId = parentGroupId self.parentGroupId = parentGroupId
self.chatListFilter = chatListFilter self.chatListFilter = chatListFilter
self.changeColors = changeColors
self.completion = completion self.completion = completion
} }
} }
@ -576,6 +578,7 @@ public protocol SharedAccountContext: AnyObject {
var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get } var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get }
var presentGlobalController: (ViewController, Any?) -> Void { get } var presentGlobalController: (ViewController, Any?) -> Void { get }
var presentCrossfadeController: () -> Void { get }
func makeTempAccountContext(account: Account) -> AccountContext func makeTempAccountContext(account: Account) -> AccountContext
@ -587,7 +590,7 @@ public protocol SharedAccountContext: AnyObject {
func openChatMessage(_ params: OpenChatMessageParams) -> Bool func openChatMessage(_ params: OpenChatMessageParams) -> Bool
func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError> func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError>
func makeOverlayAudioPlayerController(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController func makeOverlayAudioPlayerController(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController
func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController? func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController?
func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController? func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController?
func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController
func makePeersNearbyController(context: AccountContext) -> ViewController func makePeersNearbyController(context: AccountContext) -> ViewController

View File

@ -44,6 +44,8 @@ public final class AnimationNode : ASDisplayNode {
} }
} }
view.logHierarchyKeypaths()
return view return view
} else { } else {
return LOTAnimationView() return LOTAnimationView()

View File

@ -208,7 +208,7 @@ public final class CallListController: TelegramBaseController {
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
) )
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self, let peer = peer, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer._asPeer(), mode: .calls(messages: messages.map({ $0._asMessage() })), avatarInitiallyExpanded: false, fromChat: false) { if let strongSelf = self, let peer = peer, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .calls(messages: messages.map({ $0._asMessage() })), avatarInitiallyExpanded: false, fromChat: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller) (strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
} }
}) })

View File

@ -487,7 +487,7 @@ public class ContactsController: ViewController {
} }
if let peer = peer { if let peer = peer {
DispatchQueue.main.async { DispatchQueue.main.async {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} }

View File

@ -115,6 +115,9 @@ public final class ListViewReorderingGestureRecognizer: UIGestureRecognizer {
self.startLongPressTimer() self.startLongPressTimer()
} else { } else {
self.state = .began self.state = .began
if let itemNode = self.itemNode {
self.began(itemNode)
}
} }
} else { } else {
self.state = .failed self.state = .failed

View File

@ -8,7 +8,7 @@ private var backArrowImageCache: [Int32: UIImage] = [:]
public final class SparseNode: ASDisplayNode { public final class SparseNode: ASDisplayNode {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for view in self.view.subviews { for view in self.view.subviews {
if let result = view.hitTest(self.view.convert(point, to: view), with: event) { if let result = view.hitTest(self.view.convert(point, to: view), with: event), result.isUserInteractionEnabled {
return result return result
} }
} }

View File

@ -1221,7 +1221,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId) let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self { if let strongSelf = self {
if let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
strongSelf.getNavigationController()?.pushViewController(controller) strongSelf.getNavigationController()?.pushViewController(controller)
} }
} }

View File

@ -120,7 +120,7 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, media: AnyMed
}) })
} }
public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, canSendPolls: Bool, presentationData: PresentationData, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, ((String) -> UIView?)?, @escaping () -> Void) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, present: @escaping (ViewController, Any?) -> Void) -> TGMenuSheetController { public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocation: ChatLocation, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, canSendPolls: Bool, presentationData: PresentationData, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, ((String) -> UIView?)?, @escaping () -> Void) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void, presentStickers: @escaping (@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?, present: @escaping (ViewController, Any?) -> Void) -> TGMenuSheetController {
let defaultVideoPreset = defaultVideoPresetForContext(context) let defaultVideoPreset = defaultVideoPresetForContext(context)
UserDefaults.standard.set(defaultVideoPreset.rawValue as NSNumber, forKey: "TG_preferredVideoPreset_v0") UserDefaults.standard.set(defaultVideoPreset.rawValue as NSNumber, forKey: "TG_preferredVideoPreset_v0")
@ -318,7 +318,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati
legacyController.enableSizeClassSignal = true legacyController.enableSizeClassSignal = true
let presentationDisposable = context.sharedContext.presentationData.start(next: { [weak legacyController] presentationData in let presentationDisposable = updatedPresentationData.signal.start(next: { [weak legacyController] presentationData in
if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController { if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController {
controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false) controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme, forceDark: false)
} }

View File

@ -329,7 +329,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
} }
}) })

View File

@ -371,7 +371,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
if let navigationController = getNavigationControllerImpl?() { if let navigationController = getNavigationControllerImpl?() {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(participant.peer.id))) context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(participant.peer.id)))
} }
} else if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: participant.peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { } else if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: participant.peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushControllerImpl?(infoController) pushControllerImpl?(infoController)
} }
})) }))

View File

@ -423,7 +423,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
} }
})) }))
}, openPeer: { peer in }, openPeer: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
}, inviteViaLink: { }, inviteViaLink: {
@ -483,7 +483,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
return state.withUpdatedSearchingMembers(false) return state.withUpdatedSearchingMembers(false)
} }
}, openPeer: { peer, _ in }, openPeer: { peer, _ in
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushControllerImpl?(infoController) pushControllerImpl?(infoController)
} }
}, pushController: { c in }, pushController: { c in

View File

@ -698,7 +698,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}) })
}, openPeerInfo: { peer in }, openPeerInfo: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
}, openKicked: { }, openKicked: {

View File

@ -598,7 +598,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
controller?.clearItemNodesHighlight(animated: true) controller?.clearItemNodesHighlight(animated: true)
} }
navigateToProfileImpl = { [weak controller] peer, distance in navigateToProfileImpl = { [weak controller] peer, distance in
if let navigationController = controller?.navigationController as? NavigationController, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .nearbyPeer(distance: distance), avatarInitiallyExpanded: peer.largeProfileImage != nil, fromChat: false) { if let navigationController = controller?.navigationController as? NavigationController, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .nearbyPeer(distance: distance), avatarInitiallyExpanded: peer.largeProfileImage != nil, fromChat: false) {
navigationController.pushViewController(controller) navigationController.pushViewController(controller)
} }
} }

View File

@ -261,7 +261,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
} }
})) }))
}, openPeer: { peer in }, openPeer: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
}) })

View File

@ -319,7 +319,7 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
return transaction.getPeer(peerId) return transaction.getPeer(peerId)
} }
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) else { guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) else {
return return
} }
pushControllerImpl?(controller) pushControllerImpl?(controller)

View File

@ -873,7 +873,7 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
let _ = (context.account.postbox.loadedPeerWithId(peerId) let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
navigationController.pushViewController(controller) navigationController.pushViewController(controller)
} }
}) })

View File

@ -181,6 +181,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[608050278] = { return Api.SendMessageAction.parse_sendMessageUploadRoundAction($0) } dict[608050278] = { return Api.SendMessageAction.parse_sendMessageUploadRoundAction($0) }
dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) } dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) }
dict[-606432698] = { return Api.SendMessageAction.parse_sendMessageHistoryImportAction($0) } dict[-606432698] = { return Api.SendMessageAction.parse_sendMessageHistoryImportAction($0) }
dict[-1336228175] = { return Api.SendMessageAction.parse_sendMessageChooseStickerAction($0) }
dict[-1137792208] = { return Api.PrivacyKey.parse_privacyKeyStatusTimestamp($0) } dict[-1137792208] = { return Api.PrivacyKey.parse_privacyKeyStatusTimestamp($0) }
dict[1343122938] = { return Api.PrivacyKey.parse_privacyKeyChatInvite($0) } dict[1343122938] = { return Api.PrivacyKey.parse_privacyKeyChatInvite($0) }
dict[1030105979] = { return Api.PrivacyKey.parse_privacyKeyPhoneCall($0) } dict[1030105979] = { return Api.PrivacyKey.parse_privacyKeyPhoneCall($0) }

View File

@ -4281,6 +4281,7 @@ public extension Api {
case sendMessageUploadRoundAction(progress: Int32) case sendMessageUploadRoundAction(progress: Int32)
case speakingInGroupCallAction case speakingInGroupCallAction
case sendMessageHistoryImportAction(progress: Int32) case sendMessageHistoryImportAction(progress: Int32)
case sendMessageChooseStickerAction
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -4373,6 +4374,12 @@ public extension Api {
buffer.appendInt32(-606432698) buffer.appendInt32(-606432698)
} }
serializeInt32(progress, buffer: buffer, boxed: false) serializeInt32(progress, buffer: buffer, boxed: false)
break
case .sendMessageChooseStickerAction:
if boxed {
buffer.appendInt32(-1336228175)
}
break break
} }
} }
@ -4409,6 +4416,8 @@ public extension Api {
return ("speakingInGroupCallAction", []) return ("speakingInGroupCallAction", [])
case .sendMessageHistoryImportAction(let progress): case .sendMessageHistoryImportAction(let progress):
return ("sendMessageHistoryImportAction", [("progress", progress)]) return ("sendMessageHistoryImportAction", [("progress", progress)])
case .sendMessageChooseStickerAction:
return ("sendMessageChooseStickerAction", [])
} }
} }
@ -4505,6 +4514,9 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_sendMessageChooseStickerAction(_ reader: BufferReader) -> SendMessageAction? {
return Api.SendMessageAction.sendMessageChooseStickerAction
}
} }
public enum PrivacyKey: TypeConstructorDescription { public enum PrivacyKey: TypeConstructorDescription {

View File

@ -6160,17 +6160,6 @@ public final class VoiceChatController: ViewController {
}), in: .window(.root)) }), in: .window(.root))
} }
private func displayToggleVideoSourceTooltip(screencast: Bool) {
// guard let videoContainerNode = self.mainStageVideoContainerNode else {
// return
// }
//
// let location = videoContainerNode.view.convert(videoContainerNode.otherVideoWrapperNode.frame, to: nil)
// self.controller?.present(TooltipScreen(text: screencast ? self.presentationData.strings.VoiceChat_TapToViewCameraVideo : self.presentationData.strings.VoiceChat_TapToViewScreenVideo, icon: nil, location: .point(location.offsetBy(dx: -9.0, dy: 0.0), .right), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
// return .dismiss(consume: false)
// }), in: .window(.root))
}
private var isScheduled: Bool { private var isScheduled: Bool {
return self.isScheduling || self.callState?.scheduleTimestamp != nil return self.isScheduling || self.callState?.scheduleTimestamp != nil
} }

View File

@ -235,6 +235,8 @@ private var declaredEncodables: Void = {
declareEncodable(WallpapersState.self, f: { WallpapersState(decoder: $0) }) declareEncodable(WallpapersState.self, f: { WallpapersState(decoder: $0) })
declareEncodable(WallpaperDataResource.self, f: { WallpaperDataResource(decoder: $0) }) declareEncodable(WallpaperDataResource.self, f: { WallpaperDataResource(decoder: $0) })
declareEncodable(ForwardHideSendersNamesMessageAttribute.self, f: { ForwardHideSendersNamesMessageAttribute(decoder: $0) }) declareEncodable(ForwardHideSendersNamesMessageAttribute.self, f: { ForwardHideSendersNamesMessageAttribute(decoder: $0) })
declareEncodable(ChatTheme.self, f: { ChatTheme(decoder: $0) })
declareEncodable(ChatThemes.self, f: { ChatThemes(decoder: $0) })
return return
}() }()

View File

@ -77,8 +77,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
case let .inputGroupCall(id, accessHash): case let .inputGroupCall(id, accessHash):
return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, scheduleDate: scheduleDate, duration: nil)) return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, scheduleDate: scheduleDate, duration: nil))
} }
case .messageActionSetChatTheme: case let .messageActionSetChatTheme(emoji):
return nil return TelegramMediaAction(action: .setChatTheme(emoji: emoji))
} }
} }

View File

@ -5,7 +5,7 @@ import TelegramApi
extension TelegramTheme { extension TelegramTheme {
convenience init?(apiTheme: Api.Theme) { convenience init(apiTheme: Api.Theme) {
switch apiTheme { switch apiTheme {
case let .theme(flags, id, accessHash, slug, title, document, settings, installCount): case let .theme(flags, id, accessHash, slug, title, document, settings, installCount):
self.init(id: id, accessHash: accessHash, slug: slug, title: title, file: document.flatMap(telegramMediaFileFromApiDocument), settings: settings.flatMap(TelegramThemeSettings.init(apiThemeSettings:)), isCreator: (flags & 1 << 0) != 0, isDefault: (flags & 1 << 1) != 0, installCount: installCount) self.init(id: id, accessHash: accessHash, slug: slug, title: title, file: document.flatMap(telegramMediaFileFromApiDocument), settings: settings.flatMap(TelegramThemeSettings.init(apiThemeSettings:)), isCreator: (flags & 1 << 0) != 0, isDefault: (flags & 1 << 1) != 0, installCount: installCount)

View File

@ -1412,9 +1412,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
} }
updatedState.deleteMessages(messageIds) updatedState.deleteMessages(messageIds)
case let .updateTheme(theme): case let .updateTheme(theme):
if let theme = TelegramTheme(apiTheme: theme) { updatedState.updateTheme(TelegramTheme(apiTheme: theme))
updatedState.updateTheme(theme)
}
case let .updateMessageID(id, randomId): case let .updateMessageID(id, randomId):
updatedState.updatedOutgoingUniqueMessageIds[randomId] = id updatedState.updatedOutgoingUniqueMessageIds[randomId] = id
case .updateDialogFilters: case .updateDialogFilters:

View File

@ -130,7 +130,7 @@ private func actionFromActivity(_ activity: PeerInputActivity?) -> Api.SendMessa
case .speakingInGroupCall: case .speakingInGroupCall:
return .speakingInGroupCallAction return .speakingInGroupCallAction
case .choosingSticker: case .choosingSticker:
return .sendMessageGamePlayAction return .sendMessageChooseStickerAction
} }
} else { } else {
return .sendMessageCancelAction return .sendMessageCancelAction

View File

@ -66,6 +66,8 @@ extension PeerInputActivity {
self = .uploadingInstantVideo(progress: progress) self = .uploadingInstantVideo(progress: progress)
case .speakingInGroupCallAction: case .speakingInGroupCallAction:
self = .speakingInGroupCall(timestamp: timestamp) self = .speakingInGroupCall(timestamp: timestamp)
case .sendMessageChooseStickerAction:
self = .choosingSticker
case let .sendMessageHistoryImportAction: case let .sendMessageHistoryImportAction:
return nil return nil
} }

View File

@ -224,6 +224,7 @@ public final class CachedChannelData: CachedPeerData {
public let activeCall: ActiveCall? public let activeCall: ActiveCall?
public let callJoinPeerId: PeerId? public let callJoinPeerId: PeerId?
public let pendingSuggestions: [String] public let pendingSuggestions: [String]
public let themeEmoticon: String?
public let peerIds: Set<PeerId> public let peerIds: Set<PeerId>
public let messageIds: Set<MessageId> public let messageIds: Set<MessageId>
@ -257,9 +258,10 @@ public final class CachedChannelData: CachedPeerData {
self.activeCall = nil self.activeCall = nil
self.callJoinPeerId = nil self.callJoinPeerId = nil
self.pendingSuggestions = [] self.pendingSuggestions = []
self.themeEmoticon = nil
} }
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: ActiveCall?, callJoinPeerId: PeerId?, autoremoveTimeout: CachedPeerAutoremoveTimeout, pendingSuggestions: [String]) { public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: ActiveCall?, callJoinPeerId: PeerId?, autoremoveTimeout: CachedPeerAutoremoveTimeout, pendingSuggestions: [String], themeEmoticon: String?) {
self.isNotAccessible = isNotAccessible self.isNotAccessible = isNotAccessible
self.flags = flags self.flags = flags
self.about = about self.about = about
@ -283,6 +285,7 @@ public final class CachedChannelData: CachedPeerData {
self.callJoinPeerId = callJoinPeerId self.callJoinPeerId = callJoinPeerId
self.autoremoveTimeout = autoremoveTimeout self.autoremoveTimeout = autoremoveTimeout
self.pendingSuggestions = pendingSuggestions self.pendingSuggestions = pendingSuggestions
self.themeEmoticon = themeEmoticon
var peerIds = Set<PeerId>() var peerIds = Set<PeerId>()
for botInfo in botInfos { for botInfo in botInfos {
@ -310,95 +313,99 @@ public final class CachedChannelData: CachedPeerData {
} }
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData { public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData { public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedAbout(_ about: String?) -> CachedChannelData { public func withUpdatedAbout(_ about: String?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData { public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData { public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData { public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData { public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData { public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData { public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData { public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData { public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: LinkedDiscussionPeerId) -> CachedChannelData { public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: LinkedDiscussionPeerId) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData { public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData { public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData { public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData { public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData { public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData { public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedChannelData { public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedActiveCall(_ activeCall: ActiveCall?) -> CachedChannelData { public func withUpdatedActiveCall(_ activeCall: ActiveCall?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedChannelData { public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedChannelData { public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: autoremoveTimeout, pendingSuggestions: self.pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: autoremoveTimeout, pendingSuggestions: self.pendingSuggestions, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPendingSuggestions(_ pendingSuggestions: [String]) -> CachedChannelData { public func withUpdatedPendingSuggestions(_ pendingSuggestions: [String]) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions) return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon)
}
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: themeEmoticon)
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
@ -475,6 +482,8 @@ public final class CachedChannelData: CachedPeerData {
self.pendingSuggestions = decoder.decodeStringArrayForKey("sug") self.pendingSuggestions = decoder.decodeStringArrayForKey("sug")
self.themeEmoticon = decoder.decodeOptionalStringForKey("te")
if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage { if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage {
self.photo = photo self.photo = photo
} else { } else {
@ -600,6 +609,12 @@ public final class CachedChannelData: CachedPeerData {
} }
encoder.encodeStringArray(self.pendingSuggestions, forKey: "sug") encoder.encodeStringArray(self.pendingSuggestions, forKey: "sug")
if let themeEmoticon = self.themeEmoticon, !themeEmoticon.isEmpty {
encoder.encodeString(themeEmoticon, forKey: "te")
} else {
encoder.encodeNil(forKey: "te")
}
} }
public func isEqual(to: CachedPeerData) -> Bool { public func isEqual(to: CachedPeerData) -> Bool {
@ -699,6 +714,10 @@ public final class CachedChannelData: CachedPeerData {
return false return false
} }
if other.themeEmoticon != self.themeEmoticon {
return false
}
return true return true
} }
} }

View File

@ -51,14 +51,14 @@ public final class CachedGroupData: CachedPeerData {
public let invitedBy: PeerId? public let invitedBy: PeerId?
public let photo: TelegramMediaImage? public let photo: TelegramMediaImage?
public let autoremoveTimeout: CachedPeerAutoremoveTimeout public let autoremoveTimeout: CachedPeerAutoremoveTimeout
public let activeCall: CachedChannelData.ActiveCall?
public let callJoinPeerId: PeerId?
public let themeEmoticon: String?
public let peerIds: Set<PeerId> public let peerIds: Set<PeerId>
public let messageIds: Set<MessageId> public let messageIds: Set<MessageId>
public let associatedHistoryMessageId: MessageId? = nil public let associatedHistoryMessageId: MessageId? = nil
public let activeCall: CachedChannelData.ActiveCall?
public let callJoinPeerId: PeerId?
public init() { public init() {
self.participants = nil self.participants = nil
self.exportedInvitation = nil self.exportedInvitation = nil
@ -73,12 +73,12 @@ public final class CachedGroupData: CachedPeerData {
self.invitedBy = nil self.invitedBy = nil
self.photo = nil self.photo = nil
self.autoremoveTimeout = .unknown self.autoremoveTimeout = .unknown
self.activeCall = nil self.activeCall = nil
self.callJoinPeerId = nil self.callJoinPeerId = nil
self.themeEmoticon = nil
} }
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout, callJoinPeerId: PeerId?) { public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout, callJoinPeerId: PeerId?, themeEmoticon: String?) {
self.participants = participants self.participants = participants
self.exportedInvitation = exportedInvitation self.exportedInvitation = exportedInvitation
self.botInfos = botInfos self.botInfos = botInfos
@ -92,6 +92,7 @@ public final class CachedGroupData: CachedPeerData {
self.activeCall = activeCall self.activeCall = activeCall
self.autoremoveTimeout = autoremoveTimeout self.autoremoveTimeout = autoremoveTimeout
self.callJoinPeerId = callJoinPeerId self.callJoinPeerId = callJoinPeerId
self.themeEmoticon = themeEmoticon
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
@ -152,6 +153,8 @@ public final class CachedGroupData: CachedPeerData {
self.callJoinPeerId = decoder.decodeOptionalInt64ForKey("callJoinPeerId").flatMap(PeerId.init) self.callJoinPeerId = decoder.decodeOptionalInt64ForKey("callJoinPeerId").flatMap(PeerId.init)
self.themeEmoticon = decoder.decodeOptionalStringForKey("te")
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId) messageIds.insert(pinnedMessageId)
@ -229,6 +232,12 @@ public final class CachedGroupData: CachedPeerData {
} else { } else {
encoder.encodeNil(forKey: "callJoinPeerId") encoder.encodeNil(forKey: "callJoinPeerId")
} }
if let themeEmoticon = self.themeEmoticon, !themeEmoticon.isEmpty {
encoder.encodeString(themeEmoticon, forKey: "te")
} else {
encoder.encodeNil(forKey: "te")
}
} }
public func isEqual(to: CachedPeerData) -> Bool { public func isEqual(to: CachedPeerData) -> Bool {
@ -244,58 +253,62 @@ public final class CachedGroupData: CachedPeerData {
return false return false
} }
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.invitedBy == other.invitedBy return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.invitedBy == other.invitedBy && self.themeEmoticon == other.themeEmoticon
} }
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData { public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData { public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData { public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData { public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData { public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedAbout(_ about: String?) -> CachedGroupData { public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData { public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData { public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData { public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData { public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData { public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedGroupData { public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout, callJoinPeerId: self.callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedGroupData { public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId) return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId, themeEmoticon: self.themeEmoticon)
}
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId, themeEmoticon: themeEmoticon)
} }
} }

View File

@ -62,6 +62,7 @@ public final class CachedUserData: CachedPeerData {
public let canPinMessages: Bool public let canPinMessages: Bool
public let hasScheduledMessages: Bool public let hasScheduledMessages: Bool
public let autoremoveTimeout: CachedPeerAutoremoveTimeout public let autoremoveTimeout: CachedPeerAutoremoveTimeout
public let themeEmoticon: String?
public let peerIds = Set<PeerId>() public let peerIds = Set<PeerId>()
public let messageIds: Set<MessageId> public let messageIds: Set<MessageId>
@ -80,10 +81,11 @@ public final class CachedUserData: CachedPeerData {
self.canPinMessages = false self.canPinMessages = false
self.hasScheduledMessages = false self.hasScheduledMessages = false
self.autoremoveTimeout = .unknown self.autoremoveTimeout = .unknown
self.themeEmoticon = nil
self.messageIds = Set() self.messageIds = Set()
} }
public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout) { public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool, autoremoveTimeout: CachedPeerAutoremoveTimeout, themeEmoticon: String?) {
self.about = about self.about = about
self.botInfo = botInfo self.botInfo = botInfo
self.peerStatusSettings = peerStatusSettings self.peerStatusSettings = peerStatusSettings
@ -96,6 +98,8 @@ public final class CachedUserData: CachedPeerData {
self.canPinMessages = canPinMessages self.canPinMessages = canPinMessages
self.hasScheduledMessages = hasScheduledMessages self.hasScheduledMessages = hasScheduledMessages
self.autoremoveTimeout = autoremoveTimeout self.autoremoveTimeout = autoremoveTimeout
self.themeEmoticon = themeEmoticon
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId) messageIds.insert(pinnedMessageId)
@ -126,6 +130,7 @@ public final class CachedUserData: CachedPeerData {
self.canPinMessages = decoder.decodeInt32ForKey("cpm", orElse: 0) != 0 self.canPinMessages = decoder.decodeInt32ForKey("cpm", orElse: 0) != 0
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false) self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown self.autoremoveTimeout = decoder.decodeObjectForKey("artv", decoder: CachedPeerAutoremoveTimeout.init(decoder:)) as? CachedPeerAutoremoveTimeout ?? .unknown
self.themeEmoticon = decoder.decodeOptionalStringForKey("te")
var messageIds = Set<MessageId>() var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId { if let pinnedMessageId = self.pinnedMessageId {
@ -167,6 +172,11 @@ public final class CachedUserData: CachedPeerData {
encoder.encodeInt32(self.canPinMessages ? 1 : 0, forKey: "cpm") encoder.encodeInt32(self.canPinMessages ? 1 : 0, forKey: "cpm")
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm") encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
encoder.encodeObject(self.autoremoveTimeout, forKey: "artv") encoder.encodeObject(self.autoremoveTimeout, forKey: "artv")
if let themeEmoticon = self.themeEmoticon, !themeEmoticon.isEmpty {
encoder.encodeString(themeEmoticon, forKey: "te")
} else {
encoder.encodeNil(forKey: "te")
}
} }
public func isEqual(to: CachedPeerData) -> Bool { public func isEqual(to: CachedPeerData) -> Bool {
@ -181,54 +191,58 @@ public final class CachedUserData: CachedPeerData {
return false return false
} }
return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.themeEmoticon == other.themeEmoticon
} }
public func withUpdatedAbout(_ about: String?) -> CachedUserData { public func withUpdatedAbout(_ about: String?) -> CachedUserData {
return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData { public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData { public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData { public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData { public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData { public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData { public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData { public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData { public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData { public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData { public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: self.themeEmoticon)
} }
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData { public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout) return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, themeEmoticon: self.themeEmoticon)
}
public func withUpdatedThemeEmoticon(_ themeEmoticon: String?) -> CachedUserData {
return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, themeEmoticon: themeEmoticon)
} }
} }

View File

@ -350,6 +350,7 @@ private enum SharedDataKeyValues: Int32 {
case themeSettings = 6 case themeSettings = 6
case countriesList = 7 case countriesList = 7
case wallapersState = 8 case wallapersState = 8
case chatThemes = 9
} }
public struct SharedDataKeys { public struct SharedDataKeys {
@ -400,6 +401,12 @@ public struct SharedDataKeys {
key.setInt32(0, value: SharedDataKeyValues.wallapersState.rawValue) key.setInt32(0, value: SharedDataKeyValues.wallapersState.rawValue)
return key return key
}() }()
public static let chatThemes: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: SharedDataKeyValues.chatThemes.rawValue)
return key
}()
} }
public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 { public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 {

View File

@ -48,6 +48,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case geoProximityReached(from: PeerId, to: PeerId, distance: Int32) case geoProximityReached(from: PeerId, to: PeerId, distance: Int32)
case groupPhoneCall(callId: Int64, accessHash: Int64, scheduleDate: Int32?, duration: Int32?) case groupPhoneCall(callId: Int64, accessHash: Int64, scheduleDate: Int32?, duration: Int32?)
case inviteToGroupPhoneCall(callId: Int64, accessHash: Int64, peerIds: [PeerId]) case inviteToGroupPhoneCall(callId: Int64, accessHash: Int64, peerIds: [PeerId])
case setChatTheme(emoji: String)
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
@ -110,6 +111,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
peerIds = decoder.decodeInt64ArrayForKey("peerIds").map(PeerId.init) peerIds = decoder.decodeInt64ArrayForKey("peerIds").map(PeerId.init)
} }
self = .inviteToGroupPhoneCall(callId: decoder.decodeInt64ForKey("callId", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), peerIds: peerIds) self = .inviteToGroupPhoneCall(callId: decoder.decodeInt64ForKey("callId", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), peerIds: peerIds)
case 24:
self = .setChatTheme(emoji: decoder.decodeStringForKey("emoji", orElse: ""))
default: default:
self = .unknown self = .unknown
} }
@ -219,6 +222,9 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeInt64(callId, forKey: "callId") encoder.encodeInt64(callId, forKey: "callId")
encoder.encodeInt64(accessHash, forKey: "accessHash") encoder.encodeInt64(accessHash, forKey: "accessHash")
encoder.encodeInt64Array(peerIds.map { $0.toInt64() }, forKey: "peerIds") encoder.encodeInt64Array(peerIds.map { $0.toInt64() }, forKey: "peerIds")
case let .setChatTheme(emoji):
encoder.encodeInt32(24, forKey: "_rawValue")
encoder.encodeString(emoji, forKey: "emoji")
} }
} }

View File

@ -3,7 +3,6 @@ import Postbox
import SwiftSignalKit import SwiftSignalKit
import TelegramApi import TelegramApi
public struct Country: PostboxCoding, Equatable { public struct Country: PostboxCoding, Equatable {
public static func == (lhs: Country, rhs: Country) -> Bool { public static func == (lhs: Country, rhs: Country) -> Bool {
return lhs.id == rhs.id && lhs.name == rhs.name && lhs.localizedName == rhs.localizedName && lhs.countryCodes == rhs.countryCodes && lhs.hidden == rhs.hidden return lhs.id == rhs.id && lhs.name == rhs.name && lhs.localizedName == rhs.localizedName && lhs.countryCodes == rhs.countryCodes && lhs.hidden == rhs.hidden

View File

@ -210,6 +210,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages) return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
.withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedAutoremoveTimeout(autoremoveTimeout)
.withUpdatedThemeEmoticon(userFull.themeEmoticon)
} }
}) })
return true return true
@ -320,6 +321,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
.withUpdatedPhoto(photo) .withUpdatedPhoto(photo)
.withUpdatedActiveCall(updatedActiveCall) .withUpdatedActiveCall(updatedActiveCall)
.withUpdatedCallJoinPeerId(groupCallDefaultJoinAs?.peerId) .withUpdatedCallJoinPeerId(groupCallDefaultJoinAs?.peerId)
.withUpdatedThemeEmoticon(chatFull.themeEmoticon)
}) })
case .channelFull: case .channelFull:
break break
@ -540,6 +542,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
.withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId) .withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId)
.withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedAutoremoveTimeout(autoremoveTimeout)
.withUpdatedPendingSuggestions(pendingSuggestions ?? []) .withUpdatedPendingSuggestions(pendingSuggestions ?? [])
.withUpdatedThemeEmoticon(themeEmoticon)
}) })
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {

View File

@ -39,6 +39,10 @@ public final class TelegramEngine {
public lazy var localization: Localization = { public lazy var localization: Localization = {
return Localization(account: self.account) return Localization(account: self.account)
}() }()
public lazy var themes: Themes = {
return Themes(account: self.account)
}()
public lazy var messages: Messages = { public lazy var messages: Messages = {
return Messages(account: self.account) return Messages(account: self.account)

View File

@ -0,0 +1,153 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
public struct ChatTheme: PostboxCoding, Equatable {
public static func == (lhs: ChatTheme, rhs: ChatTheme) -> Bool {
return lhs.emoji == rhs.emoji && lhs.theme == rhs.theme && lhs.darkTheme == rhs.darkTheme
}
public let emoji: String
public let theme: TelegramTheme
public let darkTheme: TelegramTheme
public init(emoji: String, theme: TelegramTheme, darkTheme: TelegramTheme) {
self.emoji = emoji
self.theme = theme
self.darkTheme = darkTheme
}
public init(decoder: PostboxDecoder) {
self.emoji = decoder.decodeStringForKey("e", orElse: "")
self.theme = decoder.decodeObjectForKey("t") as! TelegramTheme
self.darkTheme = decoder.decodeObjectForKey("dt") as! TelegramTheme
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.emoji, forKey: "e")
encoder.encodeObject(self.theme, forKey: "t")
encoder.encodeObject(self.darkTheme, forKey: "dt")
}
}
public final class ChatThemes: PreferencesEntry, Equatable {
public let chatThemes: [ChatTheme]
public let hash: Int32
public init(chatThemes: [ChatTheme], hash: Int32) {
self.chatThemes = chatThemes
self.hash = hash
}
public init(decoder: PostboxDecoder) {
self.chatThemes = decoder.decodeObjectArrayForKey("c").map { $0 as! ChatTheme }
self.hash = decoder.decodeInt32ForKey("h", orElse: 0)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectArray(self.chatThemes, forKey: "c")
encoder.encodeInt32(self.hash, forKey: "h")
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? ChatThemes {
return self == to
} else {
return false
}
}
public static func ==(lhs: ChatThemes, rhs: ChatThemes) -> Bool {
return lhs.chatThemes == rhs.chatThemes && lhs.hash == rhs.hash
}
}
func _internal_getChatThemes(accountManager: AccountManager<TelegramAccountManagerTypes>, network: Network, forceUpdate: Bool = false, onlyCached: Bool = false) -> Signal<[ChatTheme], NoError> {
let fetch: ([ChatTheme]?, Int32?) -> Signal<[ChatTheme], NoError> = { current, hash in
return network.request(Api.functions.account.getChatThemes(hash: hash ?? 0))
|> retryRequest
|> mapToSignal { result -> Signal<[ChatTheme], NoError> in
switch result {
case let .chatThemes(hash, apiThemes):
let result = apiThemes.compactMap { ChatTheme(apiChatTheme: $0) }
if result == current {
return .complete()
} else {
let _ = accountManager.transaction { transaction in
transaction.updateSharedData(SharedDataKeys.chatThemes, { _ in
return ChatThemes(chatThemes: result, hash: hash)
})
}.start()
return .single(result)
}
case .chatThemesNotModified:
return .complete()
}
}
}
if forceUpdate {
return fetch(nil, nil)
} else {
return accountManager.sharedData(keys: [SharedDataKeys.chatThemes])
|> take(1)
|> map { sharedData -> ([ChatTheme], Int32) in
if let chatThemes = sharedData.entries[SharedDataKeys.chatThemes] as? ChatThemes {
return (chatThemes.chatThemes, chatThemes.hash)
} else {
return ([], 0)
}
}
|> mapToSignal { current, hash -> Signal<[ChatTheme], NoError> in
if onlyCached {
return .single(current)
} else {
return .single(current)
|> then(fetch(current, hash))
}
}
}
}
func _internal_setChatTheme(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, emoji: String?) -> Signal<Void, NoError> {
return postbox.loadedPeerWithId(peerId)
|> mapToSignal { peer in
guard let inputPeer = apiInputPeer(peer) else {
return .complete()
}
return postbox.transaction { transaction -> Signal<Void, NoError> in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedUserData {
return current.withUpdatedThemeEmoticon(emoji)
} else if let current = current as? CachedGroupData {
return current.withUpdatedThemeEmoticon(emoji)
} else if let current = current as? CachedChannelData {
return current.withUpdatedThemeEmoticon(emoji)
} else {
return current
}
})
return network.request(Api.functions.messages.setChatTheme(peer: inputPeer, emoticon: emoji ?? ""))
|> `catch` { error in
return .complete()
}
|> mapToSignal { updates -> Signal<Void, NoError> in
stateManager.addUpdates(updates)
return .complete()
}
} |> switchToLatest
}
}
extension ChatTheme {
init(apiChatTheme: Api.ChatTheme) {
switch apiChatTheme {
case let .chatTheme(emoticon, theme, darkTheme):
self.init(emoji: emoticon, theme: TelegramTheme(apiTheme: theme), darkTheme: TelegramTheme(apiTheme: darkTheme))
}
}
}

View File

@ -0,0 +1,20 @@
import SwiftSignalKit
import Postbox
public extension TelegramEngine {
final class Themes {
private let account: Account
init(account: Account) {
self.account = account
}
public func getChatThemes(accountManager: AccountManager<TelegramAccountManagerTypes>, forceUpdate: Bool = false, onlyCached: Bool = false) -> Signal<[ChatTheme], NoError> {
return _internal_getChatThemes(accountManager: accountManager, network: self.account.network, forceUpdate: forceUpdate, onlyCached: onlyCached)
}
public func setChatTheme(peerId: PeerId, emoji: String?) -> Signal<Void, NoError> {
return _internal_setChatTheme(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, emoji: emoji)
}
}
}

View File

@ -3,7 +3,6 @@ import Postbox
import SwiftSignalKit import SwiftSignalKit
import TelegramApi import TelegramApi
#if os(macOS) #if os(macOS)
let telegramThemeFormat = "macos" let telegramThemeFormat = "macos"
let telegramThemeFileExtension = "palette" let telegramThemeFileExtension = "palette"
@ -101,11 +100,7 @@ public func getTheme(account: Account, slug: String) -> Signal<TelegramTheme, Ge
return .generic return .generic
} }
|> mapToSignal { theme -> Signal<TelegramTheme, GetThemeError> in |> mapToSignal { theme -> Signal<TelegramTheme, GetThemeError> in
if let theme = TelegramTheme(apiTheme: theme) { return .single(TelegramTheme(apiTheme: theme))
return .single(theme)
} else {
return .fail(.generic)
}
} }
} }
@ -122,11 +117,7 @@ private func checkThemeUpdated(network: Network, theme: TelegramTheme) -> Signal
return network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), documentId: documentId)) return network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), documentId: documentId))
|> mapError { _ -> GetThemeError in return .generic } |> mapError { _ -> GetThemeError in return .generic }
|> map { theme -> ThemeUpdatedResult in |> map { theme -> ThemeUpdatedResult in
if let theme = TelegramTheme(apiTheme: theme) { return .updated(TelegramTheme(apiTheme: theme))
return .updated(theme)
} else {
return .notModified
}
} }
} }
@ -310,24 +301,21 @@ public func createTheme(account: Account, title: String, resource: MediaResource
return .generic return .generic
} }
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in |> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
if let theme = TelegramTheme(apiTheme: apiTheme) { let theme = TelegramTheme(apiTheme: apiTheme)
return account.postbox.transaction { transaction -> CreateThemeResult in return account.postbox.transaction { transaction -> CreateThemeResult in
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
var items = entries.map { $0.contents as! TelegramTheme } var items = entries.map { $0.contents as! TelegramTheme }
items.insert(theme, at: 0) items.insert(theme, at: 0)
var updatedEntries: [OrderedItemListEntry] = [] var updatedEntries: [OrderedItemListEntry] = []
for item in items { for item in items {
var intValue = Int32(updatedEntries.count) var intValue = Int32(updatedEntries.count)
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
updatedEntries.append(OrderedItemListEntry(id: id, contents: item)) updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
}
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
return .result(theme)
} }
|> castError(CreateThemeError.self) transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
} else { return .result(theme)
return .fail(.generic)
} }
|> castError(CreateThemeError.self)
} }
} }
else { else {
@ -348,26 +336,23 @@ public func createTheme(account: Account, title: String, resource: MediaResource
return .generic return .generic
} }
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in |> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
if var theme = TelegramTheme(apiTheme: apiTheme) { var theme = TelegramTheme(apiTheme: apiTheme)
theme = TelegramTheme(id: theme.id, accessHash: theme.accessHash, slug: theme.slug, title: theme.title, file: theme.file, settings: settings, isCreator: theme.isCreator, isDefault: theme.isDefault, installCount: theme.installCount) theme = TelegramTheme(id: theme.id, accessHash: theme.accessHash, slug: theme.slug, title: theme.title, file: theme.file, settings: settings, isCreator: theme.isCreator, isDefault: theme.isDefault, installCount: theme.installCount)
return account.postbox.transaction { transaction -> CreateThemeResult in return account.postbox.transaction { transaction -> CreateThemeResult in
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
var items = entries.map { $0.contents as! TelegramTheme } var items = entries.map { $0.contents as! TelegramTheme }
items.insert(theme, at: 0) items.insert(theme, at: 0)
var updatedEntries: [OrderedItemListEntry] = [] var updatedEntries: [OrderedItemListEntry] = []
for item in items { for item in items {
var intValue = Int32(updatedEntries.count) var intValue = Int32(updatedEntries.count)
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
updatedEntries.append(OrderedItemListEntry(id: id, contents: item)) updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
} }
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries) transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
return .result(theme) return .result(theme)
}
|> castError(CreateThemeError.self)
} else {
return .fail(.generic)
} }
|> castError(CreateThemeError.self)
} }
} }
} }
@ -429,41 +414,38 @@ public func updateTheme(account: Account, accountManager: AccountManager<Telegra
return .generic return .generic
} }
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in |> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
if let theme = TelegramTheme(apiTheme: apiTheme) { let theme = TelegramTheme(apiTheme: apiTheme)
let updatedTheme = TelegramTheme(id: theme.id, accessHash: theme.accessHash, slug: theme.slug, title: theme.title, file: theme.file, settings: settings, isCreator: theme.isCreator, isDefault: theme.isDefault, installCount: theme.installCount) let updatedTheme = TelegramTheme(id: theme.id, accessHash: theme.accessHash, slug: theme.slug, title: theme.title, file: theme.file, settings: settings, isCreator: theme.isCreator, isDefault: theme.isDefault, installCount: theme.installCount)
let _ = accountManager.transaction { transaction in let _ = accountManager.transaction { transaction in
transaction.updateSharedData(SharedDataKeys.themeSettings, { current in transaction.updateSharedData(SharedDataKeys.themeSettings, { current in
var updated = current as? ThemeSettings ?? ThemeSettings(currentTheme: nil) var updated = current as? ThemeSettings ?? ThemeSettings(currentTheme: nil)
if updatedTheme.id == updated.currentTheme?.id { if updatedTheme.id == updated.currentTheme?.id {
updated = ThemeSettings(currentTheme: updatedTheme) updated = ThemeSettings(currentTheme: updatedTheme)
}
return updated
})
}.start()
return account.postbox.transaction { transaction -> CreateThemeResult in
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
let items = entries.map { entry -> TelegramTheme in
let theme = entry.contents as! TelegramTheme
if theme.id == updatedTheme.id {
return updatedTheme
} else {
return theme
}
} }
var updatedEntries: [OrderedItemListEntry] = [] return updated
for item in items { })
var intValue = Int32(updatedEntries.count) }.start()
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) return account.postbox.transaction { transaction -> CreateThemeResult in
updatedEntries.append(OrderedItemListEntry(id: id, contents: item)) let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
let items = entries.map { entry -> TelegramTheme in
let theme = entry.contents as! TelegramTheme
if theme.id == updatedTheme.id {
return updatedTheme
} else {
return theme
} }
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
return .result(updatedTheme)
} }
|> castError(CreateThemeError.self) var updatedEntries: [OrderedItemListEntry] = []
} else { for item in items {
return .fail(.generic) var intValue = Int32(updatedEntries.count)
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
}
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
return .result(updatedTheme)
} }
|> castError(CreateThemeError.self)
} }
} }
} }

View File

@ -752,7 +752,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
dimColor: UIColor(white: 0.0, alpha: 0.4), dimColor: UIColor(white: 0.0, alpha: 0.4),
backgroundType: .light, backgroundType: .light,
opaqueItemBackgroundColor: UIColor(rgb: 0xffffff), opaqueItemBackgroundColor: UIColor(rgb: 0xffffff),
itemBackgroundColor: UIColor(white: 1.0, alpha: 0.87), itemBackgroundColor: UIColor(white: 1.0, alpha: 0.8),
opaqueItemHighlightedBackgroundColor: UIColor(white: 0.9, alpha: 1.0), opaqueItemHighlightedBackgroundColor: UIColor(white: 0.9, alpha: 1.0),
itemHighlightedBackgroundColor: UIColor(white: 0.9, alpha: 0.7), itemHighlightedBackgroundColor: UIColor(white: 0.9, alpha: 0.7),
opaqueItemSeparatorColor: UIColor(white: 0.9, alpha: 1.0), opaqueItemSeparatorColor: UIColor(white: 0.9, alpha: 1.0),

View File

@ -108,6 +108,10 @@ public final class PresentationData: Equatable {
return PresentationData(strings: self.strings, theme: theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: self.chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji) return PresentationData(strings: self.strings, theme: theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: self.chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji)
} }
public func withUpdated(chatWallpaper: TelegramWallpaper) -> PresentationData {
return PresentationData(strings: self.strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: self.chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji)
}
public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool { public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool {
return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.autoNightModeTriggered == rhs.autoNightModeTriggered && lhs.chatWallpaper == rhs.chatWallpaper && lhs.chatFontSize == rhs.chatFontSize && lhs.chatBubbleCorners == rhs.chatBubbleCorners && lhs.listsFontSize == rhs.listsFontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.reduceMotion == rhs.reduceMotion && lhs.largeEmoji == rhs.largeEmoji return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.autoNightModeTriggered == rhs.autoNightModeTriggered && lhs.chatWallpaper == rhs.chatWallpaper && lhs.chatFontSize == rhs.chatFontSize && lhs.chatBubbleCorners == rhs.chatBubbleCorners && lhs.listsFontSize == rhs.listsFontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.reduceMotion == rhs.reduceMotion && lhs.largeEmoji == rhs.largeEmoji
} }

View File

@ -528,6 +528,24 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
case let .setChatTheme(emoji):
if emoji.isEmpty {
if message.author?.id == accountPeerId {
attributedString = NSAttributedString(string: strings.Notification_YouDisabledTheme, font: titleFont, textColor: primaryTextColor)
} else {
let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let resultTitleString = strings.Notification_DisabledTheme(authorName)
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
}
} else {
if message.author?.id == accountPeerId {
attributedString = NSAttributedString(string: strings.Notification_YouChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor)
} else {
let attributePeerIds: [(Int, PeerId?)] = [(0, message.author?.id)]
let resultTitleString = strings.Notification_ChangedTheme(authorName, emoji)
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
}
}
case .unknown: case .unknown:
attributedString = nil attributedString = nil
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -243,6 +243,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var presentationInterfaceState: ChatPresentationInterfaceState private var presentationInterfaceState: ChatPresentationInterfaceState
private var presentationInterfaceStatePromise: ValuePromise<ChatPresentationInterfaceState> private var presentationInterfaceStatePromise: ValuePromise<ChatPresentationInterfaceState>
private var cachedDataPromise = Promise<CachedPeerData?>(nil)
private var chatTitleView: ChatTitleView? private var chatTitleView: ChatTitleView?
private var leftNavigationButton: ChatNavigationButton? private var leftNavigationButton: ChatNavigationButton?
private var rightNavigationButton: ChatNavigationButton? private var rightNavigationButton: ChatNavigationButton?
@ -345,8 +347,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private var canReadHistoryValue = false private var canReadHistoryValue = false
private var canReadHistoryDisposable: Disposable? private var canReadHistoryDisposable: Disposable?
private var presentationData: PresentationData private var themeEmoticonPreviewPromise = ValuePromise<String?>(nil)
private var themeDarkAppearancePreviewPromise = ValuePromise<Bool?>(nil)
private var presentationDataPromise = Promise<PresentationData>()
private var presentationData: PresentationData {
didSet {
self.presentationDataPromise.set(.single(self.presentationData))
}
}
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
var updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) {
return (self.presentationData, self.presentationDataPromise.get())
}
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
private var automaticMediaDownloadSettingsDisposable: Disposable? private var automaticMediaDownloadSettingsDisposable: Disposable?
@ -700,6 +712,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}*/ }*/
return true return true
case .setChatTheme:
strongSelf.presentThemeSelection()
return true
default: default:
break break
} }
@ -2835,7 +2850,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.chatTitleView?.pressed = { [weak self] in self.chatTitleView?.pressed = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if strongSelf.chatLocation == .peer(strongSelf.context.account.peerId) { if strongSelf.chatLocation == .peer(strongSelf.context.account.peerId) {
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true) { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} else { } else {
@ -3740,13 +3755,48 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
}) })
self.presentationDataDisposable = (context.sharedContext.presentationData let themeEmoticon: Signal<String?, NoError> = self.cachedDataPromise.get()
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> map { cachedData -> String? in
if let cachedData = cachedData as? CachedUserData {
return cachedData.themeEmoticon
} else if let cachedData = cachedData as? CachedUserData {
return cachedData.themeEmoticon
} else if let cachedData = cachedData as? CachedUserData {
return cachedData.themeEmoticon
} else {
return nil
}
}
|> distinctUntilChanged
self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: true), themeEmoticon, self.themeEmoticonPreviewPromise.get(), self.themeDarkAppearancePreviewPromise.get()).start(next: { [weak self] presentationData, chatThemes, themeEmoticon, themeEmoticonPreview, darkAppearancePreview in
if let strongSelf = self { if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings let previousStrings = strongSelf.presentationData.strings
let previousChatWallpaper = strongSelf.presentationData.chatWallpaper let previousChatWallpaper = strongSelf.presentationData.chatWallpaper
var themeEmoticon = themeEmoticon
if let themeEmoticonPreview = themeEmoticonPreview {
if !themeEmoticonPreview.isEmpty {
themeEmoticon = themeEmoticonPreview
} else {
themeEmoticon = nil
}
}
var isDarkAppearance = presentationData.theme.overallDarkAppearance
if let darkAppearancePreview = darkAppearancePreview {
isDarkAppearance = darkAppearancePreview
}
var presentationData = presentationData
if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoji == themeEmoticon }) {
let customTheme = isDarkAppearance ? theme.darkTheme : theme.theme
if let settings = customTheme.settings, let theme = makePresentationTheme(settings: settings) {
presentationData = presentationData.withUpdated(theme: theme)
presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper)
}
}
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || presentationData.chatWallpaper != previousChatWallpaper { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || presentationData.chatWallpaper != previousChatWallpaper {
@ -4471,6 +4521,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self { if let strongSelf = self {
let (cachedData, messages) = cachedDataAndMessages let (cachedData, messages) = cachedDataAndMessages
strongSelf.cachedDataPromise.set(.single(cachedData))
var pinnedMessageId: MessageId? var pinnedMessageId: MessageId?
var peerIsBlocked: Bool = false var peerIsBlocked: Bool = false
var callsAvailable: Bool = false var callsAvailable: Bool = false
@ -8425,7 +8477,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> deliverOnMainQueue).start(next: { [weak self] peerView in |> deliverOnMainQueue).start(next: { [weak self] peerView in
if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible { if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible {
if peer.id == strongSelf.context.account.peerId { if peer.id == strongSelf.context.account.peerId {
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true) { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: true) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} else { } else {
@ -8436,7 +8488,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let validLayout = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet { if let validLayout = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet {
expandAvatar = false expandAvatar = false
} }
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar, fromChat: true) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar, fromChat: true) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} }
@ -8799,7 +8851,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
slowModeEnabled = true slowModeEnabled = true
} }
let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, canSendPolls: canSendPolls, presentationData: strongSelf.presentationData, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: { let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, canSendPolls: canSendPolls, presentationData: strongSelf.presentationData, updatedPresentationData: strongSelf.updatedPresentationData, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: {
self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in
if !inputText.string.isEmpty { if !inputText.string.isEmpty {
//strongSelf.clearInputText() //strongSelf.clearInputText()
@ -11322,7 +11374,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let validLayout = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet { if let validLayout = strongSelf.validLayout, validLayout.deviceMetrics.type == .tablet {
expandAvatar = false expandAvatar = false
} }
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: mode, avatarInitiallyExpanded: expandAvatar, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: mode, avatarInitiallyExpanded: expandAvatar, fromChat: false) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} }
@ -11775,7 +11827,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} }
@ -12834,6 +12886,73 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.push(controller) self.push(controller)
} }
private func presentCrossfadeSnapshot() {
guard let snapshotView = self.view.snapshotView(afterScreenUpdates: false) else {
return
}
self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
public func presentThemeSelection() {
let context = self.context
let peerId = self.chatLocation.peerId
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
var updated = state
updated = updated.updatedInputMode({ _ in
return .none
})
updated = updated.updatedShowCommands(false)
return updated
})
let _ = (self.cachedDataPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] cachedData in
guard let strongSelf = self else {
return
}
let selectedEmoticon: String?
if let cachedData = cachedData as? CachedUserData {
selectedEmoticon = cachedData.themeEmoticon
} else if let cachedData = cachedData as? CachedGroupData {
selectedEmoticon = cachedData.themeEmoticon
} else if let cachedData = cachedData as? CachedChannelData {
selectedEmoticon = cachedData.themeEmoticon
} else {
selectedEmoticon = nil
}
let controller = ChatThemeScreen(context: context, updatedPresentationData: strongSelf.updatedPresentationData, initiallySelectedEmoticon: selectedEmoticon, previewTheme: { [weak self] emoticon in
if let strongSelf = self {
strongSelf.presentCrossfadeSnapshot()
strongSelf.themeEmoticonPreviewPromise.set(emoticon)
if emoticon == nil {
strongSelf.themeDarkAppearancePreviewPromise.set(nil)
}
}
}, previewDarkTheme: { dark in
if let strongSelf = self {
strongSelf.presentCrossfadeSnapshot()
strongSelf.themeDarkAppearancePreviewPromise.set(dark)
}
}, completion: { [weak self] emoticon in
strongSelf.themeDarkAppearancePreviewPromise.set(nil)
let _ = context.engine.themes.setChatTheme(peerId: peerId, emoji: emoticon).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.themeEmoticonPreviewPromise.set(nil)
}
})
})
strongSelf.present(controller, in: .window(.root))
})
}
private var effectiveNavigationController: NavigationController? { private var effectiveNavigationController: NavigationController? {
if let navigationController = self.navigationController as? NavigationController { if let navigationController = self.navigationController as? NavigationController {
return navigationController return navigationController

View File

@ -247,7 +247,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.inputContextPanelContainer = ChatControllerTitlePanelNodeContainer() self.inputContextPanelContainer = ChatControllerTitlePanelNodeContainer()
var getMessageTransitionNode: (() -> ChatMessageTransitionNode?)? var getMessageTransitionNode: (() -> ChatMessageTransitionNode?)?
self.historyNode = ChatHistoryListNode(context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: nil, subject: subject, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), messageTransitionNode: { self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: controller?.updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: nil, subject: subject, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), messageTransitionNode: {
return getMessageTransitionNode?() return getMessageTransitionNode?()
}) })
self.historyNode.rotated = true self.historyNode.rotated = true

View File

@ -559,7 +559,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private let clientId: Atomic<Int32> private let clientId: Atomic<Int32>
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?, source: ChatHistoryListSource = .default, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>, mode: ChatHistoryListMode = .bubbles, messageTransitionNode: @escaping () -> ChatMessageTransitionNode? = { nil }) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?, source: ChatHistoryListSource = .default, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>, mode: ChatHistoryListMode = .bubbles, messageTransitionNode: @escaping () -> ChatMessageTransitionNode? = { nil }) {
var tagMask = tagMask var tagMask = tagMask
var appendMessagesFromTheSameGroup = false var appendMessagesFromTheSameGroup = false
if case .pinnedMessages = subject { if case .pinnedMessages = subject {
@ -575,7 +575,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.mode = mode self.mode = mode
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData.initial
self.currentPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0) self.currentPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0)
self.chatPresentationDataPromise = Promise(self.currentPresentationData) self.chatPresentationDataPromise = Promise(self.currentPresentationData)
@ -1088,7 +1088,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.presentationDataDisposable = ( self.presentationDataDisposable = (
combineLatest(queue: .mainQueue(), combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData, updatedPresentationData.signal,
appConfiguration) appConfiguration)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, appConfiguration in |> deliverOnMainQueue).start(next: { [weak self] presentationData, appConfiguration in
if let strongSelf = self { if let strongSelf = self {

View File

@ -225,13 +225,15 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true)) self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true, nilIfEmpty: true))
let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false let loopAnimatedStickers = self.inputNodeInteraction?.stickerSettings?.loopAnimatedStickers ?? false
self.imageNode.isHidden = loopAnimatedStickers
let animatedStickerNode: AnimatedStickerNode let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode { if let current = self.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
if let placeholderNode = self.placeholderNode { if let placeholderNode = self.placeholderNode {
self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode) self.scalingNode.insertSubnode(animatedStickerNode, belowSubnode: placeholderNode)

View File

@ -228,11 +228,11 @@ class ChatMessageBackground: ASDisplayNode {
}) })
} }
} else if transition.isAnimated { } else if transition.isAnimated {
if let previousContents = self.imageNode.layer.contents, let image = image { // if let previousContents = self.imageNode.layer.contents, let image = image {
if (previousContents as AnyObject) !== image.cgImage { // if (previousContents as AnyObject) !== image.cgImage {
self.imageNode.layer.animate(from: previousContents as AnyObject, to: image.cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.42) // self.imageNode.layer.animate(from: previousContents as AnyObject, to: image.cgImage! as AnyObject, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.42)
} // }
} // }
} }
self.imageNode.image = image self.imageNode.image = image

View File

@ -772,7 +772,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
if peer is TelegramChannel, let navigationController = strongSelf.getNavigationController() { if peer is TelegramChannel, let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), peekData: peekData, animated: true)) strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), peekData: peekData, animated: true))
} else { } else {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
strongSelf.pushController(infoController) strongSelf.pushController(infoController)
} }
} }
@ -786,7 +786,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self { if let strongSelf = self {
if let peer = peer { if let peer = peer {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
strongSelf.pushController(infoController) strongSelf.pushController(infoController)
} }
} }

View File

@ -0,0 +1,918 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import AccountContext
import SolidRoundedButtonNode
import TelegramPresentationData
import TelegramUIPreferences
import PresentationDataUtils
import AnimationUI
import MergeLists
import WallpaperResources
import TooltipUI
private func closeButtonImage(theme: PresentationTheme) -> UIImage? {
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(theme.actionSheet.inputBackgroundColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setLineWidth(2.0)
context.setLineCap(.round)
context.setStrokeColor(theme.actionSheet.inputClearButtonColor.cgColor)
context.move(to: CGPoint(x: 10.0, y: 10.0))
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
context.strokePath()
context.move(to: CGPoint(x: 20.0, y: 10.0))
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
context.strokePath()
})
}
private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
let index: Int
let emoticon: String?
let themeReference: PresentationThemeReference?
var selected: Bool
let theme: PresentationTheme
let wallpaper: TelegramWallpaper?
var stableId: Int {
return index
}
static func ==(lhs: ThemeSettingsThemeEntry, rhs: ThemeSettingsThemeEntry) -> Bool {
if lhs.index != rhs.index {
return false
}
if lhs.emoticon != rhs.emoticon {
return false
}
if lhs.themeReference?.index != rhs.themeReference?.index {
return false
}
if lhs.selected != rhs.selected {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.wallpaper != rhs.wallpaper {
return false
}
return true
}
static func <(lhs: ThemeSettingsThemeEntry, rhs: ThemeSettingsThemeEntry) -> Bool {
return lhs.index < rhs.index
}
func item(context: AccountContext, action: @escaping (String?) -> Void) -> ListViewItem {
return ThemeSettingsThemeIconItem(context: context, emoticon: self.emoticon, themeReference: self.themeReference, selected: self.selected, theme: self.theme, wallpaper: self.wallpaper, action: action)
}
}
private class ThemeSettingsThemeIconItem: ListViewItem {
let context: AccountContext
let emoticon: String?
let themeReference: PresentationThemeReference?
let selected: Bool
let theme: PresentationTheme
let wallpaper: TelegramWallpaper?
let action: (String?) -> Void
public init(context: AccountContext, emoticon: String?, themeReference: PresentationThemeReference?, selected: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper?, action: @escaping (String?) -> Void) {
self.context = context
self.emoticon = emoticon
self.themeReference = themeReference
self.selected = selected
self.theme = theme
self.wallpaper = wallpaper
self.action = action
}
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
async {
let node = ThemeSettingsThemeItemIconNode()
let (nodeLayout, apply) = node.asyncLayout()(self, params)
node.insets = nodeLayout.insets
node.contentSize = nodeLayout.contentSize
Queue.mainQueue().async {
completion(node, {
return (nil, { _ in
apply(false)
})
})
}
}
}
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
Queue.mainQueue().async {
assert(node() is ThemeSettingsThemeItemIconNode)
if let nodeValue = node() as? ThemeSettingsThemeItemIconNode {
let layout = nodeValue.asyncLayout()
async {
let (nodeLayout, apply) = layout(self, params)
Queue.mainQueue().async {
completion(nodeLayout, { _ in
apply(animation.isAnimated)
})
}
}
}
}
}
public var selectable = true
public func selected(listView: ListView) {
self.action(self.emoticon)
}
}
private struct ThemeSettingsThemeItemNodeTransition {
let deletions: [ListViewDeleteItem]
let insertions: [ListViewInsertItem]
let updates: [ListViewUpdateItem]
let crossfade: Bool
let entries: [ThemeSettingsThemeEntry]
}
private func ensureThemeVisible(listNode: ListView, emoticon: String?, animated: Bool) -> Bool {
var resultNode: ThemeSettingsThemeItemIconNode?
listNode.forEachItemNode { node in
if resultNode == nil, let node = node as? ThemeSettingsThemeItemIconNode {
if node.item?.emoticon == emoticon {
resultNode = node
}
}
}
if let resultNode = resultNode {
listNode.ensureItemNodeVisible(resultNode, animated: animated, overflow: 57.0)
return true
} else {
return false
}
}
private func preparedTransition(context: AccountContext, action: @escaping (String?) -> Void, from fromEntries: [ThemeSettingsThemeEntry], to toEntries: [ThemeSettingsThemeEntry], crossfade: Bool) -> ThemeSettingsThemeItemNodeTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action), directionHint: .Down) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action), directionHint: nil) }
return ThemeSettingsThemeItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, crossfade: crossfade, entries: toEntries)
}
private var cachedBorderImages: [String: UIImage] = [:]
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
let key = "\(theme.list.itemBlocksBackgroundColor.hexString)_\(selected ? "s" + theme.list.itemAccentColor.hexString : theme.list.disclosureArrowColor.hexString)"
if let image = cachedBorderImages[key] {
return image
} else {
let image = generateImage(CGSize(width: 18.0, height: 18.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
let lineWidth: CGFloat
if selected {
lineWidth = 2.0
context.setLineWidth(lineWidth)
context.setStrokeColor(theme.list.itemBlocksBackgroundColor.cgColor)
context.strokeEllipse(in: bounds.insetBy(dx: 3.0 + lineWidth / 2.0, dy: 3.0 + lineWidth / 2.0))
var accentColor = theme.list.itemAccentColor
if accentColor.rgb == 0xffffff {
accentColor = UIColor(rgb: 0x999999)
}
context.setStrokeColor(accentColor.cgColor)
} else {
context.setStrokeColor(theme.list.disclosureArrowColor.withAlphaComponent(0.4).cgColor)
lineWidth = 1.0
}
if bordered || selected {
context.setLineWidth(lineWidth)
context.strokeEllipse(in: bounds.insetBy(dx: 1.0 + lineWidth / 2.0, dy: 1.0 + lineWidth / 2.0))
}
})?.stretchableImage(withLeftCapWidth: 9, topCapHeight: 9)
cachedBorderImages[key] = image
return image
}
}
private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
private let containerNode: ASDisplayNode
private let imageNode: TransformImageNode
private let overlayNode: ASImageNode
private let textNode: TextNode
private let emojiNode: TextNode
var snapshotView: UIView?
var item: ThemeSettingsThemeIconItem?
init() {
self.containerNode = ASDisplayNode()
self.imageNode = TransformImageNode()
self.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 82.0, height: 108.0))
self.imageNode.isLayerBacked = true
self.overlayNode = ASImageNode()
self.overlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 84.0, height: 110.0))
self.overlayNode.isLayerBacked = true
self.textNode = TextNode()
self.textNode.isUserInteractionEnabled = false
self.emojiNode = TextNode()
self.emojiNode.isUserInteractionEnabled = false
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.overlayNode)
self.containerNode.addSubnode(self.textNode)
self.containerNode.addSubnode(self.emojiNode)
}
func asyncLayout() -> (ThemeSettingsThemeIconItem, ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (Bool) -> Void) {
let makeTextLayout = TextNode.asyncLayout(self.textNode)
let makeEmojiLayout = TextNode.asyncLayout(self.emojiNode)
let makeImageLayout = self.imageNode.asyncLayout()
let currentItem = self.item
return { [weak self] item, params in
var updatedThemeReference = false
var updatedTheme = false
var updatedWallpaper = false
var updatedSelected = false
if currentItem?.themeReference != item.themeReference {
updatedThemeReference = true
}
if currentItem?.wallpaper != item.wallpaper {
updatedWallpaper = true
}
if currentItem?.theme !== item.theme {
updatedTheme = true
}
if currentItem?.selected != item.selected {
updatedSelected = true
}
let text = NSAttributedString(string: "No\nTheme", font: Font.semibold(15.0), textColor: item.theme.actionSheet.controlAccentColor)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let title = NSAttributedString(string: item.emoticon ?? "", font: Font.regular(22.0), textColor: .black)
let (_, emojiApply) = makeEmojiLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: 120.0, height: 90.0), insets: UIEdgeInsets())
return (itemLayout, { animated in
if let strongSelf = self {
strongSelf.item = item
if updatedThemeReference || updatedWallpaper {
if let themeReference = item.themeReference {
strongSelf.imageNode.cornerRadius = 0.0
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: nil, wallpaper: item.wallpaper, emoticon: true))
strongSelf.imageNode.backgroundColor = nil
}
}
if item.themeReference == nil {
strongSelf.imageNode.cornerRadius = 9.0
strongSelf.imageNode.backgroundColor = item.theme.actionSheet.opaqueItemBackgroundColor
}
if updatedTheme || updatedSelected {
strongSelf.overlayNode.image = generateBorderImage(theme: item.theme, bordered: false, selected: item.selected)
}
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((90.0 - textLayout.size.width) / 2.0), y: 24.0), size: textLayout.size)
strongSelf.textNode.isHidden = item.emoticon != nil
strongSelf.containerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
strongSelf.containerNode.frame = CGRect(origin: CGPoint(x: 15.0, y: -15.0), size: CGSize(width: 90.0, height: 120.0))
let _ = textApply()
let _ = emojiApply()
let imageSize = CGSize(width: 82.0, height: 108.0)
strongSelf.imageNode.frame = CGRect(origin: CGPoint(x: 4.0, y: 6.0), size: imageSize)
let applyLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 9.0), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: .clear))
applyLayout()
strongSelf.overlayNode.frame = strongSelf.imageNode.frame.insetBy(dx: -1.0, dy: -1.0)
strongSelf.emojiNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 79.0), size: CGSize(width: 90.0, height: 30.0))
}
})
}
}
func crossfade() {
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
super.animateInsertion(currentTimestamp, duration: duration, short: short)
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
super.animateRemoved(currentTimestamp, duration: duration)
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
}
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
super.animateAdded(currentTimestamp, duration: duration)
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
final class ChatThemeScreen: ViewController {
private var controllerNode: ChatThemeScreenNode {
return self.displayNode as! ChatThemeScreenNode
}
private var animatedIn = false
private let context: AccountContext
private let initiallySelectedEmoticon: String?
private let dismissByTapOutside: Bool
private let previewTheme: (String?) -> Void
private let previewDarkTheme: (Bool) -> Void
private let completion: (String?) -> Void
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), initiallySelectedEmoticon: String?, dismissByTapOutside: Bool = true, previewTheme: @escaping (String?) -> Void, previewDarkTheme: @escaping (Bool) -> Void, completion: @escaping (String?) -> Void) {
self.context = context
self.presentationData = updatedPresentationData.initial
self.initiallySelectedEmoticon = initiallySelectedEmoticon
self.dismissByTapOutside = dismissByTapOutside
self.previewTheme = previewTheme
self.previewDarkTheme = previewDarkTheme
self.completion = completion
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
self.blocksBackgroundWhenInOverlay = true
self.presentationDataDisposable = (updatedPresentationData.signal
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
strongSelf.presentationData = presentationData
strongSelf.controllerNode.updatePresentationData(presentationData)
}
})
self.statusBar.statusBarStyle = .Ignore
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.presentationDataDisposable?.dispose()
}
override public func loadDisplayNode() {
self.displayNode = ChatThemeScreenNode(context: self.context, presentationData: self.presentationData, initiallySelectedEmoticon: self.initiallySelectedEmoticon, dismissByTapOutside: self.dismissByTapOutside)
self.controllerNode.previewTheme = { [weak self] emoticon in
guard let strongSelf = self else {
return
}
strongSelf.previewTheme(emoticon ?? "")
}
self.controllerNode.present = { [weak self] c in
self?.present(c, in: .window(.root))
}
self.controllerNode.previewDarkTheme = { [weak self] dark in
guard let strongSelf = self else {
return
}
strongSelf.previewDarkTheme(dark)
}
self.controllerNode.completion = { [weak self] emoticon in
guard let strongSelf = self else {
return
}
strongSelf.completion(emoticon)
strongSelf.dismiss()
}
self.controllerNode.dismiss = { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
}
self.controllerNode.cancel = { [weak self] in
self?.previewTheme(nil)
self?.dismiss()
}
}
override public func loadView() {
super.loadView()
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.animatedIn {
self.animatedIn = true
self.controllerNode.animateIn()
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss()
}
return true
})
self.controllerNode.animateOut(completion: completion)
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
}
}
class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let context: AccountContext
private var presentationData: PresentationData
private let dismissByTapOutside: Bool
private let dimNode: ASDisplayNode
private let wrappingScrollNode: ASScrollNode
private let contentContainerNode: ASDisplayNode
private let topContentContainerNode: SparseNode
private let effectNode: ASDisplayNode
private let backgroundNode: ASDisplayNode
private let contentBackgroundNode: ASDisplayNode
private let titleNode: ASTextNode
private let textNode: ImmediateTextNode
private let cancelButton: HighlightableButtonNode
private let switchThemeButton: HighlightTrackingButtonNode
private let animationNode: AnimationNode
private let doneButton: SolidRoundedButtonNode
private let listNode: ListView
private var entries: [ThemeSettingsThemeEntry]?
private var enqueuedTransitions: [ThemeSettingsThemeItemNodeTransition] = []
private var initialized = false
private let initiallySelectedEmoticon: String?
private var selectedEmoticon: String? {
didSet {
self.selectedEmoticonPromise.set(self.selectedEmoticon)
}
}
private var selectedEmoticonPromise: ValuePromise<String?>
private var isDarkAppearancePromise: ValuePromise<Bool>
private var containerLayout: (ContainerViewLayout, CGFloat)?
private let disposable = MetaDisposable()
var present: ((ViewController) -> Void)?
var previewTheme: ((String?) -> Void)?
var previewDarkTheme: ((Bool) -> Void)?
var completion: ((String?) -> Void)?
var dismiss: (() -> Void)?
var cancel: (() -> Void)?
init(context: AccountContext, presentationData: PresentationData, initiallySelectedEmoticon: String?, dismissByTapOutside: Bool) {
self.context = context
self.initiallySelectedEmoticon = initiallySelectedEmoticon
self.selectedEmoticon = initiallySelectedEmoticon
self.selectedEmoticonPromise = ValuePromise(initiallySelectedEmoticon)
self.presentationData = presentationData
self.dismissByTapOutside = dismissByTapOutside
self.wrappingScrollNode = ASScrollNode()
self.wrappingScrollNode.view.alwaysBounceVertical = true
self.wrappingScrollNode.view.delaysContentTouches = false
self.wrappingScrollNode.view.canCancelContentTouches = true
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = .clear
self.contentContainerNode = ASDisplayNode()
self.contentContainerNode.isOpaque = false
self.topContentContainerNode = SparseNode()
self.topContentContainerNode.isOpaque = false
self.backgroundNode = ASDisplayNode()
self.backgroundNode.clipsToBounds = true
self.backgroundNode.cornerRadius = 16.0
self.isDarkAppearancePromise = ValuePromise(self.presentationData.theme.overallDarkAppearance)
let backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
let textColor = self.presentationData.theme.actionSheet.primaryTextColor
let blurStyle: UIBlurEffect.Style = self.presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark
self.effectNode = ASDisplayNode(viewBlock: {
return UIVisualEffectView(effect: UIBlurEffect(style: blurStyle))
})
self.contentBackgroundNode = ASDisplayNode()
self.contentBackgroundNode.backgroundColor = backgroundColor
let title = self.presentationData.strings.Conversation_Theme_Title
self.titleNode = ASTextNode()
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: textColor)
self.textNode = ImmediateTextNode()
self.cancelButton = HighlightableButtonNode()
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
var colors: [String: UIColor] = [:]
let accentColor = self.presentationData.theme.actionSheet.controlAccentColor
colors["Sunny.Path 14.Path.Stroke 1"] = accentColor
colors["Sunny.Path 15.Path.Stroke 1"] = accentColor
colors["Path.Path.Stroke 1"] = accentColor
colors["Sunny.Path 39.Path.Stroke 1"] = accentColor
colors["Sunny.Path 24.Path.Stroke 1"] = accentColor
colors["Sunny.Path 25.Path.Stroke 1"] = accentColor
colors["Sunny.Path 18.Path.Stroke 1"] = accentColor
colors["Sunny.Path 41.Path.Stroke 1"] = accentColor
colors["Sunny.Path 43.Path.Stroke 1"] = accentColor
colors["Path 10.Path.Fill 1"] = accentColor
colors["Path 11.Path.Fill 1"] = accentColor
self.switchThemeButton = HighlightTrackingButtonNode()
self.animationNode = AnimationNode(animation: self.presentationData.theme.overallDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: colors, scale: 1.0)
self.animationNode.isUserInteractionEnabled = false
self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 52.0, cornerRadius: 11.0, gloss: false)
self.doneButton.title = self.presentationData.strings.Conversation_Theme_Apply
self.listNode = ListView()
self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
super.init()
self.backgroundColor = nil
self.isOpaque = false
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
self.addSubnode(self.dimNode)
self.wrappingScrollNode.view.delegate = self
self.addSubnode(self.wrappingScrollNode)
self.wrappingScrollNode.addSubnode(self.backgroundNode)
self.wrappingScrollNode.addSubnode(self.contentContainerNode)
self.wrappingScrollNode.addSubnode(self.topContentContainerNode)
self.backgroundNode.addSubnode(self.effectNode)
self.backgroundNode.addSubnode(self.contentBackgroundNode)
self.contentContainerNode.addSubnode(self.titleNode)
self.contentContainerNode.addSubnode(self.textNode)
self.contentContainerNode.addSubnode(self.cancelButton)
self.contentContainerNode.addSubnode(self.doneButton)
self.topContentContainerNode.addSubnode(self.animationNode)
self.topContentContainerNode.addSubnode(self.switchThemeButton)
self.topContentContainerNode.addSubnode(self.listNode)
self.switchThemeButton.addTarget(self, action: #selector(self.switchThemePressed), forControlEvents: .touchUpInside)
self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
self.doneButton.pressed = { [weak self] in
if let strongSelf = self {
strongSelf.doneButton.isUserInteractionEnabled = false
strongSelf.completion?(strongSelf.selectedEmoticon)
}
}
self.disposable.set(combineLatest(queue: Queue.mainQueue(), self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager), self.selectedEmoticonPromise.get(), self.isDarkAppearancePromise.get()).start(next: { [weak self] themes, selectedEmoticon, isDarkAppearance in
guard let strongSelf = self else {
return
}
let presentationData = strongSelf.presentationData
var entries: [ThemeSettingsThemeEntry] = []
if strongSelf.initiallySelectedEmoticon != nil {
entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: nil, themeReference: nil, selected: selectedEmoticon == nil, theme: presentationData.theme, wallpaper: nil))
}
for theme in themes {
entries.append(ThemeSettingsThemeEntry(index: entries.count, emoticon: theme.emoji, themeReference: .cloud(PresentationCloudTheme(theme: isDarkAppearance ? theme.darkTheme : theme.theme, resolvedWallpaper: nil, creatorAccountId: nil)), selected: selectedEmoticon == theme.emoji, theme: presentationData.theme, wallpaper: nil))
}
let action: (String?) -> Void = { [weak self] emoticon in
if let strongSelf = self {
strongSelf.crossfade()
strongSelf.selectedEmoticon = emoticon
strongSelf.previewTheme?(emoticon)
let _ = ensureThemeVisible(listNode: strongSelf.listNode, emoticon: emoticon, animated: true)
strongSelf.doneButton.title = emoticon == nil ? strongSelf.presentationData.strings.Conversation_Theme_Reset : strongSelf.presentationData.strings.Conversation_Theme_Apply
}
}
let previousEntries = strongSelf.entries ?? []
let crossfade = previousEntries.count != entries.count
let transition = preparedTransition(context: strongSelf.context, action: action, from: previousEntries, to: entries, crossfade: crossfade)
strongSelf.enqueueTransition(transition)
strongSelf.entries = entries
}))
self.switchThemeButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.animationNode.layer.removeAnimation(forKey: "opacity")
strongSelf.animationNode.alpha = 0.4
} else {
strongSelf.animationNode.alpha = 1.0
strongSelf.animationNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
}
private func enqueueTransition(_ transition: ThemeSettingsThemeItemNodeTransition) {
self.enqueuedTransitions.append(transition)
while !self.enqueuedTransitions.isEmpty {
self.dequeueTransition()
}
}
private func dequeueTransition() {
guard let transition = self.enqueuedTransitions.first else {
return
}
self.enqueuedTransitions.remove(at: 0)
var options = ListViewDeleteAndInsertOptions()
if self.initialized && transition.crossfade {
options.insert(.AnimateCrossfade)
}
options.insert(.Synchronous)
var scrollToItem: ListViewScrollToItem?
if !self.initialized {
if let index = transition.entries.firstIndex(where: { entry in
return entry.emoticon == self.initiallySelectedEmoticon
}) {
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down)
self.initialized = true
}
}
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
})
}
func updatePresentationData(_ presentationData: PresentationData) {
let previousTheme = self.presentationData.theme
self.presentationData = presentationData
self.isDarkAppearancePromise.set(presentationData.theme.overallDarkAppearance)
if let effectView = self.effectNode.view as? UIVisualEffectView {
effectView.effect = UIBlurEffect(style: presentationData.theme.actionSheet.backgroundType == .light ? .light : .dark)
}
self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.itemBackgroundColor
self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
self.cancelButton.setImage(closeButtonImage(theme: self.presentationData.theme), for: .normal)
self.doneButton.updateTheme(SolidRoundedButtonTheme(theme: self.presentationData.theme))
}
override func didLoad() {
super.didLoad()
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never
}
self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true
}
@objc func cancelButtonPressed() {
self.cancel?()
}
@objc func switchThemePressed() {
var colors: [String: UIColor] = [:]
let accentColor = self.presentationData.theme.actionSheet.controlAccentColor
colors["Sunny.Path 14.Path.Stroke 1"] = accentColor
colors["Sunny.Path 15.Path.Stroke 1"] = accentColor
colors["Path.Path.Stroke 1"] = accentColor
colors["Sunny.Path 39.Path.Stroke 1"] = accentColor
colors["Sunny.Path 24.Path.Stroke 1"] = accentColor
colors["Sunny.Path 25.Path.Stroke 1"] = accentColor
colors["Sunny.Path 18.Path.Stroke 1"] = accentColor
colors["Sunny.Path 41.Path.Stroke 1"] = accentColor
colors["Sunny.Path 43.Path.Stroke 1"] = accentColor
colors["Path 10.Path.Fill 1"] = accentColor
colors["Path 11.Path.Fill 1"] = accentColor
self.crossfade()
self.animationNode.setAnimation(name: self.presentationData.theme.overallDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: colors)
self.animationNode.playOnce()
self.previewDarkTheme?(!self.presentationData.theme.overallDarkAppearance)
}
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
if self.dismissByTapOutside, case .ended = recognizer.state {
self.cancelButtonPressed()
}
}
private func crossfade() {
if let snapshotView = self.backgroundNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.backgroundNode.frame
self.backgroundNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.backgroundNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
if let snapshotView = self.contentContainerNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = self.contentContainerNode.frame
self.contentContainerNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.contentContainerNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
self.listNode.forEachVisibleItemNode { node in
if let node = node as? ThemeSettingsThemeItemIconNode {
node.crossfade()
}
}
}
private var animatedOut = false
func animateIn() {
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
let targetBounds = self.bounds
self.bounds = self.bounds.offsetBy(dx: 0.0, dy: -offset)
self.dimNode.position = CGPoint(x: dimPosition.x, y: dimPosition.y - offset)
transition.animateView({
self.bounds = targetBounds
self.dimNode.position = dimPosition
})
let frame = self.switchThemeButton.view.convert(self.switchThemeButton.bounds, to: self.view)
Queue.mainQueue().after(1.0) {
if !self.animatedOut {
self.present?(TooltipScreen(text: self.presentationData.theme.overallDarkAppearance ? self.presentationData.strings.Conversation_Theme_SwitchToLight : self.presentationData.strings.Conversation_Theme_SwitchToDark, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
}))
}
}
}
func animateOut(completion: (() -> Void)? = nil) {
self.animatedOut = true
var dimCompleted = false
var offsetCompleted = false
let internalCompletion: () -> Void = { [weak self] in
if let strongSelf = self, dimCompleted && offsetCompleted {
strongSelf.dismiss?()
}
completion?()
}
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
dimCompleted = true
internalCompletion()
})
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
offsetCompleted = true
internalCompletion()
})
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.bounds.contains(point) {
if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) {
return self.dimNode.view
}
}
return super.hitTest(point, with: event)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let contentOffset = scrollView.contentOffset
let additionalTopHeight = max(0.0, -contentOffset.y)
if additionalTopHeight >= 30.0 {
self.cancelButtonPressed()
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.containerLayout = (layout, navigationBarHeight)
var insets = layout.insets(options: [.statusBar, .input])
let cleanInsets = layout.insets(options: [.statusBar])
insets.top = max(10.0, insets.top)
let bottomInset: CGFloat = 10.0 + cleanInsets.bottom
let titleHeight: CGFloat = 54.0
let contentHeight = titleHeight + bottomInset + 188.0
let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left)
let sideInset = floor((layout.size.width - width) / 2.0)
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight))
let contentFrame = contentContainerFrame
var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height + 2000.0))
if backgroundFrame.minY < contentFrame.minY {
backgroundFrame.origin.y = contentFrame.minY
}
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
let titleSize = self.titleNode.measure(CGSize(width: width, height: titleHeight))
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 18.0), size: titleSize)
transition.updateFrame(node: self.titleNode, frame: titleFrame)
let switchThemeSize = CGSize(width: 44.0, height: 44.0)
let switchThemeFrame = CGRect(origin: CGPoint(x: 3.0, y: 6.0), size: switchThemeSize)
transition.updateFrame(node: self.switchThemeButton, frame: switchThemeFrame)
transition.updateFrame(node: self.animationNode, frame: switchThemeFrame.insetBy(dx: 9.0, dy: 9.0))
let cancelSize = CGSize(width: 44.0, height: 44.0)
let cancelFrame = CGRect(origin: CGPoint(x: contentFrame.width - cancelSize.width - 3.0, y: 6.0), size: cancelSize)
transition.updateFrame(node: self.cancelButton, frame: cancelFrame)
let buttonInset: CGFloat = 16.0
let doneButtonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition)
transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - insets.bottom - 6.0, width: contentFrame.width, height: doneButtonHeight))
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
transition.updateFrame(node: self.topContentContainerNode, frame: contentContainerFrame)
var listInsets = UIEdgeInsets()
listInsets.top += layout.safeInsets.left + 12.0
listInsets.bottom += layout.safeInsets.right + 12.0
let contentSize = CGSize(width: contentFrame.width, height: 120.0)
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: contentSize.height, height: contentSize.width)
self.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0 + titleHeight + 6.0)
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
}

View File

@ -49,6 +49,9 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if params.activateInput { if params.activateInput {
controller.activateInput() controller.activateInput()
} }
if params.changeColors {
controller.presentThemeSelection()
}
if let botStart = params.botStart { if let botStart = params.botStart {
controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in
return state.updatedBotStartPayload(botStart.payload) return state.updatedBotStartPayload(botStart.payload)
@ -116,6 +119,9 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if params.activateInput { if params.activateInput {
controller.activateInput() controller.activateInput()
} }
if params.changeColors {
controller.presentThemeSelection()
}
} }
params.navigationController.currentWindow?.forEachController { controller in params.navigationController.currentWindow?.forEachController { controller in

View File

@ -17,7 +17,7 @@ func openAddContactImpl(context: AccountContext, firstName: String = "", lastNam
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "") let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
if let peer = peer { if let peer = peer {
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushController(infoController) pushController(infoController)
} }
} else { } else {

View File

@ -192,7 +192,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
case .info: case .info:
let _ = (context.account.postbox.loadedPeerWithId(peerId) let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
context.sharedContext.applicationBindings.dismissNativeController() context.sharedContext.applicationBindings.dismissNativeController()
navigationController?.pushViewController(infoController) navigationController?.pushViewController(infoController)
} }
@ -480,7 +480,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
return transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(idValue))) return transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(idValue)))
} }
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
navigationController?.pushViewController(controller) navigationController?.pushViewController(controller)
} }
}) })

View File

@ -186,7 +186,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
self.isGlobalSearch = false self.isGlobalSearch = false
} }
self.historyNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: initialMessageId, highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) self.historyNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, source: source, subject: .message(id: initialMessageId, highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
self.historyNode.clipsToBounds = true self.historyNode.clipsToBounds = true
super.init() super.init()
@ -528,7 +528,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
} }
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil) let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = ChatHistoryListNode(context: self.context, chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: messageId, highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch)) let historyNode = ChatHistoryListNode(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: .peer(self.peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: .message(id: messageId, highlight: true, timecode: nil), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch))
historyNode.clipsToBounds = true historyNode.clipsToBounds = true
historyNode.preloadPages = true historyNode.preloadPages = true
historyNode.stackFromBottom = true historyNode.stackFromBottom = true

View File

@ -59,7 +59,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
self.selectedMessagesPromise.set(.single(self.selectedMessages)) self.selectedMessagesPromise.set(.single(self.selectedMessages))
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil) let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
self.listNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false))
self.listNode.clipsToBounds = true self.listNode.clipsToBounds = true
self.listNode.defaultToSynchronousTransactionWhileScrolling = true self.listNode.defaultToSynchronousTransactionWhileScrolling = true
self.listNode.scroller.bounces = false self.listNode.scroller.bounces = false

View File

@ -348,7 +348,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
let separatorNode: ASDisplayNode let separatorNode: ASDisplayNode
let backgroundNode: NavigationBackgroundNode let backgroundNode: NavigationBackgroundNode
init(context: AccountContext, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void) { init(context: AccountContext, presentationData: PresentationData, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void) {
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
self.deleteMessages = deleteMessages self.deleteMessages = deleteMessages
@ -356,7 +356,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
self.forwardMessages = forwardMessages self.forwardMessages = forwardMessages
self.reportMessages = reportMessages self.reportMessages = reportMessages
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = presentationData
self.separatorNode = ASDisplayNode() self.separatorNode = ASDisplayNode()
self.backgroundNode = NavigationBackgroundNode(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor) self.backgroundNode = NavigationBackgroundNode(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor)
@ -1571,7 +1571,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.peerId = peerId self.peerId = peerId
self.isOpenedFromChat = isOpenedFromChat self.isOpenedFromChat = isOpenedFromChat
self.videoCallsEnabled = VideoCallsConfiguration(appConfiguration: context.currentAppConfiguration.with { $0 }).areVideoCallsEnabled self.videoCallsEnabled = VideoCallsConfiguration(appConfiguration: context.currentAppConfiguration.with { $0 }).areVideoCallsEnabled
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = controller.presentationData
self.nearbyPeerDistance = nearbyPeerDistance self.nearbyPeerDistance = nearbyPeerDistance
self.callMessages = callMessages self.callMessages = callMessages
self.isSettings = isSettings self.isSettings = isSettings
@ -3146,7 +3146,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
strongSelf.controller?.push(infoController) strongSelf.controller?.push(infoController)
} }
} }
@ -3220,7 +3220,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
(strongSelf.controller?.navigationController as? NavigationController)?.pushViewController(infoController) (strongSelf.controller?.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} }
@ -3484,6 +3484,24 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: self.headerNode.isAvatarExpanded, videoCallsEnabled: self.videoCallsEnabled, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false)) let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: self.headerNode.isAvatarExpanded, videoCallsEnabled: self.videoCallsEnabled, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false))
let filteredButtons = allHeaderButtons.subtracting(headerButtons) let filteredButtons = allHeaderButtons.subtracting(headerButtons)
var canChangeColors = true
if let peer = peer as? TelegramChannel {
canChangeColors = peer.hasPermission(.changeInfo)
} else if let peer = peer as? TelegramGroup, case .member = peer.role {
canChangeColors = !peer.hasBannedPermission(.banChangeInfo)
}
if canChangeColors {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_ChangeColors, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
self?.openChatForThemeChange()
})))
}
if filteredButtons.contains(.addMember) { if filteredButtons.contains(.addMember) {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ButtonAddMember, icon: { theme in items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ButtonAddMember, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor)
@ -3609,7 +3627,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
}))) })))
} }
if self.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { if self.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_StartSecretChat, icon: { theme in items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_StartSecretChat, icon: { theme in
generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor)
@ -3794,6 +3812,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
} }
private func openChatForThemeChange() {
if let navigationController = (self.controller?.navigationController as? NavigationController) {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), keepStack: .default, changeColors: true, completion: { _ in
}))
}
}
private func openStartSecretChat() { private func openStartSecretChat() {
let peerId = self.peerId let peerId = self.peerId
let _ = (self.context.account.postbox.transaction { transaction -> (Peer?, PeerId?) in let _ = (self.context.account.postbox.transaction { transaction -> (Peer?, PeerId?) in
@ -4776,7 +4801,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if isMember { if isMember {
mode = .group(self.peerId) mode = .group(self.peerId)
} }
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, peer: peer, mode: mode, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer, mode: mode, avatarInitiallyExpanded: false, fromChat: false) {
(self.controller?.navigationController as? NavigationController)?.pushViewController(infoController) (self.controller?.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} }
@ -5497,7 +5522,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
})) }))
} }
fileprivate func switchToAccount(id: AccountRecordId) { fileprivate func switchToAccount(id: AccountRecordId) {
self.accountsAndPeers.set(.never()) self.accountsAndPeers.set(.never())
self.context.sharedContext.switchToAccount(id: id, fromSettingsController: nil, withChatListController: nil) self.context.sharedContext.switchToAccount(id: id, fromSettingsController: nil, withChatListController: nil)
@ -6054,7 +6079,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
selectionPanelNode = current selectionPanelNode = current
} else { } else {
wasAdded = true wasAdded = true
selectionPanelNode = PeerInfoSelectionPanelNode(context: self.context, peerId: self.peerId, deleteMessages: { [weak self] in selectionPanelNode = PeerInfoSelectionPanelNode(context: self.context, presentationData: self.presentationData, peerId: self.peerId, deleteMessages: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -6452,7 +6477,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
private let isSettings: Bool private let isSettings: Bool
private let ignoreGroupInCommon: PeerId? private let ignoreGroupInCommon: PeerId?
private var presentationData: PresentationData fileprivate var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let accountsAndPeers = Promise<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])>() private let accountsAndPeers = Promise<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])>()
@ -6474,7 +6499,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
public init(context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool = false, ignoreGroupInCommon: PeerId? = nil) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool = false, ignoreGroupInCommon: PeerId? = nil) {
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
self.avatarInitiallyExpanded = avatarInitiallyExpanded self.avatarInitiallyExpanded = avatarInitiallyExpanded
@ -6484,7 +6509,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
self.isSettings = isSettings self.isSettings = isSettings
self.ignoreGroupInCommon = ignoreGroupInCommon self.ignoreGroupInCommon = ignoreGroupInCommon
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = updatedPresentationData?.0 ?? context.sharedContext.currentPresentationData.with { $0 }
let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
super.init(navigationBarPresentationData: NavigationBarPresentationData( super.init(navigationBarPresentationData: NavigationBarPresentationData(
@ -6707,7 +6732,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
self?.controllerNode.scrollToTop() self?.controllerNode.scrollToTop()
} }
self.presentationDataDisposable = (context.sharedContext.presentationData self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme

View File

@ -349,7 +349,7 @@ public func pollResultsController(context: AccountContext, messageId: MessageId,
}) })
}, openPeer: { peer in }, openPeer: { peer in
if let peer = peer.peers[peer.peerId] { if let peer = peer.peers[peer.peerId] {
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
} }

View File

@ -1103,8 +1103,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
handleTextLinkActionImpl(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink) handleTextLinkActionImpl(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink)
} }
public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController? { public func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController? {
let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: fromChat) let controller = peerInfoControllerImpl(context: context, updatedPresentationData: updatedPresentationData, peer: peer, mode: mode, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: fromChat)
controller?.navigationPresentation = .modalInLargeLayout controller?.navigationPresentation = .modalInLargeLayout
return controller return controller
} }
@ -1419,11 +1419,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
private let defaultChatControllerInteraction = ChatControllerInteraction.default private let defaultChatControllerInteraction = ChatControllerInteraction.default
private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) -> ViewController? { private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) -> ViewController? {
if let _ = peer as? TelegramGroup { if let _ = peer as? TelegramGroup {
return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
} else if let _ = peer as? TelegramChannel { } else if let _ = peer as? TelegramChannel {
return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
} else if peer is TelegramUser { } else if peer is TelegramUser {
var nearbyPeerDistance: Int32? var nearbyPeerDistance: Int32?
var callMessages: [Message] = [] var callMessages: [Message] = []
@ -1438,9 +1438,9 @@ private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: P
case let .group(id): case let .group(id):
ignoreGroupInCommon = id ignoreGroupInCommon = id
} }
return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, ignoreGroupInCommon: ignoreGroupInCommon) return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, ignoreGroupInCommon: ignoreGroupInCommon)
} else if peer is TelegramSecretChat { } else if peer is TelegramSecretChat {
return PeerInfoScreenImpl(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: [])
} }
return nil return nil
} }

View File

@ -111,7 +111,7 @@ public final class TelegramRootController: NavigationController {
sharedContext.switchingData = (nil, nil, nil) sharedContext.switchingData = (nil, nil, nil)
} }
let accountSettingsController = PeerInfoScreenImpl(context: self.context, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true) let accountSettingsController = PeerInfoScreenImpl(context: self.context, updatedPresentationData: nil, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true)
accountSettingsController.tabBarItemDebugTapAction = { [weak self, weak accountSettingsController] in accountSettingsController.tabBarItemDebugTapAction = { [weak self, weak accountSettingsController] in
guard let strongSelf = self, let accountSettingsController = accountSettingsController else { guard let strongSelf = self, let accountSettingsController = accountSettingsController else {
return return

View File

@ -34,7 +34,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
peerSignal = context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init) peerSignal = context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init)
navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in
if let controller = controller, let peer = peer { if let controller = controller, let peer = peer {
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) { if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) {
(controller.navigationController as? NavigationController)?.pushViewController(infoController) (controller.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} }

View File

@ -53,14 +53,17 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private var isArrowInverted: Bool = false private var isArrowInverted: Bool = false
private let inset: CGFloat
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
init(text: String, textEntities: [MessageTextEntity], style: TooltipScreen.Style, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) { init(text: String, textEntities: [MessageTextEntity], style: TooltipScreen.Style, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, inset: CGFloat = 13.0, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) {
self.tooltipStyle = style self.tooltipStyle = style
self.icon = icon self.icon = icon
self.customContentNode = customContentNode self.customContentNode = customContentNode
self.location = location self.location = location
self.displayDuration = displayDuration self.displayDuration = displayDuration
self.inset = inset
self.shouldDismissOnTouch = shouldDismissOnTouch self.shouldDismissOnTouch = shouldDismissOnTouch
self.requestDismiss = requestDismiss self.requestDismiss = requestDismiss
self.openActiveTextItem = openActiveTextItem self.openActiveTextItem = openActiveTextItem
@ -295,7 +298,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.scrollingContainer.frame = CGRect(origin: CGPoint(), size: layout.size) self.scrollingContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
let sideInset: CGFloat = 13.0 + layout.safeInsets.left let sideInset: CGFloat = self.inset + layout.safeInsets.left
let bottomInset: CGFloat = 10.0 let bottomInset: CGFloat = 10.0
let contentInset: CGFloat = 11.0 let contentInset: CGFloat = 11.0
let contentVerticalInset: CGFloat = 11.0 let contentVerticalInset: CGFloat = 11.0
@ -563,6 +566,7 @@ public final class TooltipScreen: ViewController {
private let customContentNode: TooltipCustomContentNode? private let customContentNode: TooltipCustomContentNode?
private let location: TooltipScreen.Location private let location: TooltipScreen.Location
private let displayDuration: DisplayDuration private let displayDuration: DisplayDuration
private let inset: CGFloat
private let shouldDismissOnTouch: (CGPoint) -> TooltipScreen.DismissOnTouch private let shouldDismissOnTouch: (CGPoint) -> TooltipScreen.DismissOnTouch
private let openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? private let openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?
@ -578,7 +582,7 @@ public final class TooltipScreen: ViewController {
private var dismissTimer: Foundation.Timer? private var dismissTimer: Foundation.Timer?
public init(text: String, textEntities: [MessageTextEntity] = [], style: TooltipScreen.Style = .default, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? = nil) { public init(text: String, textEntities: [MessageTextEntity] = [], style: TooltipScreen.Style = .default, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, inset: CGFloat = 13.0, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? = nil) {
self.text = text self.text = text
self.textEntities = textEntities self.textEntities = textEntities
self.style = style self.style = style
@ -586,6 +590,7 @@ public final class TooltipScreen: ViewController {
self.customContentNode = customContentNode self.customContentNode = customContentNode
self.location = location self.location = location
self.displayDuration = displayDuration self.displayDuration = displayDuration
self.inset = inset
self.shouldDismissOnTouch = shouldDismissOnTouch self.shouldDismissOnTouch = shouldDismissOnTouch
self.openActiveTextItem = openActiveTextItem self.openActiveTextItem = openActiveTextItem
@ -642,7 +647,7 @@ public final class TooltipScreen: ViewController {
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = TooltipScreenNode(text: self.text, textEntities: self.textEntities, style: self.style, icon: self.icon, customContentNode: self.customContentNode, location: self.location, displayDuration: self.displayDuration, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in self.displayNode = TooltipScreenNode(text: self.text, textEntities: self.textEntities, style: self.style, icon: self.icon, customContentNode: self.customContentNode, location: self.location, displayDuration: self.displayDuration, inset: self.inset, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }

View File

@ -1291,7 +1291,7 @@ public func themeImage(account: Account, accountManager: AccountManager<Telegram
} }
} }
public func themeIconImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { public func themeIconImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, emoticon: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Int32?), NoError> let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Int32?), NoError>
var reference: MediaResourceReference? var reference: MediaResourceReference?
@ -1468,21 +1468,71 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
} }
} }
let incomingColors = colors.1
let incoming = generateGradientTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), colors: incomingColors)
let outgoingColors = colors.2
let outgoing = generateGradientTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), colors: outgoingColors)
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: 1.0, y: -1.0) c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0))
let incomingColors = colors.1
if emoticon {
let rect = CGRect(x: 8.0, y: 44.0, width: 48.0, height: 24.0)
c.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 12.0).cgPath)
c.clip()
if incomingColors.count >= 2 {
let gradientColors = incomingColors.map { $0.cgColor } as CFArray
var locations: [CGFloat] = []
for i in 0 ..< incomingColors.count {
let t = CGFloat(i) / CGFloat(incomingColors.count - 1)
locations.append(t)
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: rect.minY), end: CGPoint(x: 0.0, y: rect.maxY), options: CGGradientDrawingOptions())
} else if !incomingColors.isEmpty {
c.setFillColor(incomingColors[0].cgColor)
c.fill(rect)
}
c.resetClip()
} else {
let incoming = generateGradientTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), colors: incomingColors)
c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0))
}
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
c.scaleBy(x: -1.0, y: 1.0) c.scaleBy(x: -1.0, y: 1.0)
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
let outgoingColors = colors.2
if emoticon {
let rect = CGRect(x: 8.0, y: 72.0, width: 48.0, height: 24.0)
c.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 12.0).cgPath)
c.clip()
if outgoingColors.count >= 2 {
let gradientColors = outgoingColors.map { $0.cgColor } as CFArray
var locations: [CGFloat] = []
for i in 0 ..< outgoingColors.count {
let t = CGFloat(i) / CGFloat(outgoingColors.count - 1)
locations.append(t)
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: rect.minY), end: CGPoint(x: 0.0, y: rect.maxY), options: CGGradientDrawingOptions())
} else if !outgoingColors.isEmpty {
c.setFillColor(outgoingColors[0].cgColor)
c.fill(rect)
}
c.resetClip()
} else {
let outgoing = generateGradientTintedImage(image: UIImage(bundleImageName: "Settings/ThemeBubble"), colors: outgoingColors)
c.draw(outgoing!.cgImage!, in: CGRect(x: 9.0, y: 12.0, width: 57.0, height: 16.0))
}
} }
addCorners(context, arguments: arguments) addCorners(context, arguments: arguments)
return context return context

View File

@ -269,7 +269,7 @@ public func widgetSetupScreen(context: AccountContext) -> ViewController {
return transaction.getPeer(peerId) return transaction.getPeer(peerId)
} }
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) else { guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false) else {
return return
} }
pushControllerImpl?(controller) pushControllerImpl?(controller)