From f4cc9d1aad5f53f7f4099cd18f8528f7d89b78b8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 18 Jul 2024 04:45:10 +0400 Subject: [PATCH] Story weather display --- .../Sources/DrawingWeatherEntityView.swift | 63 +----- .../TelegramEngine/Messages/MediaArea.swift | 2 +- .../Drawing/CodableDrawingEntity.swift | 13 +- .../Drawing/DrawingWeatherEntity.swift | 42 ++-- .../Sources/MediaEditorComposerEntity.swift | 8 +- .../Sources/MediaEditorScreen.swift | 21 +- .../MediaEditorScreen/Sources/Weather.swift | 20 +- .../Sources/StoryItemOverlaysView.swift | 191 ++++++++++++++++-- 8 files changed, 245 insertions(+), 115 deletions(-) diff --git a/submodules/DrawingUI/Sources/DrawingWeatherEntityView.swift b/submodules/DrawingUI/Sources/DrawingWeatherEntityView.swift index 05d6165b24..555fefe6c6 100644 --- a/submodules/DrawingUI/Sources/DrawingWeatherEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingWeatherEntityView.swift @@ -8,6 +8,7 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import StickerResources import MediaEditor +import TelegramStringFormatting private func generateIcon(style: DrawingWeatherEntity.Style) -> UIImage? { guard let image = UIImage(bundleImageName: "Chat/Attach Menu/Location") else { @@ -50,7 +51,6 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega } let backgroundView: UIView - let blurredBackgroundView: BlurredBackgroundView let textView: DrawingTextView let iconView: UIImageView @@ -61,13 +61,14 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega private let stickerFetchedDisposable = MetaDisposable() private let cachedDisposable = MetaDisposable() + let temperature: String + init(context: AccountContext, entity: DrawingWeatherEntity) { + self.temperature = stringForTemperature(entity.temperature) + self.backgroundView = UIView() self.backgroundView.clipsToBounds = true - self.blurredBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.25), enableBlur: true) - self.blurredBackgroundView.clipsToBounds = true - self.textView = DrawingTextView(frame: .zero) self.textView.clipsToBounds = false @@ -95,7 +96,6 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega self.textView.delegate = self self.addSubview(self.backgroundView) - self.addSubview(self.blurredBackgroundView) self.addSubview(self.textView) self.addSubview(self.iconView) @@ -138,7 +138,7 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega self.imageNode.frame = self.iconView.frame.offsetBy(dx: 0.0, dy: 2.0) if let animationNode = self.animationNode { - animationNode.frame = self.iconView.frame.offsetBy(dx: 0.0, dy: 2.0) + animationNode.frame = self.iconView.frame animationNode.updateLayout(size: self.iconView.frame.size) } @@ -147,8 +147,6 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega self.textView.frame = CGRect(origin: CGPoint(x: self.bounds.width - self.textSize.width - 6.0, y: floorToScreenPixels((self.bounds.height - self.textSize.height) / 2.0)), size: self.textSize) self.backgroundView.frame = self.bounds - self.blurredBackgroundView.frame = self.bounds - self.blurredBackgroundView.update(size: self.bounds.size, transition: .immediate) } override func selectedTapAction() -> Bool { @@ -161,16 +159,6 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega case .white: updatedStyle = .black case .black: - updatedStyle = .transparent - case .transparent: - if self.weatherEntity.hasCustomColor { - updatedStyle = .custom - } else { - updatedStyle = .white - } - case .custom: - updatedStyle = .white - case .blur: updatedStyle = .white } self.weatherEntity.style = updatedStyle @@ -182,7 +170,7 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega private var displayFontSize: CGFloat { var textFontSize: CGFloat = 0.07 - let textLength = self.weatherEntity.temperature.count + let textLength = self.temperature.count if textLength > 10 { textFontSize = max(0.01, 0.07 - CGFloat(textLength - 10) / 100.0) } @@ -194,7 +182,7 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega } private func updateText() { - let text = NSMutableAttributedString(string: self.weatherEntity.temperature.uppercased()) + let text = NSMutableAttributedString(string: self.temperature.uppercased()) let range = NSMakeRange(0, text.length) let fontSize = self.displayFontSize @@ -213,15 +201,8 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega switch self.weatherEntity.style { case .white: textColor = .black - case .black, .transparent, .blur: + case .black: textColor = .white - case .custom: - let color = self.weatherEntity.color.toUIColor() - if color.lightness > 0.705 { - textColor = .black - } else { - textColor = .white - } } text.addAttribute(.foregroundColor, value: textColor, range: range) @@ -241,34 +222,10 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega self.textView.textColor = .black self.backgroundView.backgroundColor = .white self.backgroundView.isHidden = false - self.blurredBackgroundView.isHidden = true case .black: self.textView.textColor = .white self.backgroundView.backgroundColor = .black self.backgroundView.isHidden = false - self.blurredBackgroundView.isHidden = true - case .transparent: - self.textView.textColor = .white - self.backgroundView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.2) - self.backgroundView.isHidden = false - self.blurredBackgroundView.isHidden = true - case .custom: - let color = self.weatherEntity.color.toUIColor() - let textColor: UIColor - if color.lightness > 0.705 { - textColor = .black - } else { - textColor = .white - } - self.textView.textColor = textColor - self.backgroundView.backgroundColor = color - self.backgroundView.isHidden = false - self.blurredBackgroundView.isHidden = true - case .blur: - self.textView.textColor = .white - self.backgroundView.isHidden = true - self.backgroundView.backgroundColor = UIColor(rgb: 0xffffff) - self.blurredBackgroundView.isHidden = false } self.textView.textAlignment = .left @@ -282,10 +239,8 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega } self.backgroundView.layer.cornerRadius = self.textSize.height * 0.2 - self.blurredBackgroundView.layer.cornerRadius = self.backgroundView.layer.cornerRadius if #available(iOS 13.0, *) { self.backgroundView.layer.cornerCurve = .continuous - self.blurredBackgroundView.layer.cornerCurve = .continuous } super.update(animated: animated) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift index 091046c84c..22038df4f0 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift @@ -249,7 +249,7 @@ public enum MediaArea: Codable, Equatable { try container.encode(coordinates, forKey: .coordinates) try container.encode(url, forKey: .value) case let .weather(coordinates, emoji, temperature, flags): - try container.encode(MediaAreaType.link.rawValue, forKey: .type) + try container.encode(MediaAreaType.weather.rawValue, forKey: .type) try container.encode(coordinates, forKey: .coordinates) try container.encode(emoji, forKey: .value) try container.encode(temperature, forKey: .temperature) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift index 8830b70ed6..c09612a9c4 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift @@ -120,7 +120,7 @@ public enum CodableDrawingEntity: Equatable { rotation = entity.rotation scale = entity.scale if let size { - cornerRadius = 10.0 / (size.width * entity.scale) + cornerRadius = (size.height * 0.17) / size.width } default: return nil @@ -191,6 +191,17 @@ public enum CodableDrawingEntity: Equatable { coordinates: coordinates, url: url ) + case let .weather(entity): + var flags: MediaArea.WeatherFlags = [] + if entity.style == .black { + flags.insert(.isDark) + } + return .weather( + coordinates: coordinates, + emoji: entity.emoji, + temperature: entity.temperature, + flags: flags + ) default: return nil } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingWeatherEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingWeatherEntity.swift index 67fb6eebc5..6f42269245 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingWeatherEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingWeatherEntity.swift @@ -11,7 +11,7 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { case uuid case style case color - case hasCustomColor + case emoji case temperature case icon case referenceDrawingSize @@ -25,9 +25,6 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { public enum Style: Codable, Equatable { case white case black - case transparent - case custom - case blur } public var uuid: UUID @@ -37,20 +34,11 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { public var style: Style - public var temperature: String public var icon: TelegramMediaFile? - public var color: DrawingColor = DrawingColor(color: .white) { - didSet { - if self.color.toUIColor().argb == UIColor.white.argb { - self.style = .white - self.hasCustomColor = false - } else { - self.style = .custom - self.hasCustomColor = true - } - } - } - public var hasCustomColor = false + public var emoji: String + public var temperature: Double + + public var color: DrawingColor = DrawingColor.clear public var lineWidth: CGFloat = 0.0 public var referenceDrawingSize: CGSize @@ -74,13 +62,14 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { return false } - public init(temperature: String, style: Style, icon: TelegramMediaFile?) { + public init(emoji: String, emojiFile: TelegramMediaFile?, temperature: Double, style: Style) { self.uuid = UUID() + self.emoji = emoji + self.icon = emojiFile self.temperature = temperature self.style = style - self.icon = icon - + self.referenceDrawingSize = .zero self.position = .zero self.width = 100.0 @@ -91,10 +80,9 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.uuid = try container.decode(UUID.self, forKey: .uuid) - self.temperature = try container.decode(String.self, forKey: .temperature) + self.emoji = try container.decode(String.self, forKey: .emoji) + self.temperature = try container.decode(Double.self, forKey: .temperature) self.style = try container.decode(Style.self, forKey: .style) - self.color = try container.decodeIfPresent(DrawingColor.self, forKey: .color) ?? DrawingColor(color: .white) - self.hasCustomColor = try container.decodeIfPresent(Bool.self, forKey: .hasCustomColor) ?? false if let iconData = try container.decodeIfPresent(Data.self, forKey: .icon) { self.icon = PostboxDecoder(buffer: MemoryBuffer(data: iconData)).decodeRootObject() as? TelegramMediaFile @@ -113,10 +101,9 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.uuid, forKey: .uuid) + try container.encode(self.emoji, forKey: .emoji) try container.encode(self.temperature, forKey: .temperature) try container.encode(self.style, forKey: .style) - try container.encode(self.color, forKey: .color) - try container.encode(self.hasCustomColor, forKey: .hasCustomColor) var encoder = PostboxEncoder() if let icon = self.icon { @@ -137,7 +124,7 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { } public func duplicate(copy: Bool) -> DrawingEntity { - let newEntity = DrawingWeatherEntity(temperature: self.temperature, style: self.style, icon: self.icon) + let newEntity = DrawingWeatherEntity(emoji: self.emoji, emojiFile: self.icon, temperature: self.temperature, style: self.style) if copy { newEntity.uuid = self.uuid } @@ -156,6 +143,9 @@ public final class DrawingWeatherEntity: DrawingEntity, Codable { if self.uuid != other.uuid { return false } + if self.emoji != other.emoji { + return false + } if self.temperature != other.temperature { return false } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index 6a2d3760f3..4a8440725b 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -70,7 +70,9 @@ private func prerenderTextTransformations(entity: DrawingEntity, image: UIImage, } func composerEntitiesForDrawingEntity(postbox: Postbox, textScale: CGFloat, entity: DrawingEntity, colorSpace: CGColorSpace, tintColor: UIColor? = nil) -> [MediaEditorComposerEntity] { - if let entity = entity as? DrawingStickerEntity { + if entity is DrawingWeatherEntity { + return [] + } else if let entity = entity as? DrawingStickerEntity { if case let .file(_, type) = entity.content, case .reaction = type { return [] } else { @@ -126,10 +128,10 @@ func composerEntitiesForDrawingEntity(postbox: Postbox, textScale: CGFloat, enti return entities } else if let entity = entity as? DrawingLocationEntity { return [prerenderTextTransformations(entity: entity, image: renderImage, textScale: textScale, colorSpace: colorSpace)] - } else if let entity = entity as? DrawingWeatherEntity { - return [prerenderTextTransformations(entity: entity, image: renderImage, textScale: textScale, colorSpace: colorSpace)] } else if let entity = entity as? DrawingLinkEntity { return [prerenderTextTransformations(entity: entity, image: renderImage, textScale: textScale, colorSpace: colorSpace)] + } else if let entity = entity as? DrawingWeatherEntity { + return [prerenderTextTransformations(entity: entity, image: renderImage, textScale: textScale, colorSpace: colorSpace)] } } return [] diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 3f030dc502..cfde1b0530 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -4583,11 +4583,24 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } func addWeather(_ weather: StickerPickerScreen.Weather.LoadedWeather) { + let maxWeatherCount = 3 + var currentWeatherCount = 0 + self.entitiesView.eachView { entityView in + if entityView.entity is DrawingWeatherEntity { + currentWeatherCount += 1 + } + } + if currentWeatherCount >= maxWeatherCount { + self.controller?.hapticFeedback.error() + return + } + self.interaction?.insertEntity( DrawingWeatherEntity( - temperature: stringForTemperature(weather.temperature), - style: .white, - icon: weather.emojiFile + emoji: weather.emoji, + emojiFile: weather.emojiFile, + temperature: weather.temperature, + style: .white ), scale: nil, position: nil @@ -6061,7 +6074,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) self.present(controller, in: .window(.root)) } - + func maybePresentDiscardAlert() { self.hapticFeedback.impact(.light) if !self.isEligibleForDraft() { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Weather.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Weather.swift index f9ada25a30..0a0f0bfed5 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Weather.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/Weather.swift @@ -58,18 +58,14 @@ func getWeather(context: AccountContext) -> Signal mapToSignal { weather in if let weather { - return context.animatedEmojiStickers - |> take(1) - |> mapToSignal { result in - if let match = result[weather.emoji.strippedEmoji]?.first { - return .single(.loaded(StickerPickerScreen.Weather.LoadedWeather( - emoji: weather.emoji.strippedEmoji, - emojiFile: match.file, - temperature: weather.temperature - ))) - } else { - return .single(.none) - } + if let match = context.animatedEmojiStickersValue[weather.emoji.strippedEmoji]?.first { + return .single(.loaded(StickerPickerScreen.Weather.LoadedWeather( + emoji: weather.emoji.strippedEmoji, + emojiFile: match.file, + temperature: weather.temperature + ))) + } else { + return .single(.none) } } else { return .single(.none) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift index 7f7863caf7..ab69237e03 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemOverlaysView.swift @@ -20,6 +20,7 @@ import LottieComponent import LottieComponentResourceContent import StickerResources import AnimationCache +import TelegramStringFormatting private let shadowImage: UIImage = { return UIImage(bundleImageName: "Stories/ReactionShadow")! @@ -224,12 +225,16 @@ public func storyPreviewWithAddedReactions( } } +private protocol ItemView: UIView { + +} + final class StoryItemOverlaysView: UIView { static let counterFont: UIFont = { return Font.with(size: 17.0, design: .camera, weight: .semibold, traits: .monospacedNumbers) }() - private final class ItemView: HighlightTrackingButton { + private final class ReactionView: HighlightTrackingButton, ItemView { private let shadowView: UIImageView private let coverView: UIImageView @@ -524,6 +529,120 @@ final class StoryItemOverlaysView: UIView { } } + private final class WeatherView: UIView, ItemView { + private let backgroundView = UIView() + private let directStickerView = ComponentView() + private let text = ComponentView() + + private var file: TelegramMediaFile? + private var textFont: UIFont? + + private var customEmojiLoadDisposable: Disposable? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.backgroundView.clipsToBounds = true + if #available(iOS 13.0, *) { + self.backgroundView.layer.cornerCurve = .continuous + } + self.addSubview(self.backgroundView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.customEmojiLoadDisposable?.dispose() + } + + func update( + context: AccountContext, + emoji: String, + emojiFile: TelegramMediaFile?, + temperature: Double, + flags: MediaArea.WeatherFlags, + synchronous: Bool, + size: CGSize, + cornerRadius: CGFloat, + isActive: Bool + ) { + self.backgroundView.backgroundColor = flags.contains(.isDark) ? UIColor(rgb: 0x000000) : UIColor(rgb: 0xffffff) + self.backgroundView.frame = CGRect(origin: .zero, size: size) + self.backgroundView.layer.cornerRadius = cornerRadius + + let itemSize = CGSize(width: floor(size.height * 0.71), height: floor(size.height * 0.71)) + + if self.file?.fileId != emojiFile?.fileId, let file = emojiFile { + self.file = file + + self.customEmojiLoadDisposable?.dispose() + self.customEmojiLoadDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .standalone(resource: file.resource)).start() + + let placeholderColor = flags.contains(.isDark) ? UIColor(white: 1.0, alpha: 0.1) : UIColor(white: 0.0, alpha: 0.1) + let _ = self.directStickerView.update( + transition: .immediate, + component: AnyComponent(LottieComponent( + content: LottieComponent.ResourceContent(context: context, file: file, attemptSynchronously: synchronous, providesPlaceholder: true), + placeholderColor: placeholderColor, + renderingScale: 2.0, + loop: true + )), + environment: {}, + containerSize: itemSize + ) + } + + let textFont: UIFont + if let current = self.textFont { + textFont = current + } else { + textFont = Font.with(size: floorToScreenPixels(size.height * 0.69), design: .camera, weight: .semibold, traits: .monospacedNumbers) + self.textFont = textFont + } + + let string = NSMutableAttributedString( + string: stringForTemperature(temperature), + font: textFont, + textColor: flags.contains(.isDark) ? UIColor(rgb: 0xffffff) : UIColor(rgb: 0x000000) + ) + string.addAttribute(.kern, value: -(size.height / 38.0) as NSNumber, range: NSMakeRange(0, string.length)) + + let textSize = self.text.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent(text: .plain(string)) + ), + environment: {}, + containerSize: size + ) + + if let view = self.text.view { + if view.superview == nil { + self.addSubview(view) + } + let textFrame = CGRect(origin: CGPoint(x: size.width - textSize.width - size.height * 0.2, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) + let textTransition = ComponentTransition.immediate + textTransition.setFrame(view: view, frame: textFrame) + } + + if let directStickerView = self.directStickerView.view as? LottieComponent.View { + if directStickerView.superview == nil { + self.addSubview(directStickerView) + } + + let stickerFrame = itemSize.centered(around: CGPoint(x: size.height * 0.5 + size.height * 0.058, y: size.height * 0.5)) + + let stickerTransition = ComponentTransition.immediate + stickerTransition.setPosition(view: directStickerView, position: stickerFrame.center) + stickerTransition.setBounds(view: directStickerView, bounds: CGRect(origin: CGPoint(), size: stickerFrame.size)) + + directStickerView.externalShouldPlay = isActive + } + } + } + private var itemViews: [Int: ItemView] = [:] var activate: ((UIView, MessageReaction.Reaction) -> Void)? var requestUpdate: (() -> Void)? @@ -561,25 +680,37 @@ final class StoryItemOverlaysView: UIView { isActive: Bool, transition: ComponentTransition ) { + func getFrameAndRotation(coordinates: MediaArea.Coordinates, scale: CGFloat = 1.0) -> (frame: CGRect, rotation: CGFloat, cornerRadius: CGFloat)? { + let referenceSize = size + var areaSize = CGSize(width: coordinates.width / 100.0 * referenceSize.width, height: coordinates.height / 100.0 * referenceSize.height) + areaSize.width *= scale + areaSize.height *= scale + let targetFrame = CGRect(x: coordinates.x / 100.0 * referenceSize.width - areaSize.width * 0.5, y: coordinates.y / 100.0 * referenceSize.height - areaSize.height * 0.5, width: areaSize.width, height: areaSize.height) + if targetFrame.width < 5.0 || targetFrame.height < 5.0 { + return nil + } + var cornerRadius: CGFloat = 0.0 + if let radius = coordinates.cornerRadius { + cornerRadius = radius / 100.0 * areaSize.width + } + + return (targetFrame, coordinates.rotation * (CGFloat.pi / 180.0), cornerRadius) + } + var nextId = 0 for mediaArea in story.mediaAreas { switch mediaArea { case let .reaction(coordinates, reaction, flags): - let referenceSize = size - var areaSize = CGSize(width: coordinates.width / 100.0 * referenceSize.width, height: coordinates.height / 100.0 * referenceSize.height) - areaSize.width *= 0.97 - areaSize.height *= 0.97 - let targetFrame = CGRect(x: coordinates.x / 100.0 * referenceSize.width - areaSize.width * 0.5, y: coordinates.y / 100.0 * referenceSize.height - areaSize.height * 0.5, width: areaSize.width, height: areaSize.height) - if targetFrame.width < 5.0 || targetFrame.height < 5.0 { + guard let (itemFrame, itemRotation, _) = getFrameAndRotation(coordinates: coordinates, scale: 0.97) else { continue } - let itemView: ItemView + let itemView: ReactionView let itemId = nextId - if let current = self.itemViews[itemId] { + if let current = self.itemViews[itemId] as? ReactionView { itemView = current } else { - itemView = ItemView(frame: CGRect()) + itemView = ReactionView(frame: CGRect()) itemView.activate = { [weak self] view, reaction in self?.activate?(view, reaction) } @@ -590,9 +721,9 @@ final class StoryItemOverlaysView: UIView { self.addSubview(itemView) } - transition.setPosition(view: itemView, position: targetFrame.center) - transition.setBounds(view: itemView, bounds: CGRect(origin: CGPoint(), size: targetFrame.size)) - transition.setTransform(view: itemView, transform: CATransform3DMakeRotation(coordinates.rotation * (CGFloat.pi / 180.0), 0.0, 0.0, 1.0)) + transition.setPosition(view: itemView, position: itemFrame.center) + transition.setBounds(view: itemView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) + transition.setTransform(view: itemView, transform: CATransform3DMakeRotation(itemRotation, 0.0, 0.0, 1.0)) var counter = 0 if let reactionData = story.views?.reactions.first(where: { $0.value == reaction }) { @@ -607,7 +738,39 @@ final class StoryItemOverlaysView: UIView { availableReactions: availableReactions, entityFiles: entityFiles, synchronous: attemptSynchronous, - size: targetFrame.size, + size: itemFrame.size, + isActive: isActive + ) + + nextId += 1 + case let .weather(coordinates, emoji, temperature, flags): + guard let (itemFrame, itemRotation, cornerRadius) = getFrameAndRotation(coordinates: coordinates) else { + continue + } + + let itemView: WeatherView + let itemId = nextId + if let current = self.itemViews[itemId] as? WeatherView { + itemView = current + } else { + itemView = WeatherView(frame: CGRect()) + self.itemViews[itemId] = itemView + self.addSubview(itemView) + } + + transition.setPosition(view: itemView, position: itemFrame.center) + transition.setBounds(view: itemView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) + transition.setTransform(view: itemView, transform: CATransform3DMakeRotation(itemRotation, 0.0, 0.0, 1.0)) + + itemView.update( + context: context, + emoji: emoji, + emojiFile: context.animatedEmojiStickersValue[emoji]?.first?.file, + temperature: temperature, + flags: flags, + synchronous: attemptSynchronous, + size: itemFrame.size, + cornerRadius: cornerRadius, isActive: isActive )