Merge commit 'a9e441902171a81c383d6f734bcf08c66e3db3a4'

# Conflicts:
#	submodules/TelegramUI/Sources/FetchCachedRepresentations.swift
This commit is contained in:
Ali 2023-04-09 23:16:51 +04:00
commit cdc32a8910
45 changed files with 1994 additions and 395 deletions

View File

@ -9149,7 +9149,7 @@ Sorry for the inconvenience.";
"WallpaperPreview.ChatBottomText" = "Enjoy the view.";
"Conversation.Theme.SetPhotoWallpaper" = "Choose Background from Photos";
"Conversation.Theme.SetColorWallpaper" = "Choose Color as a Background";
"Conversation.Theme.SetColorWallpaper" = "Set a Color as a Background";
"Conversation.Theme.OtherOptions" = "Other Options...";
"Conversation.Theme.ChooseWallpaperTitle" = "Choose Background";
@ -9158,3 +9158,11 @@ 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.";
"WallpaperPreview.PreviewInNightMode" = "Preview this background in night mode.";
"WallpaperPreview.PreviewInDayMode" = "Preview this background in day mode.";
"Conversation.Theme.ApplyBackground" = "Set as Background";

View File

@ -756,6 +756,7 @@ public protocol SharedAccountContext: AnyObject {
var currentInAppNotificationSettings: Atomic<InAppNotificationSettings> { get }
var currentMediaInputSettings: Atomic<MediaInputSettings> { get }
var currentStickerSettings: Atomic<StickerSettings> { get }
var currentMediaDisplaySettings: Atomic<MediaDisplaySettings> { get }
var energyUsageSettings: EnergyUsageSettings { get }

View File

@ -1370,7 +1370,7 @@ public final class CalendarMessageScreen: ViewController {
if self.selectionState?.dayRange == nil {
if let selectionToolbarNode = self.selectionToolbarNode {
let toolbarFrame = selectionToolbarNode.view.convert(selectionToolbarNode.bounds, to: self.view)
self.controller?.present(TooltipScreen(account: self.context.account, text: self.presentationData.strings.MessageCalendar_EmptySelectionTooltip, style: .default, icon: .none, location: .point(toolbarFrame.insetBy(dx: 0.0, dy: 10.0), .bottom), shouldDismissOnTouch: { point in
self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.MessageCalendar_EmptySelectionTooltip, style: .default, icon: .none, location: .point(toolbarFrame.insetBy(dx: 0.0, dy: 10.0), .bottom), shouldDismissOnTouch: { point in
return .dismiss(consume: false)
}), in: .current)
}

View File

@ -2079,7 +2079,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
parentController.present(TooltipScreen(account: strongSelf.context.account, text: text, icon: .chatListPress, location: .point(location, .bottom), shouldDismissOnTouch: { point in
parentController.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: text, icon: .chatListPress, location: .point(location, .bottom), shouldDismissOnTouch: { point in
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
return .dismiss(consume: false)
}

View File

@ -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
@ -225,14 +229,16 @@ public final class NavigationBackgroundNode: ASDisplayNode {
}
}
public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
let effectiveEnableBlur = enableBlur ?? self.enableBlur
if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur {
let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation
if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation {
return
}
self._color = color
self.enableBlur = effectiveEnableBlur
self.enableSaturation = effectiveEnableSaturation
if sharedIsReduceTransparencyEnabled {
transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0))

View File

@ -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

View File

@ -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))

View File

@ -829,7 +829,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
text = strongSelf.presentationData.strings.Location_ProximityTip(EnginePeer(peer).compactDisplayTitle).string
}
strongSelf.interaction.present(TooltipScreen(account: strongSelf.context.account, text: text, icon: nil, location: .point(location.offsetBy(dx: -9.0, dy: 0.0), .right), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
strongSelf.interaction.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: text, icon: nil, location: .point(location.offsetBy(dx: -9.0, dy: 0.0), .right), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
}))
})

View File

@ -385,32 +385,40 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
override func didLoad() {
super.didLoad()
guard let controller = self.controller else {
return
}
self.gridNode.scrollView.alwaysBounceVertical = true
self.gridNode.scrollView.showsVerticalScrollIndicator = false
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
selectionGesture.delegate = self
selectionGesture.began = { [weak self] in
self?.controller?.cancelPanGesture()
}
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
self?.gridNode.scrollView.isScrollEnabled = isEnabled
}
selectionGesture.itemAt = { [weak self] point in
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
} else {
return nil
if case let .assets(_, mode) = controller.subject, case .wallpaper = mode {
} else {
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
selectionGesture.delegate = self
selectionGesture.began = { [weak self] in
self?.controller?.cancelPanGesture()
}
}
selectionGesture.updateSelection = { [weak self] asset, selected in
if let strongSelf = self {
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
self?.gridNode.scrollView.isScrollEnabled = isEnabled
}
selectionGesture.itemAt = { [weak self] point in
if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let selectableItem = itemNode.selectableItem {
return (selectableItem, strongSelf.controller?.interaction?.selectionState?.isIdentifierSelected(selectableItem.uniqueIdentifier) ?? false)
} else {
return nil
}
}
selectionGesture.updateSelection = { [weak self] asset, selected in
if let strongSelf = self {
strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil)
}
}
selectionGesture.sideInset = 44.0
self.gridNode.view.addGestureRecognizer(selectionGesture)
self.selectionGesture = selectionGesture
}
selectionGesture.sideInset = 44.0
self.gridNode.view.addGestureRecognizer(selectionGesture)
self.selectionGesture = selectionGesture
if let controller = self.controller, case let .assets(collection, _) = controller.subject, collection != nil {
self.gridNode.view.interactiveTransitionGestureRecognizerTest = { point -> Bool in
@ -741,7 +749,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
if let customSelection = controller.customSelection {
self.openingMedia = true
customSelection(fetchResult[index])
Queue.mainQueue().after(0.3) {
self.openingMedia = false
}
return
}
@ -2010,18 +2022,19 @@ public func wallpaperMediaPickerController(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
peer: EnginePeer,
canDelete: Bool,
completion: @escaping (PHAsset) -> Void = { _ in }
completion: @escaping (PHAsset) -> Void = { _ in },
openColors: @escaping () -> Void
) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
return nil
})
//controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
controller.requestController = { [weak controller] _, present in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let mediaPickerController = MediaPickerScreen(context: context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: canDelete ? AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_ResetWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true) : nil, mainButtonAction: canDelete ? {
let _ = context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil).start()
let mediaPickerController = MediaPickerScreen(context: context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_SetColorWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true), mainButtonAction: {
controller?.dismiss(animated: true)
} : nil)
openColors()
})
mediaPickerController.customSelection = completion
present(mediaPickerController, mediaPickerController.mediaPickerContext)
}

View File

