This commit is contained in:
Isaac 2024-07-18 11:41:53 +08:00
commit 4ee31cd176
26 changed files with 552 additions and 389 deletions

View file

@ -492,14 +492,22 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
self.currentError = error
if (error as NSError).code != -999 {
self.currentError = error
} else {
self.currentError = nil
}
if let (size, insets) = self.validLayout {
self.updateLayout(size: size, insets: insets, transition: .immediate)
}
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.currentError = error
if (error as NSError).code != -999 {
self.currentError = error
} else {
self.currentError = nil
}
if let (size, insets) = self.validLayout {
self.updateLayout(size: size, insets: insets, transition: .immediate)
}

View file

@ -65,7 +65,6 @@ public enum ChatOpenWebViewSource: Equatable {
case generic
case menu
case inline(bot: EnginePeer)
case webApp(botApp: BotApp)
}
public final class ChatPanelInterfaceInteraction {

View file

@ -282,7 +282,7 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes
let transition: ContainedViewLayoutTransition
let dismissProgress: CGFloat
if (velocity.y < -0.5 || progress >= 0.5) && self.checkInteractiveDismissWithControllers() {
if self.isDraggingHeader, let controller = self.container.controllers.last as? MinimizableController {
if let controller = self.container.controllers.last as? MinimizableController, self.isDraggingHeader || "".isEmpty {
dismissProgress = 0.0
targetOffset = 0.0
transition = .immediate

View file

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

View file

@ -1556,11 +1556,13 @@ private func monetizationEntries(
entries.append(.adsProceedsTitle(presentationData.theme, presentationData.strings.Monetization_StarsProceeds_Title))
entries.append(.adsProceedsOverview(presentationData.theme, canViewRevenue ? data : nil, canViewStarsRevenue ? starsData : nil))
let hasTonBalance = data.balances.overallRevenue > 0
let hasStarsBalance = (starsData?.balances.overallRevenue ?? 0) > 0
let proceedsInfo: String
if canViewStarsRevenue && canViewRevenue {
if (canViewStarsRevenue && hasStarsBalance) && (canViewRevenue && hasTonBalance) {
proceedsInfo = presentationData.strings.Monetization_Proceeds_TonAndStars_Info
} else if canViewStarsRevenue {
} else if canViewStarsRevenue && hasStarsBalance {
proceedsInfo = presentationData.strings.Monetization_Proceeds_Stars_Info
} else {
proceedsInfo = presentationData.strings.Monetization_Proceeds_Ton_Info

View file

@ -520,6 +520,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) }
dict[926421125] = { return Api.MediaArea.parse_mediaAreaUrl($0) }
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
dict[1132918857] = { return Api.MediaArea.parse_mediaAreaWeather($0) }
dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
dict[-1808510398] = { return Api.Message.parse_message($0) }
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }

View file

@ -231,6 +231,7 @@ public extension Api {
case mediaAreaSuggestedReaction(flags: Int32, coordinates: Api.MediaAreaCoordinates, reaction: Api.Reaction)
case mediaAreaUrl(coordinates: Api.MediaAreaCoordinates, url: String)
case mediaAreaVenue(coordinates: Api.MediaAreaCoordinates, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
case mediaAreaWeather(flags: Int32, coordinates: Api.MediaAreaCoordinates, emoji: String, temperatureC: Double)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -294,6 +295,15 @@ public extension Api {
serializeString(venueId, buffer: buffer, boxed: false)
serializeString(venueType, buffer: buffer, boxed: false)
break
case .mediaAreaWeather(let flags, let coordinates, let emoji, let temperatureC):
if boxed {
buffer.appendInt32(1132918857)
}
serializeInt32(flags, buffer: buffer, boxed: false)
coordinates.serialize(buffer, true)
serializeString(emoji, buffer: buffer, boxed: false)
serializeDouble(temperatureC, buffer: buffer, boxed: false)
break
}
}
@ -313,6 +323,8 @@ public extension Api {
return ("mediaAreaUrl", [("coordinates", coordinates as Any), ("url", url as Any)])
case .mediaAreaVenue(let coordinates, let geo, let title, let address, let provider, let venueId, let venueType):
return ("mediaAreaVenue", [("coordinates", coordinates as Any), ("geo", geo as Any), ("title", title as Any), ("address", address as Any), ("provider", provider as Any), ("venueId", venueId as Any), ("venueType", venueType as Any)])
case .mediaAreaWeather(let flags, let coordinates, let emoji, let temperatureC):
return ("mediaAreaWeather", [("flags", flags as Any), ("coordinates", coordinates as Any), ("emoji", emoji as Any), ("temperatureC", temperatureC as Any)])
}
}
@ -471,6 +483,28 @@ public extension Api {
return nil
}
}
public static func parse_mediaAreaWeather(_ reader: BufferReader) -> MediaArea? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.MediaAreaCoordinates?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.MediaAreaCoordinates
}
var _3: String?
_3 = parseString(reader)
var _4: Double?
_4 = reader.readDouble()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.MediaArea.mediaAreaWeather(flags: _1!, coordinates: _2!, emoji: _3!, temperatureC: _4!)
}
else {
return nil
}
}
}
}

