From 0197abb08277584a377e0c47434357d10b92a158 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 15 Jul 2019 18:17:50 +0200 Subject: [PATCH] Added option to disable animated stickers looped playback across all the app --- .../ArchivedStickerPacksController.swift | 35 +++-- .../TelegramUI/ChatController.swift | 26 +++- .../ChatControllerInteraction.swift | 22 ++- .../TelegramUI/ChatControllerNode.swift | 8 + .../ChatMediaInputStickerGridItem.swift | 20 +-- .../ChatMediaInputStickerPackItem.swift | 50 +++---- .../ChatMessageAnimatedStickerItemNode.swift | 48 +++--- .../ChatMessageBubbleItemNode.swift | 1 - .../TelegramUI/ChatMessageItemView.swift | 3 + .../FeaturedStickerPacksController.swift | 37 +++-- .../GroupStickerPackSetupController.swift | 139 ++++++++++-------- .../InstalledStickerPacksController.swift | 105 ++++++++----- .../TelegramUI/ItemListStickerPackItem.swift | 8 +- .../OverlayPlayerControllerNode.swift | 2 +- .../PeerMediaCollectionController.swift | 2 +- .../StickerPackPreviewController.swift | 24 +-- .../StickerPackPreviewControllerNode.swift | 22 +-- .../StickerPackPreviewGridItem.swift | 32 +--- .../Sources/MediaAutoDownloadSettings.swift | 16 +- .../Sources/StickerSettings.swift | 16 +- 20 files changed, 361 insertions(+), 255 deletions(-) diff --git a/submodules/TelegramUI/TelegramUI/ArchivedStickerPacksController.swift b/submodules/TelegramUI/TelegramUI/ArchivedStickerPacksController.swift index c35840f84b..8f4d08b52c 100644 --- a/submodules/TelegramUI/TelegramUI/ArchivedStickerPacksController.swift +++ b/submodules/TelegramUI/TelegramUI/ArchivedStickerPacksController.swift @@ -5,6 +5,7 @@ import SwiftSignalKit import Postbox import TelegramCore import TelegramPresentationData +import TelegramUIPreferences public enum ArchivedStickerPacksControllerMode { case stickers @@ -65,7 +66,7 @@ private enum ArchivedStickerPacksEntryId: Hashable { private enum ArchivedStickerPacksEntry: ItemListNodeEntry { case info(PresentationTheme, String) - case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, ItemListStickerPackItemEditing) + case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing) var section: ItemListSectionId { switch self { @@ -78,7 +79,7 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { switch self { case .info: return .index(0) - case let .pack(_, _, _, info, _, _, _, _): + case let .pack(_, _, _, info, _, _, _, _, _): return .pack(info.id) } } @@ -91,8 +92,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { } else { return false } - case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsEnabled, lhsEditing): - if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsEnabled, rhsEditing) = rhs { + case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsEnabled, lhsEditing): + if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsEnabled, rhsEditing) = rhs { if lhsIndex != rhsIndex { return false } @@ -111,6 +112,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { if lhsCount != rhsCount { return false } + if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers { + return false + } if lhsEnabled != rhsEnabled { return false } @@ -133,9 +137,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { default: return true } - case let .pack(lhsIndex, _, _, _, _, _, _, _): + case let .pack(lhsIndex, _, _, _, _, _, _, _, _): switch rhs { - case let .pack(rhsIndex, _, _, _, _, _, _, _): + case let .pack(rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex default: return false @@ -147,8 +151,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { switch self { case let .info(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) - case let .pack(_, theme, strings, info, topItem, count, enabled, editing): - return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .installation(installed: false), editing: editing, enabled: enabled, sectionId: self.section, action: { + case let .pack(_, theme, strings, info, topItem, count, animatedStickers, enabled, editing): + return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .installation(installed: false), editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: { arguments.openStickerPack(info) }, setPackIdWithRevealedOptions: { current, previous in arguments.setPackIdWithRevealedOptions(current, previous) @@ -205,7 +209,7 @@ private struct ArchivedStickerPacksControllerState: Equatable { } } -private func archivedStickerPacksControllerEntries(presentationData: PresentationData, state: ArchivedStickerPacksControllerState, packs: [ArchivedStickerPackItem]?, installedView: CombinedView) -> [ArchivedStickerPacksEntry] { +private func archivedStickerPacksControllerEntries(presentationData: PresentationData, state: ArchivedStickerPacksControllerState, packs: [ArchivedStickerPackItem]?, installedView: CombinedView, stickerSettings: StickerSettings) -> [ArchivedStickerPacksEntry] { var entries: [ArchivedStickerPacksEntry] = [] if let packs = packs { @@ -219,7 +223,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio var index: Int32 = 0 for item in packs { if !installedIds.contains(item.info.id) { - entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false))) + entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), stickerSettings.loopAnimatedStickers, !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false))) index += 1 } } @@ -369,9 +373,14 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv var previousPackCount: Int? - let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, installedStickerPacks.get() |> deliverOnMainQueue) + let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, installedStickerPacks.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue) |> deliverOnMainQueue - |> map { presentationData, state, packs, installedView -> (ItemListControllerState, (ItemListNodeState, ArchivedStickerPacksEntry.ItemGenerationArguments)) in + |> map { presentationData, state, packs, installedView, sharedData -> (ItemListControllerState, (ItemListNodeState, ArchivedStickerPacksEntry.ItemGenerationArguments)) in + var stickerSettings = StickerSettings.defaultSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings { + stickerSettings = value + } + var rightNavigationButton: ItemListNavigationButton? if let packs = packs, packs.count != 0 { if state.editing { @@ -399,7 +408,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.StickerPacksSettings_ArchivedPacks), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(entries: archivedStickerPacksControllerEntries(presentationData: presentationData, state: state, packs: packs, installedView: installedView), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && packs != nil && (previous! != 0 && previous! >= packs!.count - 10)) + let listState = ItemListNodeState(entries: archivedStickerPacksControllerEntries(presentationData: presentationData, state: state, packs: packs, installedView: installedView, stickerSettings: stickerSettings), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && packs != nil && (previous! != 0 && previous! >= packs!.count - 10)) return (controllerState, (listState, arguments)) } |> afterDisposed { actionsDisposable.dispose() diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index e054968c29..5fbedb19f0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -208,6 +208,9 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, private var automaticMediaDownloadSettings: MediaAutoDownloadSettings private var automaticMediaDownloadSettingsDisposable: Disposable? + private var stickerSettings: ChatInterfaceStickerSettings + private var stickerSettingsDisposable: Disposable? + private var applicationInForegroundDisposable: Disposable? private var checkedPeerChatServiceActions = false @@ -264,6 +267,8 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 } + self.stickerSettings = ChatInterfaceStickerSettings(loopAnimatedStickers: false) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.fontSize, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation) var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none @@ -1378,8 +1383,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, }, cancelInteractiveKeyboardGestures: { [weak self] in (self?.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures() self?.chatDisplayNode.cancelInteractiveKeyboardGestures() - }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, - pollActionState: ChatInterfacePollActionState()) + }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings) self.controllerInteraction = controllerInteraction @@ -1884,6 +1888,24 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, } }) + self.stickerSettingsDisposable = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) + |> deliverOnMainQueue).start(next: { [weak self] sharedData in + var stickerSettings = StickerSettings.defaultSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings { + stickerSettings = value + } + + let chatStickerSettings = ChatInterfaceStickerSettings(stickerSettings: stickerSettings) + + if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings { + strongSelf.stickerSettings = chatStickerSettings + strongSelf.controllerInteraction?.stickerSettings = chatStickerSettings + if strongSelf.isNodeLoaded { + strongSelf.chatDisplayNode.updateStickerSettings(chatStickerSettings) + } + } + }) + self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground |> distinctUntilChanged |> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift b/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift index 8c522afb15..90e26ecfd3 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift @@ -31,6 +31,22 @@ struct ChatInterfaceHighlightedState: Equatable { } } +struct ChatInterfaceStickerSettings: Equatable { + let loopAnimatedStickers: Bool + + public init(loopAnimatedStickers: Bool) { + self.loopAnimatedStickers = loopAnimatedStickers + } + + public init(stickerSettings: StickerSettings) { + self.loopAnimatedStickers = stickerSettings.loopAnimatedStickers + } + + static func ==(lhs: ChatInterfaceStickerSettings, rhs: ChatInterfaceStickerSettings) -> Bool { + return lhs.loopAnimatedStickers == rhs.loopAnimatedStickers + } +} + public enum ChatControllerInteractionLongTapAction { case url(String) case mention(String) @@ -104,9 +120,10 @@ public final class ChatControllerInteraction { var contextHighlightedState: ChatInterfaceHighlightedState? var automaticMediaDownloadSettings: MediaAutoDownloadSettings var pollActionState: ChatInterfacePollActionState + var stickerSettings: ChatInterfaceStickerSettings var searchTextHighightState: String? - init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState) { + init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) { self.openMessage = openMessage self.openPeer = openPeer self.openPeerMention = openPeerMention @@ -154,6 +171,7 @@ public final class ChatControllerInteraction { self.automaticMediaDownloadSettings = automaticMediaDownloadSettings self.pollActionState = pollActionState + self.stickerSettings = stickerSettings } static var `default`: ChatControllerInteraction { @@ -175,6 +193,6 @@ public final class ChatControllerInteraction { }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, - pollActionState: ChatInterfacePollActionState()) + pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift index ad4510afe7..9be192a99a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift @@ -1497,6 +1497,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.historyNode.prefetchManager.updateAutoDownloadSettings(settings) } + func updateStickerSettings(_ settings: ChatInterfaceStickerSettings) { + self.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView { + itemNode.updateStickerSettings() + } + } + } + var isInputViewFocused: Bool { if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode { return inputPanelNode.isFocused diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift index 3cf1fd44d8..414e20455d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift @@ -145,7 +145,6 @@ final class ChatMediaInputStickerGridItem: GridItem { let node = ChatMediaInputStickerGridItemNode() node.interfaceInteraction = self.interfaceInteraction node.inputNodeInteraction = self.inputNodeInteraction - node.setup(account: self.account, stickerItem: self.stickerItem) node.selected = self.selected return node } @@ -157,7 +156,6 @@ final class ChatMediaInputStickerGridItem: GridItem { } node.interfaceInteraction = self.interfaceInteraction node.inputNodeInteraction = self.inputNodeInteraction - node.setup(account: self.account, stickerItem: self.stickerItem) node.selected = self.selected } } @@ -209,11 +207,6 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { self.imageNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:)))) } - func setup(account: Account, stickerItem: StickerPackItem) { - //self.updateSelectionState(animated: false) - //self.updateHiddenMedia() - } - override func updateLayout(item: GridItem, size: CGSize, isVisible: Bool, synchronousLoads: Bool) { guard let item = item as? ChatMediaInputStickerGridItem else { return @@ -232,13 +225,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { self.addSubnode(animationNode) } self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0))) - if self.isPlaying { - self.didSetUpAnimationNode = true - self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached) - } else { - self.didSetUpAnimationNode = false - } - self.animationNode?.visibility = self.isPlaying + self.updateVisibility() self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start()) } else { if let animationNode = self.animationNode { @@ -297,7 +284,10 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { } func updateVisibility() { - let isPlaying = self.isPanelVisible && self.isVisibleInGrid + guard let item = self.item else { + return + } + let isPlaying = self.isPanelVisible && self.isVisibleInGrid && (item.interfaceInteraction?.stickerSettings.loopAnimatedStickers ?? true) if self.isPlaying != isPlaying { self.isPlaying = isPlaying self.animationNode?.visibility = isPlaying diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift index 26aeb93446..5212c983ce 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift @@ -103,7 +103,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { override var visibility: ListViewItemNodeVisibility { didSet { - self.visibilityStatus = self.visibility != .none + self.visibilityStatus = self.visibility != .none && false } } @@ -171,30 +171,30 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode { self.currentThumbnailItem = thumbnailItem if let thumbnailItem = thumbnailItem { switch thumbnailItem { - case let .still(representation): - let imageSize = representation.dimensions.aspectFitted(boundingImageSize) - let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) - imageApply() - self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation)) - - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) - case let .animated(resource): - let imageSize = boundingImageSize - - let animatedStickerNode: AnimatedStickerNode - if let current = self.animatedStickerNode { - animatedStickerNode = current - } else { - animatedStickerNode = AnimatedStickerNode() - self.animatedStickerNode = animatedStickerNode - animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - self.addSubnode(animatedStickerNode) - animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached) - animatedStickerNode.visibility = self.visibilityStatus - } - if let animatedStickerNode = self.animatedStickerNode { - animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) - } + case let .still(representation): + let imageSize = representation.dimensions.aspectFitted(boundingImageSize) + let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) + imageApply() + self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation)) + + self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) + case let .animated(resource): + let imageSize = boundingImageSize + + let animatedStickerNode: AnimatedStickerNode + if let current = self.animatedStickerNode { + animatedStickerNode = current + } else { + animatedStickerNode = AnimatedStickerNode() + self.animatedStickerNode = animatedStickerNode + animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + self.addSubnode(animatedStickerNode) + animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached) + animatedStickerNode.visibility = self.visibilityStatus && false + } + if let animatedStickerNode = self.animatedStickerNode { + animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) + } } if let resourceReference = resourceReference { self.stickerFetchedDisposable.set(fetchedMediaResource(postbox: account.postbox, reference: resourceReference).start()) diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index 7ffe44291d..b1feb4f240 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -17,6 +17,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let imageNode: TransformImageNode private let animationNode: AnimatedStickerNode private var didSetUpAnimationNode = false + private var isPlaying = false private var swipeToReplyNode: ChatMessageSwipeToReplyNode? private var swipeToReplyFeedback: HapticFeedback? @@ -90,8 +91,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } self.view.addGestureRecognizer(replyRecognizer) } - - private var visibilityPromise = ValuePromise(false, ignoreRepeated: true) + override var visibility: ListViewItemNodeVisibility { didSet { let wasVisible = oldValue != .none @@ -106,21 +106,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private var visibilityStatus: Bool = false { didSet { if self.visibilityStatus != oldValue { - if self.visibilityStatus { - self.animationNode.visibility = true - self.visibilityPromise.set(true) - if let item = self.item, !self.didSetUpAnimationNode { - for media in item.message.media { - if let telegramFile = media as? TelegramMediaFile { - self.didSetUpAnimationNode = true - self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached) - } - } - } - } else { - self.animationNode.visibility = false - self.visibilityPromise.set(false) - } + self.updateVisibility() } } } @@ -133,7 +119,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if self.telegramFile?.id != telegramFile.id { self.telegramFile = telegramFile self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 384.0, height: 384.0), thumbnail: false)) - if self.visibilityStatus { + self.updateVisibility() + if self.visibilityStatus && false { self.didSetUpAnimationNode = true self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached) } @@ -144,6 +131,31 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } + func updateVisibility() { + guard let item = self.item else { + return + } + + let isPlaying = self.visibilityStatus && item.controllerInteraction.stickerSettings.loopAnimatedStickers + if self.isPlaying != isPlaying { + self.isPlaying = isPlaying + self.animationNode.visibility = isPlaying + if let item = self.item, isPlaying, !self.didSetUpAnimationNode { + self.didSetUpAnimationNode = true + for media in item.message.media { + if let telegramFile = media as? TelegramMediaFile { + self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached) + break + } + } + } + } + } + + override func updateStickerSettings() { + self.updateVisibility() + } + override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) { let displaySize = CGSize(width: 184.0, height: 184.0) let telegramFile = self.telegramFile diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift index 4403bf84c3..5459e75993 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift @@ -2191,7 +2191,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } } - override func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? { for contentNode in self.contentNodes { if let playMediaWithSound = contentNode.playMediaWithSound() { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift b/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift index 06bf3cd96a..a4f1d3fd33 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift @@ -698,6 +698,9 @@ public class ChatMessageItemView: ListViewItemNode { func updateAutomaticMediaDownloadSettings() { } + func updateStickerSettings() { + } + func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? { return nil } diff --git a/submodules/TelegramUI/TelegramUI/FeaturedStickerPacksController.swift b/submodules/TelegramUI/TelegramUI/FeaturedStickerPacksController.swift index 51088e34d9..207189c093 100644 --- a/submodules/TelegramUI/TelegramUI/FeaturedStickerPacksController.swift +++ b/submodules/TelegramUI/TelegramUI/FeaturedStickerPacksController.swift @@ -5,6 +5,7 @@ import SwiftSignalKit import Postbox import TelegramCore import TelegramPresentationData +import TelegramUIPreferences private final class FeaturedStickerPacksControllerArguments { let account: Account @@ -46,7 +47,7 @@ private enum FeaturedStickerPacksEntryId: Hashable { } private enum FeaturedStickerPacksEntry: ItemListNodeEntry { - case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, Bool, StickerPackItem?, String, Bool) + case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, Bool, StickerPackItem?, String, Bool, Bool) var section: ItemListSectionId { switch self { @@ -57,15 +58,15 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry { var stableId: FeaturedStickerPacksEntryId { switch self { - case let .pack(_, _, _, info, _, _, _, _): + case let .pack(_, _, _, info, _, _, _, _, _): return .pack(info.id) } } static func ==(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool { switch lhs { - case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsUnread, lhsTopItem, lhsCount, lhsInstalled): - if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsUnread, rhsTopItem, rhsCount, rhsInstalled) = rhs { + case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsUnread, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsInstalled): + if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsUnread, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsInstalled) = rhs { if lhsIndex != rhsIndex { return false } @@ -87,6 +88,9 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry { if lhsCount != rhsCount { return false } + if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers { + return false + } if lhsInstalled != rhsInstalled { return false } @@ -99,9 +103,9 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry { static func <(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool { switch lhs { - case let .pack(lhsIndex, _, _, _, _, _, _, _): + case let .pack(lhsIndex, _, _, _, _, _, _, _, _): switch rhs { - case let .pack(rhsIndex, _, _, _, _, _, _, _): + case let .pack(rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex } } @@ -109,8 +113,8 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry { func item(_ arguments: FeaturedStickerPacksControllerArguments) -> ListViewItem { switch self { - case let .pack(_, theme, strings, info, unread, topItem, count, installed): - return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, sectionId: self.section, action: { + case let .pack(_, theme, strings, info, unread, topItem, count, playAnimatedStickers, installed): + return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: { arguments.openStickerPack(info) }, setPackIdWithRevealedOptions: { _, _ in }, addPack: { @@ -130,7 +134,7 @@ private struct FeaturedStickerPacksControllerState: Equatable { } } -private func featuredStickerPacksControllerEntries(presentationData: PresentationData, state: FeaturedStickerPacksControllerState, view: CombinedView, featured: [FeaturedStickerPackItem], unreadPacks: [ItemCollectionId: Bool]) -> [FeaturedStickerPacksEntry] { +private func featuredStickerPacksControllerEntries(presentationData: PresentationData, state: FeaturedStickerPacksControllerState, view: CombinedView, featured: [FeaturedStickerPackItem], unreadPacks: [ItemCollectionId: Bool], stickerSettings: StickerSettings) -> [FeaturedStickerPacksEntry] { var entries: [FeaturedStickerPacksEntry] = [] if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView, !featured.isEmpty { @@ -145,7 +149,7 @@ private func featuredStickerPacksControllerEntries(presentationData: Presentatio if let value = unreadPacks[item.info.id] { unread = value } - entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, unread, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), installedPacks.contains(item.info.id))) + entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, unread, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), stickerSettings.loopAnimatedStickers, installedPacks.contains(item.info.id))) index += 1 } } @@ -195,9 +199,14 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr var previousPackCount: Int? var initialUnreadPacks: [ItemCollectionId: Bool] = [:] - let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, featured.get() |> deliverOnMainQueue) + let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, featured.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue) |> deliverOnMainQueue - |> map { presentationData, state, view, featured -> (ItemListControllerState, (ItemListNodeState, FeaturedStickerPacksEntry.ItemGenerationArguments)) in + |> map { presentationData, state, view, featured, sharedData -> (ItemListControllerState, (ItemListNodeState, FeaturedStickerPacksEntry.ItemGenerationArguments)) in + var stickerSettings = StickerSettings.defaultSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings { + stickerSettings = value + } + let packCount: Int? = featured.count for item in featured { @@ -212,7 +221,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.FeaturedStickerPacks_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(entries: featuredStickerPacksControllerEntries(presentationData: presentationData, state: state, view: view, featured: featured, unreadPacks: initialUnreadPacks), style: .blocks, animateChanges: false) + let listState = ItemListNodeState(entries: featuredStickerPacksControllerEntries(presentationData: presentationData, state: state, view: view, featured: featured, unreadPacks: initialUnreadPacks, stickerSettings: stickerSettings), style: .blocks, animateChanges: false) return (controllerState, (listState, arguments)) } |> afterDisposed { actionsDisposable.dispose() @@ -226,7 +235,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr var unreadIds: [ItemCollectionId] = [] for entry in entries { switch entry { - case let .pack(_, _, _, info, unread, _, _, _): + case let .pack(_, _, _, info, unread, _, _, _, _): if unread && !alreadyReadIds.contains(info.id) { unreadIds.append(info.id) } diff --git a/submodules/TelegramUI/TelegramUI/GroupStickerPackSetupController.swift b/submodules/TelegramUI/TelegramUI/GroupStickerPackSetupController.swift index 0cb5ec6d30..e37813635f 100644 --- a/submodules/TelegramUI/TelegramUI/GroupStickerPackSetupController.swift +++ b/submodules/TelegramUI/TelegramUI/GroupStickerPackSetupController.swift @@ -5,6 +5,7 @@ import SwiftSignalKit import Postbox import TelegramCore import TelegramPresentationData +import TelegramUIPreferences private final class GroupStickerPackSetupControllerArguments { let account: Account @@ -64,7 +65,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { case currentPack(Int32, PresentationTheme, PresentationStrings, GroupStickerPackCurrentItemContent) case searchInfo(PresentationTheme, String) case packsTitle(PresentationTheme, String) - case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool) + case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool) var section: ItemListSectionId { switch self { @@ -85,7 +86,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { return .index(2) case .packsTitle: return .index(3) - case let .pack(_, _, _, info, _, _, _): + case let .pack(_, _, _, info, _, _, _, _): return .pack(info.id) } } @@ -128,8 +129,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { } else { return false } - case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsSelected): - if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsSelected) = rhs { + case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsSelected): + if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsSelected) = rhs { if lhsIndex != rhsIndex { return false } @@ -148,6 +149,9 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { if lhsCount != rhsCount { return false } + if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers { + return false + } if lhsSelected != rhsSelected { return false } @@ -188,9 +192,9 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { default: return true } - case let .pack(lhsIndex, _, _, _, _, _, _): + case let .pack(lhsIndex, _, _, _, _, _, _, _): switch rhs { - case let .pack(rhsIndex, _, _, _, _, _, _): + case let .pack(rhsIndex, _, _, _, _, _, _, _): return lhsIndex < rhsIndex default: return false @@ -216,8 +220,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section, linkAction: nil) case let .packsTitle(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) - case let .pack(_, theme, strings, info, topItem, count, selected): - return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: selected ? .selection : .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, sectionId: self.section, action: { + case let .pack(_, theme, strings, info, topItem, count, playAnimatedStickers, selected): + return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: selected ? .selection : .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: { if selected { arguments.openStickerPack(info) } else { @@ -258,7 +262,7 @@ private struct GroupStickerPackSetupControllerState: Equatable { var isSaving: Bool } -private func groupStickerPackSetupControllerEntries(presentationData: PresentationData, searchText: String, view: CombinedView, initialData: InitialStickerPackData?, searchState: GroupStickerPackSearchState) -> [GroupStickerPackEntry] { +private func groupStickerPackSetupControllerEntries(presentationData: PresentationData, searchText: String, view: CombinedView, initialData: InitialStickerPackData?, searchState: GroupStickerPackSearchState, stickerSettings: StickerSettings) -> [GroupStickerPackEntry] { if initialData == nil { return [] } @@ -288,7 +292,7 @@ private func groupStickerPackSetupControllerEntries(presentationData: Presentati if case let .found(found) = searchState { selected = found.info.id == info.id } - entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), selected)) + entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), stickerSettings.loopAnimatedStickers, selected)) index += 1 } } @@ -400,66 +404,71 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee let previousHadData = Atomic(value: false) - let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, initialData.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, searchState.get() |> deliverOnMainQueue) - |> map { presentationData, state, initialData, view, searchState -> (ItemListControllerState, (ItemListNodeState, GroupStickerPackEntry.ItemGenerationArguments)) in - let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - dismissImpl?() - }) - - var rightNavigationButton: ItemListNavigationButton? - if initialData != nil { - if state.isSaving { - rightNavigationButton = ItemListNavigationButton(content: .text(""), style: .activity, enabled: true, action: {}) - } else { - let enabled: Bool - var info: StickerPackCollectionInfo? - switch searchState.1 { - case .searching, .notFound: - enabled = false - case .none: - enabled = true - case let .found(data): - enabled = true - info = data.info - } - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: { - if info?.id == currentPackInfo?.id { - dismissImpl?() - } else { + let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, initialData.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, searchState.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue) + |> map { presentationData, state, initialData, view, searchState, sharedData -> (ItemListControllerState, (ItemListNodeState, GroupStickerPackEntry.ItemGenerationArguments)) in + var stickerSettings = StickerSettings.defaultSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings { + stickerSettings = value + } + + let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { + dismissImpl?() + }) + + var rightNavigationButton: ItemListNavigationButton? + if initialData != nil { + if state.isSaving { + rightNavigationButton = ItemListNavigationButton(content: .text(""), style: .activity, enabled: true, action: {}) + } else { + let enabled: Bool + var info: StickerPackCollectionInfo? + switch searchState.1 { + case .searching, .notFound: + enabled = false + case .none: + enabled = true + case let .found(data): + enabled = true + info = data.info + } + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: { + if info?.id == currentPackInfo?.id { + dismissImpl?() + } else { + updateState { state in + var state = state + state.isSaving = true + return state + } + saveDisposable.set((updateGroupSpecificStickerset(postbox: context.account.postbox, network: context.account.network, peerId: peerId, info: info) + |> deliverOnMainQueue).start(error: { _ in updateState { state in var state = state - state.isSaving = true + state.isSaving = false return state } - saveDisposable.set((updateGroupSpecificStickerset(postbox: context.account.postbox, network: context.account.network, peerId: peerId, info: info) - |> deliverOnMainQueue).start(error: { _ in - updateState { state in - var state = state - state.isSaving = false - return state - } - }, completed: { - dismissImpl?() - })) - } - }) - } + }, completed: { + dismissImpl?() + })) + } + }) } - - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Info_Stickers), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - - let hasData = initialData != nil - let hadData = previousHadData.swap(hasData) - - var emptyStateItem: ItemListLoadingIndicatorEmptyStateItem? - if !hasData { - emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) - } - - let listState = ItemListNodeState(entries: groupStickerPackSetupControllerEntries(presentationData: presentationData, searchText: searchState.0, view: view, initialData: initialData, searchState: searchState.1), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: hasData && hadData) - return (controllerState, (listState, arguments)) - } |> afterDisposed { - actionsDisposable.dispose() + } + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Info_Stickers), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) + + let hasData = initialData != nil + let hadData = previousHadData.swap(hasData) + + var emptyStateItem: ItemListLoadingIndicatorEmptyStateItem? + if !hasData { + emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) + } + + let listState = ItemListNodeState(entries: groupStickerPackSetupControllerEntries(presentationData: presentationData, searchText: searchState.0, view: view, initialData: initialData, searchState: searchState.1, stickerSettings: stickerSettings), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: hasData && hadData) + return (controllerState, (listState, arguments)) + } |> afterDisposed { + actionsDisposable.dispose() } let controller = ItemListController(context: context, state: signal) diff --git a/submodules/TelegramUI/TelegramUI/InstalledStickerPacksController.swift b/submodules/TelegramUI/TelegramUI/InstalledStickerPacksController.swift index c2f6745deb..1e8a02f9c1 100644 --- a/submodules/TelegramUI/TelegramUI/InstalledStickerPacksController.swift +++ b/submodules/TelegramUI/TelegramUI/InstalledStickerPacksController.swift @@ -18,8 +18,9 @@ private final class InstalledStickerPacksControllerArguments { let openFeatured: () -> Void let openArchived: ([ArchivedStickerPackItem]?) -> Void let openSuggestOptions: () -> Void + let toggleAnimatedStickers: (Bool) -> Void - init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void) { + init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void) { self.account = account self.openStickerPack = openStickerPack self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions @@ -29,6 +30,7 @@ private final class InstalledStickerPacksControllerArguments { self.openFeatured = openFeatured self.openArchived = openArchived self.openSuggestOptions = openSuggestOptions + self.toggleAnimatedStickers = toggleAnimatedStickers } } @@ -85,13 +87,15 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry { case trending(PresentationTheme, String, Int32) case archived(PresentationTheme, String, Int32, [ArchivedStickerPackItem]?) case masks(PresentationTheme, String) + case animatedStickers(PresentationTheme, String, Bool) + case animatedStickersInfo(PresentationTheme, String) case packsTitle(PresentationTheme, String) - case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, ItemListStickerPackItemEditing) + case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing) case packsInfo(PresentationTheme, String) var section: ItemListSectionId { switch self { - case .suggestOptions, .trending, .masks, .archived: + case .suggestOptions, .trending, .masks, .archived, .animatedStickers, .animatedStickersInfo: return InstalledStickerPacksSection.service.rawValue case .packsTitle, .pack, .packsInfo: return InstalledStickerPacksSection.stickers.rawValue @@ -108,12 +112,16 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry { return .index(2) case .masks: return .index(3) - case .packsTitle: + case .animatedStickers: return .index(4) - case let .pack(_, _, _, info, _, _, _, _): + case .animatedStickersInfo: + return .index(5) + case .packsTitle: + return .index(6) + case let .pack(_, _, _, info, _, _, _, _, _): return .pack(info.id) case .packsInfo: - return .index(5) + return .index(7) } } @@ -143,14 +151,26 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry { } else { return false } + case let .animatedStickers(lhsTheme, lhsText, lhsValue): + if case let .animatedStickers(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .animatedStickersInfo(lhsTheme, lhsText): + if case let .animatedStickersInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } case let .packsTitle(lhsTheme, lhsText): if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true } else { return false } - case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsEnabled, lhsEditing): - if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsEnabled, rhsEditing) = rhs { + case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsEnabled, lhsEditing): + if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsEnabled, rhsEditing) = rhs { if lhsIndex != rhsIndex { return false } @@ -169,6 +189,9 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry { if lhsCount != rhsCount { return false } + if lhsAnimatedStickers != rhsAnimatedStickers { + return false + } if lhsEnabled != rhsEnabled { return false } @@ -218,16 +241,30 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry { default: return true } - case .packsTitle: + case .animatedStickers: switch rhs { - case .suggestOptions, .trending, .masks, .archived, .packsTitle: + case .suggestOptions, .trending, .archived, .masks, .animatedStickers: return false default: return true } - case let .pack(lhsIndex, _, _, _, _, _, _, _): + case .animatedStickersInfo: switch rhs { - case let .pack(rhsIndex, _, _, _, _, _, _, _): + case .suggestOptions, .trending, .archived, .masks, .animatedStickers, .animatedStickersInfo: + return false + default: + return true + } + case .packsTitle: + switch rhs { + case .suggestOptions, .trending, .masks, .archived, .animatedStickers, .animatedStickersInfo, .packsTitle: + return false + default: + return true + } + case let .pack(lhsIndex, _, _, _, _, _, _, _, _): + switch rhs { + case let .pack(rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex case .packsInfo: return true @@ -262,10 +299,16 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry { return ItemListDisclosureItem(theme: theme, title: text, label: count == 0 ? "" : "\(count)", sectionId: self.section, style: .blocks, action: { arguments.openArchived(archived) }) + case let .animatedStickers(theme, text, value): + return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.toggleAnimatedStickers(value) + }) + case let .animatedStickersInfo(theme, text): + return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) case let .packsTitle(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) - case let .pack(_, theme, strings, info, topItem, count, enabled, editing): - return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .none, editing: editing, enabled: enabled, sectionId: self.section, action: { + case let .pack(_, theme, strings, info, topItem, count, animatedStickers, enabled, editing): + return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .none, editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: { arguments.openStickerPack(info) }, setPackIdWithRevealedOptions: { current, previous in arguments.setPackIdWithRevealedOptions(current, previous) @@ -353,6 +396,10 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedPacks, Int32(archived.count), archived)) } entries.append(.masks(presentationData.theme, presentationData.strings.MaskStickerSettings_Title)) + + entries.append(.animatedStickers(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickers, stickerSettings.loopAnimatedStickers)) + entries.append(.animatedStickersInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickersInfo)) + entries.append(.packsTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_StickerPacksSection)) case .masks: if let archived = archived, !archived.isEmpty { @@ -365,7 +412,7 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati var index: Int32 = 0 for entry in packsEntries { if let info = entry.info as? StickerPackCollectionInfo { - entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), true, ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == entry.id, reorderable: true))) + entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), stickerSettings.loopAnimatedStickers, true, ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == entry.id, reorderable: true))) index += 1 } } @@ -505,18 +552,10 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ]) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - /* - let suggestString: String - switch stickerSettings.emojiStickerSuggestionMode { - case .none: - suggestString = presentationData.strings.Stickers_SuggestNone - case .all: - - case .installed: - suggestString = presentationData.strings.Stickers_SuggestAdded - } - */ - + }, toggleAnimatedStickers: { value in + let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in + return current.withUpdatedLoopAnimatedStickers(value) + }).start() }) let stickerPacks = Promise() stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])])) @@ -531,10 +570,8 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta featured.set(.single([])) archivedPromise.set(.single(nil) |> then(archivedStickerPacks(account: context.account, namespace: .masks) |> map(Optional.init))) } - var previousPackCount: Int? - let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), stickerPacks.get(), combineLatest(queue: .mainQueue(), featured.get(), archivedPromise.get()), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])) |> deliverOnMainQueue |> map { presentationData, state, view, featuredAndArchived, sharedData -> (ItemListControllerState, (ItemListNodeState, InstalledStickerPacksEntry.ItemGenerationArguments)) in @@ -549,12 +586,6 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } let leftNavigationButton: ItemListNavigationButton? = nil - /*if case .modal = mode { - leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - dismissImpl?() - }) - }*/ - var rightNavigationButton: ItemListNavigationButton? if let packCount = packCount, packCount != 0 { if case .modal = mode { @@ -603,7 +634,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta controller.reorderEntry = { fromIndex, toIndex, entries in let fromEntry = entries[fromIndex] - guard case let .pack(_, _, _, fromPackInfo, _, _, _, _) = fromEntry else { + guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _) = fromEntry else { return } var referenceId: ItemCollectionId? @@ -611,7 +642,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta var afterAll = false if toIndex < entries.count { switch entries[toIndex] { - case let .pack(_, _, _, toPackInfo, _, _, _, _): + case let .pack(_, _, _, toPackInfo, _, _, _, _, _): referenceId = toPackInfo.id default: if entries[toIndex] < fromEntry { diff --git a/submodules/TelegramUI/TelegramUI/ItemListStickerPackItem.swift b/submodules/TelegramUI/TelegramUI/ItemListStickerPackItem.swift index b49c7c0f15..879f756a5e 100644 --- a/submodules/TelegramUI/TelegramUI/ItemListStickerPackItem.swift +++ b/submodules/TelegramUI/TelegramUI/ItemListStickerPackItem.swift @@ -47,13 +47,14 @@ final class ItemListStickerPackItem: ListViewItem, ItemListItem { let control: ItemListStickerPackItemControl let editing: ItemListStickerPackItemEditing let enabled: Bool + let playAnimatedStickers: Bool let sectionId: ItemListSectionId let action: (() -> Void)? let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void let addPack: () -> Void let removePack: () -> Void - init(theme: PresentationTheme, strings: PresentationStrings, account: Account, packInfo: StickerPackCollectionInfo, itemCount: String, topItem: StickerPackItem?, unread: Bool, control: ItemListStickerPackItemControl, editing: ItemListStickerPackItemEditing, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping () -> Void, removePack: @escaping () -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, account: Account, packInfo: StickerPackCollectionInfo, itemCount: String, topItem: StickerPackItem?, unread: Bool, control: ItemListStickerPackItemControl, editing: ItemListStickerPackItemEditing, enabled: Bool, playAnimatedStickers: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping () -> Void, removePack: @escaping () -> Void) { self.theme = theme self.strings = strings self.account = account @@ -64,6 +65,7 @@ final class ItemListStickerPackItem: ListViewItem, ItemListItem { self.control = control self.editing = editing self.enabled = enabled + self.playAnimatedStickers = playAnimatedStickers self.sectionId = sectionId self.action = action self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions @@ -162,7 +164,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { let isVisible = self.visibility != .none if wasVisible != isVisible { - self.animationNode?.visibility = isVisible + self.animationNode?.visibility = isVisible && (self.layoutParams?.0.playAnimatedStickers ?? true) } } } @@ -558,8 +560,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { strongSelf.animationNode = animationNode strongSelf.addSubnode(animationNode) animationNode.setup(account: item.account, resource: resource, width: 80, height: 80, mode: .cached) - animationNode.visibility = strongSelf.visibility != .none } + animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers if let animationNode = strongSelf.animationNode { transition.updateFrame(node: animationNode, frame: imageFrame) } diff --git a/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift index 8417e760dd..790162a1fc 100644 --- a/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift @@ -101,7 +101,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec }, seekToTimecode: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { - }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState()) + }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) self.dimNode = ASDisplayNode() self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) diff --git a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift index dee56a9619..43997090c3 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift @@ -272,7 +272,7 @@ public class PeerMediaCollectionController: TelegramController { }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, - pollActionState: ChatInterfacePollActionState()) + pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) self.controllerInteraction = controllerInteraction diff --git a/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift b/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift index c8c0c3b355..cce3af5d24 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift @@ -5,6 +5,7 @@ import AsyncDisplayKit import Postbox import TelegramCore import SwiftSignalKit +import TelegramUIPreferences enum StickerPackPreviewControllerMode { case `default` @@ -140,8 +141,13 @@ final class StickerPackPreviewController: ViewController { } let account = self.context.account self.displayNodeDidLoad() - self.stickerPackDisposable.set((self.stickerPackContents.get() - |> mapToSignal { next -> Signal in + self.stickerPackDisposable.set((combineLatest(self.stickerPackContents.get(), self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> take(1)) + |> mapToSignal { next, sharedData -> Signal<(LoadedStickerPack, StickerSettings), NoError> in + var stickerSettings = StickerSettings.defaultSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings { + stickerSettings = value + } + switch next { case let .result(_, items, _): var preloadSignals: [Signal] = [] @@ -172,26 +178,26 @@ final class StickerPackPreviewController: ViewController { return !values.contains(false) } |> distinctUntilChanged - |> mapToSignal { loaded -> Signal in + |> mapToSignal { loaded -> Signal<(LoadedStickerPack, StickerSettings), NoError> in if !loaded { - return .single(.fetching) + return .single((.fetching, stickerSettings)) } else { - return .single(next) + return .single((next, stickerSettings)) } } default: - return .single(next) + return .single((next, stickerSettings)) } } |> deliverOnMainQueue).start(next: { [weak self] next in if let strongSelf = self { - if case .none = next { + if case .none = next.0 { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongSelf.dismiss() } else { - strongSelf.controllerNode.updateStickerPack(next) - strongSelf.stickerPackContentsValue = next + strongSelf.controllerNode.updateStickerPack(next.0, stickerSettings: next.1) + strongSelf.stickerPackContentsValue = next.0 } } })) diff --git a/submodules/TelegramUI/TelegramUI/StickerPackPreviewControllerNode.swift b/submodules/TelegramUI/TelegramUI/StickerPackPreviewControllerNode.swift index 870c783b78..03c62c1ed6 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPackPreviewControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPackPreviewControllerNode.swift @@ -6,6 +6,7 @@ import SwiftSignalKit import Postbox import TelegramCore import TelegramPresentationData +import TelegramUIPreferences private struct StickerPackPreviewGridEntry: Comparable, Identifiable { let index: Int @@ -75,6 +76,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol private var stickerPack: LoadedStickerPack? private var stickerPackUpdated = false private var stickerPackInitiallyInstalled : Bool? + private var stickerSettings: StickerSettings? private var currentItems: [StickerPackPreviewGridEntry] = [] @@ -131,13 +133,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol super.init() - self.interaction = StickerPackPreviewInteraction(sendSticker: { [weak self] item in - if let strongSelf = self, let sendSticker = strongSelf.sendSticker { - /*if sendSticker(item.file) { - strongSelf.cancel?() - }*/ - } - }) + self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false) self.backgroundColor = nil self.isOpaque = false @@ -510,16 +506,16 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol } else { dismissOnAction = true } - if let stickerPack = self.stickerPack { + if let stickerPack = self.stickerPack, let stickerSettings = self.stickerSettings { switch stickerPack { case let .result(info, items, installed): if installed { let _ = removeStickerPackInteractively(postbox: self.context.account.postbox, id: info.id, option: .delete).start() - updateStickerPack(.result(info: info, items: items, installed: false)) + self.updateStickerPack(.result(info: info, items: items, installed: false), stickerSettings: stickerSettings) } else { let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start() if !dismissOnAction { - updateStickerPack(.result(info: info, items: items, installed: true)) + self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings) } } if dismissOnAction { @@ -566,9 +562,13 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol }) } - func updateStickerPack(_ stickerPack: LoadedStickerPack) { + func updateStickerPack(_ stickerPack: LoadedStickerPack, stickerSettings: StickerSettings) { self.stickerPack = stickerPack + self.stickerSettings = stickerSettings self.stickerPackUpdated = true + + self.interaction.playAnimatedStickers = stickerSettings.loopAnimatedStickers + if let _ = self.containerLayout { self.dequeueUpdateStickerPack() } diff --git a/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift b/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift index 412ed8e834..06b0bdc2b1 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift @@ -8,11 +8,10 @@ import Postbox final class StickerPackPreviewInteraction { var previewedItem: StickerPreviewPeekItem? + var playAnimatedStickers: Bool - let sendSticker: (StickerPackItem) -> Void - - init(sendSticker: @escaping (StickerPackItem) -> Void) { - self.sendSticker = sendSticker + init(playAnimatedStickers: Bool) { + self.playAnimatedStickers = playAnimatedStickers } } @@ -53,12 +52,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode { override var isVisibleInGrid: Bool { didSet { - self.animationNode?.visibility = self.isVisibleInGrid + self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true } } - private let textNode: ASTextNode - private var currentIsPreviewing = false private let stickerFetchedDisposable = MetaDisposable() @@ -74,16 +71,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode { override init() { self.imageNode = TransformImageNode() self.imageNode.isLayerBacked = !smartInvertColorsEnabled() - //self.imageNode.alphaTransitionOnFirstUpdate = true - - self.textNode = ASTextNode() - self.textNode.isUserInteractionEnabled = false - self.textNode.displaysAsynchronously = true super.init() self.addSubnode(self.imageNode) - //self.addSubnode(self.textNode) } deinit { @@ -100,14 +91,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode { self.interaction = interaction if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem { - var text = "" - for attribute in stickerItem.file.attributes { - if case let .Sticker(displayText, _, _) = attribute { - text = displayText - break - } - } - self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: .black, paragraphAlignment: .right) if let dimensions = stickerItem.file.dimensions { if stickerItem.file.isAnimatedSticker { self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0))) @@ -121,7 +104,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { } } self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached) - self.animationNode?.visibility = self.isVisibleInGrid + self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) } else { if let animationNode = self.animationNode { @@ -157,9 +140,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode { animationNode.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - imageSize.width) / 2.0), y: (bounds.size.height - imageSize.height) / 2.0), size: imageSize) animationNode.updateLayout(size: imageSize) } - let boundingFrame = CGRect(origin: CGPoint(x: floor((bounds.size.width - boundingSize.width) / 2.0), y: (bounds.size.height - boundingSize.height) / 2.0), size: boundingSize) - let textSize = CGSize(width: 32.0, height: 24.0) - self.textNode.frame = CGRect(origin: CGPoint(x: boundingFrame.maxX - 1.0 - textSize.width, y: boundingFrame.height + 10.0 - textSize.height), size: textSize) } } @@ -169,7 +149,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { @objc func imageNodeTap(_ recognizer: UITapGestureRecognizer) { if let interaction = self.interaction, let (_, item, _) = self.currentState, case .ended = recognizer.state { - interaction.sendSticker(item) + //interaction.sendSticker(item) } } diff --git a/submodules/TelegramUIPreferences/Sources/MediaAutoDownloadSettings.swift b/submodules/TelegramUIPreferences/Sources/MediaAutoDownloadSettings.swift index dd1e6abf7a..4240e5768f 100644 --- a/submodules/TelegramUIPreferences/Sources/MediaAutoDownloadSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/MediaAutoDownloadSettings.swift @@ -151,14 +151,14 @@ public struct MediaAutoDownloadSettings: PreferencesEntry, Equatable { public static var defaultSettings: MediaAutoDownloadSettings { let mb: Int32 = 1024 * 1024 let presets = MediaAutoDownloadPresets(low: MediaAutoDownloadCategories(basePreset: .low, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false), - video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false), - file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false)), - medium: MediaAutoDownloadCategories(basePreset: .medium, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false), - video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int32(2.5 * CGFloat(mb)), predownload: false), - file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false)), - high: MediaAutoDownloadCategories(basePreset: .high, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false), - video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true), - file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false))) + video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false), + file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false)), + medium: MediaAutoDownloadCategories(basePreset: .medium, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false), + video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int32(2.5 * CGFloat(mb)), predownload: false), + file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false)), + high: MediaAutoDownloadCategories(basePreset: .high, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false), + video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true), + file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false))) let saveDownloadedPhotos = MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 0, predownload: false) return MediaAutoDownloadSettings(presets: presets, cellular: MediaAutoDownloadConnection(enabled: true, preset: .medium, custom: nil), wifi: MediaAutoDownloadConnection(enabled: true, preset: .high, custom: nil), saveDownloadedPhotos: saveDownloadedPhotos, autoplayGifs: true, autoplayVideos: true, downloadInBackground: true) diff --git a/submodules/TelegramUIPreferences/Sources/StickerSettings.swift b/submodules/TelegramUIPreferences/Sources/StickerSettings.swift index 79244f2e03..4eea5be1da 100644 --- a/submodules/TelegramUIPreferences/Sources/StickerSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/StickerSettings.swift @@ -10,21 +10,25 @@ public enum EmojiStickerSuggestionMode: Int32 { public struct StickerSettings: PreferencesEntry, Equatable { public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode + public var loopAnimatedStickers: Bool public static var defaultSettings: StickerSettings { - return StickerSettings(emojiStickerSuggestionMode: .all) + return StickerSettings(emojiStickerSuggestionMode: .all, loopAnimatedStickers: true) } - init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode) { + init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, loopAnimatedStickers: Bool) { self.emojiStickerSuggestionMode = emojiStickerSuggestionMode + self.loopAnimatedStickers = loopAnimatedStickers } public init(decoder: PostboxDecoder) { self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: decoder.decodeInt32ForKey("emojiStickerSuggestionMode", orElse: 0))! + self.loopAnimatedStickers = decoder.decodeBoolForKey("loopAnimatedStickers", orElse: true) } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode") + encoder.encodeBool(self.loopAnimatedStickers, forKey: "loopAnimatedStickers") } public func isEqual(to: PreferencesEntry) -> Bool { @@ -36,11 +40,15 @@ public struct StickerSettings: PreferencesEntry, Equatable { } public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool { - return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode + return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.loopAnimatedStickers == rhs.loopAnimatedStickers } public func withUpdatedEmojiStickerSuggestionMode(_ emojiStickerSuggestionMode: EmojiStickerSuggestionMode) -> StickerSettings { - return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode) + return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers) + } + + public func withUpdatedLoopAnimatedStickers(_ loopAnimatedStickers: Bool) -> StickerSettings { + return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: loopAnimatedStickers) } }