Chat wallpaper improvements

This commit is contained in:
Ilya Laktyushin 2023-04-08 20:10:49 +04:00
parent 8e6ed4c4d0
commit 1e2ed2d2f4
21 changed files with 518 additions and 194 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";
@ -9161,3 +9161,8 @@ Sorry for the inconvenience.";
"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

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

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

@ -2010,18 +2010,18 @@ 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.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

@ -196,7 +196,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
}).start()
}
public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGalleryEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, brightness: 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, _):
@ -279,24 +279,6 @@ public func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: Wallpa
croppedImage = blurredImage(croppedImage, radius: 30.0)!
}
if let brightness, abs(brightness) > 0.01 {
if let updatedImage = generateImage(croppedImage.size, contextGenerator: { size, context in
let bounds = CGRect(origin: .zero, size: size)
if let cgImage = croppedImage.cgImage {
context.draw(cgImage, in: bounds)
}
if brightness > 0.0 {
context.setFillColor(UIColor(rgb: 0xffffff, alpha: brightness).cgColor)
context.setBlendMode(.overlay)
} else {
context.setFillColor(UIColor(rgb: 0x000000, alpha: brightness * -1.0).cgColor)
}
context.fill(bounds)
}) {
croppedImage = updatedImage
}
}
let thumbnailDimensions = finalCropRect.size.fitted(CGSize(width: 320.0, height: 320.0))
let thumbnailImage = generateScaledImage(image: croppedImage, size: thumbnailDimensions, scale: 1.0)
@ -309,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 = 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
@ -326,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