View file

@ -522,6 +522,12 @@ func mediaAreaFromApiMediaArea(_ mediaArea: Api.MediaArea) -> MediaArea? {
return .link(coordinates: coodinatesFromApiMediaAreaCoordinates(coordinates), url: url)
case let .mediaAreaChannelPost(coordinates, channelId, messageId):
return .channelMessage(coordinates: coodinatesFromApiMediaAreaCoordinates(coordinates), messageId: EngineMessage.Id(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), namespace: Namespaces.Message.Cloud, id: messageId))
case let .mediaAreaWeather(flags, coordinates, emoji, temperatureC):
var parsedFlags = MediaArea.WeatherFlags()
if (flags & (1 << 0)) != 0 {
parsedFlags.insert(.isDark)
}
return .weather(coordinates: coodinatesFromApiMediaAreaCoordinates(coordinates), emoji: emoji, temperature: temperatureC, flags: parsedFlags)
}
}
@ -574,6 +580,12 @@ func apiMediaAreasFromMediaAreas(_ mediaAreas: [MediaArea], transaction: Transac
}
case let .link(_, url):
apiMediaAreas.append(.mediaAreaUrl(coordinates: inputCoordinates, url: url))
case let .weather(_, emoji, temperature, flags):
var apiFlags: Int32 = 0
if flags.contains(.isDark) {
apiFlags |= (1 << 0)
}
apiMediaAreas.append(.mediaAreaWeather(flags: apiFlags, coordinates: inputCoordinates, emoji: emoji, temperatureC: temperature))
}
}
return apiMediaAreas

View file

@ -7,6 +7,7 @@ public enum MediaArea: Codable, Equatable {
case coordinates
case value
case flags
case temperature
}
public struct Coordinates: Codable, Equatable {
@ -149,6 +150,7 @@ public enum MediaArea: Codable, Equatable {
case reaction(coordinates: Coordinates, reaction: MessageReaction.Reaction, flags: ReactionFlags)
case channelMessage(coordinates: Coordinates, messageId: EngineMessage.Id)
case link(coordinates: Coordinates, url: String)
case weather(coordinates: Coordinates, emoji: String, temperature: Double, flags: WeatherFlags)
public struct ReactionFlags: OptionSet {
public var rawValue: Int32
@ -164,6 +166,20 @@ public enum MediaArea: Codable, Equatable {
public static let isDark = ReactionFlags(rawValue: 1 << 0)
public static let isFlipped = ReactionFlags(rawValue: 1 << 1)
}
public struct WeatherFlags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let isDark = WeatherFlags(rawValue: 1 << 0)
}
private enum MediaAreaType: Int32 {
@ -171,6 +187,7 @@ public enum MediaArea: Codable, Equatable {
case reaction
case channelMessage
case link
case weather
}
public enum DecodingError: Error {
@ -201,6 +218,12 @@ public enum MediaArea: Codable, Equatable {
let coordinates = try container.decode(MediaArea.Coordinates.self, forKey: .coordinates)
let url = try container.decode(String.self, forKey: .value)
self = .link(coordinates: coordinates, url: url)
case .weather:
let coordinates = try container.decode(MediaArea.Coordinates.self, forKey: .coordinates)
let emoji = try container.decode(String.self, forKey: .value)
let temperature = try container.decode(Double.self, forKey: .temperature)
let flags = WeatherFlags(rawValue: try container.decodeIfPresent(Int32.self, forKey: .flags) ?? 0)
self = .weather(coordinates: coordinates, emoji: emoji, temperature: temperature, flags: flags)
}
}
@ -225,6 +248,12 @@ public enum MediaArea: Codable, Equatable {
try container.encode(MediaAreaType.link.rawValue, forKey: .type)
try container.encode(coordinates, forKey: .coordinates)
try container.encode(url, forKey: .value)
case let .weather(coordinates, emoji, temperature, flags):
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)
try container.encode(flags.rawValue, forKey: .flags)
}
}
}
@ -240,6 +269,8 @@ public extension MediaArea {
return coordinates
case let .link(coordinates, _):
return coordinates
case let .weather(coordinates, _, _, _):
return coordinates
}
}
}