@ -669,12 +669,8 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts))
switch kind {
case .presence, .voiceCalls, .forwards, .phoneNumber, .voiceMessages, .profilePhoto:
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody))
case .groupInvitations:
break
}
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody))
let phoneLink = "https://t.me/+\(phoneNumber)"
if let settingInfoText = settingInfoText {
entries.append(.settingInfo(presentationData.theme, settingInfoText, phoneLink))

View File

@ -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<UIImage, NoError>
switch wallpaper {
case let .wallpaper(wallpaper, _):
@ -131,12 +131,12 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
if let data = croppedImage.jpegData(compressionQuality: 0.8), let thumbnailImage = thumbnailImage, let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
context.sharedContext.accountManager.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData)
context.account.postbox.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData)
context.sharedContext.accountManager.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData, synchronous: true)
context.account.postbox.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData, synchronous: true)
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with {$0 }.autoNightModeTriggered
let accountManager = context.sharedContext.accountManager
@ -196,14 +196,14 @@ 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<UIImage, NoError>
switch wallpaper {
case let .wallpaper(wallpaper, _):
switch wallpaper {
case let .file(file):
if let path = context.account.postbox.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data)
context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data, synchronous: true)
let _ = context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start()
let _ = context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start()
}
@ -211,7 +211,7 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa
for representation in representations {
let resource = representation.resource
if let path = context.account.postbox.mediaBox.completedResourcePath(resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
let _ = context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start()
}
}
@ -276,7 +276,7 @@ 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)!
}
let thumbnailDimensions = finalCropRect.size.fitted(CGSize(width: 320.0, height: 320.0))
@ -291,7 +291,12 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), colors: [], intensity: nil)
var intensity: Int32?
if let brightness {
intensity = max(1, Int32(brightness * 100.0))
}
let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), colors: [], intensity: intensity)
let temporaryWallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), TelegramMediaImageRepresentation(dimensions: PixelDimensions(croppedImage.size), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], settings)
let _ = context.account.postbox.transaction({ transaction in
@ -308,7 +313,7 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa
completion()
}
let _ = uploadWallpaper(account: context.account, resource: resource, settings: WallpaperSettings(blur: false, motion: mode.contains(.motion), colors: [], intensity: nil), forChat: true).start(next: { status in
let _ = uploadWallpaper(account: context.account, resource: resource, settings: WallpaperSettings(blur: false, motion: mode.contains(.motion), colors: [], intensity: intensity), forChat: true).start(next: { status in
if case let .complete(wallpaper) = status {
if case let .file(file) = wallpaper {
context.account.postbox.mediaBox.copyResourceData(from: resource.id, to: file.file.resource.id, synchronous: true)

View File

@ -119,7 +119,11 @@ final class ThemeAccentColorController: ViewController {
} else {
self.navigationItem.titleView = self.segmentedTitleView
}
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
if case .peer = resultMode {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
} else {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView())
}
}
required init(coder aDecoder: NSCoder) {
@ -129,6 +133,10 @@ final class ThemeAccentColorController: ViewController {
deinit {
self.applyDisposable.dispose()
}
@objc private func cancelPressed() {
self.dismiss()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
@ -169,6 +177,12 @@ final class ThemeAccentColorController: ViewController {
}
}
if case let .peer(peer) = strongSelf.resultMode {
let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: coloredWallpaper).start()
strongSelf.completion?()
return
}
let prepareWallpaper: Signal<CreateThemeResult, CreateThemeError>
if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper, !state.backgroundColors.isEmpty {
let resource = file.file.resource

View File

@ -152,6 +152,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
private let context: AccountContext
private var theme: PresentationTheme
private let mode: ThemeAccentColorControllerMode
private let resultMode: ThemeAccentColorController.ResultMode
private var presentationData: PresentationData
private let animationCache: AnimationCache
@ -227,6 +228,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
init(context: AccountContext, mode: ThemeAccentColorControllerMode, resultMode: ThemeAccentColorController.ResultMode, theme: PresentationTheme, wallpaper: TelegramWallpaper, dismiss: @escaping () -> Void, apply: @escaping (ThemeColorState, UIColor?) -> Void, ready: Promise<Bool>) {
self.context = context
self.mode = mode
self.resultMode = resultMode
self.state = ThemeColorState()
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.theme = theme
@ -766,6 +768,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} else {
if case .edit(_, _, _, _, _, true, _) = self.mode {
doneButtonType = .proceed
} else if case .peer = self.resultMode {
doneButtonType = .setPeer
} else {
doneButtonType = .set
}

View File

@ -11,8 +11,8 @@ import TelegramUIPreferences
import AccountContext
import AttachmentUI
private func availableGradients(theme: PresentationTheme) -> [[UInt32]] {
if theme.overallDarkAppearance {
private func availableGradients(dark: Bool) -> [[UInt32]] {
if dark {
return [
[0x1e3557, 0x151a36, 0x1c4352, 0x2a4541] as [UInt32],
[0x1d223f, 0x1d1832, 0x1b2943, 0x141631] as [UInt32],
@ -39,8 +39,8 @@ private func availableGradients(theme: PresentationTheme) -> [[UInt32]] {
}
}
private func availableColors(theme: PresentationTheme) -> [UInt32] {
if theme.overallDarkAppearance {
private func availableColors(dark: Bool) -> [UInt32] {
if dark {
return [
0x1D2D3C,
0x111B26,
@ -149,6 +149,8 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
fileprivate let mainButtonStatePromise = Promise<AttachmentMainButtonState?>(nil)
var pushController: (ViewController) -> Void = { _ in }
var dismissControllers: (() -> Void)?
var openGallery: (() -> Void)?
public init(context: AccountContext, mode: Mode = .default, canDelete: Bool = false) {
self.context = context
@ -191,9 +193,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
self?.push(controller)
}
if canDelete {
self.mainButtonStatePromise.set(.single(AttachmentMainButtonState(text: self.presentationData.strings.Conversation_Theme_ResetWallpaper, font: .regular, background: .color(.clear), textColor: self.presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true)))
}
self.mainButtonStatePromise.set(.single(AttachmentMainButtonState(text: self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper, font: .regular, background: .color(.clear), textColor: self.presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true)))
}
required public init(coder aDecoder: NSCoder) {
@ -240,23 +240,28 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
}
let controller = ThemeAccentColorController(context: strongSelf.context, mode: .background(themeReference: themeReference), resultMode: strongSelf.mode.colorPickerMode)
controller.completion = { [weak self] in
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers
controllers = controllers.filter { controller in
if controller is ThemeColorsGridController {
return false
controller.completion = { [weak self, weak controller] in
if let strongSelf = self {
if let dismissControllers = strongSelf.dismissControllers {
dismissControllers()
controller?.dismiss(animated: true)
} else if let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers
controllers = controllers.filter { controller in
if controller is ThemeColorsGridController {
return false
}
return true
}
return true
}
navigationController.setViewControllers(controllers, animated: false)
controllers = controllers.filter { controller in
if controller is ThemeAccentColorController {
return false
navigationController.setViewControllers(controllers, animated: false)
controllers = controllers.filter { controller in
if controller is ThemeAccentColorController {
return false
}
return true
}
return true
navigationController.setViewControllers(controllers, animated: true)
}
navigationController.setViewControllers(controllers, animated: true)
}
}
strongSelf.pushController(controller)
@ -264,7 +269,11 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
}
public override func loadDisplayNode() {
self.displayNode = ThemeColorsGridControllerNode(context: self.context, presentationData: self.presentationData, controller: self, gradients: availableGradients(theme: self.presentationData.theme), colors: availableColors(theme: self.presentationData.theme), push: { [weak self] controller in
var dark = false
if case .default = self.mode {
dark = self.presentationData.theme.overallDarkAppearance
}
self.displayNode = ThemeColorsGridControllerNode(context: self.context, presentationData: self.presentationData, controller: self, gradients: availableGradients(dark: dark), colors: availableColors(dark: dark), push: { [weak self] controller in
self?.pushController(controller)
}, pop: { [weak self] in
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
@ -321,12 +330,8 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
}
@objc fileprivate func mainButtonPressed() {
guard case let .peer(peer) = self.mode else {
return
}
let _ = self.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil).start()
self.dismiss(animated: true)
self.openGallery?()
}
public var requestAttachmentMenuExpansion: () -> Void = {}
@ -379,15 +384,26 @@ private final class ThemeColorsGridContext: AttachmentMediaPickerContext {
}
public func standaloneColorPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, canDelete: Bool, push: @escaping (ViewController) -> Void) -> ViewController {
public func standaloneColorPickerController(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
peer: EnginePeer,
push: @escaping (ViewController) -> Void,
openGallery: @escaping () -> Void
) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
return nil
})
//controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
controller.requestController = { _, present in
let colorPickerController = ThemeColorsGridController(context: context, mode: .peer(peer), canDelete: canDelete)
let colorPickerController = ThemeColorsGridController(context: context, mode: .peer(peer))
colorPickerController.pushController = { controller in
push(controller)
}
colorPickerController.dismissControllers = { [weak controller] in
controller?.dismiss(animated: true)
}
colorPickerController.openGallery = openGallery
present(colorPickerController, colorPickerController.mediaPickerContext)
}
return controller

View File

@ -132,27 +132,33 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
self.addSubnode(self.gridNode)
let previousEntries = Atomic<[ThemeColorsGridControllerEntry]?>(value: nil)
let dismissControllers = { [weak self] in
if let self, let navigationController = self.controller?.navigationController as? NavigationController {
let controllers = navigationController.viewControllers.filter({ controller in
if controller is ThemeColorsGridController || controller is WallpaperGalleryController {
return false
}
return true
})
navigationController.setViewControllers(controllers, animated: true)
}
}
let interaction = ThemeColorsGridControllerInteraction(openWallpaper: { [weak self] wallpaper in
if let strongSelf = self {
let entries = previousEntries.with { $0 }
if let entries = entries, !entries.isEmpty {
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)
let dismissControllers = { [weak self, weak controller] in
if let self {
if let dismissControllers = self.controller?.dismissControllers {
dismissControllers()
controller?.dismiss(animated: true)
} else if let navigationController = self.controller?.navigationController as? NavigationController {
let controllers = navigationController.viewControllers.filter({ controller in
if controller is ThemeColorsGridController || controller is WallpaperGalleryController {
return false
}
return true
})
navigationController.setViewControllers(controllers, animated: true)
}
}
}
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: {

View File

@ -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)
}

View File

@ -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))
}

View File

@ -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<Bool>()
override public var ready: Promise<Bool> {
@ -437,6 +437,12 @@ public class WallpaperGalleryController: ViewController {
}
let toolbarNode = WallpaperGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings, doneButtonType: doneButtonType)
switch self.source {
case .asset, .contextResult:
toolbarNode.dark = false
default:
toolbarNode.dark = true
}
self.toolbarNode = toolbarNode
overlayNode.addSubnode(toolbarNode)
@ -453,7 +459,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 +617,7 @@ public class WallpaperGalleryController: ViewController {
break
}
strongSelf.apply?(entry, options, centralItemNode.cropRect)
strongSelf.apply?(entry, options, centralItemNode.cropRect, centralItemNode.brightness)
}
}
}

View File

@ -19,6 +19,8 @@ import WallpaperResources
import AppBundle
import WallpaperBackgroundNode
import TextFormat
import TooltipUI
import TelegramNotices
struct WallpaperGalleryItemArguments {
let colorPreview: Bool
@ -83,7 +85,7 @@ private func reference(for resource: MediaResource, media: Media, message: Messa
final class WallpaperGalleryItemNode: GalleryItemNode {
private let context: AccountContext
private let presentationData: PresentationData
private var presentationData: PresentationData
var entry: WallpaperGalleryEntry?
var source: WallpaperListSource?
@ -95,23 +97,28 @@ 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 let dayNightButtonNode: 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<Void>()
private let fetchDisposable = MetaDisposable()
private let statusDisposable = MetaDisposable()
@ -135,10 +142,16 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
private var isReadyDisposable: Disposable?
private var isDarkAppearance: Bool = false
private var didChangeAppearance: Bool = false
private var darkAppearanceIntensity: CGFloat = 0.8
init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.isDarkAppearance = self.presentationData.theme.overallDarkAppearance
self.wrapperNode = ASDisplayNode()
self.imageNode = TransformImageNode()
self.imageNode.contentAnimations = .subsequentUpdates
@ -149,6 +162,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.statusNode.isUserInteractionEnabled = false
self.blurredNode = BlurredImageNode()
self.brightnessNode = ASDisplayNode()
self.brightnessNode.alpha = 0.0
self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
@ -160,12 +175,24 @@ 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: 0x333333, alpha: 0.33))
self.serviceBackgroundNode.isHidden = true
var sliderValueChangedImpl: ((CGFloat) -> Void)?
self.sliderNode = WallpaperSliderNode(minValue: 0.0, maxValue: 1.0, value: 0.7, 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: true)
self.cancelButtonNode.enableSaturation = true
self.shareButtonNode = WallpaperNavigationButtonNode(content: .icon(image: UIImage(bundleImageName: "Chat/Links/Share"), size: CGSize(width: 28.0, height: 28.0)), dark: true)
self.shareButtonNode.enableSaturation = true
self.dayNightButtonNode = WallpaperNavigationButtonNode(content: .dayNight(isNight: self.isDarkAppearance), dark: true)
self.dayNightButtonNode.enableSaturation = true
self.playButtonPlayImage = generateImage(CGSize(width: 48.0, height: 48.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
@ -193,7 +220,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 +244,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,8 +252,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.addSubnode(self.dayNightButtonNode)
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)
@ -234,6 +266,13 @@ 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)
self.dayNightButtonNode.addTarget(self, action: #selector(self.dayNightPressed), forControlEvents: .touchUpInside)
sliderValueChangedImpl = { [weak self] value in
if let self {
self.updateIntensity(transition: .immediate)
}
}
}
deinit {
@ -255,6 +294,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
default:
return nil
}
}
override func ready() -> Signal<Void, NoError> {
return self._ready.get()
}
@ -263,6 +314,173 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.action?()
}
private func switchTheme() {
if let messageNodes = self.messageNodes {
for messageNode in messageNodes.prefix(2) {
if let snapshotView = messageNode.view.snapshotContentTree() {
messageNode.view.addSubview(snapshotView)
snapshotView.frame = messageNode.bounds
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
}
let themeSettings = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|> map { sharedData -> PresentationThemeSettings in
let themeSettings: PresentationThemeSettings
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) {
themeSettings = current
} else {
themeSettings = PresentationThemeSettings.defaultSettings
}
return themeSettings
}
let _ = (themeSettings
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] themeSettings in
guard let strongSelf = self else {
return
}
var presentationData = strongSelf.presentationData
let lightTheme: PresentationTheme
let lightWallpaper: TelegramWallpaper
let darkTheme: PresentationTheme
let darkWallpaper: TelegramWallpaper
if !strongSelf.isDarkAppearance {
darkTheme = presentationData.theme
darkWallpaper = presentationData.chatWallpaper
var currentColors = themeSettings.themeSpecificAccentColors[themeSettings.theme.index]
if let colors = currentColors, colors.baseColor == .theme {
currentColors = nil
}
let themeSpecificWallpaper = (themeSettings.themeSpecificChatWallpapers[coloredThemeIndex(reference: themeSettings.theme, accentColor: currentColors)] ?? themeSettings.themeSpecificChatWallpapers[themeSettings.theme.index])
if let themeSpecificWallpaper = themeSpecificWallpaper {
lightWallpaper = themeSpecificWallpaper
} else {
let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: currentColors?.color, bubbleColors: currentColors?.customBubbleColors ?? [], wallpaper: currentColors?.wallpaper, baseColor: currentColors?.baseColor, preview: true) ?? defaultPresentationTheme
lightWallpaper = theme.chat.defaultWallpaper
}
var preferredBaseTheme: TelegramBaseTheme?
if let baseTheme = themeSettings.themePreferredBaseTheme[themeSettings.theme.index], [.classic, .day].contains(baseTheme) {
preferredBaseTheme = baseTheme
}
lightTheme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeSettings.theme, baseTheme: preferredBaseTheme, accentColor: currentColors?.color, bubbleColors: currentColors?.customBubbleColors ?? [], wallpaper: currentColors?.wallpaper, baseColor: currentColors?.baseColor, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
} else {
lightTheme = presentationData.theme
lightWallpaper = presentationData.chatWallpaper
let automaticTheme = themeSettings.automaticThemeSwitchSetting.theme
let effectiveColors = themeSettings.themeSpecificAccentColors[automaticTheme.index]
let themeSpecificWallpaper = (themeSettings.themeSpecificChatWallpapers[coloredThemeIndex(reference: automaticTheme, accentColor: effectiveColors)] ?? themeSettings.themeSpecificChatWallpapers[automaticTheme.index])
var preferredBaseTheme: TelegramBaseTheme?
if let baseTheme = themeSettings.themePreferredBaseTheme[automaticTheme.index], [.night, .tinted].contains(baseTheme) {
preferredBaseTheme = baseTheme
} else {
preferredBaseTheme = .night
}
darkTheme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: automaticTheme, baseTheme: preferredBaseTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors ?? [], wallpaper: effectiveColors?.wallpaper, baseColor: effectiveColors?.baseColor, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
if let themeSpecificWallpaper = themeSpecificWallpaper {
darkWallpaper = themeSpecificWallpaper
} else {
switch lightWallpaper {
case .builtin, .color, .gradient:
darkWallpaper = darkTheme.chat.defaultWallpaper
case .file:
if lightWallpaper.isPattern {
darkWallpaper = darkTheme.chat.defaultWallpaper
} else {
darkWallpaper = lightWallpaper
}
default:
darkWallpaper = lightWallpaper
}
}
}
if strongSelf.isDarkAppearance {
darkTheme.forceSync = true
Queue.mainQueue().after(1.0, {
darkTheme.forceSync = false
})
presentationData = presentationData.withUpdated(theme: darkTheme).withUpdated(chatWallpaper: darkWallpaper)
} else {
lightTheme.forceSync = true
Queue.mainQueue().after(1.0, {
lightTheme.forceSync = false
})
presentationData = presentationData.withUpdated(theme: lightTheme).withUpdated(chatWallpaper: lightWallpaper)
}
strongSelf.presentationData = presentationData
strongSelf.nativeNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners)
if let (layout, _) = strongSelf.validLayout {
strongSelf.updateMessagesLayout(layout: layout, offset: CGPoint(), transition: .animated(duration: 0.3, curve: .easeInOut))
}
})
}
@objc private func dayNightPressed() {
self.isDarkAppearance = !self.isDarkAppearance
self.dayNightButtonNode.setIsNight(self.isDarkAppearance)
if let layout = self.validLayout?.0 {
let offset = CGPoint(x: self.validOffset ?? 0.0, y: 0.0)
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
self.updateButtonsLayout(layout: layout, offset: offset, transition: transition)
self.updateMessagesLayout(layout: layout, offset: offset, transition: transition)
if !self.didChangeAppearance {
self.didChangeAppearance = true
self.animateIntensityChange(delay: 0.15)
} else {
self.updateIntensity(transition: .animated(duration: 0.3, curve: .easeInOut))
}
}
self.switchTheme()
}
private func animateIntensityChange(delay: Double) {
let targetValue: CGFloat = self.sliderNode.value
self.sliderNode.internalUpdateLayout(size: self.sliderNode.frame.size, value: 1.0)
self.sliderNode.ignoreUpdates = true
Queue.mainQueue().after(delay, {
self.brightnessNode.backgroundColor = UIColor(rgb: 0x000000)
self.brightnessNode.layer.compositingFilter = nil
self.sliderNode.ignoreUpdates = false
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .easeInOut)
self.sliderNode.animateValue(from: 1.0, to: targetValue, transition: transition)
self.updateIntensity(transition: transition)
})
}
private func updateIntensity(transition: ContainedViewLayoutTransition) {
let value = self.isDarkAppearance ? self.sliderNode.value : 1.0
if value < 1.0 {
self.brightnessNode.backgroundColor = UIColor(rgb: 0x000000)
self.brightnessNode.layer.compositingFilter = nil
transition.updateAlpha(node: self.brightnessNode, alpha: 1.0 - value)
} else {
self.brightnessNode.layer.compositingFilter = nil
transition.updateAlpha(node: self.brightnessNode, alpha: 0.0)
}
}
@objc private func cancelPressed() {
self.dismiss()
}
@ -281,6 +499,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
}
var showPreviewTooltip = false
if self.entry != entry || self.arguments.colorPreview != previousArguments.colorPreview {
let previousEntry = self.entry
self.entry = entry
@ -308,7 +528,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
let progressAction = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: presentationData.theme.rootController.navigationBar.accentTextColor))
var isBlurrable = true
self.nativeNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners)
switch entry {
@ -345,7 +565,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
case .asset:
self.nativeNode._internalUpdateIsSettingUpWallpaper()
self.nativeNode.isHidden = true
//self.nativeNode.update(wallpaper: .color(0xff000000))
self.nativeNode.isHidden = false
self.patternButtonNode.isSelected = false
self.playButtonNode.setIcon(self.playButtonRotateImage)
default:
@ -353,7 +575,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.patternButtonNode.isSelected = false
self.playButtonNode.setIcon(self.playButtonRotateImage)
}
var canShare = false
switch entry {
case let .wallpaper(wallpaper, message):
@ -526,6 +748,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
subtitleSignal = .single(nil)
colorSignal = .single(UIColor(rgb: 0x000000, alpha: 0.3))
self.wrapperNode.addSubnode(self.cropNode)
showPreviewTooltip = true
self.serviceBackgroundNode.isHidden = false
case let .contextResult(result):
var imageDimensions: CGSize?
var imageResource: TelegramMediaResource?
@ -580,6 +804,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
colorSignal = .single(UIColor(rgb: 0x000000, alpha: 0.3))
subtitleSignal = .single(nil)
self.wrapperNode.addSubnode(self.cropNode)
showPreviewTooltip = true
self.serviceBackgroundNode.isHidden = false
}
self.contentSize = contentSize
@ -590,6 +816,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.wrapperNode.addSubnode(self.imageNode)
self.wrapperNode.addSubnode(self.nativeNode)
} else {
self.wrapperNode.insertSubnode(self.nativeNode, at: 0)
self.imageNode.contentMode = .scaleToFill
}
@ -609,6 +836,14 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
strongSelf.blurredNode.image = image
imagePromise.set(.single(image))
if case .asset = entry, let image, let data = image.jpegData(compressionQuality: 0.5) {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(image.size), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], WallpaperSettings())
strongSelf.nativeNode.update(wallpaper: wallpaper)
}
}
}
self.fetchDisposable.set(fetchSignal.start())
@ -660,6 +895,18 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.updateButtonsLayout(layout: layout, offset: CGPoint(), transition: .immediate)
self.updateMessagesLayout(layout: layout, offset: CGPoint(), transition: .immediate)
}
if showPreviewTooltip {
Queue.mainQueue().after(0.35) {
self.maybePresentPreviewTooltip()
}
if self.isDarkAppearance && !self.didChangeAppearance {
Queue.mainQueue().justDispatch {
self.didChangeAppearance = true
self.animateIntensityChange(delay: 0.35)
}
}
}
}
override func screenFrameUpdated(_ frame: CGRect) {
@ -721,7 +968,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 +979,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 +1156,17 @@ 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:
if self.isDarkAppearance {
additionalYOffset -= 44.0
}
default:
break
}
}
let buttonSpacing: CGFloat = 18.0
@ -937,13 +1184,26 @@ 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)
var 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 sliderAlpha: CGFloat = 0.0
var sliderScale: CGFloat = 0.2
if !additionalYOffset.isZero {
sliderAlpha = 1.0
sliderScale = 1.0
} else {
sliderFrame = sliderFrame.offsetBy(dx: 0.0, dy: 22.0)
}
var dayNightHidden = 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 +1218,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
blurFrame = leftButtonFrame
motionAlpha = 1.0
motionFrame = rightButtonFrame
dayNightHidden = false
case .contextResult:
blurAlpha = 1.0
blurFrame = leftButtonFrame
motionAlpha = 1.0
motionFrame = rightButtonFrame
dayNightHidden = false
case let .wallpaper(wallpaper, _):
switch wallpaper {
case .builtin:
@ -1047,17 +1309,21 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
transition.updateAlpha(node: self.playButtonNode, alpha: playAlpha * alpha)
transition.updateSublayerTransformScale(node: self.playButtonNode, scale: max(0.1, playAlpha))
transition.updateFrameAsPositionAndBounds(node: self.sliderNode, frame: sliderFrame)
transition.updateAlpha(node: self.sliderNode, alpha: sliderAlpha * alpha)
transition.updateTransformScale(node: self.sliderNode, scale: sliderScale)
self.sliderNode.updateLayout(size: sliderFrame.size)
transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame)
transition.updateFrame(node: self.shareButtonNode, frame: shareFrame)
transition.updateFrame(node: self.dayNightButtonNode, frame: shareFrame)
self.dayNightButtonNode.isHidden = dayNightHidden
}
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 +1391,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
case .asset, .contextResult:
topMessageText = presentationData.strings.WallpaperPreview_CropTopText
bottomMessageText = presentationData.strings.WallpaperPreview_CropBottomText
if self.isDarkAppearance {
bottomInset += 44.0
}
case .customColor:
topMessageText = presentationData.strings.WallpaperPreview_CustomColorTopText
bottomMessageText = presentationData.strings.WallpaperPreview_CustomColorBottomText
@ -1139,7 +1408,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
}
let theme = self.presentationData.theme.withUpdated(preview: true)
let theme = self.presentationData.theme
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false))
@ -1157,18 +1426,15 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let messageNodes = self.messageNodes {
if self.validMessages != [topMessageText, bottomMessageText] {
self.validMessages = [topMessageText, bottomMessageText]
for i in 0 ..< items.count {
items[i].updateNode(async: { f in f() }, node: { return messageNodes[i] }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None) { layout, apply in
let nodeFrame = CGRect(origin: messageNodes[i].frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
for i in 0 ..< items.count {
items[i].updateNode(async: { f in f() }, node: { return messageNodes[i] }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None) { layout, apply in
let nodeFrame = CGRect(origin: messageNodes[i].frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
messageNodes[i].contentSize = layout.contentSize
messageNodes[i].insets = layout.insets
messageNodes[i].frame = nodeFrame
messageNodes[i].contentSize = layout.contentSize
messageNodes[i].insets = layout.insets
messageNodes[i].frame = nodeFrame
apply(ListViewItemApply(isOnScreen: true))
}
apply(ListViewItemApply(isOnScreen: true))
}
}
} else {
@ -1187,7 +1453,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
self.messageNodes = messageNodes
}
let alpha = 1.0 - min(1.0, max(0.0, abs(offset.y) / 50.0))
if let messageNodes = self.messageNodes {
@ -1199,6 +1465,15 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
transition.updateAlpha(node: itemNode, alpha: alpha)
}
}
if let _ = serviceMessageText, let messageNodes = self.messageNodes, let node = messageNodes.last {
if let backgroundNode = node.subnodes?.first?.subnodes?.first?.subnodes?.first?.subnodes?.first, let backdropNode = node.subnodes?.first?.subnodes?.first?.subnodes?.first?.subnodes?.last?.subnodes?.last?.subnodes?.first {
backdropNode.isHidden = true
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)
}
}
}
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
@ -1236,7 +1511,18 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.cropNode.zoom(to: CGRect(x: (contentSize.width - fittedSize.width) / 2.0, y: (contentSize.height - fittedSize.height) / 2.0, width: fittedSize.width, height: fittedSize.height))
}
self.blurredNode.frame = self.imageNode.bounds
let displayMode: WallpaperDisplayMode
if case .regular = layout.metrics.widthClass {
displayMode = .aspectFit
} else {
displayMode = .aspectFill
}
self.nativeNode.frame = self.wrapperNode.bounds
self.nativeNode.updateLayout(size: self.nativeNode.bounds.size, displayMode: displayMode, transition: .immediate)
}
self.brightnessNode.frame = self.imageNode.bounds
let additionalYOffset: CGFloat = 0.0
@ -1255,4 +1541,41 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
func animateWallpaperAppeared() {
self.nativeNode.animateEvent(transition: .animated(duration: 2.0, curve: .spring), extendAnimation: true)
}
private var displayedPreviewTooltip = false
private func maybePresentPreviewTooltip() {
guard !self.displayedPreviewTooltip else {
return
}
let frame = self.dayNightButtonNode.view.convert(self.dayNightButtonNode.bounds, to: self.view)
let currentTimestamp = Int32(Date().timeIntervalSince1970)
let isDark = self.isDarkAppearance
let signal: Signal<(Int32, Int32), NoError>
if isDark {
signal = ApplicationSpecificNotice.getChatWallpaperLightPreviewTip(accountManager: self.context.sharedContext.accountManager)
} else {
signal = ApplicationSpecificNotice.getChatWallpaperDarkPreviewTip(accountManager: self.context.sharedContext.accountManager)
}
let _ = (signal
|> deliverOnMainQueue).start(next: { [weak self] count, timestamp in
if let strongSelf = self, (count < 2 && currentTimestamp > timestamp + 24 * 60 * 60) {
strongSelf.displayedPreviewTooltip = true
let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode, style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.33)), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
})
strongSelf.galleryController()?.present(controller, in: .current)
if isDark {
let _ = ApplicationSpecificNotice.incrementChatWallpaperLightPreviewTip(accountManager: strongSelf.context.sharedContext.accountManager, timestamp: currentTimestamp).start()
} else {
let _ = ApplicationSpecificNotice.incrementChatWallpaperDarkPreviewTip(accountManager: strongSelf.context.sharedContext.accountManager, timestamp: currentTimestamp).start()
}
}
})
}
}