@ -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,
@ -150,6 +150,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina
var pushController: (ViewController) -> Void = { _ in }
var dismissControllers: (() -> Void)?
var openGallery: (() -> Void)?
public init(context: AccountContext, mode: Mode = .default, canDelete: Bool = false) {
self.context = context
@ -192,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) {
@ -270,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 {
@ -327,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 = {}
@ -385,18 +384,25 @@ 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.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

@ -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?
@ -102,6 +104,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
private let cancelButtonNode: WallpaperNavigationButtonNode
private let shareButtonNode: WallpaperNavigationButtonNode
private let dayNightButtonNode: WallpaperNavigationButtonNode
private let blurButtonNode: WallpaperOptionButtonNode
private let motionButtonNode: WallpaperOptionButtonNode
@ -139,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
@ -154,6 +163,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.blurredNode = BlurredImageNode()
self.brightnessNode = ASDisplayNode()
self.brightnessNode.alpha = 0.0
self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
@ -169,16 +179,18 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.serviceBackgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x333333, alpha: 0.33))
var sliderValueChangedImpl: ((CGFloat) -> Void)?
self.sliderNode = WallpaperSliderNode(minValue: 0.0, maxValue: 1.0, value: 0.5, valueChanged: { value, _ in
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), dark: false)
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: false)
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))
@ -242,6 +254,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.addSubnode(self.sliderNode)
self.addSubnode(self.cancelButtonNode)
self.addSubnode(self.shareButtonNode)
self.addSubnode(self.dayNightButtonNode)
self.imageNode.addSubnode(self.brightnessNode)
@ -252,22 +265,11 @@ 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 {
let value = (value - 0.5) * 2.0
if value < 0.0 {
self.brightnessNode.backgroundColor = UIColor(rgb: 0x000000)
self.brightnessNode.layer.compositingFilter = nil
self.brightnessNode.alpha = value * -1.0
} else if value > 0.0 {
self.brightnessNode.backgroundColor = UIColor(rgb: 0xffffff)
self.brightnessNode.layer.compositingFilter = "overlayBlendMode"
self.brightnessNode.alpha = value
} else {
self.brightnessNode.layer.compositingFilter = nil
self.brightnessNode.alpha = 0.0
}
self.updateIntensity(transition: .immediate)
}
}
}
@ -297,7 +299,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
switch entry {
case .asset, .contextResult:
return (self.sliderNode.value - 0.5) * 2.0
return self.sliderNode.value
default:
return nil
}
@ -311,6 +313,53 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.action?()
}
@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))
}
}
}
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()
}
@ -329,6 +378,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
}
var showPreviewTooltip = false
if self.entry != entry || self.arguments.colorPreview != previousArguments.colorPreview {
let previousEntry = self.entry
self.entry = entry
@ -357,8 +408,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
var isBlurrable = true
self.nativeNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners)
switch entry {
case let .wallpaper(wallpaper, _):
self.nativeNode.update(wallpaper: wallpaper)
@ -393,6 +442,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
case .asset:
self.nativeNode._internalUpdateIsSettingUpWallpaper()
//self.nativeNode.update(wallpaper: self.presentationData.chatWallpaper)
self.nativeNode.isHidden = true
self.patternButtonNode.isSelected = false
self.playButtonNode.setIcon(self.playButtonRotateImage)
@ -402,7 +452,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.playButtonNode.setIcon(self.playButtonRotateImage)
}
var isEditable = false
self.nativeNode.updateBubbleTheme(bubbleTheme: presentationData.theme, bubbleCorners: presentationData.chatBubbleCorners)
var canShare = false
switch entry {
case let .wallpaper(wallpaper, message):
@ -566,7 +617,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
self.cropNode.removeFromSupernode()
case let .asset(asset):
isEditable = true
let dimensions = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
contentSize = dimensions
displaySize = dimensions.dividedByScreenScale().integralFloor
@ -576,8 +626,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
subtitleSignal = .single(nil)
colorSignal = .single(UIColor(rgb: 0x000000, alpha: 0.3))
self.wrapperNode.addSubnode(self.cropNode)
showPreviewTooltip = true
case let .contextResult(result):
isEditable = true
var imageDimensions: CGSize?
var imageResource: TelegramMediaResource?
var thumbnailDimensions: CGSize?
@ -631,11 +681,12 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
colorSignal = .single(UIColor(rgb: 0x000000, alpha: 0.3))
subtitleSignal = .single(nil)
self.wrapperNode.addSubnode(self.cropNode)
showPreviewTooltip = true
}
self.contentSize = contentSize
self.cancelButtonNode.dark = !isEditable
self.shareButtonNode.dark = !isEditable
//self.cancelButtonNode.dark = !isEditable
//self.shareButtonNode.dark = !isEditable
self.shareButtonNode.isHidden = !canShare
if self.cropNode.supernode == nil {
@ -713,6 +764,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) {
@ -966,7 +1029,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
if let source = self.source {
switch source {
case .asset, .contextResult:
additionalYOffset -= 44.0
if self.isDarkAppearance {
additionalYOffset -= 44.0
}
default:
break
}
@ -996,8 +1061,17 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
var playAlpha: CGFloat = 0.0
let sliderSize = CGSize(width: 268.0, height: 30.0)
let sliderFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - sliderSize.width) / 2.0) + offset.x, y: layout.size.height - toolbarHeight - layout.intrinsicInsets.bottom - 52.0 + offset.y), size: sliderSize)
var sliderIsHidden = true
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)
@ -1013,13 +1087,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
blurFrame = leftButtonFrame
motionAlpha = 1.0
motionFrame = rightButtonFrame
sliderIsHidden = false
dayNightHidden = false
case .contextResult:
blurAlpha = 1.0
blurFrame = leftButtonFrame
motionAlpha = 1.0
motionFrame = rightButtonFrame
sliderIsHidden = false
dayNightHidden = false
case let .wallpaper(wallpaper, _):
switch wallpaper {
case .builtin:
@ -1104,12 +1178,16 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
transition.updateAlpha(node: self.playButtonNode, alpha: playAlpha * alpha)
transition.updateSublayerTransformScale(node: self.playButtonNode, scale: max(0.1, playAlpha))
transition.updateFrame(node: self.sliderNode, frame: sliderFrame)
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)
self.sliderNode.isHidden = sliderIsHidden
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) {
@ -1182,7 +1260,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
case .asset, .contextResult:
topMessageText = presentationData.strings.WallpaperPreview_CropTopText
bottomMessageText = presentationData.strings.WallpaperPreview_CropBottomText
bottomInset += 44.0
if self.isDarkAppearance {
bottomInset += 44.0
}
case .customColor:
topMessageText = presentationData.strings.WallpaperPreview_CustomColorTopText
bottomMessageText = presentationData.strings.WallpaperPreview_CustomColorBottomText
@ -1197,7 +1277,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
}
let theme = self.presentationData.theme.withUpdated(preview: true)
let theme = self.presentationData.theme.withUpdated(preview: false)
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))
@ -1302,6 +1382,16 @@ 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
@ -1322,4 +1412,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) || "".isEmpty {
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

@ -5,6 +5,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import CheckNode
import AnimationUI
enum WallpaperOptionButtonValue {
case check(Bool)
@ -93,6 +94,7 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
enum Content {
case icon(image: UIImage?, size: CGSize)
case text(String)
case dayNight(isNight: Bool)
}
var enableSaturation: Bool = false
@ -115,6 +117,7 @@ final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode {
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)
@ -141,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.5
animationNode.isUserInteractionEnabled = false
self.animationNode = animationNode
}
self.textNode = ImmediateTextNode()
@ -152,19 +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.5
Queue.mainQueue().after(0.01) {
self.animationNode?.playOnce()
}
}
var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) {
didSet {
}
@ -179,6 +222,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)
}
}
@ -199,6 +244,11 @@ 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)
}
}
}
@ -491,7 +541,16 @@ final class WallpaperSliderNode: ASDisplayNode {
self.view.addGestureRecognizer(tapGestureRecognizer)
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
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))
@ -506,10 +565,17 @@ final class WallpaperSliderNode: ASDisplayNode {
}
let range = self.maxValue - self.minValue
let value = (self.value - self.minValue) / range
let value = (value - self.minValue) / range
let foregroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: value * size.width, height: size.height))
transition.updateFrameAdditive(node: self.foregroundNode, frame: foregroundFrame)
transition.updateFrameAdditive(node: self.foregroundLightNode, frame: foregroundFrame)
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) {
@ -525,14 +591,10 @@ final class WallpaperSliderNode: ASDisplayNode {
self.value = max(self.minValue, min(self.maxValue, self.value + delta))
gestureRecognizer.setTranslation(CGPoint(), in: gestureRecognizer.view)
if self.value == 2.0 && previousValue != 2.0 {
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)
} else if self.value == 2.5 && previousValue != 2.5 {
self.hapticFeedback.impact(.soft)
} else if self.value == 0.05 && previousValue != 0.05 {
self.hapticFeedback.impact(.soft)
}
if abs(previousValue - self.value) >= 0.001 {
self.valueChanged(self.value, false)

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

@ -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 {
@ -5839,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
@ -5846,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
}
@ -5857,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
@ -5896,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
@ -5905,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
@ -5944,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
@ -14314,7 +14343,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 {
@ -14407,7 +14436,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 {
@ -14521,7 +14550,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 {
@ -18508,12 +18537,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
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,
peerName: strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? "",
canResetWallpaper: canResetWallpaper,
previewTheme: { [weak self] emoticon, dark in
if let strongSelf = self {
strongSelf.presentCrossfadeSnapshot()
@ -18540,54 +18575,47 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
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, brightness in
if let strongSelf = self {
uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, brightness: brightness, peerId: peerId, completion: {
dismissControllers()
})
var openWallpaperPickerImpl: (() -> Void)?
let openWallpaperPicker = {
let controller = wallpaperMediaPickerController(
context: strongSelf.context,
updatedPresentationData: strongSelf.updatedPresentationData,
peer: EnginePeer(peer),
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, 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)
}
strongSelf.push(controller)
}
)
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
},
changeColor: {
guard let strongSelf = self else {
return
)
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
}
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)
openWallpaperPickerImpl = openWallpaperPicker
openWallpaperPicker()
},
completion: { [weak self] emoticon in
guard let strongSelf = self, let peerId else {

View File

@ -1483,7 +1483,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
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
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

View File

@ -554,9 +554,9 @@ 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
private let completion: (String?) -> Void
private var presentationData: PresentationData
@ -578,9 +578,9 @@ final class ChatThemeScreen: ViewController {
animatedEmojiStickers: [String: [StickerPackItem]],
initiallySelectedEmoticon: String?,
peerName: String,
canResetWallpaper: Bool,
previewTheme: @escaping (String?, Bool?) -> Void,
changeWallpaper: @escaping () -> Void,
changeColor: @escaping () -> Void,
completion: @escaping (String?) -> Void
) {
self.context = context
@ -588,9 +588,9 @@ 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.completion = completion
super.init(navigationBarPresentationData: nil)
@ -1013,18 +1013,22 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
}
private func updateButtons() {
let canResetWallpaper = self.controller?.canResetWallpaper ?? false
let doneButtonTitle: String
let otherButtonTitle: String
var accentButtonTheme = true
var destructiveOtherButton = false
if self.selectedEmoticon == self.initiallySelectedEmoticon {
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 +1044,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?
@ -1102,7 +1106,11 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, UIScrollViewDelega
if self.selectedEmoticon != self.initiallySelectedEmoticon {
self.setEmoticon(self.initiallySelectedEmoticon)
} else {
self.controller?.changeColor()
if self.controller?.canResetWallpaper == true {
} else {
self.cancelButtonPressed()
}
}
}
@ -1229,7 +1237,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

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

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