View file

@ -0,0 +1,44 @@
import Foundation
private enum TemperatureUnit {
case celsius
case fahrenheit
var suffix: String {
switch self {
case .celsius:
return "°C"
case .fahrenheit:
return "°F"
}
}
}
private var cachedTemperatureUnit: TemperatureUnit?
private func currentTemperatureUnit() -> TemperatureUnit {
if let cachedTemperatureUnit {
return cachedTemperatureUnit
}
let temperatureFormatter = MeasurementFormatter()
temperatureFormatter.locale = Locale.current
let fahrenheitMeasurement = Measurement(value: 0, unit: UnitTemperature.fahrenheit)
let fahrenheitString = temperatureFormatter.string(from: fahrenheitMeasurement)
var temperatureUnit: TemperatureUnit = .celsius
if fahrenheitString.contains("F") || fahrenheitString.contains("Fahrenheit") {
temperatureUnit = .fahrenheit
}
cachedTemperatureUnit = temperatureUnit
return temperatureUnit
}
public func stringForTemperature(_ value: Double) -> String {
let formatter = MeasurementFormatter()
formatter.locale = Locale.current
formatter.unitStyle = .short
formatter.numberFormatter.maximumFractionDigits = 0
formatter.unitOptions = .temperatureWithoutUnit
let valueString = formatter.string(from: Measurement(value: value, unit: UnitTemperature.celsius)).trimmingCharacters(in: CharacterSet(charactersIn: "0123456789-,.").inverted)
return valueString + currentTemperatureUnit().suffix
}

View file

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

View file

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

View file

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

View file