View File

@ -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
@ -65,8 +32,23 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode {
}
}
var dark: Bool {
didSet {
if self.dark != oldValue {
self.doneButtonBackgroundNode.removeFromSupernode()
if self.dark {
self.doneButtonBackgroundNode = WallpaperOptionBackgroundNode(enableSaturation: true)
} else {
self.doneButtonBackgroundNode = WallpaperLightButtonBackgroundNode()
}
self.doneButtonBackgroundNode.cornerRadius = 14.0
self.insertSubnode(self.doneButtonBackgroundNode, at: 0)
}
}
}
private let doneButton = HighlightTrackingButtonNode()
private let doneButtonBackgroundNode: WallpaperLightButtonBackgroundNode
private var doneButtonBackgroundNode: ASDisplayNode
private let doneButtonTitleNode: ImmediateTextNode
@ -81,6 +63,7 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode {
self.strings = strings
self.cancelButtonType = cancelButtonType
self.doneButtonType = doneButtonType
self.dark = false
self.doneButtonBackgroundNode = WallpaperLightButtonBackgroundNode()
self.doneButtonBackgroundNode.cornerRadius = 14.0
@ -106,7 +89,7 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode {
super.init()
self.addSubnode(self.doneButtonBackgroundNode)
self.doneButtonBackgroundNode.addSubnode(self.doneButtonTitleNode)
self.addSubnode(self.doneButtonTitleNode)
self.addSubnode(self.doneButtonSolidBackgroundNode)
self.addSubnode(self.doneButtonSolidTitleNode)
@ -196,14 +179,18 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode {
let doneFrame = CGRect(origin: CGPoint(x: inset, y: 2.0), size: CGSize(width: size.width - inset * 2.0, height: buttonHeight))
self.doneButton.frame = doneFrame
self.doneButtonBackgroundNode.frame = doneFrame
self.doneButtonBackgroundNode.updateLayout(size: doneFrame.size)
if let backgroundNode = self.doneButtonBackgroundNode as? WallpaperOptionBackgroundNode {
backgroundNode.updateLayout(size: doneFrame.size)
} else if let backgroundNode = self.doneButtonBackgroundNode as? WallpaperLightButtonBackgroundNode {
backgroundNode.updateLayout(size: doneFrame.size)
}
self.doneButtonSolidBackgroundNode.frame = doneFrame
let doneTitleSize = self.doneButtonTitleNode.updateLayout(doneFrame.size)
self.doneButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((doneFrame.width - doneTitleSize.width) / 2.0), y: floorToScreenPixels((doneFrame.height - doneTitleSize.height) / 2.0)), size: doneTitleSize)
self.doneButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((doneFrame.width - doneTitleSize.width) / 2.0), y: floorToScreenPixels((doneFrame.height - doneTitleSize.height) / 2.0)), size: doneTitleSize).offsetBy(dx: doneFrame.minX, dy: doneFrame.minY)
let _ = self.doneButtonSolidTitleNode.updateLayout(doneFrame.size)
self.doneButtonSolidTitleNode.frame = self.doneButtonTitleNode.frame.offsetBy(dx: doneFrame.minX, dy: doneFrame.minY)
self.doneButtonSolidTitleNode.frame = self.doneButtonTitleNode.frame
}
@objc func cancelPressed() {

View File

@ -5,6 +5,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import CheckNode
import AnimationUI
enum WallpaperOptionButtonValue {
case check(Bool)
@ -34,28 +35,103 @@ 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
init(enableSaturation: Bool = false) {
self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.3), enableBlur: true, enableSaturation: enableSaturation)
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)
case text(String)
case dayNight(isNight: Bool)
}
var enableSaturation: Bool = false
private let content: Content
var dark: Bool {
didSet {
if self.dark != oldValue {
self.backgroundNode.removeFromSupernode()
if self.dark {
self.backgroundNode = WallpaperOptionBackgroundNode(enableSaturation: self.enableSaturation)
} else {
self.backgroundNode = WallpaperLightButtonBackgroundNode()
}
self.insertSubnode(self.backgroundNode, at: 0)
}
}
}
private let backgroundNode: WallpaperLightButtonBackgroundNode
private var backgroundNode: ASDisplayNode
private let iconNode: ASImageNode
private let textNode: ImmediateTextNode
private var animationNode: AnimationNode?
func setIcon(_ image: UIImage?) {
self.iconNode.image = generateTintedImage(image: image, color: .white)
}
init(content: Content) {
init(content: Content, dark: Bool) {
self.content = content
self.dark = dark
self.backgroundNode = WallpaperLightButtonBackgroundNode()
if dark {
self.backgroundNode = WallpaperOptionBackgroundNode()
} else {
self.backgroundNode = WallpaperLightButtonBackgroundNode()
}
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
@ -68,6 +144,12 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
case let .icon(icon, _):
title = ""
self.iconNode.image = generateTintedImage(image: icon, color: .white)
case let .dayNight(isNight):
title = ""
let animationNode = AnimationNode(animation: isNight ? "anim_sun_reverse" : "anim_sun", colors: [:], scale: 1.0)
animationNode.speed = 1.66
animationNode.isUserInteractionEnabled = false
self.animationNode = animationNode
}
self.textNode = ImmediateTextNode()
@ -79,26 +161,53 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
self.addSubnode(self.iconNode)
self.addSubnode(self.textNode)
if let animationNode = self.animationNode {
self.addSubnode(animationNode)
}
self.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
strongSelf.backgroundNode.alpha = 0.4
strongSelf.iconNode.layer.removeAnimation(forKey: "opacity")
strongSelf.iconNode.alpha = 0.4
strongSelf.textNode.layer.removeAnimation(forKey: "opacity")
strongSelf.textNode.alpha = 0.4
// if let animationNode = strongSelf.animationNode {
// animationNode.layer.removeAnimation(forKey: "opacity")
// animationNode.alpha = 0.4
// }
} else {
strongSelf.backgroundNode.alpha = 1.0
strongSelf.backgroundNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
strongSelf.iconNode.alpha = 1.0
strongSelf.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
strongSelf.textNode.alpha = 1.0
strongSelf.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
// if let animationNode = strongSelf.animationNode {
// animationNode.alpha = 1.0
// animationNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
// }
}
}
}
}
func setIsNight(_ isNight: Bool) {
self.animationNode?.setAnimation(name: !isNight ? "anim_sun_reverse" : "anim_sun", colors: [:])
self.animationNode?.speed = 1.66
self.animationNode?.playOnce()
}
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)
// }
}
}
@ -111,6 +220,8 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
return CGSize(width: ceil(size.width) + 16.0, height: 28.0)
case let .icon(_, size):
return size
case .dayNight:
return CGSize(width: 28.0, height: 28.0)
}
}
@ -119,7 +230,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
@ -127,12 +242,17 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
if let textSize = self.textSize {
self.textNode.frame = CGRect(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0), width: textSize.width, height: textSize.height)
}
if let animationNode = self.animationNode {
animationNode.bounds = CGRect(origin: .zero, size: CGSize(width: 24.0, height: 24.0))
animationNode.position = CGPoint(x: 14.0, y: 14.0)
}
}
}
final class WallpaperOptionButtonNode: HighlightTrackingButtonNode {
private let backgroundNode: NavigationBackgroundNode
private let backgroundNode: WallpaperOptionBackgroundNode
private let checkNode: CheckNode
private let colorNode: ASImageNode
@ -172,9 +292,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 +305,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 +324,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode {
}
self.addSubnode(self.backgroundNode)
self.addSubnode(self.checkNode)
self.addSubnode(self.textNode)
self.addSubnode(self.colorNode)
@ -227,11 +350,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 +440,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 +458,159 @@ 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)
}
var ignoreUpdates = false
func animateValue(from: CGFloat, to: CGFloat, transition: ContainedViewLayoutTransition = .immediate) {
guard let size = self.validLayout else {
return
}
self.internalUpdateLayout(size: size, value: from)
self.internalUpdateLayout(size: size, value: to, transition: transition)
}
func internalUpdateLayout(size: CGSize, value: CGFloat, 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 = (value - self.minValue) / range
let foregroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: value * size.width, height: size.height))
transition.updateFrame(node: self.foregroundNode, frame: foregroundFrame)
transition.updateFrame(node: self.foregroundLightNode, frame: foregroundFrame)
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
guard !self.ignoreUpdates else {
return
}
self.internalUpdateLayout(size: size, value: self.value, transition: transition)
}
@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 == 0.0 && previousValue != 0.0 {
self.hapticFeedback.impact(.soft)
} else if self.value == 1.0 && previousValue != 1.0 {
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)
}
}

