diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 8c6579dc49..5d55c6e923 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9158,3 +9158,6 @@ Sorry for the inconvenience."; "Conversation.Theme.SetCustomColor" = "Set Custom"; "Appearance.ShowNextMediaOnTap" = "Show Next Media on Tap"; + +"WebApp.LaunchMoreInfo" = "More about this bot"; +"WebApp.LaunchConfirmation" = "To launch this web app, you will connect to its website."; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 3173913004..0869ada5a6 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -756,6 +756,7 @@ public protocol SharedAccountContext: AnyObject { var currentInAppNotificationSettings: Atomic { get } var currentMediaInputSettings: Atomic { get } var currentStickerSettings: Atomic { get } + var currentMediaDisplaySettings: Atomic { get } var energyUsageSettings: EnergyUsageSettings { get } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 234eb1e768..92c5873c06 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -138,6 +138,7 @@ public final class NavigationBackgroundNode: ASDisplayNode { private var _color: UIColor private var enableBlur: Bool + private var enableSaturation: Bool public var effectView: UIVisualEffectView? private let backgroundNode: ASDisplayNode @@ -152,9 +153,10 @@ public final class NavigationBackgroundNode: ASDisplayNode { } } - public init(color: UIColor, enableBlur: Bool = true) { + public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true) { self._color = .clear self.enableBlur = enableBlur + self.enableSaturation = enableSaturation self.backgroundNode = ASDisplayNode() @@ -195,10 +197,12 @@ public final class NavigationBackgroundNode: ASDisplayNode { if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { sublayer.backgroundColor = nil sublayer.isOpaque = false - let allowedKeys: [String] = [ - "colorSaturate", + var allowedKeys: [String] = [ "gaussianBlur" ] + if self.enableSaturation { + allowedKeys.append("colorSaturate") + } sublayer.filters = filters.filter { filter in guard let filter = filter as? NSObject else { return true diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index b1fa7a8a98..5823fa9537 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -1211,7 +1211,9 @@ public class GalleryController: ViewController, StandalonePresentableController, }) } }) - self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction) + + let disableTapNavigation = !(self.context.sharedContext.currentMediaDisplaySettings.with { $0 }.showNextMediaOnTap) + self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction, disableTapNavigation: disableTapNavigation) self.displayNodeDidLoad() self.galleryNode.statusBar = self.statusBar diff --git a/submodules/ImageBlur/Sources/ImageBlur.swift b/submodules/ImageBlur/Sources/ImageBlur.swift index 05fb8da1be..1340bddc0b 100644 --- a/submodules/ImageBlur/Sources/ImageBlur.swift +++ b/submodules/ImageBlur/Sources/ImageBlur.swift @@ -39,6 +39,7 @@ public func blurredImage(_ image: UIImage, radius: CGFloat, iterations: Int = 3) let source = CFDataGetBytePtr(providerData) memcpy(inBuffer.data, source, bytes) + for _ in 0 ..< iterations { vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, tempData, 0, 0, boxSize, boxSize, nil, vImage_Flags(kvImageEdgeExtend)) diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index a3831910cd..2edaaea2d2 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -25,9 +25,9 @@ func presentCustomWallpaperPicker(context: AccountContext, present: @escaping (V controller.selectionBlock = { [weak legacyController] asset, _ in if let asset = asset { let controller = WallpaperGalleryController(context: context, source: .asset(asset.backingAsset)) - controller.apply = { [weak legacyController, weak controller] wallpaper, mode, cropRect in + controller.apply = { [weak legacyController, weak controller] wallpaper, mode, cropRect, brightness in if let legacyController = legacyController, let controller = controller { - uploadCustomWallpaper(context: context, wallpaper: wallpaper, mode: mode, cropRect: cropRect, completion: { [weak legacyController, weak controller] in + uploadCustomWallpaper(context: context, wallpaper: wallpaper, mode: mode, cropRect: cropRect, brightness: brightness, completion: { [weak legacyController, weak controller] in if let legacyController = legacyController, let controller = controller { legacyController.dismiss() controller.dismiss(forceAway: true) @@ -47,7 +47,7 @@ func presentCustomWallpaperPicker(context: AccountContext, present: @escaping (V }) } -func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, completion: @escaping () -> Void) { +func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, brightness: CGFloat?, completion: @escaping () -> Void) { let imageSignal: Signal switch wallpaper { case let .wallpaper(wallpaper, _): @@ -196,7 +196,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE }).start() } -public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, brightnessMultiplier: CGFloat?, peerId: PeerId, completion: @escaping () -> Void) { +public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, brightness: CGFloat?, peerId: PeerId, completion: @escaping () -> Void) { let imageSignal: Signal switch wallpaper { case let .wallpaper(wallpaper, _): @@ -276,7 +276,25 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa croppedImage = TGPhotoEditorCrop(image, nil, .up, 0.0, finalCropRect, false, CGSize(width: 1440.0, height: 2960.0), image.size, true) if mode.contains(.blur) { - croppedImage = blurredImage(croppedImage, radius: 20.0)! + croppedImage = blurredImage(croppedImage, radius: 30.0)! + } + + if let brightness, abs(brightness) > 0.01 { + if let updatedImage = generateImage(croppedImage.size, contextGenerator: { size, context in + let bounds = CGRect(origin: .zero, size: size) + if let cgImage = croppedImage.cgImage { + context.draw(cgImage, in: bounds) + } + if brightness > 0.0 { + context.setFillColor(UIColor(rgb: 0xffffff, alpha: brightness).cgColor) + context.setBlendMode(.overlay) + } else { + context.setFillColor(UIColor(rgb: 0x000000, alpha: brightness * -1.0).cgColor) + } + context.fill(bounds) + }) { + croppedImage = updatedImage + } } let thumbnailDimensions = finalCropRect.size.fitted(CGSize(width: 320.0, height: 320.0)) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift index 94bbdcb6d1..5ac533d4c9 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorsGridControllerNode.swift @@ -152,7 +152,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode { let wallpapers = entries.map { $0.wallpaper } let controller = WallpaperGalleryController(context: context, source: .list(wallpapers: wallpapers, central: wallpaper, type: .colors), mode: strongSelf.controller?.mode.galleryMode ?? .default) controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, _, _ in + controller.apply = { [weak self] wallpaper, _, _, _ in if let strongSelf = self, let mode = strongSelf.controller?.mode, case let .peer(peer) = mode, case let .wallpaper(wallpaperValue, _) = wallpaper { let _ = (strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: wallpaperValue) |> deliverOnMainQueue).start(completed: { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift index 14658adb61..cef1f6070c 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift @@ -120,9 +120,9 @@ public final class ThemeGridController: ViewController { self.displayNode = ThemeGridControllerNode(context: self.context, presentationData: self.presentationData, presentPreviewController: { [weak self] source in if let strongSelf = self { let controller = WallpaperGalleryController(context: strongSelf.context, source: source) - controller.apply = { [weak self, weak controller] wallpaper, options, cropRect in + controller.apply = { [weak self, weak controller] wallpaper, options, cropRect, brightness in if let strongSelf = self { - uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, completion: { [weak self, weak controller] in + uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, completion: { [weak self, weak controller] in if let strongSelf = self { strongSelf.deactivateSearch(animated: false) strongSelf.controllerNode.scrollToTop(animated: false) @@ -148,9 +148,9 @@ public final class ThemeGridController: ViewController { return } let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset)) - controller.apply = { [weak self, weak controller] wallpaper, options, cropRect in + controller.apply = { [weak self, weak controller] wallpaper, options, cropRect, brightness in if let strongSelf = self, let controller = controller { - uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, completion: { [weak controller] in + uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, completion: { [weak controller] in if let controller = controller { controller.dismiss(forceAway: true) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 93eab9cfd5..0e3fd9f894 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -58,13 +58,13 @@ private final class ThemeSettingsControllerArguments { let openBubbleSettings: () -> Void let openPowerSavingSettings: () -> Void let openStickersAndEmoji: () -> Void - let disableAnimations: (Bool) -> Void + let toggleShowNextMediaOnTap: (Bool) -> Void let selectAppIcon: (PresentationAppIcon) -> Void let editTheme: (PresentationCloudTheme) -> Void let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void let colorContextAction: (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void - init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, openThemeSettings: @escaping () -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, toggleNightTheme: @escaping (Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, openBubbleSettings: @escaping () -> Void, openPowerSavingSettings: @escaping () -> Void, openStickersAndEmoji: @escaping () -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (PresentationAppIcon) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void) { + init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, openThemeSettings: @escaping () -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, toggleNightTheme: @escaping (Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, openBubbleSettings: @escaping () -> Void, openPowerSavingSettings: @escaping () -> Void, openStickersAndEmoji: @escaping () -> Void, toggleShowNextMediaOnTap: @escaping (Bool) -> Void, selectAppIcon: @escaping (PresentationAppIcon) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void) { self.context = context self.selectTheme = selectTheme self.openThemeSettings = openThemeSettings @@ -77,7 +77,7 @@ private final class ThemeSettingsControllerArguments { self.openBubbleSettings = openBubbleSettings self.openPowerSavingSettings = openPowerSavingSettings self.openStickersAndEmoji = openStickersAndEmoji - self.disableAnimations = disableAnimations + self.toggleShowNextMediaOnTap = toggleShowNextMediaOnTap self.selectAppIcon = selectAppIcon self.editTheme = editTheme self.themeContextAction = themeContextAction @@ -128,7 +128,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case powerSaving case stickersAndEmoji case otherHeader(PresentationTheme, String) - case animations(PresentationTheme, String, Bool) + case showNextMediaOnTap(PresentationTheme, String, Bool) case animationsInfo(PresentationTheme, String) var section: ItemListSectionId { @@ -143,7 +143,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ThemeSettingsControllerSection.icon.rawValue case .powerSaving, .stickersAndEmoji: return ThemeSettingsControllerSection.message.rawValue - case .otherHeader, .animations, .animationsInfo: + case .otherHeader, .showNextMediaOnTap, .animationsInfo: return ThemeSettingsControllerSection.other.rawValue } } @@ -178,7 +178,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return 12 case .otherHeader: return 13 - case .animations: + case .showNextMediaOnTap: return 14 case .animationsInfo: return 15 @@ -271,8 +271,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } else { return false } - case let .animations(lhsTheme, lhsTitle, lhsValue): - if case let .animations(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue { + case let .showNextMediaOnTap(lhsTheme, lhsTitle, lhsValue): + if case let .showNextMediaOnTap(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue { return true } else { return false @@ -343,9 +343,9 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { }) case let .otherHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .animations(_, title, value): + case let .showNextMediaOnTap(_, title, value): return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in - arguments.disableAnimations(value) + arguments.toggleShowNextMediaOnTap(value) }, tag: ThemeSettingsEntryTag.animations) case let .animationsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) @@ -353,7 +353,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } } -private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, themeReference: PresentationThemeReference, availableThemes: [PresentationThemeReference], availableAppIcons: [PresentationAppIcon], currentAppIconName: String?, isPremium: Bool, chatThemes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]]) -> [ThemeSettingsControllerEntry] { +private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, mediaSettings: MediaDisplaySettings, themeReference: PresentationThemeReference, availableThemes: [PresentationThemeReference], availableAppIcons: [PresentationAppIcon], currentAppIconName: String?, isPremium: Bool, chatThemes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]]) -> [ThemeSettingsControllerEntry] { var entries: [ThemeSettingsControllerEntry] = [] let strings = presentationData.strings @@ -404,8 +404,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, } entries.append(.otherHeader(presentationData.theme, strings.Appearance_Other.uppercased())) - entries.append(.animations(presentationData.theme, strings.Appearance_ReduceMotion, presentationData.reduceMotion)) - entries.append(.animationsInfo(presentationData.theme, strings.Appearance_ReduceMotionInfo)) + entries.append(.showNextMediaOnTap(presentationData.theme, strings.Appearance_ShowNextMediaOnTap, mediaSettings.showNextMediaOnTap)) return entries } @@ -522,9 +521,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The pushControllerImpl?(installedStickerPacksController(context: context, mode: .general, archivedPacks: archivedStickerPacks, updatedPacks: { _ in })) }) - }, disableAnimations: { reduceMotion in - let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in - return current.withUpdatedReduceMotion(reduceMotion) + }, toggleShowNextMediaOnTap: { value in + let _ = updateMediaDisplaySettingsInteractively(accountManager: context.sharedContext.accountManager, { current in + return current.withUpdatedShowNextMediaOnTap(value) }).start() }, selectAppIcon: { icon in let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) @@ -1000,10 +999,11 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The }) }) - let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings, SharedDataKeys.chatThemes]), cloudThemes.get(), availableAppIcons, currentAppIconName.get(), removedThemeIndexesPromise.get(), animatedEmojiStickers, context.account.postbox.peerView(id: context.account.peerId)) + let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings, SharedDataKeys.chatThemes, ApplicationSpecificSharedDataKeys.mediaDisplaySettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get(), removedThemeIndexesPromise.get(), animatedEmojiStickers, context.account.postbox.peerView(id: context.account.peerId)) |> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName, removedThemeIndexes, animatedEmojiStickers, peerView -> (ItemListControllerState, (ItemListNodeState, Any)) in let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings - + let mediaSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaDisplaySettings]?.get(MediaDisplaySettings.self) ?? MediaDisplaySettings.defaultSettings + let isPremium = peerView.peers[peerView.peerId]?.isPremium ?? false let themeReference: PresentationThemeReference @@ -1041,7 +1041,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The chatThemes.insert(.builtin(.dayClassic), at: 0) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName, isPremium: isPremium, chatThemes: chatThemes, animatedEmojiStickers: animatedEmojiStickers), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, mediaSettings: mediaSettings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName, isPremium: isPremium, chatThemes: chatThemes, animatedEmojiStickers: animatedEmojiStickers), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) return (controllerState, (listState, arguments)) } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index e0e357ec39..3e8880194c 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -176,7 +176,7 @@ public class WallpaperGalleryController: ViewController { private let context: AccountContext private let source: WallpaperListSource private let mode: Mode - public var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, CGRect?) -> Void)? + public var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, CGRect?, CGFloat?) -> Void)? private let _ready = Promise() override public var ready: Promise { @@ -453,7 +453,7 @@ public class WallpaperGalleryController: ViewController { let entry = strongSelf.entries[centralItemNode.index] if case .peer = strongSelf.mode { - strongSelf.apply?(entry, options, centralItemNode.cropRect) + strongSelf.apply?(entry, options, centralItemNode.cropRect, centralItemNode.brightness) return } @@ -611,7 +611,7 @@ public class WallpaperGalleryController: ViewController { break } - strongSelf.apply?(entry, options, centralItemNode.cropRect) + strongSelf.apply?(entry, options, centralItemNode.cropRect, centralItemNode.brightness) } } } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index a63e7ff887..ab3dc13b3d 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -95,23 +95,27 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let wrapperNode: ASDisplayNode let imageNode: TransformImageNode let nativeNode: WallpaperBackgroundNode + let brightnessNode: ASDisplayNode private let statusNode: RadialStatusNode private let blurredNode: BlurredImageNode let cropNode: WallpaperCropNode - private var cancelButtonNode: WallpaperNavigationButtonNode - private var shareButtonNode: WallpaperNavigationButtonNode + private let cancelButtonNode: WallpaperNavigationButtonNode + private let shareButtonNode: WallpaperNavigationButtonNode - private var blurButtonNode: WallpaperOptionButtonNode - private var motionButtonNode: WallpaperOptionButtonNode - private var patternButtonNode: WallpaperOptionButtonNode - private var colorsButtonNode: WallpaperOptionButtonNode - private var playButtonNode: WallpaperNavigationButtonNode + private let blurButtonNode: WallpaperOptionButtonNode + private let motionButtonNode: WallpaperOptionButtonNode + private let patternButtonNode: WallpaperOptionButtonNode + private let colorsButtonNode: WallpaperOptionButtonNode + private let playButtonNode: WallpaperNavigationButtonNode + private let sliderNode: WallpaperSliderNode private let messagesContainerNode: ASDisplayNode private var messageNodes: [ListViewItemNode]? private var validMessages: [String]? + private let serviceBackgroundNode: NavigationBackgroundNode + fileprivate let _ready = Promise() private let fetchDisposable = MetaDisposable() private let statusDisposable = MetaDisposable() @@ -149,6 +153,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.statusNode.isUserInteractionEnabled = false self.blurredNode = BlurredImageNode() + self.brightnessNode = ASDisplayNode() self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) @@ -160,11 +165,18 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.motionButtonNode.setEnabled(false) self.patternButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_Pattern, value: .check(false)) self.patternButtonNode.setEnabled(false) + + self.serviceBackgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x000000, alpha: 0.35)) + + var sliderValueChangedImpl: ((CGFloat) -> Void)? + self.sliderNode = WallpaperSliderNode(minValue: 0.0, maxValue: 1.0, value: 0.5, valueChanged: { value, _ in + sliderValueChangedImpl?(value) + }) self.colorsButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_WallpaperColors, value: .colors(false, [.clear])) - self.cancelButtonNode = WallpaperNavigationButtonNode(content: .text(self.presentationData.strings.Common_Cancel)) - self.shareButtonNode = WallpaperNavigationButtonNode(content: .icon(image: UIImage(bundleImageName: "Chat/Links/Share"), size: CGSize(width: 28.0, height: 28.0))) + self.cancelButtonNode = WallpaperNavigationButtonNode(content: .text(self.presentationData.strings.Common_Cancel), dark: false) + self.shareButtonNode = WallpaperNavigationButtonNode(content: .icon(image: UIImage(bundleImageName: "Chat/Links/Share"), size: CGSize(width: 28.0, height: 28.0)), dark: false) self.playButtonPlayImage = generateImage(CGSize(width: 48.0, height: 48.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -193,7 +205,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.playButtonRotateImage = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRotateIcon"), color: .white) - self.playButtonNode = WallpaperNavigationButtonNode(content: .icon(image: self.playButtonPlayImage, size: CGSize(width: 48.0, height: 48.0))) + self.playButtonNode = WallpaperNavigationButtonNode(content: .icon(image: self.playButtonPlayImage, size: CGSize(width: 48.0, height: 48.0)), dark: true) super.init() @@ -217,6 +229,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.addSubnode(self.wrapperNode) //self.addSubnode(self.statusNode) + self.addSubnode(self.serviceBackgroundNode) self.addSubnode(self.messagesContainerNode) self.addSubnode(self.blurButtonNode) @@ -224,9 +237,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.addSubnode(self.patternButtonNode) self.addSubnode(self.colorsButtonNode) self.addSubnode(self.playButtonNode) + self.addSubnode(self.sliderNode) self.addSubnode(self.cancelButtonNode) self.addSubnode(self.shareButtonNode) + self.imageNode.addSubnode(self.brightnessNode) + self.blurButtonNode.addTarget(self, action: #selector(self.toggleBlur), forControlEvents: .touchUpInside) self.motionButtonNode.addTarget(self, action: #selector(self.toggleMotion), forControlEvents: .touchUpInside) self.patternButtonNode.addTarget(self, action: #selector(self.togglePattern), forControlEvents: .touchUpInside) @@ -234,6 +250,24 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.playButtonNode.addTarget(self, action: #selector(self.togglePlay), forControlEvents: .touchUpInside) self.cancelButtonNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) self.shareButtonNode.addTarget(self, action: #selector(self.actionPressed), forControlEvents: .touchUpInside) + + sliderValueChangedImpl = { [weak self] value in + if let self { + let value = (value - 0.5) * 2.0 + if value < 0.0 { + self.brightnessNode.backgroundColor = UIColor(rgb: 0x000000) + self.brightnessNode.layer.compositingFilter = nil + self.brightnessNode.alpha = value * -1.0 + } else if value > 0.0 { + self.brightnessNode.backgroundColor = UIColor(rgb: 0xffffff) + self.brightnessNode.layer.compositingFilter = "overlayBlendMode" + self.brightnessNode.alpha = value + } else { + self.brightnessNode.layer.compositingFilter = nil + self.brightnessNode.alpha = 0.0 + } + } + } } deinit { @@ -255,6 +289,18 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } } + var brightness: CGFloat? { + guard let entry = self.entry else { + return nil + } + switch entry { + case .asset, .contextResult: + return (self.sliderNode.value - 0.5) * 2.0 + default: + return nil + } + } + override func ready() -> Signal { return self._ready.get() } @@ -721,7 +767,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } func setBlurEnabled(_ enabled: Bool, animated: Bool) { - let blurRadius: CGFloat = 45.0 + let blurRadius: CGFloat = 30.0 var animated = animated if animated, let (layout, _) = self.validLayout { @@ -732,13 +778,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { if enabled { if self.blurredNode.supernode == nil { - if self.cropNode.supernode != nil { - self.blurredNode.frame = self.imageNode.bounds - self.imageNode.addSubnode(self.blurredNode) - } else { - self.blurredNode.frame = self.imageNode.bounds - self.imageNode.addSubnode(self.blurredNode) - } + self.blurredNode.frame = self.imageNode.bounds + self.imageNode.insertSubnode(self.blurredNode, at: 0) } if animated { @@ -914,12 +955,15 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let buttonSize = CGSize(width: maxButtonWidth, height: 30.0) let alpha = 1.0 - min(1.0, max(0.0, abs(offset.y) / 50.0)) - let additionalYOffset: CGFloat = 0.0 - /*if self.patternButtonNode.isSelected { - additionalYOffset = -235.0 - } else if self.colorsButtonNode.isSelected { - additionalYOffset = -235.0 - }*/ + var additionalYOffset: CGFloat = 0.0 + if let source = self.source { + switch source { + case .asset, .contextResult: + additionalYOffset -= 44.0 + default: + break + } + } let buttonSpacing: CGFloat = 18.0 @@ -937,13 +981,17 @@ final class WallpaperGalleryItemNode: GalleryItemNode { var motionFrame = centerButtonFrame var motionAlpha: CGFloat = 0.0 - + var colorsFrame = CGRect(origin: CGPoint(x: rightButtonFrame.maxX - colorsButtonSize.width, y: rightButtonFrame.minY), size: colorsButtonSize) var colorsAlpha: CGFloat = 0.0 let playFrame = CGRect(origin: CGPoint(x: centerButtonFrame.midX - playButtonSize.width / 2.0, y: centerButtonFrame.midY - playButtonSize.height / 2.0), size: playButtonSize) var playAlpha: CGFloat = 0.0 + let sliderSize = CGSize(width: 268.0, height: 30.0) + let sliderFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - sliderSize.width) / 2.0) + offset.x, y: layout.size.height - toolbarHeight - layout.intrinsicInsets.bottom - 52.0 + offset.y), size: sliderSize) + var sliderIsHidden = true + let cancelSize = self.cancelButtonNode.measure(layout.size) let cancelFrame = CGRect(origin: CGPoint(x: 16.0 + offset.x, y: 16.0), size: cancelSize) @@ -958,11 +1006,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode { blurFrame = leftButtonFrame motionAlpha = 1.0 motionFrame = rightButtonFrame + sliderIsHidden = false case .contextResult: blurAlpha = 1.0 blurFrame = leftButtonFrame motionAlpha = 1.0 motionFrame = rightButtonFrame + sliderIsHidden = false case let .wallpaper(wallpaper, _): switch wallpaper { case .builtin: @@ -1047,17 +1097,17 @@ final class WallpaperGalleryItemNode: GalleryItemNode { transition.updateAlpha(node: self.playButtonNode, alpha: playAlpha * alpha) transition.updateSublayerTransformScale(node: self.playButtonNode, scale: max(0.1, playAlpha)) + transition.updateFrame(node: self.sliderNode, frame: sliderFrame) + self.sliderNode.updateLayout(size: sliderFrame.size) + self.sliderNode.isHidden = sliderIsHidden + transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame) transition.updateFrame(node: self.shareButtonNode, frame: shareFrame) } private func updateMessagesLayout(layout: ContainerViewLayout, offset: CGPoint, transition: ContainedViewLayoutTransition) { - let bottomInset: CGFloat = 132.0 + var bottomInset: CGFloat = 132.0 - if self.patternButtonNode.isSelected || self.colorsButtonNode.isSelected { - //bottomInset = 350.0 - } - var items: [ListViewItem] = [] let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)) let otherPeerId = self.context.account.peerId @@ -1125,6 +1175,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { case .asset, .contextResult: topMessageText = presentationData.strings.WallpaperPreview_CropTopText bottomMessageText = presentationData.strings.WallpaperPreview_CropBottomText + bottomInset += 44.0 case .customColor: topMessageText = presentationData.strings.WallpaperPreview_CustomColorTopText bottomMessageText = presentationData.strings.WallpaperPreview_CustomColorBottomText @@ -1188,6 +1239,14 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.messageNodes = messageNodes } + if let _ = serviceMessageText, let messageNodes = self.messageNodes, let node = messageNodes.last { + if let backgroundNode = node.subnodes?.first?.subnodes?.first?.subnodes?.first?.subnodes?.first { + let serviceBackgroundFrame = backgroundNode.view.convert(backgroundNode.bounds, to: self.view).offsetBy(dx: 0.0, dy: -1.0).insetBy(dx: 0.0, dy: -1.0) + transition.updateFrame(node: self.serviceBackgroundNode, frame: serviceBackgroundFrame) + self.serviceBackgroundNode.update(size: serviceBackgroundFrame.size, cornerRadius: serviceBackgroundFrame.height / 2.0, transition: transition) + } + } + let alpha = 1.0 - min(1.0, max(0.0, abs(offset.y) / 50.0)) if let messageNodes = self.messageNodes { @@ -1237,6 +1296,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } self.blurredNode.frame = self.imageNode.bounds } + self.brightnessNode.frame = self.imageNode.bounds let additionalYOffset: CGFloat = 0.0 diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift index 13c47595c0..7c595e0f15 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift @@ -17,39 +17,6 @@ enum WallpaperGalleryToolbarDoneButtonType { case none } -final class WallpaperLightButtonBackgroundNode: ASDisplayNode { - private let backgroundNode: NavigationBackgroundNode - private let overlayNode: ASDisplayNode - private let lightNode: ASDisplayNode - - override init() { - self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x000000, alpha: 0.01), enableBlur: true) - self.overlayNode = ASDisplayNode() - self.overlayNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.55) - self.overlayNode.layer.compositingFilter = "overlayBlendMode" - - self.lightNode = ASDisplayNode() - self.lightNode.backgroundColor = UIColor(rgb: 0xf2f2f2, alpha: 0.3) - - super.init() - - self.clipsToBounds = true - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.overlayNode) - //self.addSubnode(self.lightNode) - } - - func updateLayout(size: CGSize) { - let frame = CGRect(origin: .zero, size: size) - self.backgroundNode.frame = frame - self.overlayNode.frame = frame - self.lightNode.frame = frame - - self.backgroundNode.update(size: size, transition: .immediate) - } -} - final class WallpaperGalleryToolbarNode: ASDisplayNode { private var theme: PresentationTheme private let strings: PresentationStrings diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift index 5ccd6243de..ecec374692 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift @@ -34,6 +34,61 @@ private func generateColorsImage(diameter: CGFloat, colors: [UIColor]) -> UIImag }) } +final class WallpaperLightButtonBackgroundNode: ASDisplayNode { + private let backgroundNode: NavigationBackgroundNode + private let overlayNode: ASDisplayNode + private let lightNode: ASDisplayNode + + override init() { + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.3), enableBlur: true, enableSaturation: false) + self.overlayNode = ASDisplayNode() + self.overlayNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.75) + self.overlayNode.layer.compositingFilter = "overlayBlendMode" + + self.lightNode = ASDisplayNode() + self.lightNode.backgroundColor = UIColor(rgb: 0xf2f2f2, alpha: 0.2) + + super.init() + + self.clipsToBounds = true + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.overlayNode) + self.addSubnode(self.lightNode) + } + + func updateLayout(size: CGSize) { + let frame = CGRect(origin: .zero, size: size) + self.backgroundNode.frame = frame + self.overlayNode.frame = frame + self.lightNode.frame = frame + + self.backgroundNode.update(size: size, transition: .immediate) + } +} + +final class WallpaperOptionBackgroundNode: ASDisplayNode { + private let backgroundNode: NavigationBackgroundNode + + override init() { + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.3), enableBlur: true, enableSaturation: false) + + super.init() + + self.clipsToBounds = true + self.isUserInteractionEnabled = false + + self.addSubnode(self.backgroundNode) + } + + func updateLayout(size: CGSize) { + let frame = CGRect(origin: .zero, size: size) + self.backgroundNode.frame = frame + + self.backgroundNode.update(size: size, transition: .immediate) + } +} + final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { enum Content { case icon(image: UIImage?, size: CGSize) @@ -42,7 +97,7 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { private let content: Content - private let backgroundNode: WallpaperLightButtonBackgroundNode + private let backgroundNode: ASDisplayNode private let iconNode: ASImageNode @@ -52,10 +107,14 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { self.iconNode.image = generateTintedImage(image: image, color: .white) } - init(content: Content) { + init(content: Content, dark: Bool) { self.content = content - self.backgroundNode = WallpaperLightButtonBackgroundNode() + if dark { + self.backgroundNode = WallpaperOptionBackgroundNode() + } else { + self.backgroundNode = WallpaperLightButtonBackgroundNode() + } self.iconNode = ASImageNode() self.iconNode.displaysAsynchronously = false @@ -94,11 +153,6 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) { didSet { -// if self.buttonColor == UIColor(rgb: 0x000000, alpha: 0.3) { -// self.backgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.75), transition: .immediate) -// } else { -// self.backgroundNode.updateColor(color: self.buttonColor, transition: .immediate) -// } } } @@ -119,7 +173,11 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { let size = self.bounds.size self.backgroundNode.frame = self.bounds - self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size) + if let backgroundNode = self.backgroundNode as? WallpaperOptionBackgroundNode { + backgroundNode.updateLayout(size: self.backgroundNode.bounds.size) + } else if let backgroundNode = self.backgroundNode as? WallpaperLightButtonBackgroundNode { + backgroundNode.updateLayout(size: self.backgroundNode.bounds.size) + } self.backgroundNode.cornerRadius = size.height / 2.0 self.iconNode.frame = self.bounds @@ -132,7 +190,7 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { - private let backgroundNode: NavigationBackgroundNode + private let backgroundNode: WallpaperOptionBackgroundNode private let checkNode: CheckNode private let colorNode: ASImageNode @@ -172,9 +230,8 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { self._value = value self.title = title - self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x000000, alpha: 0.01)) - self.backgroundNode.cornerRadius = 14.0 - + self.backgroundNode = WallpaperOptionBackgroundNode() + self.checkNode = CheckNode(theme: CheckNodeTheme(backgroundColor: .white, strokeColor: .clear, borderColor: .white, overlayBorder: false, hasInset: false, hasShadow: false, borderWidth: 1.5)) self.checkNode.isUserInteractionEnabled = false @@ -186,6 +243,9 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { super.init() + self.clipsToBounds = true + self.cornerRadius = 14.0 + switch value { case let .check(selected): self.checkNode.isHidden = false @@ -202,6 +262,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { } self.addSubnode(self.backgroundNode) + self.addSubnode(self.checkNode) self.addSubnode(self.textNode) self.addSubnode(self.colorNode) @@ -227,11 +288,6 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) { didSet { -// if self.buttonColor == UIColor(rgb: 0x000000, alpha: 0.3) { -// self.backgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.75), transition: .immediate) -// } else { -// self.backgroundNode.updateColor(color: self.buttonColor, transition: .immediate) -// } } } @@ -322,7 +378,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { super.layout() self.backgroundNode.frame = self.bounds - self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: 15.0, transition: .immediate) + self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size) guard let _ = self.textSize else { return @@ -340,3 +396,147 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { } } } + +final class WallpaperSliderNode: ASDisplayNode { + let minValue: CGFloat + let maxValue: CGFloat + var value: CGFloat = 1.0 { + didSet { + if let size = self.validLayout { + self.updateLayout(size: size) + } + } + } + + private let backgroundNode: NavigationBackgroundNode + + private let foregroundNode: ASDisplayNode + private let foregroundLightNode: ASDisplayNode + private let leftIconNode: ASImageNode + private let rightIconNode: ASImageNode + + private let valueChanged: (CGFloat, Bool) -> Void + + private let hapticFeedback = HapticFeedback() + + private var validLayout: CGSize? + + init(minValue: CGFloat, maxValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) { + self.minValue = minValue + self.maxValue = maxValue + self.value = value + self.valueChanged = valueChanged + + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.3), enableBlur: true, enableSaturation: false) + + self.foregroundNode = ASDisplayNode() + self.foregroundNode.clipsToBounds = true + self.foregroundNode.cornerRadius = 3.0 + self.foregroundNode.isAccessibilityElement = false + self.foregroundNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.75) + self.foregroundNode.layer.compositingFilter = "overlayBlendMode" + self.foregroundNode.isUserInteractionEnabled = false + + self.foregroundLightNode = ASDisplayNode() + self.foregroundLightNode.clipsToBounds = true + self.foregroundLightNode.cornerRadius = 3.0 + self.foregroundLightNode.backgroundColor = UIColor(rgb: 0xf2f2f2, alpha: 0.2) + + self.leftIconNode = ASImageNode() + self.leftIconNode.displaysAsynchronously = false + self.leftIconNode.image = UIImage(bundleImageName: "Settings/WallpaperBrightnessMin") + self.leftIconNode.contentMode = .center + + self.rightIconNode = ASImageNode() + self.rightIconNode.displaysAsynchronously = false + self.rightIconNode.image = UIImage(bundleImageName: "Settings/WallpaperBrightnessMax") + self.rightIconNode.contentMode = .center + + super.init() + + self.clipsToBounds = true + self.cornerRadius = 15.0 + self.isUserInteractionEnabled = true + + self.addSubnode(self.backgroundNode) + + self.addSubnode(self.foregroundNode) + self.addSubnode(self.foregroundLightNode) + + self.addSubnode(self.leftIconNode) + self.addSubnode(self.rightIconNode) + } + + override func didLoad() { + super.didLoad() + + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) + self.view.addGestureRecognizer(panGestureRecognizer) + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.view.addGestureRecognizer(tapGestureRecognizer) + } + + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition = .immediate) { + self.validLayout = size + + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: .zero, size: size)) + self.backgroundNode.update(size: size, transition: transition) + + if let icon = self.leftIconNode.image { + transition.updateFrame(node: self.leftIconNode, frame: CGRect(origin: CGPoint(x: 7.0, y: floorToScreenPixels((size.height - icon.size.height) / 2.0)), size: icon.size)) + } + + if let icon = self.rightIconNode.image { + transition.updateFrame(node: self.rightIconNode, frame: CGRect(origin: CGPoint(x: size.width - icon.size.width - 6.0, y: floorToScreenPixels((size.height - icon.size.height) / 2.0)), size: icon.size)) + } + + let range = self.maxValue - self.minValue + let value = (self.value - self.minValue) / range + let foregroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: value * size.width, height: size.height)) + transition.updateFrameAdditive(node: self.foregroundNode, frame: foregroundFrame) + transition.updateFrameAdditive(node: self.foregroundLightNode, frame: foregroundFrame) + } + + @objc private func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) { + let range = self.maxValue - self.minValue + switch gestureRecognizer.state { + case .began: + break + case .changed: + let previousValue = self.value + + let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x + let delta = translation / self.bounds.width * range + self.value = max(self.minValue, min(self.maxValue, self.value + delta)) + gestureRecognizer.setTranslation(CGPoint(), in: gestureRecognizer.view) + + if self.value == 2.0 && previousValue != 2.0 { + self.hapticFeedback.impact(.soft) + } else if self.value == 1.0 && previousValue != 1.0 { + self.hapticFeedback.impact(.soft) + } else if self.value == 2.5 && previousValue != 2.5 { + self.hapticFeedback.impact(.soft) + } else if self.value == 0.05 && previousValue != 0.05 { + self.hapticFeedback.impact(.soft) + } + if abs(previousValue - self.value) >= 0.001 { + self.valueChanged(self.value, false) + } + case .ended: + let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x + let delta = translation / self.bounds.width * range + self.value = max(self.minValue, min(self.maxValue, self.value + delta)) + self.valueChanged(self.value, true) + default: + break + } + } + + @objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) { + let range = self.maxValue - self.minValue + let location = gestureRecognizer.location(in: gestureRecognizer.view) + self.value = max(self.minValue, min(self.maxValue, self.minValue + location.x / self.bounds.width * range)) + self.valueChanged(self.value, true) + } +} diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index 3458593c55..002043cccf 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -218,16 +218,18 @@ public final class InitialPresentationDataAndSettings { public let callListSettings: CallListSettings public let inAppNotificationSettings: InAppNotificationSettings public let mediaInputSettings: MediaInputSettings + public let mediaDisplaySettings: MediaDisplaySettings public let stickerSettings: StickerSettings public let experimentalUISettings: ExperimentalUISettings - public init(presentationData: PresentationData, automaticMediaDownloadSettings: MediaAutoDownloadSettings, autodownloadSettings: AutodownloadSettings, callListSettings: CallListSettings, inAppNotificationSettings: InAppNotificationSettings, mediaInputSettings: MediaInputSettings, stickerSettings: StickerSettings, experimentalUISettings: ExperimentalUISettings) { + public init(presentationData: PresentationData, automaticMediaDownloadSettings: MediaAutoDownloadSettings, autodownloadSettings: AutodownloadSettings, callListSettings: CallListSettings, inAppNotificationSettings: InAppNotificationSettings, mediaInputSettings: MediaInputSettings, mediaDisplaySettings: MediaDisplaySettings, stickerSettings: StickerSettings, experimentalUISettings: ExperimentalUISettings) { self.presentationData = presentationData self.automaticMediaDownloadSettings = automaticMediaDownloadSettings self.autodownloadSettings = autodownloadSettings self.callListSettings = callListSettings self.inAppNotificationSettings = inAppNotificationSettings self.mediaInputSettings = mediaInputSettings + self.mediaDisplaySettings = mediaDisplaySettings self.stickerSettings = stickerSettings self.experimentalUISettings = experimentalUISettings } @@ -242,6 +244,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +1.000000 1.000000 1.000000 scn +9.140625 16.273438 m +9.140625 16.648438 8.828125 16.960938 8.453125 16.960938 c +8.085938 16.960938 7.773438 16.648438 7.773438 16.273438 c +7.773438 14.632812 l +7.773438 14.265625 8.085938 13.953125 8.453125 13.953125 c +8.828125 13.953125 9.140625 14.265625 9.140625 14.632812 c +9.140625 16.273438 l +h +12.312500 13.296875 m +12.054688 13.031250 12.054688 12.601562 12.312500 12.335938 c +12.578125 12.078125 13.015625 12.070312 13.281250 12.335938 c +14.445312 13.500000 l +14.710938 13.765625 14.710938 14.210938 14.445312 14.468750 c +14.187500 14.734375 13.750000 14.734375 13.492188 14.468750 c +12.312500 13.296875 l +h +3.625000 12.335938 m +3.890625 12.078125 4.328125 12.078125 4.585938 12.335938 c +4.851562 12.585938 4.851562 13.039062 4.593750 13.296875 c +3.429688 14.468750 l +3.179688 14.726562 2.734375 14.734375 2.468750 14.468750 c +2.210938 14.210938 2.210938 13.765625 2.460938 13.507812 c +3.625000 12.335938 l +h +8.453125 12.468750 m +6.273438 12.468750 4.468750 10.664062 4.468750 8.476562 c +4.468750 6.296875 6.273438 4.492188 8.453125 4.492188 c +10.625000 4.492188 12.429688 6.296875 12.429688 8.476562 c +12.429688 10.664062 10.625000 12.468750 8.453125 12.468750 c +h +16.226562 7.796875 m +16.601562 7.796875 16.914062 8.109375 16.914062 8.476562 c +16.914062 8.843750 16.601562 9.156250 16.226562 9.156250 c +14.593750 9.156250 l +14.226562 9.156250 13.914062 8.843750 13.914062 8.476562 c +13.914062 8.109375 14.226562 7.796875 14.593750 7.796875 c +16.226562 7.796875 l +h +0.679688 9.156250 m +0.312500 9.156250 0.000000 8.843750 0.000000 8.476562 c +0.000000 8.109375 0.312500 7.796875 0.679688 7.796875 c +2.312500 7.796875 l +2.687500 7.796875 3.000000 8.109375 3.000000 8.476562 c +3.000000 8.843750 2.687500 9.156250 2.312500 9.156250 c +0.679688 9.156250 l +h +13.273438 4.609375 m +13.015625 4.875000 12.578125 4.875000 12.312500 4.609375 c +12.054688 4.351562 12.054688 3.914062 12.312500 3.648438 c +13.492188 2.476562 l +13.750000 2.218750 14.187500 2.226562 14.445312 2.484375 c +14.710938 2.750000 14.710938 3.187500 14.445312 3.445312 c +13.273438 4.609375 l +h +2.460938 3.453125 m +2.203125 3.195312 2.203125 2.757812 2.453125 2.492188 c +2.710938 2.234375 3.156250 2.226562 3.421875 2.484375 c +4.585938 3.648438 l +4.851562 3.906250 4.851562 4.343750 4.593750 4.609375 c +4.335938 4.867188 3.890625 4.867188 3.625000 4.609375 c +2.460938 3.453125 l +h +9.140625 2.320312 m +9.140625 2.695312 8.828125 3.007812 8.453125 3.007812 c +8.085938 3.007812 7.773438 2.695312 7.773438 2.320312 c +7.773438 0.679688 l +7.773438 0.312500 8.085938 0.000000 8.453125 0.000000 c +8.828125 0.000000 9.140625 0.312500 9.140625 0.679688 c +9.140625 2.320312 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 2760 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 16.914062 16.960938 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002850 00000 n +0000002873 00000 n +0000003046 00000 n +0000003120 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3179 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/WallpaperBrightnessMin.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperBrightnessMin.imageset/Contents.json new file mode 100644 index 0000000000..68cb6916eb --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperBrightnessMin.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "brightness_min.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/WallpaperBrightnessMin.imageset/brightness_min.pdf b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperBrightnessMin.imageset/brightness_min.pdf new file mode 100644 index 0000000000..ec3ccfc3af --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/WallpaperBrightnessMin.imageset/brightness_min.pdf @@ -0,0 +1,198 @@ +%PDF-1.7 + +1 0 obj + << /Length 2 0 R >> +stream +1.154297 0 0.087402 -0.137207 1.066895 1.066895 d1 + +endstream +endobj + +2 0 obj + 51 +endobj + +3 0 obj + [ 1.154297 ] +endobj + +4 0 obj + << /Length 5 0 R >> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (FigmaPDF) + /Ordering (FigmaPDF) + /Supplement 0 +>> def +/CMapName /A-B-C def +/CMapType 2 def +1 begincodespacerange +<00> +endcodespacerange +1 beginbfchar +<00> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +endstream +endobj + +5 0 obj + 336 +endobj + +6 0 obj + << /Subtype /Type3 + /CharProcs << /C0 1 0 R >> + /Encoding << /Type /Encoding + /Differences [ 0 /C0 ] + >> + /Widths 3 0 R + /FontBBox [ 0.000000 0.000000 0.000000 0.000000 ] + /FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ] + /Type /Font + /ToUnicode 4 0 R + /FirstChar 0 + /LastChar 0 + /Resources << >> + >> +endobj + +7 0 obj + << /Font << /F1 6 0 R >> >> +endobj + +8 0 obj + << /Length 9 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 -1.653809 1.365723 cm +1.000000 1.000000 1.000000 scn +0.342773 0.692383 m +h +9.000000 11.612793 m +9.468750 11.612793 9.856934 12.000977 9.856934 12.469727 c +9.856934 12.938477 9.468750 13.326660 9.000000 13.326660 c +8.531250 13.326660 8.143066 12.938477 8.143066 12.469727 c +8.143066 12.000977 8.531250 11.612793 9.000000 11.612793 c +h +4.407715 9.715820 m +4.883789 9.715820 5.264648 10.104004 5.264648 10.572754 c +5.264648 11.041504 4.883789 11.429688 4.407715 11.429688 c +3.938965 11.429688 3.550781 11.041504 3.550781 10.572754 c +3.550781 10.104004 3.938965 9.715820 4.407715 9.715820 c +h +13.592285 9.715820 m +14.061035 9.715820 14.449219 10.104004 14.449219 10.572754 c +14.449219 11.041504 14.061035 11.429688 13.592285 11.429688 c +13.123535 11.429688 12.735352 11.041504 12.735352 10.572754 c +12.735352 10.104004 13.123535 9.715820 13.592285 9.715820 c +h +9.000000 2.252441 m +11.036133 2.252441 12.735352 3.944336 12.735352 5.980469 c +12.735352 8.023926 11.036133 9.715820 9.000000 9.715820 c +6.963867 9.715820 5.271973 8.023926 5.271973 5.980469 c +5.271973 3.944336 6.963867 2.252441 9.000000 2.252441 c +h +2.510742 5.123535 m +2.979492 5.123535 3.367676 5.511719 3.367676 5.980469 c +3.367676 6.456543 2.979492 6.837402 2.510742 6.837402 c +2.041992 6.837402 1.653809 6.456543 1.653809 5.980469 c +1.653809 5.511719 2.041992 5.123535 2.510742 5.123535 c +h +15.489258 5.123535 m +15.958008 5.123535 16.346191 5.511719 16.346191 5.980469 c +16.346191 6.456543 15.958008 6.837402 15.489258 6.837402 c +15.020508 6.837402 14.632324 6.456543 14.632324 5.980469 c +14.632324 5.511719 15.020508 5.123535 15.489258 5.123535 c +h +13.592285 0.531250 m +14.061035 0.531250 14.449219 0.919434 14.449219 1.388184 c +14.449219 1.864258 14.061035 2.245117 13.592285 2.245117 c +13.123535 2.245117 12.735352 1.864258 12.735352 1.388184 c +12.735352 0.919434 13.123535 0.531250 13.592285 0.531250 c +h +4.407715 0.531250 m +4.883789 0.531250 5.264648 0.919434 5.264648 1.388184 c +5.264648 1.864258 4.883789 2.245117 4.407715 2.245117 c +3.938965 2.245117 3.550781 1.864258 3.550781 1.388184 c +3.550781 0.919434 3.938965 0.531250 4.407715 0.531250 c +h +9.000000 -1.365723 m +9.468750 -1.365723 9.856934 -0.977539 9.856934 -0.508789 c +9.856934 -0.032715 9.468750 0.348145 9.000000 0.348145 c +8.531250 0.348145 8.143066 -0.032715 8.143066 -0.508789 c +8.143066 -0.977539 8.531250 -1.365723 9.000000 -1.365723 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 -1.653809 1.365723 cm +BT +15.000000 0.000000 0.000000 15.000000 0.342773 0.692383 Tm +/F1 1.000000 Tf +[ (\000) ] TJ +ET +Q + +endstream +endobj + +9 0 obj + 2605 +endobj + +10 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 14.692383 14.692383 ] + /Resources 7 0 R + /Contents 8 0 R + /Parent 11 0 R + >> +endobj + +11 0 obj + << /Kids [ 10 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +12 0 obj + << /Pages 11 0 R + /Type /Catalog + >> +endobj + +xref +0 13 +0000000000 65535 f +0000000010 00000 n +0000000117 00000 n +0000000138 00000 n +0000000169 00000 n +0000000561 00000 n +0000000583 00000 n +0000000995 00000 n +0000001041 00000 n +0000003702 00000 n +0000003725 00000 n +0000003900 00000 n +0000003976 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 12 0 R + /Size 13 +>> +startxref +4037 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f0772f4229..a71255f408 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -845,7 +845,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.chatDisplayNode.dismissInput() let wallpaperPreviewController = WallpaperGalleryController(context: strongSelf.context, source: .wallpaper(wallpaper, nil, [], nil, nil, nil), mode: .peer(EnginePeer(peer), true)) - wallpaperPreviewController.apply = { wallpaper, options, _ in + wallpaperPreviewController.apply = { wallpaper, options, _, _ in let _ = (strongSelf.context.engine.themes.setExistingChatWallpaper(messageId: message.id, wallpaper: nil) |> deliverOnMainQueue).start(completed: { [weak wallpaperPreviewController] in wallpaperPreviewController?.dismiss() @@ -4264,12 +4264,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if value { openWebView() } else { - strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: strongSelf.presentationData.strings.WebApp_OpenWebViewAlertTitle, text: strongSelf.presentationData.strings.WebApp_OpenWebViewAlertText(botName).string, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { - if let strongSelf = self { - let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peer.id).start() - openWebView() - } - })], parseMarkdown: true), in: .window(.root), with: nil) + let controller = webAppLaunchConfirmationController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), commit: { + let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peer.id).start() + openWebView() + }, showMore: nil) + strongSelf.present(controller, in: .window(.root)) } }) }, activateAdAction: { [weak self] messageId in @@ -17173,7 +17172,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) } - private func openResolved(result: ResolvedUrl, sourceMessageId: MessageId?, forceExternal: Bool = false) { + private func openResolved(result: ResolvedUrl, sourceMessageId: MessageId?, forceExternal: Bool = false, concealed: Bool = false) { guard let peerId = self.chatLocation.peerId else { return } @@ -17230,7 +17229,28 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), attachBotStart: attachBotStart)) } case let .withBotApp(botAppStart): - strongSelf.presentBotApp(botApp: botAppStart.botApp, payload: botAppStart.payload) + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId.id)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + if let strongSelf = self, let peer { + let openBotApp = { [weak self] in + if let strongSelf = self { + strongSelf.presentBotApp(botApp: botAppStart.botApp, payload: botAppStart.payload) + } + } + if concealed { + let controller = webAppLaunchConfirmationController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, commit: { + openBotApp() + }, showMore: { [weak self] in + if let strongSelf = self { + strongSelf.openResolved(result: .peer(peer._asPeer(), .info), sourceMessageId: nil) + } + }) + strongSelf.present(controller, in: .window(.root)) + } else { + openBotApp() + } + } + }) default: break } @@ -17256,7 +17276,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G openUserGeneratedUrl(context: self.context, peerId: self.peerView?.peerId, url: url, concealed: concealed, skipUrlAuth: skipUrlAuth, skipConcealedAlert: skipConcealedAlert, present: { [weak self] c in self?.present(c, in: .window(.root)) }, openResolved: { [weak self] resolved in - self?.openResolved(result: resolved, sourceMessageId: message?.id, forceExternal: forceExternal) + self?.openResolved(result: resolved, sourceMessageId: message?.id, forceExternal: forceExternal, concealed: concealed) }) }, performAction: true) } @@ -18535,9 +18555,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false)) controller.navigationPresentation = .modal - controller.apply = { [weak self] wallpaper, options, cropRect in + controller.apply = { [weak self] wallpaper, options, cropRect, brightness in if let strongSelf = self { - uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightnessMultiplier: nil, peerId: peerId, completion: { + uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, peerId: peerId, completion: { dismissControllers() }) } diff --git a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift index be5e680b97..2ee3fccb9a 100644 --- a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift +++ b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift @@ -398,7 +398,7 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource, let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) - if let colorImage = blurredImage(image, radius: 20.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { + if let colorImage = blurredImage(image, radius: 30.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary) let colorQuality: Float = 0.5 @@ -447,7 +447,7 @@ private func fetchCachedBlurredWallpaperRepresentation(account: Account, resourc let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) - if let colorImage = blurredImage(image, radius: 20.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { + if let colorImage = blurredImage(image, radius: 30.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary) let colorQuality: Float = 0.5 diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 2d8bcb82c5..43d8e09c8e 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -162,6 +162,9 @@ public final class SharedAccountContextImpl: SharedAccountContext { public let currentMediaInputSettings: Atomic private var mediaInputSettingsDisposable: Disposable? + public let currentMediaDisplaySettings: Atomic + private var mediaDisplaySettingsDisposable: Disposable? + public let currentStickerSettings: Atomic private var stickerSettingsDisposable: Disposable? @@ -241,6 +244,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.currentAutomaticMediaDownloadSettings = initialPresentationDataAndSettings.automaticMediaDownloadSettings self.currentAutodownloadSettings = Atomic(value: initialPresentationDataAndSettings.autodownloadSettings) self.currentMediaInputSettings = Atomic(value: initialPresentationDataAndSettings.mediaInputSettings) + self.currentMediaDisplaySettings = Atomic(value: initialPresentationDataAndSettings.mediaDisplaySettings) self.currentStickerSettings = Atomic(value: initialPresentationDataAndSettings.stickerSettings) self.currentInAppNotificationSettings = Atomic(value: initialPresentationDataAndSettings.inAppNotificationSettings) @@ -359,6 +363,15 @@ public final class SharedAccountContextImpl: SharedAccountContext { } }) + self.mediaDisplaySettingsDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.mediaDisplaySettings]) + |> deliverOnMainQueue).start(next: { [weak self] sharedData in + if let strongSelf = self { + if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaDisplaySettings]?.get(MediaDisplaySettings.self) { + let _ = strongSelf.currentMediaDisplaySettings.swap(settings) + } + } + }) + self.stickerSettingsDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue).start(next: { [weak self] sharedData in if let strongSelf = self { @@ -895,6 +908,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.currentAutodownloadSettingsDisposable.dispose() self.inAppNotificationSettingsDisposable?.dispose() self.mediaInputSettingsDisposable?.dispose() + self.mediaDisplaySettingsDisposable?.dispose() self.callDisposable?.dispose() self.groupCallDisposable?.dispose() self.callStateDisposable?.dispose() diff --git a/submodules/TelegramUIPreferences/Sources/MediaDisplaySettings.swift b/submodules/TelegramUIPreferences/Sources/MediaDisplaySettings.swift new file mode 100644 index 0000000000..2e59eab787 --- /dev/null +++ b/submodules/TelegramUIPreferences/Sources/MediaDisplaySettings.swift @@ -0,0 +1,50 @@ +import Foundation +import Postbox +import TelegramCore +import SwiftSignalKit + +public struct MediaDisplaySettings: Codable, Equatable { + public let showNextMediaOnTap: Bool + + public static var defaultSettings: MediaDisplaySettings { + return MediaDisplaySettings(showNextMediaOnTap: true) + } + + public init(showNextMediaOnTap: Bool) { + self.showNextMediaOnTap = showNextMediaOnTap + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.showNextMediaOnTap = (try container.decode(Int32.self, forKey: "showNextMediaOnTap")) != 0 + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode((self.showNextMediaOnTap ? 1 : 0) as Int32, forKey: "showNextMediaOnTap") + } + + public static func ==(lhs: MediaDisplaySettings, rhs: MediaDisplaySettings) -> Bool { + return lhs.showNextMediaOnTap == rhs.showNextMediaOnTap + } + + public func withUpdatedShowNextMediaOnTap(_ showNextMediaOnTap: Bool) -> MediaDisplaySettings { + return MediaDisplaySettings(showNextMediaOnTap: showNextMediaOnTap) + } +} + +public func updateMediaDisplaySettingsInteractively(accountManager: AccountManager, _ f: @escaping (MediaDisplaySettings) -> MediaDisplaySettings) -> Signal { + return accountManager.transaction { transaction -> Void in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.mediaDisplaySettings, { entry in + let currentSettings: MediaDisplaySettings + if let entry = entry?.get(MediaDisplaySettings.self) { + currentSettings = entry + } else { + currentSettings = MediaDisplaySettings.defaultSettings + } + return PreferencesEntry(f(currentSettings)) + }) + } +} diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index f53c296037..5ac78a3560 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -39,6 +39,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 { case intentsSettings = 17 case translationSettings = 18 case drawingSettings = 19 + case mediaDisplaySettings = 20 } public struct ApplicationSpecificSharedDataKeys { @@ -62,6 +63,7 @@ public struct ApplicationSpecificSharedDataKeys { public static let intentsSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.intentsSettings.rawValue) public static let translationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.translationSettings.rawValue) public static let drawingSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.drawingSettings.rawValue) + public static let mediaDisplaySettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.mediaDisplaySettings.rawValue) } private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { diff --git a/submodules/WebUI/Sources/WebAppOpenConfirmationController.swift b/submodules/WebUI/Sources/WebAppOpenConfirmationController.swift new file mode 100644 index 0000000000..2e379c1d3e --- /dev/null +++ b/submodules/WebUI/Sources/WebAppOpenConfirmationController.swift @@ -0,0 +1,287 @@ +import Foundation +import UIKit +import SwiftSignalKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import AppBundle +import AvatarNode + +private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode { + private let strings: PresentationStrings + private let title: String + private let text: String + private let showMore: Bool + + private let titleNode: ImmediateTextNode + private let textNode: ASTextNode + private let avatarNode: AvatarNode + + private let moreButton: HighlightableButtonNode + private let arrowNode: ASImageNode + + private let actionNodesSeparator: ASDisplayNode + private let actionNodes: [TextAlertContentActionNode] + private let actionVerticalSeparators: [ASDisplayNode] + + private var validLayout: CGSize? + + private let morePressed: () -> Void + + override var dismissOnOutsideTap: Bool { + return self.isUserInteractionEnabled + } + + init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, title: String, text: String, showMore: Bool, actions: [TextAlertAction], morePressed: @escaping () -> Void) { + self.strings = strings + self.title = title + self.text = text + self.showMore = showMore + self.morePressed = morePressed + + self.titleNode = ImmediateTextNode() + self.titleNode.displaysAsynchronously = false + self.titleNode.maximumNumberOfLines = 1 + self.titleNode.textAlignment = .center + + self.textNode = ASTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.maximumNumberOfLines = 0 + + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) + + self.moreButton = HighlightableButtonNode() + + self.arrowNode = ASImageNode() + self.arrowNode.displaysAsynchronously = false + self.arrowNode.displayWithoutProcessing = true + self.arrowNode.isHidden = !showMore + self.arrowNode.contentMode = .scaleAspectFit + + self.actionNodesSeparator = ASDisplayNode() + self.actionNodesSeparator.isLayerBacked = true + + self.actionNodes = actions.map { action -> TextAlertContentActionNode in + return TextAlertContentActionNode(theme: theme, action: action) + } + + var actionVerticalSeparators: [ASDisplayNode] = [] + if actions.count > 1 { + for _ in 0 ..< actions.count - 1 { + let separatorNode = ASDisplayNode() + separatorNode.isLayerBacked = true + actionVerticalSeparators.append(separatorNode) + } + } + self.actionVerticalSeparators = actionVerticalSeparators + + super.init() + + self.addSubnode(self.titleNode) + self.addSubnode(self.textNode) + self.addSubnode(self.avatarNode) + self.addSubnode(self.moreButton) + self.moreButton.addSubnode(self.arrowNode) + + self.addSubnode(self.actionNodesSeparator) + + for actionNode in self.actionNodes { + self.addSubnode(actionNode) + } + + for separatorNode in self.actionVerticalSeparators { + self.addSubnode(separatorNode) + } + + self.updateTheme(theme) + + self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) + + self.moreButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside) + } + + @objc private func moreButtonPressed() { + self.morePressed() + } + + override func updateTheme(_ theme: AlertControllerTheme) { + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) + self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + + self.moreButton.setAttributedTitle(NSAttributedString(string: self.strings.WebApp_LaunchMoreInfo, font: Font.regular(13.0), textColor: theme.accentColor), for: .normal) + self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.accentColor) + + self.actionNodesSeparator.backgroundColor = theme.separatorColor + for actionNode in self.actionNodes { + actionNode.updateTheme(theme) + } + for separatorNode in self.actionVerticalSeparators { + separatorNode.backgroundColor = theme.separatorColor + } + + if let size = self.validLayout { + _ = self.updateLayout(size: size, transition: .immediate) + } + } + + override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + var size = size + size.width = min(size.width, 270.0) + + self.validLayout = size + + var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) + + let avatarSize = CGSize(width: 60.0, height: 60.0) + self.avatarNode.updateSize(size: avatarSize) + + let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0), y: origin.y), size: avatarSize) + transition.updateFrame(node: self.avatarNode, frame: avatarFrame) + + origin.y += avatarSize.height + 17.0 + + if let arrowImage = self.arrowNode.image { + let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) + transition.updateFrame(node: self.arrowNode, frame: arrowFrame) + } + + let titleSize = self.titleNode.updateLayout(CGSize(width: size.width - 32.0, height: size.height)) + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) + origin.y += titleSize.height + 6.0 + + if self.showMore { + let moreButtonSize = self.moreButton.measure(CGSize(width: size.width - 32.0, height: size.height)) + transition.updateFrame(node: self.moreButton, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - moreButtonSize.width) / 2.0) - 5.0, y: origin.y), size: moreButtonSize)) + transition.updateFrame(node: self.arrowNode, frame: CGRect(origin: CGPoint(x: moreButtonSize.width + 3.0, y: 4.0), size: CGSize(width: 9.0, height: 9.0))) + origin.y += moreButtonSize.height + 22.0 + } + + let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) + transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) + + let actionButtonHeight: CGFloat = 44.0 + var minActionsWidth: CGFloat = 0.0 + let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) + let actionTitleInsets: CGFloat = 8.0 + + var effectiveActionLayout = TextAlertContentActionLayout.horizontal + for actionNode in self.actionNodes { + let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) + if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { + effectiveActionLayout = .vertical + } + switch effectiveActionLayout { + case .horizontal: + minActionsWidth += actionTitleSize.width + actionTitleInsets + case .vertical: + minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) + } + } + + let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) + + let contentWidth = max(size.width, minActionsWidth) + + var actionsHeight: CGFloat = 0.0 + switch effectiveActionLayout { + case .horizontal: + actionsHeight = actionButtonHeight + case .vertical: + actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) + } + + var resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 25.0 + insets.top + insets.bottom) + if self.showMore { + resultSize.height += 37.0 + } + + transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + + var actionOffset: CGFloat = 0.0 + let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) + var separatorIndex = -1 + var nodeIndex = 0 + for actionNode in self.actionNodes { + if separatorIndex >= 0 { + let separatorNode = self.actionVerticalSeparators[separatorIndex] + switch effectiveActionLayout { + case .horizontal: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) + case .vertical: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + } + } + separatorIndex += 1 + + let currentActionWidth: CGFloat + switch effectiveActionLayout { + case .horizontal: + if nodeIndex == self.actionNodes.count - 1 { + currentActionWidth = resultSize.width - actionOffset + } else { + currentActionWidth = actionWidth + } + case .vertical: + currentActionWidth = resultSize.width + } + + let actionNodeFrame: CGRect + switch effectiveActionLayout { + case .horizontal: + actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += currentActionWidth + case .vertical: + actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += actionButtonHeight + } + + transition.updateFrame(node: actionNode, frame: actionNodeFrame) + + nodeIndex += 1 + } + + return resultSize + } +} + +public func webAppLaunchConfirmationController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peer: EnginePeer, commit: @escaping () -> Void, showMore: (() -> Void)?) -> AlertController { + let theme = defaultDarkColorPresentationTheme + let presentationData: PresentationData + if let updatedPresentationData { + presentationData = updatedPresentationData.initial + } else { + presentationData = context.sharedContext.currentPresentationData.with { $0 } + } + let strings = presentationData.strings + + var dismissImpl: ((Bool) -> Void)? + var contentNode: WebAppLaunchConfirmationAlertContentNode? + let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + dismissImpl?(true) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + dismissImpl?(true) + commit() + })] + + let title = peer.compactDisplayTitle + let text = presentationData.strings.WebApp_LaunchConfirmation + + contentNode = WebAppLaunchConfirmationAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peer: peer, title: title, text: text, showMore: showMore != nil, actions: actions, morePressed: { + dismissImpl?(true) + showMore?() + }) + + let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) + dismissImpl = { [weak controller] animated in + if animated { + controller?.dismissAnimated() + } else { + controller?.dismiss() + } + } + return controller +}