@ -4583,16 +4583,24 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
func addWeather(_ weather: StickerPickerScreen.Weather.LoadedWeather) {
let weatherFormatter = MeasurementFormatter()
weatherFormatter.locale = Locale.current
weatherFormatter.unitStyle = .short
weatherFormatter.numberFormatter.maximumFractionDigits = 0
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: weatherFormatter.string(from: Measurement(value: weather.temperature, unit: UnitTemperature.celsius)),
style: .white,
icon: weather.emojiFile
emoji: weather.emoji,
emojiFile: weather.emojiFile,
temperature: weather.temperature,
style: .white
),
scale: nil,
position: nil
@ -6066,7 +6074,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
})
self.present(controller, in: .window(.root))
}
func maybePresentDiscardAlert() {
self.hapticFeedback.impact(.light)
if !self.isEligibleForDraft() {

View file

@ -1,257 +0,0 @@
import Foundation
import CoreLocation
import SwiftSignalKit
import TelegramCore
import StickerPickerScreen
import AccountContext
import DeviceLocationManager
func emojiFor(for meteocode: Int, date: Date, location: CLLocationCoordinate2D) -> String? {
var emoji = weatherEmoji(for: meteocode)
if ["☀️", "🌤️"].contains(emoji) && isNightTime(date: date, location: location) && !"".isEmpty {
emoji = moonPhaseEmoji(for: date)
}
return emoji
}
private func moonPhaseEmoji(for date: Date) -> String {
let newMoonDate = Date(timeIntervalSince1970: 1612137600)
let lunarMonth: TimeInterval = 29.53058867 * 24 * 60 * 60
let daysSinceNewMoon = date.timeIntervalSince(newMoonDate) / (24 * 60 * 60)
let currentMoonPhase = daysSinceNewMoon.truncatingRemainder(dividingBy: lunarMonth) / lunarMonth
switch currentMoonPhase {
case 0..<0.03:
return "🌑"
case 0.03..<0.22:
return "🌒"
case 0.22..<0.28:
return "🌓"
case 0.28..<0.47:
return "🌔"
case 0.47..<0.53:
return "🌕"
case 0.53..<0.72:
return "🌖"
case 0.72..<0.78:
return "🌗"
case 0.78..<0.97:
return "🌘"
default:
return "🌑"
}
}
func weatherEmoji(for meteocode: Int) -> String? {
switch meteocode {
case 0:
return "☀️"
case 1, 2, 3:
return "🌤️"
case 45, 48:
return "🌫️"
case 51, 53, 55:
return "🌧️" // Drizzle: Light, moderate, and dense intensity
case 56, 57:
return "🌧️" // Freezing Drizzle: Light and dense intensity
case 61, 63, 65:
return "🌧️" // Rain: Slight, moderate, and heavy intensity
case 66, 67:
return "🌧️" // Freezing Rain: Light and heavy intensity
case 71, 73, 75:
return "🌨️" // Snow fall: Slight, moderate, and heavy intensity
case 77:
return "🌨️" // Snow grains
case 80, 81, 82:
return "🌦️" // Rain showers: Slight, moderate, and violent
case 85, 86:
return "🌨️"
case 95, 96, 99:
return "⛈️" // Thunderstorm: Slight or moderate
default:
return nil
}
}
struct StoryWeather {
let emoji: String
let temperature: Double
}
private func getWeatherData(location: CLLocationCoordinate2D) -> Signal<StoryWeather?, NoError> {
let latitude = "\(location.latitude)"
let longitude = "\(location.longitude)"
let url = "https://api.open-meteo.com/v1/forecast?latitude=\(latitude)&longitude=\(longitude)&current=temperature_2m,weather_code"
return Signal { subscriber in
let disposable = fetchHttpResource(url: url).start(next: { result in
if case let .dataPart(_, data, _, complete) = result, complete {
guard let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
subscriber.putNext(nil)
subscriber.putCompletion()
return
}
guard let current = dict["current"] as? [String: Any], let temperature = current["temperature_2m"] as? Double, let weatherCode = current["weather_code"] as? Int else {
subscriber.putNext(nil)
subscriber.putCompletion()
return
}
if let emoji = emojiFor(for: weatherCode, date: Date(), location: location) {
subscriber.putNext(StoryWeather(emoji: emoji, temperature: temperature))
} else {
subscriber.putNext(nil)
}
subscriber.putCompletion()
}
})
return disposable
}
}
func getWeather(context: AccountContext) -> Signal<StickerPickerScreen.Weather, NoError> {
guard let locationManager = context.sharedContext.locationManager else {
return .single(.none)
}
return .single(.fetching)
|> then(
currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|> mapToSignal { location in
if let location {
return getWeatherData(location: location)
|> 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)
}
}
} else {
return .single(.none)
}
}
} else {
return .single(.none)
}
}
)
}
private func calculateSunriseSunset(date: Date, location: CLLocationCoordinate2D) -> (sunrise: Date, sunset: Date)? {
guard let utcTimezone = TimeZone(identifier: "UTC") else { return nil }
let zenith: Double = 90.83
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = utcTimezone
guard let dayOfYear = calendar.ordinality(of: .day, in: .year, for: date) else {
return nil
}
func toRadians(_ degrees: Double) -> Double {
return degrees * .pi / 180.0
}
func toDegrees(_ radians: Double) -> Double {
return radians * 180.0 / .pi
}
func normalise(_ value: Double, maximum: Double) -> Double {
var value = value
if value < 0 {
value += maximum
}
if value > maximum {
value -= maximum
}
return value
}
func calculateTime(isSunrise: Bool) -> Date? {
let day = Double(dayOfYear)
let lngHour = location.longitude / 15.0
let hourTime: Double = isSunrise ? 6 : 18
let t = day + ((hourTime - lngHour) / 24)
let M = (0.9856 * t) - 3.289
var L = M + 1.916 * sin(toRadians(M)) + 0.020 * sin(2 * toRadians(M)) + 282.634
L = normalise(L, maximum: 360)
var RA = toDegrees(atan(0.91764 * tan(toRadians(L))))
RA = normalise(RA, maximum: 360)
let Lquadrant = floor(L / 90) * 90
let RAquadrant = floor(RA / 90) * 90
RA = RA + (Lquadrant - RAquadrant)
RA = RA / 15
let sinDec = 0.39782 * sin(toRadians(L))
let cosDec = cos(asin(sinDec))
let cosH = (cos(toRadians(zenith)) - (sinDec * sin(toRadians(location.latitude)))) / (cosDec * cos(toRadians(location.latitude)))
guard cosH < 1 else {
return nil
}
guard cosH > -1 else {
return nil
}
let tempH = isSunrise ? 360.0 - toDegrees(acos(cosH)) : toDegrees(acos(cosH))
let H = tempH / 15.0
let T = H + RA - (0.06571 * t) - 6.622
var UT = T - lngHour
UT = normalise(UT, maximum: 24)
let hour = floor(UT)
let minute = floor((UT - hour) * 60.0)
let second = (((UT - hour) * 60) - minute) * 60.0
let shouldBeYesterday = lngHour > 0 && UT > 12 && isSunrise
let shouldBeTomorrow = lngHour < 0 && UT < 12 && !isSunrise
let setDate: Date
if shouldBeYesterday {
setDate = Date(timeInterval: -(60 * 60 * 24), since: date)
} else if shouldBeTomorrow {
setDate = Date(timeInterval: (60 * 60 * 24), since: date)
} else {
setDate = date
}
var components = calendar.dateComponents([.day, .month, .year], from: setDate)
components.hour = Int(hour)
components.minute = Int(minute)
components.second = Int(second)
calendar.timeZone = utcTimezone
return calendar.date(from: components)
}
guard let sunrise = calculateTime(isSunrise: true),
let sunset = calculateTime(isSunrise: false) else {
return nil
}
return (sunrise, sunset)
}
private func isNightTime(date: Date, location: CLLocationCoordinate2D) -> Bool {
let calendar = Calendar.current
let date = calendar.startOfDay(for: date)
guard let (sunrise, sunset) = calculateSunriseSunset(date: date, location: location) else {
return false
}
return date < sunrise || date > sunset
}