View File

@ -526,7 +526,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
let _ = (ApplicationSpecificNotice.incrementAudioRateOptionsTip(accountManager: self.context.sharedContext.accountManager)
|> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self, let controller = strongSelf.getController?(), value == 2 {
let tooltipController = TooltipScreen(account: strongSelf.context.account, text: strongSelf.strings.Conversation_AudioRateOptionsTooltip, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 0.0, dy: 4.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
let tooltipController = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: strongSelf.strings.Conversation_AudioRateOptionsTooltip, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 0.0, dy: 4.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
})
controller.present(tooltipController, in: .window(.root))

View File

@ -760,8 +760,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}) else {
return
}
self.present?(TooltipScreen(account: self.account, text: self.presentationData.strings.Call_CameraOrScreenTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
self.present?(TooltipScreen(account: self.account, sharedContext: self.sharedContext, text: self.presentationData.strings.Call_CameraOrScreenTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
}))
}

View File

@ -2310,7 +2310,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
} else {
text = presentationData.strings.VoiceChat_RecordingInProgress
}
strongSelf.controller?.present(TooltipScreen(account: strongSelf.context.account, text: text, icon: nil, location: .point(location.offsetBy(dx: 1.0, dy: 0.0), .top), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
strongSelf.controller?.present(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: text, icon: nil, location: .point(location.offsetBy(dx: 1.0, dy: 0.0), .top), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: true)
}), in: .window(.root))
}
@ -3507,7 +3507,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
if !callState.subscribedToScheduled {
let location = self.actionButton.view.convert(self.actionButton.bounds, to: self.view).center
let point = CGRect(origin: CGPoint(x: location.x - 5.0, y: location.y - 5.0 - 68.0), size: CGSize(width: 10.0, height: 10.0))
self.controller?.present(TooltipScreen(account: self.context.account, text: self.presentationData.strings.VoiceChat_ReminderNotify, style: .gradient(UIColor(rgb: 0x262c5a), UIColor(rgb: 0x5d2835)), icon: nil, location: .point(point, .bottom), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.VoiceChat_ReminderNotify, style: .gradient(UIColor(rgb: 0x262c5a), UIColor(rgb: 0x5d2835)), icon: nil, location: .point(point, .bottom), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
}), in: .window(.root))
}
@ -6411,7 +6411,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
point.origin.y += 32.0
}
}
self.controller?.present(TooltipScreen(account: self.context.account, text: self.presentationData.strings.VoiceChat_UnmuteSuggestion, style: .gradient(UIColor(rgb: 0x1d446c), UIColor(rgb: 0x193e63)), icon: nil, location: .point(point, position), displayDuration: .custom(8.0), shouldDismissOnTouch: { _ in
self.controller?.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.VoiceChat_UnmuteSuggestion, style: .gradient(UIColor(rgb: 0x1d446c), UIColor(rgb: 0x193e63)), icon: nil, location: .point(point, position), displayDuration: .custom(8.0), shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
}), in: .window(.root))
}

