diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index bad4ac6864..dbdfd53f41 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -6371,6 +6371,7 @@ Sorry for the inconvenience."; "StickerPacks.ActionDelete" = "Delete"; "StickerPacks.ActionArchive" = "Archive"; +"StickerPacks.ActionUnarchive" = "Unarchive"; "StickerPacks.ActionShare" = "Share"; "StickerPacks.DeleteStickerPacksConfirmation_0" = "Delete %@ Sticker Sets"; @@ -8956,3 +8957,6 @@ Sorry for the inconvenience."; "Conversation.ShareLinkTooltip.Chat.One" = "Link shared with **%@**"; "Conversation.ShareLinkTooltip.TwoChats.One" = "Link shared with **%@** and **%@**"; "Conversation.ShareLinkTooltip.ManyChats.One" = "Link shared with **%@** and %@ others"; + +"StickerPacks.UnarchiveStickerPacksConfirmation_1" = "Unarchive %@ Pack"; +"StickerPacks.UnarchiveStickerPacksConfirmation_any" = "Unarchive %@ Packs"; diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceAwaitingAccountResetControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceAwaitingAccountResetControllerNode.swift index def523172c..53f0c8f3e3 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceAwaitingAccountResetControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceAwaitingAccountResetControllerNode.swift @@ -162,7 +162,7 @@ final class AuthorizationSequenceAwaitingAccountResetControllerNode: ASDisplayNo items.append(AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 20.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.timerTitleNode, size: timerTitleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 100.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - items.append(AuthorizationLayoutItem(node: self.self.timerValueNode, size: timerValueSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.timerValueNode, size: timerValueSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.resetNode, size: resetSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 20.0)), items: items, transition: transition, failIfDoesNotFit: false) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 44262dfbec..9a15970be5 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2651,14 +2651,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } private func readAllInFilter(id: Int32) { - guard case let .chatList(groupId) = self.chatListDisplayNode.effectiveContainerNode.location else { - return - } for filter in self.chatListDisplayNode.mainContainerNode.availableFilters { if case let .filter(filter) = filter, case let .filter(filterId, _, _, data) = filter, filterId == id { let filterPredicate = chatListFilterPredicate(filter: data) var markItems: [(groupId: EngineChatList.Group, filterPredicate: ChatListFilterPredicate?)] = [] - markItems.append((groupId, filterPredicate)) + markItems.append((.root, filterPredicate)) for additionalGroupId in filterPredicate.includeAdditionalPeerGroupIds { markItems.append((EngineChatList.Group(additionalGroupId), filterPredicate)) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 3381739f4a..e28137e442 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -170,13 +170,13 @@ private enum ChatListRecentEntry: Comparable, Identifiable { status = .custom(string: strings.GroupInfo_ParticipantCount(Int32(group.participantCount)), multiline: false, isActive: false, icon: nil) } else if case let .channel(channel) = primaryPeer { if case .group = channel.info { - if let count = peer.subpeerSummary?.count { + if let count = peer.subpeerSummary?.count, count > 0 { status = .custom(string: strings.GroupInfo_ParticipantCount(Int32(count)), multiline: false, isActive: false, icon: nil) } else { status = .custom(string: strings.Group_Status, multiline: false, isActive: false, icon: nil) } } else { - if let count = peer.subpeerSummary?.count { + if let count = peer.subpeerSummary?.count, count > 0 { status = .custom(string: strings.Conversation_StatusSubscribers(Int32(count)), multiline: false, isActive: false, icon: nil) } else { status = .custom(string: strings.Channel_Status, multiline: false, isActive: false, icon: nil) diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index e25d74ea62..4145a747e9 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -408,8 +408,10 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { self.navigationBarOrigin = navigationBarFrame.origin.y - let isLandscape = layout.size.width > layout.size.height - + var isLandscape = layout.size.width > layout.size.height + if case .regular = layout.metrics.widthClass { + isLandscape = false + } if let navigationBar = self.navigationBar { if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar { navigationBarFrame.origin.y -= navigationLayout.defaultContentHeight @@ -420,6 +422,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { navigationBarFrame.size.height += NavigationBar.defaultSecondaryContentHeight //navigationBarFrame.origin.y += NavigationBar.defaultSecondaryContentHeight } + navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: statusBarHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition) if !transition.isAnimated { navigationBar.layer.removeAnimation(forKey: "bounds") diff --git a/submodules/DrawingUI/Sources/DrawingPenTool.swift b/submodules/DrawingUI/Sources/DrawingPenTool.swift index d31bb660d8..a132bb6b64 100644 --- a/submodules/DrawingUI/Sources/DrawingPenTool.swift +++ b/submodules/DrawingUI/Sources/DrawingPenTool.swift @@ -140,7 +140,7 @@ final class PenTool: DrawingElement { self.start = newStart } - if !element.isEraser && !element.isBlur { + if element.hasAnimations { let count = CGFloat(element.segments.count - self.segmentsCount) if count > 0 { let dryingPath = CGMutablePath() @@ -193,7 +193,7 @@ final class PenTool: DrawingElement { return } - if !element.isEraser && !element.isBlur { + if element.hasAnimations { let dryingPath = CGMutablePath() for segment in element.activeSegments { let segmentPath = element.pathForSegment(segment) @@ -216,7 +216,7 @@ final class PenTool: DrawingElement { context.scaleBy(x: 1.0 / parent.drawScale.width, y: 1.0 / parent.drawScale.height) element.drawSegments(in: context, from: parent.start, to: parent.segmentsCount) - if !element.isEraser && !element.isBlur { + if element.hasAnimations { element.drawActiveSegments(in: context, strokeWidth: !parent.isActiveDrying ? element.renderLineWidth * parent.dryingFactor : nil) } else { element.drawActiveSegments(in: context, strokeWidth: nil) @@ -266,6 +266,12 @@ final class PenTool: DrawingElement { private var segmentPaths: [Int: CGPath] = [:] private var useCubicBezier = true + + private let animationsEnabled: Bool + + var hasAnimations: Bool { + return self.animationsEnabled && !self.isEraser && !self.isBlur + } var isValid: Bool { if self.hasArrow { @@ -284,7 +290,7 @@ final class PenTool: DrawingElement { return normalizeDrawingRect(combinedBounds, drawingSize: self.drawingSize) } - required init(drawingSize: CGSize, color: DrawingColor, lineWidth: CGFloat, hasArrow: Bool, isEraser: Bool, isBlur: Bool, blurredImage: UIImage?) { + required init(drawingSize: CGSize, color: DrawingColor, lineWidth: CGFloat, hasArrow: Bool, isEraser: Bool, isBlur: Bool, blurredImage: UIImage?, animationsEnabled: Bool) { self.uuid = UUID() self.drawingSize = drawingSize self.color = isEraser || isBlur ? DrawingColor(rgb: 0x000000) : color @@ -292,6 +298,7 @@ final class PenTool: DrawingElement { self.isEraser = isEraser self.isBlur = isBlur self.blurredImage = blurredImage + self.animationsEnabled = animationsEnabled let minLineWidth = max(1.0, max(drawingSize.width, drawingSize.height) * 0.002) let maxLineWidth = max(10.0, max(drawingSize.width, drawingSize.height) * 0.07) diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 087f0c124a..995cdc3864 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -1994,6 +1994,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U var drawingView: DrawingView { if self._drawingView == nil, let controller = self.controller { self._drawingView = DrawingView(size: controller.size) + self._drawingView?.animationsEnabled = self.context.sharedContext.energyUsageSettings.fullTranslucency self._drawingView?.shouldBegin = { [weak self] _ in if let strongSelf = self { if strongSelf._entitiesView?.hasSelection == true { diff --git a/submodules/DrawingUI/Sources/DrawingView.swift b/submodules/DrawingUI/Sources/DrawingView.swift index 142032d07c..58a57dd239 100644 --- a/submodules/DrawingUI/Sources/DrawingView.swift +++ b/submodules/DrawingUI/Sources/DrawingView.swift @@ -103,6 +103,8 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt private var isDrawing = false private var drawingGestureStartTimestamp: Double? + var animationsEnabled = true + private func loadTemplates() { func load(_ name: String) { if let url = getAppBundle().url(forResource: name, withExtension: "json"), @@ -920,7 +922,8 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt hasArrow: false, isEraser: false, isBlur: false, - blurredImage: nil + blurredImage: nil, + animationsEnabled: self.animationsEnabled ) element = penTool case .arrow: @@ -931,7 +934,8 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt hasArrow: true, isEraser: false, isBlur: false, - blurredImage: nil + blurredImage: nil, + animationsEnabled: self.animationsEnabled ) element = penTool case .marker: @@ -956,7 +960,8 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt hasArrow: false, isEraser: false, isBlur: true, - blurredImage: self.preparedBlurredImage + blurredImage: self.preparedBlurredImage, + animationsEnabled: self.animationsEnabled ) element = penTool case .eraser: @@ -967,7 +972,8 @@ public final class DrawingView: UIView, UIGestureRecognizerDelegate, UIPencilInt hasArrow: false, isEraser: true, isBlur: false, - blurredImage: nil + blurredImage: nil, + animationsEnabled: self.animationsEnabled ) element = penTool } diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 890828c391..fb1fd3a18d 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -651,41 +651,76 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { let _ = titleApply() let _ = statusApply() - let _ = installApply() switch item.control { - case .none: - strongSelf.installationActionNode.isHidden = true - strongSelf.installationActionBackgroundNode.isHidden = true - strongSelf.selectionIconNode.isHidden = true - case let .installation(installed): - strongSelf.installationActionBackgroundNode.isHidden = false - strongSelf.installationActionNode.isHidden = false - strongSelf.selectionIconNode.isHidden = true - strongSelf.installationActionNode.isUserInteractionEnabled = !installed + case .none: + strongSelf.installationActionNode.isHidden = true + strongSelf.installationActionBackgroundNode.isHidden = true + strongSelf.selectionIconNode.isHidden = true + let _ = installApply() + case let .installation(installed): + strongSelf.installationActionBackgroundNode.isHidden = false + if let previousControl = currentItem?.control, case .check = previousControl { + strongSelf.installationActionBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + transition.animateTransformScale(node: strongSelf.installationActionBackgroundNode, from: 0.01) + transition.animatePosition(node: strongSelf.installationActionBackgroundNode, from: strongSelf.installationActionBackgroundNode.position.offsetBy(dx: 64.0, dy: 0.0)) + + strongSelf.installTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + transition.animateTransformScale(node: strongSelf.installTextNode, from: 0.01) + transition.animatePosition(node: strongSelf.installTextNode, from: strongSelf.installTextNode.position.offsetBy(dx: 64.0, dy: 0.0)) + } + strongSelf.installationActionNode.isHidden = false + strongSelf.selectionIconNode.isHidden = true + strongSelf.installationActionNode.isUserInteractionEnabled = !installed - if let backgroundImage = installationBackgroundImage { - strongSelf.installationActionBackgroundNode.image = backgroundImage + if let backgroundImage = installationBackgroundImage { + strongSelf.installationActionBackgroundNode.image = backgroundImage + } + + let installationActionFrame = CGRect(origin: CGPoint(x: params.width - rightInset - installWidth - 16.0, y: 0.0), size: CGSize(width: installWidth, height: layout.contentSize.height)) + strongSelf.installationActionNode.frame = installationActionFrame + + let buttonFrame = CGRect(origin: CGPoint(x: params.width - rightInset - installWidth - 16.0, y: installationActionFrame.minY + floor((installationActionFrame.size.height - 28.0) / 2.0)), size: CGSize(width: installWidth, height: 28.0)) + strongSelf.installationActionBackgroundNode.frame = buttonFrame + strongSelf.installTextNode.frame = CGRect(origin: CGPoint(x: buttonFrame.minX + floorToScreenPixels((buttonFrame.width - installLayout.size.width) / 2.0), y: buttonFrame.minY + floorToScreenPixels((buttonFrame.height - installLayout.size.height) / 2.0) + 1.0), size: installLayout.size) + let _ = installApply() + case .selection: + strongSelf.installationActionBackgroundNode.isHidden = true + strongSelf.installationActionNode.isHidden = true + strongSelf.selectionIconNode.isHidden = false + if let image = checkImage { + strongSelf.selectionIconNode.image = image + strongSelf.selectionIconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - image.size.width - floor((44.0 - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size) + } + let _ = installApply() + case .check: + if let previousControl = currentItem?.control, case .installation = previousControl { + strongSelf.installationActionBackgroundNode.alpha = 0.0 + strongSelf.installationActionBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + strongSelf.installationActionBackgroundNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, completion: { [weak self] _ in + if let strongSelf = self { + strongSelf.installationActionBackgroundNode.isHidden = true + strongSelf.installationActionBackgroundNode.alpha = 1.0 + } + }) + transition.animatePosition(node: strongSelf.installationActionBackgroundNode, to: CGPoint(x: 64.0, y: 0.0), additive: true) + + if let installTextSnapshot = strongSelf.installTextNode.view.snapshotContentTree() { + installTextSnapshot.frame = strongSelf.installTextNode.frame + strongSelf.installTextNode.view.superview?.addSubview(installTextSnapshot) + installTextSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + installTextSnapshot.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) + transition.animatePosition(layer: installTextSnapshot.layer, from: .zero, to: CGPoint(x: 64.0, y: 0.0), additive: true, completion: { [weak installTextSnapshot] _ in + installTextSnapshot?.removeFromSuperview() + }) } - - let installationActionFrame = CGRect(origin: CGPoint(x: params.width - rightInset - installWidth - 16.0, y: 0.0), size: CGSize(width: installWidth, height: layout.contentSize.height)) - strongSelf.installationActionNode.frame = installationActionFrame - - let buttonFrame = CGRect(origin: CGPoint(x: params.width - rightInset - installWidth - 16.0, y: installationActionFrame.minY + floor((installationActionFrame.size.height - 28.0) / 2.0)), size: CGSize(width: installWidth, height: 28.0)) - strongSelf.installationActionBackgroundNode.frame = buttonFrame - strongSelf.installTextNode.frame = CGRect(origin: CGPoint(x: buttonFrame.minX + floorToScreenPixels((buttonFrame.width - installLayout.size.width) / 2.0), y: buttonFrame.minY + floorToScreenPixels((buttonFrame.height - installLayout.size.height) / 2.0) + 1.0), size: installLayout.size) - case .selection: - strongSelf.installationActionNode.isHidden = true + let _ = installApply() + } else { strongSelf.installationActionBackgroundNode.isHidden = true - strongSelf.selectionIconNode.isHidden = false - if let image = checkImage { - strongSelf.selectionIconNode.image = image - strongSelf.selectionIconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - image.size.width - floor((44.0 - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size) - } - case .check: - strongSelf.installationActionNode.isHidden = true - strongSelf.installationActionBackgroundNode.isHidden = true - strongSelf.selectionIconNode.isHidden = true + let _ = installApply() + } + strongSelf.installationActionNode.isHidden = true + strongSelf.selectionIconNode.isHidden = true } if strongSelf.backgroundNode.supernode == nil { diff --git a/submodules/PremiumUI/Sources/PremiumStarComponent.swift b/submodules/PremiumUI/Sources/PremiumStarComponent.swift index 5988c21f73..aa9a28c7b5 100644 --- a/submodules/PremiumUI/Sources/PremiumStarComponent.swift +++ b/submodules/PremiumUI/Sources/PremiumStarComponent.swift @@ -8,7 +8,7 @@ import GZip import AppBundle import LegacyComponents -private let sceneVersion: Int = 3 +private let sceneVersion: Int = 4 private func deg2rad(_ number: Float) -> Float { return number * .pi / 180 diff --git a/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift index 4ed1549c49..c72c87f6c8 100644 --- a/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift @@ -13,6 +13,7 @@ import AccountContext import StickerPackPreviewUI import ItemListStickerPackItem import UndoUI +import ShareController public enum ArchivedStickerPacksControllerMode { case stickers @@ -27,13 +28,15 @@ private final class ArchivedStickerPacksControllerArguments { let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void let addPack: (StickerPackCollectionInfo) -> Void let removePack: (StickerPackCollectionInfo) -> Void + let togglePackSelected: (ItemCollectionId) -> Void - init(context: AccountContext, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping (StickerPackCollectionInfo) -> Void, removePack: @escaping (StickerPackCollectionInfo) -> Void) { + init(context: AccountContext, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping (StickerPackCollectionInfo) -> Void, removePack: @escaping (StickerPackCollectionInfo) -> Void, togglePackSelected: @escaping (ItemCollectionId) -> Void) { self.context = context self.openStickerPack = openStickerPack self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions self.addPack = addPack self.removePack = removePack + self.togglePackSelected = togglePackSelected } } @@ -48,7 +51,7 @@ private enum ArchivedStickerPacksEntryId: Hashable { private enum ArchivedStickerPacksEntry: ItemListNodeEntry { case info(PresentationTheme, String) - case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing) + case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing, Bool?) var section: ItemListSectionId { switch self { @@ -61,7 +64,7 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { switch self { case .info: return .index(0) - case let .pack(_, _, _, info, _, _, _, _, _): + case let .pack(_, _, _, info, _, _, _, _, _, _): return .pack(info.id) } } @@ -74,8 +77,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { } else { return false } - 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 { + case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsEnabled, lhsEditing, lhsSelected): + if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsEnabled, rhsEditing, rhsSelected) = rhs { if lhsIndex != rhsIndex { return false } @@ -103,6 +106,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { if lhsEditing != rhsEditing { return false } + if lhsSelected != rhsSelected { + return false + } return true } else { return false @@ -119,9 +125,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 @@ -134,8 +140,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { switch self { case let .info(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) - case let .pack(_, _, _, info, topItem, count, animatedStickers, enabled, editing): - return ItemListStickerPackItem(presentationData: presentationData, context: arguments.context, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .installation(installed: false), editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: { + case let .pack(_, _, _, info, topItem, count, animatedStickers, enabled, editing, selected): + return ItemListStickerPackItem(presentationData: presentationData, context: arguments.context, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: editing.editing ? .check(checked: selected ?? false) : .installation(installed: false), editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: { arguments.openStickerPack(info) }, setPackIdWithRevealedOptions: { current, previous in arguments.setPackIdWithRevealedOptions(current, previous) @@ -144,6 +150,7 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { }, removePack: { arguments.removePack(info) }, toggleSelected: { + arguments.togglePackSelected(info.id) }) } } @@ -151,17 +158,20 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry { private struct ArchivedStickerPacksControllerState: Equatable { let editing: Bool + let selectedPackIds: Set? let packIdWithRevealedOptions: ItemCollectionId? let removingPackIds: Set init() { self.editing = false + self.selectedPackIds = nil self.packIdWithRevealedOptions = nil self.removingPackIds = Set() } - init(editing: Bool, packIdWithRevealedOptions: ItemCollectionId?, removingPackIds: Set) { + init(editing: Bool, selectedPackIds: Set?, packIdWithRevealedOptions: ItemCollectionId?, removingPackIds: Set) { self.editing = editing + self.selectedPackIds = selectedPackIds self.packIdWithRevealedOptions = packIdWithRevealedOptions self.removingPackIds = removingPackIds } @@ -170,6 +180,9 @@ private struct ArchivedStickerPacksControllerState: Equatable { if lhs.editing != rhs.editing { return false } + if lhs.selectedPackIds != rhs.selectedPackIds { + return false + } if lhs.packIdWithRevealedOptions != rhs.packIdWithRevealedOptions { return false } @@ -181,15 +194,19 @@ private struct ArchivedStickerPacksControllerState: Equatable { } func withUpdatedEditing(_ editing: Bool) -> ArchivedStickerPacksControllerState { - return ArchivedStickerPacksControllerState(editing: editing, packIdWithRevealedOptions: self.packIdWithRevealedOptions, removingPackIds: self.removingPackIds) + return ArchivedStickerPacksControllerState(editing: editing, selectedPackIds: self.selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions, removingPackIds: self.removingPackIds) + } + + func withUpdatedSelectedPackIds(_ selectedPackIds: Set?) -> ArchivedStickerPacksControllerState { + return ArchivedStickerPacksControllerState(editing: self.editing, selectedPackIds: selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions, removingPackIds: self.removingPackIds) } func withUpdatedPackIdWithRevealedOptions(_ packIdWithRevealedOptions: ItemCollectionId?) -> ArchivedStickerPacksControllerState { - return ArchivedStickerPacksControllerState(editing: self.editing, packIdWithRevealedOptions: packIdWithRevealedOptions, removingPackIds: self.removingPackIds) + return ArchivedStickerPacksControllerState(editing: self.editing, selectedPackIds: self.selectedPackIds, packIdWithRevealedOptions: packIdWithRevealedOptions, removingPackIds: self.removingPackIds) } func withUpdatedRemovingPackIds(_ removingPackIds: Set) -> ArchivedStickerPacksControllerState { - return ArchivedStickerPacksControllerState(editing: editing, packIdWithRevealedOptions: self.self.packIdWithRevealedOptions, removingPackIds: removingPackIds) + return ArchivedStickerPacksControllerState(editing: self.editing, selectedPackIds: self.selectedPackIds, packIdWithRevealedOptions: self.packIdWithRevealedOptions, removingPackIds: removingPackIds) } } @@ -216,7 +233,7 @@ private func archivedStickerPacksControllerEntries(context: AccountContext, pres countTitle = presentationData.strings.StickerPack_StickerCount(item.info.count) } - entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, countTitle, context.sharedContext.energyUsageSettings.loopStickers, !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false, selectable: true))) + entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, countTitle, context.sharedContext.energyUsageSettings.loopStickers, !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false, selectable: true), state.selectedPackIds?.contains(item.info.id))) index += 1 } } @@ -295,10 +312,10 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv return .complete() } else { return context.engine.stickers.addStickerPackInteractively(info: info, items: items) - |> ignoreValues - |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [StickerPackItem]), NoError> in - } - |> then(.single((info, items))) + |> ignoreValues + |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [StickerPackItem]), NoError> in + } + |> then(.single((info, items))) } case .fetching: break @@ -357,22 +374,22 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv } if remove { let applyPacks: Signal = stickerPacks.get() - |> filter { $0 != nil } - |> take(1) - |> deliverOnMainQueue - |> mapToSignal { packs -> Signal in - if let packs = packs { - var updatedPacks = packs - for i in 0 ..< updatedPacks.count { - if updatedPacks[i].info.id == info.id { - updatedPacks.remove(at: i) - break - } + |> filter { $0 != nil } + |> take(1) + |> deliverOnMainQueue + |> mapToSignal { packs -> Signal in + if let packs = packs { + var updatedPacks = packs + for i in 0 ..< updatedPacks.count { + if updatedPacks[i].info.id == info.id { + updatedPacks.remove(at: i) + break } - stickerPacks.set(.single(updatedPacks)) } - - return .complete() + stickerPacks.set(.single(updatedPacks)) + } + + return .complete() } removePackDisposables.set((context.engine.stickers.removeArchivedStickerPack(info: info) |> then(applyPacks) |> deliverOnMainQueue).start(completed: { updateState { state in @@ -382,49 +399,155 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv } }), forKey: info.id) } + }, togglePackSelected: { packId in + updateState { state in + if var selectedPackIds = state.selectedPackIds { + if selectedPackIds.contains(packId) { + selectedPackIds.remove(packId) + } else { + selectedPackIds.insert(packId) + } + return state.withUpdatedSelectedPackIds(selectedPackIds) + } else { + return state + } + } }) var previousPackCount: Int? 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, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in - var stickerSettings = StickerSettings.defaultSettings - if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) { - stickerSettings = value - } - - var rightNavigationButton: ItemListNavigationButton? - if let packs = packs, packs.count != 0 { - if state.editing { - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { + |> deliverOnMainQueue + |> map { presentationData, state, packs, installedView, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in + var stickerSettings = StickerSettings.defaultSettings + if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) { + stickerSettings = value + } + + var rightNavigationButton: ItemListNavigationButton? + var toolbarItem: ItemListToolbarItem? + if let packs = packs, packs.count != 0 { + if state.editing { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { + updateState { + $0.withUpdatedEditing(false) + } + }) + + let selectedCount = Int32(state.selectedPackIds?.count ?? 0) + toolbarItem = StickersToolbarItem(selectedCount: selectedCount, actions: [.init(title: presentationData.strings.StickerPacks_ActionDelete, isEnabled: selectedCount > 0, action: { + let actionSheet = ActionSheetController(presentationData: presentationData) + var items: [ActionSheetItem] = [] + + let title: String + switch mode { + case .emoji: + title = presentationData.strings.StickerPacks_DeleteEmojiPacksConfirmation(selectedCount) + default: + title = presentationData.strings.StickerPacks_DeleteStickerPacksConfirmation(selectedCount) + } + + items.append(ActionSheetButtonItem(title: title, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + updateState { - $0.withUpdatedEditing(false) + $0.withUpdatedEditing(false).withUpdatedSelectedPackIds(nil) } - }) - } else { - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { + + for entry in packs { + if let selectedPackIds = state.selectedPackIds, selectedPackIds.contains(entry.info.id) { + let _ = (context.engine.stickers.loadedStickerPack(reference: .id(id: entry.info.id.id, accessHash: entry.info.accessHash), forceActualized: false) + |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [StickerPackItem]), NoError> in + switch result { + case let .result(info, items, installed): + if installed { + return .complete() + } else { + return context.engine.stickers.addStickerPackInteractively(info: info, items: items) + |> ignoreValues + |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [StickerPackItem]), NoError> in + } + |> then(.single((info, items))) + } + case .fetching: + break + case .none: + break + } + return .complete() + }).start() + } + } + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, nil) + }), .init(title: presentationData.strings.StickerPacks_ActionUnarchive, isEnabled: selectedCount > 0, action: { + let actionSheet = ActionSheetController(presentationData: presentationData) + var items: [ActionSheetItem] = [] + items.append(ActionSheetButtonItem(title: presentationData.strings.StickerPacks_UnarchiveStickerPacksConfirmation(selectedCount), color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + updateState { - $0.withUpdatedEditing(true) + $0.withUpdatedEditing(false).withUpdatedSelectedPackIds(nil) } - }) - } + + var packIds: [ItemCollectionId] = [] + for entry in packs { + if let selectedPackIds = state.selectedPackIds, selectedPackIds.contains(entry.info.id) { + packIds.append(entry.info.id) + } + } + + let _ = context.engine.stickers.removeStickerPacksInteractively(ids: packIds, option: .archive).start() + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, nil) + }), .init(title: presentationData.strings.StickerPacks_ActionShare, isEnabled: selectedCount > 0, action: { + updateState { + $0.withUpdatedEditing(true).withUpdatedSelectedPackIds(nil) + } + + var packNames: [String] = [] + for entry in packs { + if let selectedPackIds = state.selectedPackIds, selectedPackIds.contains(entry.info.id) { + packNames.append(entry.info.shortName) + } + } + let text = packNames.map { "https://t.me/addstickers/\($0)" }.joined(separator: "\n") + let shareController = ShareController(context: context, subject: .text(text), externalShare: true) + presentControllerImpl?(shareController, nil) + })]) + } else { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { + updateState { + $0.withUpdatedEditing(true).withUpdatedSelectedPackIds(Set()) + } + }) } - - let previous = previousPackCount - previousPackCount = packs?.count - - var emptyStateItem: ItemListControllerEmptyStateItem? - if packs == nil { - emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) - } - - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.StickerPacksSettings_ArchivedPacks), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: archivedStickerPacksControllerEntries(context: context, 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() + } + + let previous = previousPackCount + previousPackCount = packs?.count + + var emptyStateItem: ItemListControllerEmptyStateItem? + if packs == nil { + emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) + } + + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.StickerPacksSettings_ArchivedPacks), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) + + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: archivedStickerPacksControllerEntries(context: context, presentationData: presentationData, state: state, packs: packs, installedView: installedView, stickerSettings: stickerSettings), style: .blocks, emptyStateItem: emptyStateItem, toolbarItem: toolbarItem, animateChanges: previous != nil && packs != nil && (previous! != 0 && previous! >= packs!.count - 10)) + return (controllerState, (listState, arguments)) + } |> afterDisposed { + actionsDisposable.dispose() } let controller = ItemListController(context: context, state: signal) diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index 0df2a932c7..5c244678cc 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -59,9 +59,8 @@ private final class InstalledStickerPacksControllerArguments { } private enum InstalledStickerPacksSection: Int32 { - case settings case categories - case order + case settings case stickers } @@ -85,8 +84,6 @@ private enum InstalledStickerPacksEntryId: Hashable { private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry { case suggestOptions(PresentationTheme, String, String) case largeEmoji(PresentationTheme, String, Bool) - case animatedStickers(PresentationTheme, String, Bool) - case animatedStickersInfo(PresentationTheme, String) case trending(PresentationTheme, String, Int32) case archived(PresentationTheme, String, Int32, [ArchivedStickerPackItem]?) case masks(PresentationTheme, String) @@ -101,12 +98,10 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry { var section: ItemListSectionId { switch self { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .suggestAnimatedEmoji: - return InstalledStickerPacksSection.settings.rawValue case .trending, .masks, .emoji, .quickReaction, .archived: return InstalledStickerPacksSection.categories.rawValue - case .packOrder, .packOrderInfo: - return InstalledStickerPacksSection.order.rawValue + case .suggestOptions, .largeEmoji, .suggestAnimatedEmoji, .packOrder, .packOrderInfo: + return InstalledStickerPacksSection.settings.rawValue case .packsTitle, .pack, .packsInfo: return InstalledStickerPacksSection.stickers.rawValue } @@ -114,36 +109,32 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry { var stableId: InstalledStickerPacksEntryId { switch self { - case .suggestOptions: - return .index(0) - case .largeEmoji: - return .index(1) - case .animatedStickers: - return .index(2) - case .animatedStickersInfo: - return .index(3) case .trending: - return .index(4) + return .index(0) case .archived: - return .index(5) + return .index(1) case .emoji: - return .index(6) + return .index(2) case .masks: - return .index(7) + return .index(3) case .quickReaction: - return .index(8) + return .index(4) + case .suggestOptions: + return .index(5) + case .largeEmoji: + return .index(6) case .suggestAnimatedEmoji: - return .index(9) + return .index(7) case .packOrder: - return .index(10) + return .index(8) case .packOrderInfo: - return .index(11) + return .index(9) case .packsTitle: - return .index(12) + return .index(10) case let .pack(_, _, _, info, _, _, _, _, _, _): return .pack(info.id) case .packsInfo: - return .index(13) + return .index(11) } } @@ -191,18 +182,6 @@ private indirect 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 .packOrder(lhsTheme, lhsText, lhsValue): if case let .packOrder(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { return true @@ -274,113 +253,99 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry { static func <(lhs: InstalledStickerPacksEntry, rhs: InstalledStickerPacksEntry) -> Bool { switch lhs { - case .suggestOptions: - switch rhs { - case .suggestOptions: - return false - default: - return true - } - case .largeEmoji: - switch rhs { - case .suggestOptions, .largeEmoji: - return false - default: - return true - } - case .animatedStickers: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers: - return false - default: - return true - } - case .animatedStickersInfo: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo: - return false - default: - return true - } + case .trending: + switch rhs { case .trending: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending: - return false - default: - return true - } - case .archived: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived: - return false - default: - return true - } - case .masks: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks: - return false - default: - return true - } - case .emoji: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji: - return false - default: - return true - } - case .quickReaction: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction: - return false - default: - return true - } - case .packOrder: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction, .packOrder: - return false - default: - return true + return false + default: + return true } - case .packOrderInfo: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction, .packOrder, .packOrderInfo: - return false - default: - return true + case .archived: + switch rhs { + case .trending, .archived: + return false + default: + return true } - case .suggestAnimatedEmoji: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .archived, .masks, .emoji, .quickReaction, .packOrder, .packOrderInfo, .suggestAnimatedEmoji: - return false - default: - return true - } - case .packsTitle: - switch rhs { - case .suggestOptions, .largeEmoji, .animatedStickers, .animatedStickersInfo, .trending, .masks, .emoji, .quickReaction, .archived, .packOrder, .packOrderInfo, .suggestAnimatedEmoji, .packsTitle: - return false - default: - return true - } - case let .pack(lhsIndex, _, _, _, _, _, _, _, _, _): - switch rhs { - case let .pack(rhsIndex, _, _, _, _, _, _, _, _, _): - return lhsIndex < rhsIndex - case .packsInfo: - return true - default: - return false - } + case .masks: + switch rhs { + case .trending, .archived, .masks: + return false + default: + return true + } + case .emoji: + switch rhs { + case .trending, .archived, .masks, .emoji: + return false + default: + return true + } + case .quickReaction: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction: + return false + default: + return true + } + case .suggestOptions: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions: + return false + default: + return true + } + case .largeEmoji: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji: + return false + default: + return true + } + case .packOrder: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder: + return false + default: + return true + } + case .packOrderInfo: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder, .packOrderInfo: + return false + default: + return true + } + case .suggestAnimatedEmoji: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder, .packOrderInfo, .suggestAnimatedEmoji: + return false + default: + return true + } + case .packsTitle: + switch rhs { + case .trending, .archived, .masks, .emoji, .quickReaction, .suggestOptions, .largeEmoji, .packOrder, .packOrderInfo, .suggestAnimatedEmoji, .packsTitle: + return false + default: + return true + } + case let .pack(lhsIndex, _, _, _, _, _, _, _, _, _): + switch rhs { + case let .pack(rhsIndex, _, _, _, _, _, _, _, _, _): + return lhsIndex < rhsIndex case .packsInfo: - switch rhs { - case .packsInfo: - return false - default: - return false - } + return true + default: + return false + } + case .packsInfo: + switch rhs { + case .packsInfo: + return false + default: + return false + } } } @@ -395,11 +360,6 @@ private indirect enum InstalledStickerPacksEntry: ItemListNodeEntry { return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleLargeEmoji(value) }) - case let .animatedStickers(_, text, value): - return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { _ in - }) - case let .animatedStickersInfo(_, text): - return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .trending(theme, text, count): return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Trending")?.precomposed(), title: text, label: count == 0 ? "" : "\(count)", labelStyle: .badge(theme.list.itemAccentColor), sectionId: self.section, style: .blocks, action: { arguments.openFeatured() @@ -535,6 +495,17 @@ private func installedStickerPacksControllerEntries(context: AccountContext, pre switch mode { case .general, .modal: + if !featured.isEmpty { + entries.append(.trending(presentationData.theme, presentationData.strings.StickerPacksSettings_TrendingStickers, Int32(featured.count))) + } + if let archived = archived, !archived.isEmpty { + entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedPacks, Int32(archived.count), archived)) + } + entries.append(.emoji(presentationData.theme, presentationData.strings.StickerPacksSettings_Emoji, emojiCount)) + if let quickReaction = quickReaction, let availableReactions = availableReactions { + entries.append(.quickReaction(presentationData.strings.Settings_QuickReactionSetup_NavigationTitle, quickReaction, availableReactions)) + } + let suggestString: String switch stickerSettings.emojiStickerSuggestionMode { case .none: @@ -545,22 +516,7 @@ private func installedStickerPacksControllerEntries(context: AccountContext, pre suggestString = presentationData.strings.Stickers_SuggestAdded } entries.append(.suggestOptions(presentationData.theme, presentationData.strings.Stickers_SuggestStickers, suggestString)) - entries.append(.largeEmoji(presentationData.theme, presentationData.strings.Appearance_LargeEmoji, presentationData.largeEmoji)) - - if !featured.isEmpty { - entries.append(.trending(presentationData.theme, presentationData.strings.StickerPacksSettings_TrendingStickers, Int32(featured.count))) - } - if let archived = archived, !archived.isEmpty { - entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedPacks, Int32(archived.count), archived)) - } - - entries.append(.emoji(presentationData.theme, presentationData.strings.StickerPacksSettings_Emoji, emojiCount)) - - if let quickReaction = quickReaction, let availableReactions = availableReactions { - entries.append(.quickReaction(presentationData.strings.Settings_QuickReactionSetup_NavigationTitle, quickReaction, availableReactions)) - } - entries.append(.packOrder(presentationData.theme, presentationData.strings.StickerPacksSettings_DynamicOrder, stickerSettings.dynamicPackOrder)) entries.append(.packOrderInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_DynamicOrderInfo)) @@ -1283,7 +1239,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta return controller } -private class StickersToolbarItem: ItemListToolbarItem { +class StickersToolbarItem: ItemListToolbarItem { private let selectedCount: Int32 init(selectedCount: Int32, actions: [Action]) { diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index 02365f72e7..04b67e4e6f 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -103,7 +103,7 @@ final class TabBarControllerNode: ASDisplayNode { transition.updateFrame(node: toolbarNode, frame: tabBarFrame) toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) } else { - let toolbarNode = ToolbarNode(theme: ToolbarTheme(tabBarTheme: self.theme), left: { [weak self] in + let toolbarNode = ToolbarNode(theme: ToolbarTheme(tabBarTheme: self.theme), displaySeparator: true, left: { [weak self] in self?.toolbarActionSelected(.left) }, right: { [weak self] in self?.toolbarActionSelected(.right) diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index f553736c93..39bef9090a 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -527,7 +527,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi return speedList } - private func contextMenuSpeedItems(scheduleTooltip: @escaping (MediaNavigationAccessoryPanel.ChangeType) -> Void) -> Signal { + private func contextMenuSpeedItems(scheduleTooltip: @escaping (MediaNavigationAccessoryPanel.ChangeType?) -> Void) -> Signal { var presetItems: [ContextMenuItem] = [] let previousValue = self.playbackBaseRate?.doubleValue ?? 1.0 @@ -548,6 +548,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi return nil } }, action: { [weak self] _, f in + scheduleTooltip(nil) f(.default) self?.setRate?(rate, .preset) diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift index 1764fbb6b4..0a5e243d86 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift @@ -1030,7 +1030,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { return speedList } - private func contextMenuSpeedItems(scheduleTooltip: @escaping (MediaNavigationAccessoryPanel.ChangeType) -> Void) -> Signal { + private func contextMenuSpeedItems(scheduleTooltip: @escaping (MediaNavigationAccessoryPanel.ChangeType?) -> Void) -> Signal { var presetItems: [ContextMenuItem] = [] let previousValue = self.currentRate?.doubleValue ?? 1.0 @@ -1051,6 +1051,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { return nil } }, action: { [weak self] _, f in + scheduleTooltip(nil) f(.default) self?.control?(.setBaseRate(rate)) diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index ceac7166a2..bd1f1dfe9c 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -262,7 +262,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { strongSelf.contentOffsetChanged?(offset) } } - + self.mainContainerNode?.contentOffsetChanged = { [weak self] offset in guard let strongSelf = self else { return @@ -569,10 +569,19 @@ final class PeerSelectionControllerNode: ASDisplayNode { } else { var selectedPeerIds: [PeerId] = [] var selectedPeerMap: [PeerId: Peer] = [:] - strongSelf.chatListNode?.updateState { state in - selectedPeerIds = Array(state.selectedPeerIds) - selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() }) - return state + if let mainContainerNode = strongSelf.mainContainerNode { + mainContainerNode.currentItemNode.updateState { state in + selectedPeerIds = Array(state.selectedPeerIds) + selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() }) + return state + } + } + if let chatListNode = strongSelf.chatListNode { + chatListNode.updateState { state in + selectedPeerIds = Array(state.selectedPeerIds) + selectedPeerMap = state.selectedPeerMap.mapValues({ $0._asPeer() }) + return state + } } if !selectedPeerIds.isEmpty { var selectedPeers: [Peer] = [] @@ -598,10 +607,21 @@ final class PeerSelectionControllerNode: ASDisplayNode { return ContactListNodeGroupSelectionState() }) } else { - self.chatListNode?.updateState { state in - var state = state - state.editing = true - return state + if let mainContainerNode = self.mainContainerNode { + mainContainerNode.currentItemNode.selectionCountChanged = { [weak self] count in + self?.textInputPanelNode?.updateSendButtonEnabled(count > 0, animated: true) + } + mainContainerNode.currentItemNode.updateState({ state in + var state = state + state.editing = true + return state + }) + } else if let chatListNode = self.chatListNode { + chatListNode.updateState { state in + var state = state + state.editing = true + return state + } } } } @@ -691,7 +711,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } insets.top += navigationBarHeight - insets.bottom = max(insets.bottom, cleanInsets.bottom + 44.0) + insets.bottom = max(insets.bottom, toolbarHeight) insets.left += layout.safeInsets.left insets.right += layout.safeInsets.right @@ -829,9 +849,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { contactListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) contactListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) - let contactsInsets = insets - - contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: contactsInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) + contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition) } if let searchDisplayController = self.searchDisplayController {