View file

@ -0,0 +1,99 @@
import Foundation
import CoreLocation
import SwiftSignalKit
import TelegramCore
import StickerPickerScreen
import AccountContext
import DeviceLocationManager
struct StoryWeather {
let emoji: String
let temperature: Double
}
private func getWeatherData(context: AccountContext, location: CLLocationCoordinate2D) -> Signal<StoryWeather?, NoError> {
let appConfiguration = context.currentAppConfiguration.with { $0 }
let botConfiguration = WeatherBotConfiguration.with(appConfiguration: appConfiguration)
if let botUsername = botConfiguration.botName {
return context.engine.peers.resolvePeerByName(name: botUsername)
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
guard case let .result(result) = result else {
return .complete()
}
return .single(result)
}
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> in
guard let peer = peer else {
return .single(nil)
}
return context.engine.messages.requestChatContextResults(botId: peer.id, peerId: context.account.peerId, query: "", location: .single((location.latitude, location.longitude)), offset: "")
|> map { results -> ChatContextResultCollection? in
return results?.results
}
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
return .single(nil)
}
}
|> map { contextResult -> StoryWeather? in
guard let contextResult, let result = contextResult.results.first, let emoji = result.title, let temperature = result.description.flatMap(Double.init) else {
return nil
}
return StoryWeather(emoji: emoji, temperature: temperature)
}
} else {
return .single(nil)
}
}
func getWeather(context: AccountContext) -> Signal<StickerPickerScreen.Weather, NoError> {
guard let locationManager = context.sharedContext.locationManager else {
return .single(.none)
}
return .single(.fetching)
|> then(
currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|> mapToSignal { location in
if let location {
return getWeatherData(context: context, location: location)
|> mapToSignal { weather in
if let weather {
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)
}
}
} else {
return .single(.none)
}
}
)
}
private struct WeatherBotConfiguration {
static var defaultValue: WeatherBotConfiguration {
return WeatherBotConfiguration(botName: "izweatherbot")
}
let botName: String?
fileprivate init(botName: String?) {
self.botName = botName
}
public static func with(appConfiguration: AppConfiguration) -> WeatherBotConfiguration {
if let data = appConfiguration.data, let botName = data["weather_search_username"] as? String {
return WeatherBotConfiguration(botName: botName)
} else {
return .defaultValue
}
}
}