View File

@ -170,6 +170,8 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case audioRateOptionsTip = 36
case translationSuggestion = 37
case sendWhenOnlineTip = 38
case chatWallpaperLightPreviewTip = 39
case chatWallpaperDarkPreviewTip = 40
var key: ValueBoxKey {
let v = ValueBoxKey(length: 4)
@ -332,6 +334,14 @@ private struct ApplicationSpecificNoticeKeys {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatSpecificThemeDarkPreviewTip.key)
}
static func chatWallpaperLightPreviewTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatWallpaperLightPreviewTip.key)
}
static func chatWallpaperDarkPreviewTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatWallpaperDarkPreviewTip.key)
}
static func chatForwardOptionsTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.chatForwardOptionsTip.key)
}
@ -1109,6 +1119,60 @@ public struct ApplicationSpecificNotice {
}
}
public static func getChatWallpaperLightPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<(Int32, Int32), NoError> {
return accountManager.transaction { transaction -> (Int32, Int32) in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatWallpaperLightPreviewTip())?.get(ApplicationSpecificTimestampAndCounterNotice.self) {
return (value.counter, value.timestamp)
} else {
return (0, 0)
}
}
}
public static func incrementChatWallpaperLightPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1, timestamp: Int32) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatWallpaperLightPreviewTip())?.get(ApplicationSpecificTimestampAndCounterNotice.self) {
currentValue = value.counter
}
let previousValue = currentValue
currentValue += Int32(count)
if let entry = CodableEntry(ApplicationSpecificTimestampAndCounterNotice(counter: currentValue, timestamp: timestamp)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.chatWallpaperLightPreviewTip(), entry)
}
return Int(previousValue)
}
}
public static func getChatWallpaperDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<(Int32, Int32), NoError> {
return accountManager.transaction { transaction -> (Int32, Int32) in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatWallpaperDarkPreviewTip())?.get(ApplicationSpecificTimestampAndCounterNotice.self) {
return (value.counter, value.timestamp)
} else {
return (0, 0)
}
}
}
public static func incrementChatWallpaperDarkPreviewTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1, timestamp: Int32) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatWallpaperDarkPreviewTip())?.get(ApplicationSpecificTimestampAndCounterNotice.self) {
currentValue = value.counter
}
let previousValue = currentValue
currentValue += Int32(count)
if let entry = CodableEntry(ApplicationSpecificTimestampAndCounterNotice(counter: currentValue, timestamp: timestamp)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.chatWallpaperDarkPreviewTip(), entry)
}
return Int(previousValue)
}
}
public static func getChatForwardOptionsTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatForwardOptionsTip())?.get(ApplicationSpecificCounterNotice.self) {

View File

@ -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<Te
var callListSettings: PreferencesEntry?
var inAppNotificationSettings: PreferencesEntry?
var mediaInputSettings: PreferencesEntry?
var mediaDisplaySettings: PreferencesEntry?
var experimentalUISettings: PreferencesEntry?
var contactSynchronizationSettings: PreferencesEntry?
var stickerSettings: PreferencesEntry?
@ -254,6 +257,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
callListSettings: PreferencesEntry?,
inAppNotificationSettings: PreferencesEntry?,
mediaInputSettings: PreferencesEntry?,
mediaDisplaySettings: PreferencesEntry?,
experimentalUISettings: PreferencesEntry?,
contactSynchronizationSettings: PreferencesEntry?,
stickerSettings: PreferencesEntry?
@ -265,6 +269,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
self.callListSettings = callListSettings
self.inAppNotificationSettings = inAppNotificationSettings
self.mediaInputSettings = mediaInputSettings
self.mediaDisplaySettings = mediaDisplaySettings
self.experimentalUISettings = experimentalUISettings
self.contactSynchronizationSettings = contactSynchronizationSettings
self.stickerSettings = stickerSettings
@ -279,6 +284,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
let callListSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings)
let inAppNotificationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings)
let mediaInputSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.mediaInputSettings)
let mediaDisplaySettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.mediaDisplaySettings)
let experimentalUISettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings)
let contactSynchronizationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings)
let stickerSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.stickerSettings)
@ -291,6 +297,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
callListSettings: callListSettings,
inAppNotificationSettings: inAppNotificationSettings,
mediaInputSettings: mediaInputSettings,
mediaDisplaySettings: mediaDisplaySettings,
experimentalUISettings: experimentalUISettings,
contactSynchronizationSettings: contactSynchronizationSettings,
stickerSettings: stickerSettings
@ -347,6 +354,13 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
mediaInputSettings = MediaInputSettings.defaultSettings
}
let mediaDisplaySettings: MediaDisplaySettings
if let value = internalData.mediaDisplaySettings?.get(MediaDisplaySettings.self) {
mediaDisplaySettings = value
} else {
mediaDisplaySettings = MediaDisplaySettings.defaultSettings
}
let stickerSettings: StickerSettings
if let value = internalData.stickerSettings?.get(StickerSettings.self) {
stickerSettings = value
@ -402,7 +416,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners)
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, stickerSettings: stickerSettings, experimentalUISettings: experimentalUISettings)
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, mediaDisplaySettings: mediaDisplaySettings, stickerSettings: stickerSettings, experimentalUISettings: experimentalUISettings)
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "brightness_max.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,135 @@
%PDF-1.7
1 0 obj
<< >>
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

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "brightness_min.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -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> <FF>
endcodespacerange
1 beginbfchar
<00> <DBC0DDAC>
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

View File

@ -130,7 +130,7 @@ final class ChatBotStartInputPanelNode: ChatInputPanelNode {
tooltipController.location = .point(location, .bottom)
}
} else {
let controller = TooltipScreen(account: context.account, text: self.strings.Bot_TapToUse, icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in
let controller = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: self.strings.Bot_TapToUse, icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in
return .ignore
})
controller.alwaysVisible = true

View File