View file

@ -815,12 +815,14 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
title: presentationData.strings.Stars_Intro_PurchasedTitle,
content: .universal(
animation: "StarsBuy",
scale: 0.066,
colors: [:],
title: presentationData.strings.Stars_Intro_PurchasedTitle,
text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(stars))).string,
round: false,
undoText: nil
customUndoText: nil,
timeout: nil
),
elevatedLayout: false,
action: { _ in return true})
@ -850,8 +852,42 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
starsContext: starsContext,
options: options,
purpose: .gift(peerId: peerId),
completion: { stars in
completion: { [weak self] stars in
guard let self else {
return
}
Queue.mainQueue().after(2.0) {
//TODO:localize
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .universal(
animation: "StarsSend",
scale: 0.066,
colors: [:],
title: nil,
text: "\(stars) Stars sent.",
customUndoText: "View Chat",
timeout: nil
),
elevatedLayout: false,
action: { [weak self] action in
if case .undo = action, let navigationController = self?.navigationController as? NavigationController {
let _ = (context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
)
|> deliverOnMainQueue).start(next: { peer in
guard let peer else {
return
}
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peer), subject: nil, botStart: nil, updateTextInputState: nil, keepStack: .always, useExisting: true, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: nil, animated: true))
})
}
return true
})
self.present(resultController, in: .window(.root))
}
}
)
self.push(purchaseController)

View file