@ -676,14 +676,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return false
}
if strongSelf.presentVoiceMessageDiscardAlert(action: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.1, {
let _ = strongSelf.controllerInteraction?.openMessage(message, mode)
})
let displayVoiceMessageDiscardAlert: () -> Bool = {
if strongSelf.presentVoiceMessageDiscardAlert(action: { [weak self] in
if let strongSelf = self {
Queue.mainQueue().after(0.1, {
let _ = strongSelf.controllerInteraction?.openMessage(message, mode)
})
}
}, performAction: false) {
return false
}
}, performAction: false) {
return false
return true
}
strongSelf.commitPurposefulAction()
@ -696,6 +699,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
for media in message.media {
if media is TelegramMediaMap {
if !displayVoiceMessageDiscardAlert() {
return false
}
isLocation = true
}
if let file = media as? TelegramMediaFile, file.isInstantVideo {
@ -707,12 +713,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia {
switch extendedMedia {
case .preview:
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id)
return true
if displayVoiceMessageDiscardAlert() {
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id)
return true
} else {
return false
}
case .full:
break
}
} else if let action = media as? TelegramMediaAction {
if !displayVoiceMessageDiscardAlert() {
return false
}
switch action.action {
case .pinnedMessageUpdated:
for attribute in message.attributes {
@ -845,7 +858,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, settings: nil)
|> deliverOnMainQueue).start(completed: { [weak wallpaperPreviewController] in
wallpaperPreviewController?.dismiss()
@ -4264,12 +4277,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
@ -5840,6 +5852,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self {
let (themeEmoticonPreview, darkAppearancePreview) = themeEmoticonAndDarkAppearance
var chatWallpaper = chatWallpaper
let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings
let previousChatWallpaper = strongSelf.presentationData.chatWallpaper
@ -5847,7 +5861,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var themeEmoticon = themeEmoticon
if let themeEmoticonPreview = themeEmoticonPreview {
if !themeEmoticonPreview.isEmpty {
themeEmoticon = themeEmoticonPreview
if themeEmoticon != themeEmoticonPreview {
chatWallpaper = nil
themeEmoticon = themeEmoticonPreview
}
} else {
themeEmoticon = nil
}
@ -5858,7 +5875,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var presentationData = presentationData
var useDarkAppearance = presentationData.theme.overallDarkAppearance
if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoticon?.strippedEmoji == themeEmoticon.strippedEmoji }) {
if let darkAppearancePreview = darkAppearancePreview {
useDarkAppearance = darkAppearancePreview
@ -5897,7 +5914,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
lightWallpaper = theme.chat.defaultWallpaper
}
lightTheme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, accentColor: currentColors?.color, bubbleColors: currentColors?.customBubbleColors ?? [], wallpaper: currentColors?.wallpaper, baseColor: currentColors?.baseColor, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
var preferredBaseTheme: TelegramBaseTheme?
if let baseTheme = themeSettings.themePreferredBaseTheme[themeSettings.theme.index], [.classic, .day].contains(baseTheme) {
preferredBaseTheme = baseTheme
}
lightTheme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: themeSettings.theme, baseTheme: preferredBaseTheme, accentColor: currentColors?.color, bubbleColors: currentColors?.customBubbleColors ?? [], wallpaper: currentColors?.wallpaper, baseColor: currentColors?.baseColor, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
} else {
lightTheme = presentationData.theme
lightWallpaper = presentationData.chatWallpaper
@ -5906,7 +5928,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let effectiveColors = themeSettings.themeSpecificAccentColors[automaticTheme.index]
let themeSpecificWallpaper = (themeSettings.themeSpecificChatWallpapers[coloredThemeIndex(reference: automaticTheme, accentColor: effectiveColors)] ?? themeSettings.themeSpecificChatWallpapers[automaticTheme.index])
darkTheme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: automaticTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors ?? [], wallpaper: effectiveColors?.wallpaper, baseColor: effectiveColors?.baseColor, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
var preferredBaseTheme: TelegramBaseTheme?
if let baseTheme = themeSettings.themePreferredBaseTheme[automaticTheme.index], [.night, .tinted].contains(baseTheme) {
preferredBaseTheme = baseTheme
} else {
preferredBaseTheme = .night
}
darkTheme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: automaticTheme, baseTheme: preferredBaseTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors ?? [], wallpaper: effectiveColors?.wallpaper, baseColor: effectiveColors?.baseColor, serviceBackgroundColor: defaultServiceBackgroundColor) ?? defaultPresentationTheme
if let themeSpecificWallpaper = themeSpecificWallpaper {
darkWallpaper = themeSpecificWallpaper
@ -5945,7 +5974,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
presentationData = presentationData.withUpdated(chatWallpaper: chatWallpaper)
}
let isFirstTime = !strongSelf.didSetPresentationData
strongSelf.presentationData = presentationData
strongSelf.didSetPresentationData = true
@ -10942,6 +10970,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return false
}
if strongSelf.effectiveNavigationController?.topViewController !== strongSelf {
return false
}
if strongSelf.presentationInterfaceState.inputTextPanelState.mediaRecordingState != nil {
return false
}
@ -12765,7 +12797,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.presentAttachmentMenu(subject: .bot(id: botId, payload: payload, justInstalled: justInstalled))
}
public func presentBotApp(botApp: BotApp, payload: String?) {
public func presentBotApp(botApp: BotApp, botPeer: EnginePeer, payload: String?) {
guard let peerId = self.chatLocation.peerId else {
return
}
@ -12813,6 +12845,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
let botAddress = botPeer.addressName ?? ""
self.messageActionCallbackDisposable.set(((self.context.engine.messages.requestAppWebView(peerId: peerId, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: payload, themeParams: generateWebAppThemeParams(self.presentationData.theme), allowWrite: false)
|> afterDisposed {
updateProgress()
@ -12824,6 +12858,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, fromMenu: false, fromAttachMenu: false, isInline: false, isSimple: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in
self?.openUrl(url, concealed: true, forceExternal: true)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
if let strongSelf = self {
if let chatTypes {
let controller = strongSelf.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: strongSelf.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
controller.peerSelected = { [weak self, weak controller] peer, _ in
if let strongSelf = self {
completion()
controller?.dismiss()
strongSelf.controllerInteraction?.activateSwitchInline(peer.id, "@\(botAddress) \(query)")
}
}
strongSelf.push(controller)
} else {
strongSelf.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)")
}
}
}, completion: { [weak self] in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
}, getNavigationController: { [weak self] in
@ -14315,7 +14365,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
let tooltipScreen = TooltipScreen(account: self.context.account, text: solution.text, textEntities: solution.entities, icon: .info, location: .top, shouldDismissOnTouch: { point in
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: solution.text, textEntities: solution.entities, icon: .info, location: .top, shouldDismissOnTouch: { point in
return .ignore
}, openActiveTextItem: { [weak self] item, action in
guard let strongSelf = self else {
@ -14408,7 +14458,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
let tooltipScreen = TooltipScreen(account: self.context.account, text: psaText, textEntities: psaEntities, icon: .info, location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: psaText, textEntities: psaEntities, icon: .info, location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
return .ignore
}, openActiveTextItem: { [weak self] item, action in
guard let strongSelf = self else {
@ -14522,7 +14572,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
let tooltipScreen = TooltipScreen(account: self.context.account, text: psaText, textEntities: psaEntities, icon: .info, location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: psaText, textEntities: psaEntities, icon: .info, location: .top, displayDuration: .custom(10.0), shouldDismissOnTouch: { point in
return .ignore
}, openActiveTextItem: { [weak self] item, action in
guard let strongSelf = self else {
@ -17173,7 +17223,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 +17280,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, botPeer: peerId, 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 +17327,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)
}
@ -18485,22 +18556,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
let selectedEmoticon: String? = themeEmoticon
var canResetWallpaper = false
if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData {
canResetWallpaper = cachedUserData.wallpaper != nil
}
let controller = ChatThemeScreen(
context: context,
updatedPresentationData: strongSelf.updatedPresentationData,
animatedEmojiStickers: animatedEmojiStickers,
initiallySelectedEmoticon: selectedEmoticon,
initiallySelectedEmoticon: themeEmoticon,
peerName: strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "",
canResetWallpaper: canResetWallpaper,
previewTheme: { [weak self] emoticon, dark in
if let strongSelf = self {
strongSelf.presentCrossfadeSnapshot()
strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon, dark)))
}
},
changeWallpaper: {
changeWallpaper: { [weak self] in
guard let strongSelf = self, let peerId else {
return
}
@ -18519,60 +18593,64 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
navigationController.setViewControllers(controllers, animated: true)
}
}
var canDelete = false
if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData {
canDelete = cachedUserData.wallpaper != nil
}
let controller = wallpaperMediaPickerController(
context: strongSelf.context,
updatedPresentationData: strongSelf.updatedPresentationData,
peer: EnginePeer(peer),
canDelete: canDelete,
completion: { asset in
guard let strongSelf = self else {
return
}
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
if let strongSelf = self {
uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightnessMultiplier: nil, peerId: peerId, completion: {
dismissControllers()
})
}
}
strongSelf.push(controller)
var openWallpaperPickerImpl: (() -> Void)?
let openWallpaperPicker = { [weak self] in
guard let strongSelf = self else {
return
}
)
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
let controller = wallpaperMediaPickerController(
context: strongSelf.context,
updatedPresentationData: strongSelf.updatedPresentationData,
peer: EnginePeer(peer),
completion: { [weak self] asset in
guard let strongSelf = self else {
return
}
let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false))
controller.navigationPresentation = .modal
controller.apply = { [weak self] wallpaper, options, cropRect, brightness in
if let strongSelf = self {
uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, peerId: peerId, completion: {
dismissControllers()
})
}
}
strongSelf.push(controller)
},
openColors: { [weak self] in
guard let strongSelf = self else {
return
}
let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), push: { [weak self] controller in
if let strongSelf = self {
strongSelf.push(controller)
}
}, openGallery: {
openWallpaperPickerImpl?()
})
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
}
)
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
}
openWallpaperPickerImpl = openWallpaperPicker
openWallpaperPicker()
},
changeColor: {
guard let strongSelf = self else {
resetWallpaper: { [weak self] in
guard let strongSelf = self, let peerId else {
return
}
if let themeController = strongSelf.themeScreen {
strongSelf.themeScreen = nil
themeController.dimTapped()
}
var canDelete = false
if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData {
canDelete = cachedUserData.wallpaper != nil
}
let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), canDelete: canDelete, push: { [weak self] controller in
if let strongSelf = self {
strongSelf.push(controller)
}
})
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil).start()
},
completion: { [weak self] emoticon in
guard let strongSelf = self, let peerId else {
return
}
if canResetWallpaper {
let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil).start()
}
strongSelf.themeEmoticonAndDarkAppearancePreviewPromise.set(.single((emoticon ?? "", nil)))
let _ = context.engine.themes.setChatTheme(peerId: peerId, emoticon: emoticon).start(completed: { [weak self] in
if let strongSelf = self {
@ -18581,6 +18659,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
)
controller.navigationPresentation = .flatModal
controller.passthroughHitTestImpl = { [weak self] _ in
if let strongSelf = self {
return strongSelf.chatDisplayNode.historyNode.view
@ -18596,7 +18675,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.historyNode.tapped = { [weak controller] in
controller?.dimTapped()
}
strongSelf.present(controller, in: .window(.root))
strongSelf.push(controller)
strongSelf.themeScreen = controller
})
}
@ -18630,7 +18709,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}) {
let isEmoji = actions[0].0.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_MultipleRemovedText(Int32(actions.count)) : presentationData.strings.StickerPackActionInfo_MultipleRemovedText(Int32(actions.count)), undo: true, info: actions[0].0, topItem: actions[0].1.first, context: context), elevatedLayout: true, animateInAsReplacement: false, action: { action in
if case .undo = action {
var itemsAndIndices: [(StickerPackCollectionInfo, [StickerPackItem], Int)] = actions.compactMap { action -> (StickerPackCollectionInfo, [StickerPackItem], Int)? in
@ -18650,7 +18728,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
} else if let (info, items, action) = actions.first {
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
switch action {
case .add:
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedTitle : presentationData.strings.StickerPackActionInfo_AddedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedText(info.title).string : presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: false, action: { _ in

View File

@ -1472,43 +1472,47 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} else {
transition.animatePosition(layer: self.startButton.layer, from: CGPoint(x: 0.0, y: 80.0), to: CGPoint(), additive: true)
}
}
if let context = self.context {
let absoluteFrame = self.startButton.view.convert(self.startButton.bounds, to: nil)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 1.0), size: CGSize())
if let tooltipController = self.tooltipController {
if self.view.window != nil {
tooltipController.location = .point(location, .bottom)
}
} else {
let controller = TooltipScreen(account: context.account, text: interfaceState.strings.Bot_TapToUse, icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in
return .ignore
})
controller.alwaysVisible = true
self.tooltipController = controller
if let context = self.context {
let absoluteFrame = self.startButton.view.convert(self.startButton.bounds, to: nil)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 1.0), size: CGSize())
let delay: Double
if case .regular = metrics.widthClass {
delay = 0.1
if let tooltipController = self.tooltipController {
if self.view.window != nil {
tooltipController.location = .point(location, .bottom)
}
} else {
delay = 0.35
let controller = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: interfaceState.strings.Bot_TapToUse, icon: .downArrows, location: .point(location, .bottom), displayDuration: .infinite, shouldDismissOnTouch: { _ in
return .ignore
})
controller.alwaysVisible = true
self.tooltipController = controller
let delay: Double
if case .regular = metrics.widthClass {
delay = 0.1
} else {
delay = 0.35
}
Queue.mainQueue().after(delay, {
let absoluteFrame = self.startButton.view.convert(self.startButton.bounds, to: nil)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 1.0), size: CGSize())
controller.location = .point(location, .bottom)
self.interfaceInteraction?.presentControllerInCurrent(controller, nil)
})
}
Queue.mainQueue().after(delay, {
let absoluteFrame = self.startButton.view.convert(self.startButton.bounds, to: nil)
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 1.0), size: CGSize())
controller.location = .point(location, .bottom)
self.interfaceInteraction?.presentControllerInCurrent(controller, nil)
})
}
} else {
if hasMenuButton && !self.animatingTransition {
self.menuButton.isHidden = true
}
}
} else if !self.startButton.isHidden {
if hasMenuButton {
self.animateBotButtonOutToMenu(transition: transition)
} else {
transition.animatePosition(node: self.startButton, to: CGPoint(x: 0.0, y: 80.0), additive: true, completion: { _ in
transition.animatePosition(node: self.startButton, to: CGPoint(x: 0.0, y: 80.0), removeOnCompletion: false, additive: true, completion: { _ in
self.startButton.isHidden = true
self.startButton.layer.removeAllAnimations()
})
}
}

View File

@ -554,9 +554,10 @@ final class ChatThemeScreen: ViewController {
private let animatedEmojiStickers: [String: [StickerPackItem]]
private let initiallySelectedEmoticon: String?
private let peerName: String
let canResetWallpaper: Bool
private let previewTheme: (String?, Bool?) -> Void
fileprivate let changeWallpaper: () -> Void
fileprivate let changeColor: () -> Void
fileprivate let resetWallpaper: () -> Void
private let completion: (String?) -> Void
private var presentationData: PresentationData
@ -578,9 +579,10 @@ final class ChatThemeScreen: ViewController {
animatedEmojiStickers: [String: [StickerPackItem]],
initiallySelectedEmoticon: String?,
peerName: String,
canResetWallpaper: Bool,
previewTheme: @escaping (String?, Bool?) -> Void,
changeWallpaper: @escaping () -> Void,
changeColor: @escaping () -> Void,
resetWallpaper: @escaping () -> Void,
completion: @escaping (String?) -> Void
) {
self.context = context
@ -588,15 +590,18 @@ final class ChatThemeScreen: ViewController {
self.animatedEmojiStickers = animatedEmojiStickers
self.initiallySelectedEmoticon = initiallySelectedEmoticon
self.peerName = peerName
self.canResetWallpaper = canResetWallpaper
self.previewTheme = previewTheme
self.changeWallpaper = changeWallpaper
self.changeColor = changeColor
self.resetWallpaper = resetWallpaper
self.completion = completion
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.blocksBackgroundWhenInOverlay = true
self.presentationDataDisposable = (updatedPresentationData.signal
@ -634,20 +639,20 @@ final class ChatThemeScreen: ViewController {
guard let strongSelf = self else {
return
}
strongSelf.dismiss()
strongSelf.dismiss(animated: true)
if strongSelf.initiallySelectedEmoticon == nil && emoticon == nil {
} else {
strongSelf.completion(emoticon)
}
}
self.controllerNode.dismiss = { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
self?.dismiss(animated: false)
}
self.controllerNode.cancel = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.dismiss()
strongSelf.dismiss(animated: true)
strongSelf.previewTheme(nil, nil)
}
}
@ -667,15 +672,22 @@ final class ChatThemeScreen: ViewController {
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
self.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss()
}
return true
})
self.controllerNode.animateOut(completion: completion)
if flag {
self.controllerNode.animateOut(completion: {
super.dismiss(animated: flag, completion: completion)
completion?()
})
} else {
super.dismiss(animated: flag, completion: completion)
}
self.dismissed?()
}
@ -889,6 +901,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
return
}
let selectedEmoticon = selectedEmoticon?.strippedEmoji
let isFirstTime = strongSelf.entries == nil
let presentationData = strongSelf.presentationData
@ -898,7 +912,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
guard let emoticon = theme.emoticon else {
continue
}
entries.append(ThemeSettingsThemeEntry(index: entries.count, emoticon: emoticon, emojiFile: animatedEmojiStickers[emoticon]?.first?.file, themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)), nightMode: isDarkAppearance, selected: selectedEmoticon == theme.emoticon, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil))
entries.append(ThemeSettingsThemeEntry(index: entries.count, emoticon: emoticon, emojiFile: animatedEmojiStickers[emoticon]?.first?.file, themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)), nightMode: isDarkAppearance, selected: selectedEmoticon == theme.emoticon?.strippedEmoji, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil))
}
let action: (String?) -> Void = { [weak self] emoticon in
@ -981,7 +995,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
var scrollToItem: ListViewScrollToItem?
if !self.initialized {
if let index = transition.entries.firstIndex(where: { entry in
return entry.emoticon == self.initiallySelectedEmoticon
return entry.emoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji
}) {
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down)
self.initialized = true
@ -1013,18 +1027,22 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
}
private func updateButtons() {
let canResetWallpaper = self.controller?.canResetWallpaper ?? false
let doneButtonTitle: String
let otherButtonTitle: String
var accentButtonTheme = true
if self.selectedEmoticon == self.initiallySelectedEmoticon {
var destructiveOtherButton = false
if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji {
doneButtonTitle = self.presentationData.strings.Conversation_Theme_SetPhotoWallpaper
otherButtonTitle = self.presentationData.strings.Conversation_Theme_SetColorWallpaper
otherButtonTitle = canResetWallpaper ? self.presentationData.strings.Conversation_Theme_ResetWallpaper : self.presentationData.strings.Common_Cancel
accentButtonTheme = false
destructiveOtherButton = canResetWallpaper
} else if self.selectedEmoticon == nil && self.initiallySelectedEmoticon != nil {
doneButtonTitle = self.presentationData.strings.Conversation_Theme_Reset
otherButtonTitle = self.presentationData.strings.Conversation_Theme_OtherOptions
} else {
doneButtonTitle = self.presentationData.strings.Conversation_Theme_Apply
doneButtonTitle = self.presentationData.strings.Conversation_Theme_ApplyBackground
otherButtonTitle = self.presentationData.strings.Conversation_Theme_OtherOptions
}
@ -1040,7 +1058,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
}
self.doneButton.updateTheme(buttonTheme)
self.otherButton.setTitle(otherButtonTitle, with: Font.regular(17.0), with: self.presentationData.theme.actionSheet.controlAccentColor, for: .normal)
self.otherButton.setTitle(otherButtonTitle, with: Font.regular(17.0), with: destructiveOtherButton ? self.presentationData.theme.actionSheet.destructiveActionTextColor : self.presentationData.theme.actionSheet.controlAccentColor, for: .normal)
}
private var switchThemeIconAnimator: DisplayLinkAnimator?
@ -1099,15 +1117,20 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
}
@objc func otherButtonPressed() {
if self.selectedEmoticon != self.initiallySelectedEmoticon {
if self.selectedEmoticon?.strippedEmoji != self.initiallySelectedEmoticon?.strippedEmoji {
self.setEmoticon(self.initiallySelectedEmoticon)
} else {
self.controller?.changeColor()
if self.controller?.canResetWallpaper == true {
self.controller?.resetWallpaper()
self.cancelButtonPressed()
} else {
self.cancelButtonPressed()
}
}
}
func dimTapped() {
if self.selectedEmoticon == self.initiallySelectedEmoticon {
if self.selectedEmoticon?.strippedEmoji == self.initiallySelectedEmoticon?.strippedEmoji {
self.cancelButtonPressed()
} else {
let alertController = textAlertController(context: self.context, updatedPresentationData: (self.presentationData, .single(self.presentationData)), title: nil, text: self.presentationData.strings.Conversation_Theme_DismissAlert, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_Theme_DismissAlertApply, action: { [weak self] in
@ -1229,7 +1252,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
if let strongSelf = self, count < 2 && currentTimestamp > timestamp + 24 * 60 * 60 {
strongSelf.displayedPreviewTooltip = true
strongSelf.present?(TooltipScreen(account: strongSelf.context.account, text: isDark ? strongSelf.presentationData.strings.Conversation_Theme_PreviewLight(strongSelf.peerName).string : strongSelf.presentationData.strings.Conversation_Theme_PreviewDark(strongSelf.peerName).string, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
strongSelf.present?(TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: isDark ? strongSelf.presentationData.strings.Conversation_Theme_PreviewLight(strongSelf.peerName).string : strongSelf.presentationData.strings.Conversation_Theme_PreviewDark(strongSelf.peerName).string, style: .default, icon: nil, location: .point(frame.offsetBy(dx: 3.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _ in
return .dismiss(consume: false)
}))

View File

@ -398,9 +398,9 @@ 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, NSDictionary() as CFDictionary)
let colorQuality: Float = 0.5
let options = NSMutableDictionary()
@ -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, NSDictionary() as CFDictionary)
let colorQuality: Float = 0.5

View File

@ -132,8 +132,8 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if let attachBotStart = params.attachBotStart {
controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled)
}
if let botAppStart = params.botAppStart {
controller.presentBotApp(botApp: botAppStart.botApp, payload: botAppStart.payload)
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload)
}
} else {
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation.asChatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, attachBotStart: params.attachBotStart, botAppStart: params.botAppStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter, chatNavigationStack: params.chatNavigationStack)

View File

@ -8467,7 +8467,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
return
}
let buttonFrame = buttonNode.view.convert(buttonNode.bounds, to: self.view)
controller.present(TooltipScreen(account: self.context.account, text: self.presentationData.strings.SharedMedia_CalendarTooltip, style: .default, icon: .none, location: .point(buttonFrame.insetBy(dx: 0.0, dy: 5.0), .top), shouldDismissOnTouch: { point in
controller.present(TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: self.presentationData.strings.SharedMedia_CalendarTooltip, style: .default, icon: .none, location: .point(buttonFrame.insetBy(dx: 0.0, dy: 5.0), .top), shouldDismissOnTouch: { point in
return .dismiss(consume: false)
}), in: .current)
}

View File

@ -162,6 +162,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
public let currentMediaInputSettings: Atomic<MediaInputSettings>
private var mediaInputSettingsDisposable: Disposable?
public let currentMediaDisplaySettings: Atomic<MediaDisplaySettings>
private var mediaDisplaySettingsDisposable: Disposable?
public let currentStickerSettings: Atomic<StickerSettings>
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()

View File

@ -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<TelegramAccountManagerTypes>, _ f: @escaping (MediaDisplaySettings) -> MediaDisplaySettings) -> Signal<Void, NoError> {
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))
})
}
}

View File

@ -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 {

View File

@ -11,6 +11,7 @@ import TelegramCore
import TextFormat
import Postbox
import UrlEscaping
import AccountContext
public protocol TooltipCustomContentNode: ASDisplayNode {
func animateIn()
@ -127,12 +128,11 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private let backgroundContainerNode: ASDisplayNode
private let backgroundClipNode: ASDisplayNode
private let backgroundMaskNode: ASDisplayNode
private var effectView: UIView?
private var effectNode: NavigationBackgroundNode?
private var gradientNode: ASDisplayNode?
private var arrowGradientNode: ASDisplayNode?
private let arrowNode: ASImageNode
private let arrowContainer: ASDisplayNode
private var arrowEffectView: UIView?
private let animatedStickerNode: AnimatedStickerNode
private var downArrowsNode: DownArrowsIconNode?
private let textNode: ImmediateTextNode
@ -143,7 +143,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private var validLayout: ContainerViewLayout?
init(account: Account, text: String, textEntities: [MessageTextEntity], style: TooltipScreen.Style, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, inset: CGFloat = 13.0, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) {
init(account: Account, sharedContext: SharedAccountContext, text: String, textEntities: [MessageTextEntity], style: TooltipScreen.Style, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, inset: CGFloat = 13.0, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) {
self.tooltipStyle = style
self.icon = icon
self.customContentNode = customContentNode
@ -210,9 +210,16 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.arrowContainer = ASDisplayNode()
let theme = sharedContext.currentPresentationData.with { $0 }.theme
let fontSize: CGFloat
if case .top = location {
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
let backgroundColor: UIColor
if theme.overallDarkAppearance {
backgroundColor = theme.rootController.navigationBar.blurredBackgroundColor
} else {
backgroundColor = UIColor(rgb: 0x000000, alpha: 0.6)
}
self.effectNode = NavigationBackgroundNode(color: backgroundColor)
self.backgroundMaskNode.addSubnode(self.backgroundClipNode)
self.backgroundClipNode.clipsToBounds = true
if case let .point(_, arrowPosition) = location, case .right = arrowPosition {
@ -258,20 +265,28 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
maskLayer.frame = CGRect(origin: CGPoint(), size: arrowSize)
self.arrowContainer.layer.mask = maskLayer
} else {
let effect: UIBlurEffect
if case .light = style {
effect = UIBlurEffect(style: .light)
var enableSaturation = true
let backgroundColor: UIColor
if case let .customBlur(color) = style {
backgroundColor = color
enableSaturation = false
} else if case .light = style {
backgroundColor = theme.rootController.navigationBar.blurredBackgroundColor
} else {
effect = UIBlurEffect(style: .dark)
if theme.overallDarkAppearance {
backgroundColor = theme.rootController.navigationBar.blurredBackgroundColor
} else {
backgroundColor = UIColor(rgb: 0x000000, alpha: 0.6)
}
}
self.effectView = UIVisualEffectView(effect: effect)
self.effectNode = NavigationBackgroundNode(color: backgroundColor, enableBlur: true, enableSaturation: enableSaturation)
self.backgroundMaskNode.addSubnode(self.backgroundClipNode)
self.backgroundClipNode.clipsToBounds = true
if case let .point(_, arrowPosition) = location, case .right = arrowPosition {
self.backgroundClipNode.cornerRadius = 8.5
} else {
self.backgroundClipNode.cornerRadius = 14.0
self.backgroundClipNode.cornerRadius = 12.5
}
if #available(iOS 13.0, *) {
self.backgroundClipNode.layer.cornerCurve = .continuous
@ -318,8 +333,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
if let gradientNode = self.gradientNode {
self.backgroundContainerNode.addSubnode(gradientNode)
self.containerNode.addSubnode(self.arrowContainer)
} else if let effectView = self.effectView {
self.backgroundContainerNode.view.addSubview(effectView)
} else if let effectNode = self.effectNode {
self.backgroundContainerNode.addSubnode(effectNode)
self.backgroundContainerNode.layer.mask = self.backgroundMaskNode.layer
}
self.containerNode.addSubnode(self.textNode)
@ -430,7 +445,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
let backgroundHeight: CGFloat
switch self.tooltipStyle {
case .default, .gradient:
case .default, .gradient, .customBlur:
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
case .light:
backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
@ -472,8 +487,10 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
transition.updateFrame(node: self.backgroundMaskNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0))
transition.updateFrame(node: self.backgroundClipNode, frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: backgroundFrame.size))
if let effectView = self.effectView {
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0))
if let effectNode = self.effectNode {
let effectFrame = CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0)
transition.updateFrame(node: effectNode, frame: effectFrame)
effectNode.update(size: effectFrame.size, transition: transition)
}
if let gradientNode = self.gradientNode {
transition.updateFrame(node: gradientNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
@ -501,7 +518,6 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
let arrowBounds = CGRect(origin: CGPoint(), size: arrowSize)
self.arrowNode.frame = arrowBounds
self.arrowEffectView?.frame = arrowBounds
self.arrowGradientNode?.frame = CGRect(origin: CGPoint(x: -arrowFrame.minX + backgroundFrame.minX, y: 0.0), size: backgroundFrame.size)
case .right:
arrowFrame = CGRect(origin: CGPoint(x: backgroundFrame.width + arrowSize.height, y: rect.midY), size: CGSize(width: arrowSize.height, height: arrowSize.width))
@ -512,12 +528,10 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
let arrowBounds = CGRect(origin: .zero, size: arrowSize)
self.arrowNode.frame = arrowBounds
self.arrowEffectView?.frame = arrowBounds
self.arrowGradientNode?.frame = arrowBounds
}
} else {
self.arrowNode.isHidden = true
self.arrowEffectView?.isHidden = true
}
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize))
@ -672,10 +686,12 @@ public final class TooltipScreen: ViewController {
public enum Style {
case `default`
case light
case customBlur(UIColor)
case gradient(UIColor, UIColor)
}
private let account: Account
private let sharedContext: SharedAccountContext
public let text: String
public let textEntities: [MessageTextEntity]
private let style: TooltipScreen.Style
@ -707,8 +723,9 @@ public final class TooltipScreen: ViewController {
public var alwaysVisible = false
public init(account: Account, text: String, textEntities: [MessageTextEntity] = [], style: TooltipScreen.Style = .default, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, inset: CGFloat = 13.0, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? = nil) {
public init(account: Account, sharedContext: SharedAccountContext, text: String, textEntities: [MessageTextEntity] = [], style: TooltipScreen.Style = .default, icon: TooltipScreen.Icon?, customContentNode: TooltipCustomContentNode? = nil, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, inset: CGFloat = 13.0, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)? = nil) {
self.account = account
self.sharedContext = sharedContext
self.text = text
self.textEntities = textEntities
self.style = style
@ -776,7 +793,7 @@ public final class TooltipScreen: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = TooltipScreenNode(account: self.account, text: self.text, textEntities: self.textEntities, style: self.style, icon: self.icon, customContentNode: self.customContentNode, location: self.location, displayDuration: self.displayDuration, inset: self.inset, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
self.displayNode = TooltipScreenNode(account: self.account, sharedContext: self.sharedContext, text: self.text, textEntities: self.textEntities, style: self.style, icon: self.icon, customContentNode: self.customContentNode, location: self.location, displayDuration: self.displayDuration, inset: self.inset, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
guard let strongSelf = self else {
return
}

View File

@ -743,6 +743,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
private var gradientBackgroundNode: GradientBackgroundNode?
private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode?
private let patternImageLayer: EffectImageLayer
private let dimLayer: SimpleLayer
private var isGeneratingPatternImage: Bool = false
private let bakedBackgroundView: UIImageView
@ -862,6 +863,9 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
self.bakedBackgroundView = UIImageView()
self.bakedBackgroundView.isHidden = true
self.dimLayer = SimpleLayer()
self.dimLayer.backgroundColor = UIColor.black.cgColor
super.init()
if #available(iOS 12.0, *) {
@ -885,6 +889,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
self.contentNode.frame = self.bounds
self.addSubnode(self.contentNode)
self.layer.addSublayer(self.patternImageLayer)
self.layer.addSublayer(self.dimLayer)
}
deinit {
@ -892,6 +898,19 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
self.wallpaperDisposable.dispose()
self.imageDisposable.dispose()
}
private func updateDimming() {
guard let wallpaper = self.wallpaper, let theme = self.bubbleTheme else {
return
}
var dimAlpha: Float = 0.0
if case let .file(file) = wallpaper, !file.isPattern {
if let intensity = file.settings.intensity, intensity < 100, theme.overallDarkAppearance == true {
dimAlpha = 1.0 - max(0.0, min(1.0, Float(intensity) / 100.0))
}
}
self.dimLayer.opacity = dimAlpha
}
func update(wallpaper: TelegramWallpaper) {
if self.wallpaper == wallpaper {
@ -915,7 +934,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
gradientColors = file.settings.colors
gradientAngle = file.settings.rotation ?? 0
}
var scheduleLoopingEvent = false
if gradientColors.count >= 3 {
let mappedColors = gradientColors.map { color -> UIColor in
@ -1032,6 +1051,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
self.animateEvent(transition: .animated(duration: 0.7, curve: .linear), extendAnimation: false)
}
}
self.updateDimming()
}
func _internalUpdateIsSettingUpWallpaper() {
@ -1304,6 +1325,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
transition.updateFrame(node: outgoingBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
outgoingBackgroundNode.update(rect: CGRect(origin: CGPoint(), size: size), within: size, transition: transition)
}
transition.updateFrame(layer: self.dimLayer, frame: CGRect(origin: CGPoint(), size: size))
self.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: transition)
@ -1378,6 +1401,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
}
self.updateBubbles()
self.updateDimming()
}
}

View File

@ -144,7 +144,11 @@ public func wallpaperDatas(account: Account, accountManager: AccountManager<Tele
return (thumbnailData, betterFullSizeData ?? fullSizeData, complete)
})
} else {
return .single((thumbnailData, fullSizeData, complete))
if thumbnailData == nil, let decodedThumbnailData = decodedThumbnailData {
return .single((decodedThumbnailData, fullSizeData, complete))
} else {
return .single((thumbnailData, fullSizeData, complete))
}
}
}
}

View File

@ -629,7 +629,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
case "web_app_ready":
self.animateTransitionIn()
case "web_app_switch_inline_query":
if controller.isInline, let json, let query = json["query"] as? String {
if let json, let query = json["query"] as? String {
if let chatTypes = json["chat_types"] as? [String], !chatTypes.isEmpty {
var requestPeerTypes: [ReplyMarkupButtonRequestPeerType] = []
for type in chatTypes {

View File

@ -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<PresentationData, NoError>)?, 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
}