@ -518,12 +518,21 @@ private final class SheetContent: CombinedComponent {
if let lastController = navigationController.viewControllers.last as? ViewController {
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
// content: .image(
// image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!,
// title: presentationData.strings.Stars_Transfer_PurchasedTitle,
// text: text,
// round: false,
// undoText: nil
// ),
content: .universal(
animation: "StarsSend",
scale: 0.066,
colors: [:],
title: presentationData.strings.Stars_Transfer_PurchasedTitle,
text: text,
round: false,
undoText: nil
customUndoText: nil,
timeout: nil
),
elevatedLayout: lastController is ChatController,
action: { _ in return true}

View file

@ -25,6 +25,7 @@ import LottieComponentResourceContent
import UndoUI
import GalleryUI
import TextLoadingEffect
import TelegramStringFormatting
private final class StickerSelectionComponent: Component {
typealias EnvironmentType = Empty
@ -2618,7 +2619,6 @@ final class ItemStack<ChildEnvironment: Equatable>: CombinedComponent {
final class StoryStickersContentView: UIView, EmojiCustomContentView {
private let context: AccountContext
private let weatherFormatter: MeasurementFormatter
let tintContainerView = UIView()
private let container = ComponentView<Empty>()
@ -2636,10 +2636,6 @@ final class StoryStickersContentView: UIView, EmojiCustomContentView {
init(context: AccountContext, weather: Signal<StickerPickerScreen.Weather, NoError>) {
self.context = context
self.weatherFormatter = MeasurementFormatter()
self.weatherFormatter.locale = Locale.current
self.weatherFormatter.unitStyle = .short
self.weatherFormatter.numberFormatter.maximumFractionDigits = 0
super.init(frame: .zero)
@ -2740,7 +2736,7 @@ final class StoryStickersContentView: UIView, EmojiCustomContentView {
InteractiveStickerButtonContent(
context: self.context,
theme: theme,
title: self.weatherFormatter.string(from: Measurement(value: weather.temperature, unit: UnitTemperature.celsius)),
title: stringForTemperature(weather.temperature),
iconName: weather.emoji,
iconFile: weather.emojiFile,
useOpaqueTheme: useOpaqueTheme,

View file

@ -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<Empty>()
private let text = ComponentView<Empty>()
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
)

View file

@ -3434,6 +3434,8 @@ final class StoryItemSetContainerSendMessage {
actions.append(ContextMenuAction(content: .textWithSubtitleAndIcon(title: updatedPresentationData.initial.strings.Story_ViewLink, subtitle: url, icon: generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: .white)), action: {
action()
}))
case .weather:
return
}
self.selectedMediaArea = mediaArea

View file

@ -346,8 +346,6 @@ public extension ChatControllerImpl {
}
self.attachmentController?.dismiss(animated: true, completion: nil)
let openBotApp: (Bool, Bool) -> Void = { [weak self] allowWrite, justInstalled in
guard let strongSelf = self else {
return

View file

@ -1692,13 +1692,17 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
var clearCacheAsDelete = false
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info, !isMigrated {
var views: Int = 0
var forwards: Int = 0
for attribute in message.attributes {
if let attribute = attribute as? ViewCountMessageAttribute {
views = attribute.count
}
if let attribute = attribute as? ForwardCountMessageAttribute {
forwards = attribute.count
}
}
if infoSummaryData.canViewStats, views >= 100 {
if infoSummaryData.canViewStats, forwards >= 1 || views >= 100 {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextViewStats, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor)
}, action: { c, _ in

View file

@ -326,12 +326,15 @@ public final class WebAppController: ViewController, AttachmentContainable {
guard let controller = self.controller else {
return
}
if let url = controller.url, controller.source != .menu {
self.queryId = controller.queryId
if let parsedUrl = URL(string: url) {
self.webView?.load(URLRequest(url: parsedUrl))
}
self.checkBotIdAndUrl(url)
if let keepAliveSignal = controller.keepAliveSignal {
self.keepAliveDisposable = (keepAliveSignal
|> deliverOnMainQueue).start(error: { [weak self] _ in
@ -351,6 +354,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
guard let strongSelf = self else {
return
}
strongSelf.checkBotIdAndUrl(result.url)
if let parsedUrl = URL(string: result.url) {
strongSelf.queryId = result.queryId
strongSelf.webView?.load(URLRequest(url: parsedUrl))
@ -372,6 +376,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
guard let self, let parsedUrl = URL(string: result.url) else {
return
}
self.checkBotIdAndUrl(result.url)
self.controller?.titleView?.title = WebAppTitle(title: appStart.botApp.title, counter: self.presentationData.strings.WebApp_Miniapp, isVerified: controller.botVerified)
self.webView?.load(URLRequest(url: parsedUrl))
})
@ -385,6 +390,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
strongSelf.queryId = result.queryId
strongSelf.webView?.load(URLRequest(url: parsedUrl))
strongSelf.checkBotIdAndUrl(result.url)
if let keepAliveSignal = result.keepAliveSignal {
strongSelf.keepAliveDisposable = (keepAliveSignal
|> deliverOnMainQueue).start(error: { [weak self] _ in
@ -404,6 +411,15 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
}
func checkBotIdAndUrl(_ url: String) {
//1985737506
if url.hasPrefix("https://walletbot.me"), let botId = self.controller?.botId.id._internalGetInt64Value(), botId != 1985737506 {
let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: "Bot id mismatch, please report steps to app developer", actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
})])
self.controller?.present(alertController, in: .window(.root))
}
}
@objc fileprivate func mainButtonPressed() {
if let mainButtonState = self.mainButtonState, !mainButtonState.isVisible || !mainButtonState.isEnabled {
return