mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-19 12:10:55 +00:00
Live location fixes
This commit is contained in:
parent
27c6b78465
commit
9e2447d78b
@ -158,7 +158,7 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
|
||||
let addedStopped = stopMessageIds.subtracting(self.stopMessageIds)
|
||||
self.stopMessageIds = stopMessageIds
|
||||
for id in addedStopped {
|
||||
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, coordinate: nil, heading: nil)
|
||||
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, stop: true, coordinate: nil, heading: nil, proximityNotificationRadius: nil)
|
||||
|> deliverOn(self.queue)).start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.editMessageDisposables.set(nil, forKey: id)
|
||||
@ -213,7 +213,7 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
|
||||
let ids = self.broadcastToMessageIds
|
||||
let remainingIds = Atomic<Set<MessageId>>(value: Set(ids.keys))
|
||||
for id in ids.keys {
|
||||
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude, accuracyRadius: Int32(accuracyRadius)), heading: Int32(heading ?? 0))
|
||||
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, stop: false, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude, accuracyRadius: Int32(accuracyRadius)), heading: Int32(heading ?? 0), proximityNotificationRadius: nil)
|
||||
|> deliverOn(self.queue)).start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.editMessageDisposables.set(nil, forKey: id)
|
||||
|
@ -375,8 +375,12 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||
self.updateDoneButtonTitle()
|
||||
self.update()
|
||||
if pickerView.selectedRow(inComponent: 0) == 0 && pickerView.selectedRow(inComponent: 1) == 0 {
|
||||
pickerView.selectRow(1, inComponent: 1, animated: true)
|
||||
} else {
|
||||
self.updateDoneButtonTitle()
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
|
@ -154,9 +154,9 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
|
||||
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
||||
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
|
||||
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/NotificationIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/MuteIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
||||
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/MuteIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
|
||||
self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
|
||||
self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false)
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
||||
var radius: Double
|
||||
var alpha: CGFloat {
|
||||
didSet {
|
||||
self.alphaTransition = (oldValue, CACurrentMediaTime(), 0.5)
|
||||
self.alphaTransition = (oldValue, CACurrentMediaTime(), 0.3)
|
||||
}
|
||||
}
|
||||
var alphaTransition: (from: CGFloat, startTimestamp: Double, duration: Double)?
|
||||
@ -151,7 +151,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
||||
|
||||
class InvertedProximityCircleRenderer: MKOverlayRenderer {
|
||||
var radius: Double = 0.0
|
||||
var fillColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.4)
|
||||
var fillColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.5)
|
||||
|
||||
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
|
||||
guard let overlay = self.overlay as? InvertedProximityCircle else {
|
||||
|
@ -7,8 +7,8 @@ import TelegramStringFormatting
|
||||
import MapKit
|
||||
|
||||
extension TelegramMediaMap {
|
||||
convenience init(coordinate: CLLocationCoordinate2D, liveBroadcastingTimeout: Int32? = nil) {
|
||||
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: liveBroadcastingTimeout, liveProximityNotificationRadius: nil)
|
||||
convenience init(coordinate: CLLocationCoordinate2D, liveBroadcastingTimeout: Int32? = nil, proximityNotificationRadius: Int32? = nil) {
|
||||
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: liveBroadcastingTimeout, liveProximityNotificationRadius: proximityNotificationRadius)
|
||||
}
|
||||
|
||||
var coordinate: CLLocationCoordinate2D {
|
||||
|
@ -173,8 +173,7 @@ public final class LocationViewController: ViewController {
|
||||
|
||||
if reset {
|
||||
if let messageId = messageId {
|
||||
|
||||
// let _ = cancelProximityNotification(postbox: context.account.postbox, network: context.account.network, messageId: messageId).start()
|
||||
let _ = requestEditLiveLocation(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, messageId: messageId, stop: false, coordinate: nil, heading: nil, proximityNotificationRadius: 0).start()
|
||||
}
|
||||
} else {
|
||||
strongSelf.controllerNode.setProximityIndicator(radius: 0)
|
||||
@ -189,8 +188,10 @@ public final class LocationViewController: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
if let coordinate = coordinate {
|
||||
self?.interaction?.sendLiveLocation(coordinate, distance)
|
||||
if let messageId = messageId {
|
||||
let _ = requestEditLiveLocation(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, messageId: messageId, stop: false, coordinate: nil, heading: nil, proximityNotificationRadius: distance).start()
|
||||
} else if let coordinate = coordinate {
|
||||
strongSelf.interaction?.sendLiveLocation(coordinate, distance)
|
||||
}
|
||||
completion()
|
||||
}, willDismiss: { [weak self] in
|
||||
@ -218,42 +219,44 @@ public final class LocationViewController: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (context.account.postbox.loadedPeerWithId(subject.id.peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
var title = strongSelf.presentationData.strings.Map_LiveLocationGroupDescription
|
||||
if let user = peer as? TelegramUser {
|
||||
title = strongSelf.presentationData.strings.Map_LiveLocationPrivateDescription(user.compactDisplayTitle).0
|
||||
}
|
||||
|
||||
let sendLiveLocationImpl: (Int32) -> Void = { [weak self, weak controller] period in
|
||||
controller?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
|
||||
if let distance = distance {
|
||||
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: 30 * 60, proximityNotificationRadius: distance))
|
||||
} else {
|
||||
let _ = (context.account.postbox.loadedPeerWithId(subject.id.peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
var title = strongSelf.presentationData.strings.Map_LiveLocationGroupDescription
|
||||
if let user = peer as? TelegramUser {
|
||||
title = strongSelf.presentationData.strings.Map_LiveLocationPrivateDescription(user.compactDisplayTitle).0
|
||||
}
|
||||
}
|
||||
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: title),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor15Minutes, color: .accent, action: {
|
||||
sendLiveLocationImpl(15 * 60)
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor1Hour, color: .accent, action: {
|
||||
sendLiveLocationImpl(60 * 60 - 1)
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor8Hours, color: .accent, action: {
|
||||
sendLiveLocationImpl(8 * 60 * 60)
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
})
|
||||
|
||||
let sendLiveLocationImpl: (Int32) -> Void = { [weak self, weak controller] period in
|
||||
controller?.dismissAnimated()
|
||||
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
|
||||
}
|
||||
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: title),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor15Minutes, color: .accent, action: {
|
||||
sendLiveLocationImpl(15 * 60)
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor1Hour, color: .accent, action: {
|
||||
sendLiveLocationImpl(60 * 60 - 1)
|
||||
}),
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor8Hours, color: .accent, action: {
|
||||
sendLiveLocationImpl(8 * 60 * 60)
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
})
|
||||
}
|
||||
}
|
||||
}, stopLiveLocation: { [weak self] in
|
||||
params.stopLiveLocation(nil)
|
||||
|
@ -566,33 +566,117 @@ private extension SemanticStatusNodeState {
|
||||
|
||||
private final class SemanticStatusNodeTransitionDrawingState {
|
||||
let transition: CGFloat
|
||||
let drawingState: SemanticStatusNodeStateDrawingState
|
||||
let drawingState: SemanticStatusNodeStateDrawingState?
|
||||
let appearanceState: SemanticStatusNodeAppearanceDrawingState?
|
||||
|
||||
init(transition: CGFloat, drawingState: SemanticStatusNodeStateDrawingState) {
|
||||
init(transition: CGFloat, drawingState: SemanticStatusNodeStateDrawingState?, appearanceState: SemanticStatusNodeAppearanceDrawingState?) {
|
||||
self.transition = transition
|
||||
self.drawingState = drawingState
|
||||
self.appearanceState = appearanceState
|
||||
}
|
||||
}
|
||||
|
||||
private final class SemanticStatusNodeAppearanceContext {
|
||||
let background: UIColor
|
||||
let foreground: UIColor
|
||||
let backgroundImage: UIImage?
|
||||
let overlayForeground: UIColor?
|
||||
let cutout: CGRect?
|
||||
|
||||
init(background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
|
||||
self.background = background
|
||||
self.foreground = foreground
|
||||
self.backgroundImage = backgroundImage
|
||||
self.overlayForeground = overlayForeground
|
||||
self.cutout = cutout
|
||||
}
|
||||
|
||||
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState {
|
||||
return SemanticStatusNodeAppearanceDrawingState(transitionFraction: transitionFraction, background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: self.cutout)
|
||||
}
|
||||
|
||||
func withUpdatedBackground(_ background: UIColor) -> SemanticStatusNodeAppearanceContext {
|
||||
return SemanticStatusNodeAppearanceContext(background: background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
|
||||
}
|
||||
|
||||
func withUpdatedForeground(_ foreground: UIColor) -> SemanticStatusNodeAppearanceContext {
|
||||
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
|
||||
}
|
||||
|
||||
func withUpdatedBackgroundImage(_ backgroundImage: UIImage?) -> SemanticStatusNodeAppearanceContext {
|
||||
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: self.foreground, backgroundImage: backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
|
||||
}
|
||||
|
||||
func withUpdatedOverlayForeground(_ overlayForeground: UIColor?) -> SemanticStatusNodeAppearanceContext {
|
||||
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: overlayForeground, cutout: cutout)
|
||||
}
|
||||
|
||||
func withUpdatedCutout(_ cutout: CGRect?) -> SemanticStatusNodeAppearanceContext {
|
||||
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
|
||||
}
|
||||
}
|
||||
|
||||
private final class SemanticStatusNodeAppearanceDrawingState {
|
||||
let transitionFraction: CGFloat
|
||||
let background: UIColor
|
||||
let foreground: UIColor
|
||||
let backgroundImage: UIImage?
|
||||
let overlayForeground: UIColor?
|
||||
let cutout: CGRect?
|
||||
|
||||
var effectiveForegroundColor: UIColor {
|
||||
if let _ = self.backgroundImage, let overlayForeground = self.overlayForeground {
|
||||
return overlayForeground
|
||||
} else {
|
||||
return self.foreground
|
||||
}
|
||||
}
|
||||
|
||||
init(transitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
|
||||
self.transitionFraction = transitionFraction
|
||||
self.background = background
|
||||
self.foreground = foreground
|
||||
self.backgroundImage = backgroundImage
|
||||
self.overlayForeground = overlayForeground
|
||||
self.cutout = cutout
|
||||
}
|
||||
|
||||
func drawBackground(context: CGContext, size: CGSize) {
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
if let backgroundImage = self.backgroundImage?.cgImage {
|
||||
context.saveGState()
|
||||
context.translateBy(x: 0.0, y: bounds.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.setAlpha(self.transitionFraction)
|
||||
context.draw(backgroundImage, in: bounds)
|
||||
context.restoreGState()
|
||||
} else {
|
||||
context.setFillColor(self.background.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: bounds.size))
|
||||
}
|
||||
}
|
||||
|
||||
func drawForeground(context: CGContext, size: CGSize) {
|
||||
if let cutout = self.cutout {
|
||||
let size = CGSize(width: cutout.width * self.transitionFraction, height: cutout.height * self.transitionFraction)
|
||||
let rect = CGRect(origin: CGPoint(x: cutout.midX - size.width / 2.0, y: cutout.midY - size.height / 2.0), size: size)
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.fillEllipse(in: rect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SemanticStatusNodeDrawingState: NSObject {
|
||||
let background: UIColor
|
||||
let foreground: UIColor
|
||||
let hollow: Bool
|
||||
let transitionState: SemanticStatusNodeTransitionDrawingState?
|
||||
let drawingState: SemanticStatusNodeStateDrawingState
|
||||
let backgroundImage: UIImage?
|
||||
let overlayForeground: UIColor?
|
||||
let cutout: SemanticStatusNode.Cutout?
|
||||
let appearanceState: SemanticStatusNodeAppearanceDrawingState
|
||||
|
||||
init(background: UIColor, foreground: UIColor, hollow: Bool, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: SemanticStatusNode.Cutout?) {
|
||||
self.background = background
|
||||
self.foreground = foreground
|
||||
self.hollow = hollow
|
||||
init(transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState, appearanceState: SemanticStatusNodeAppearanceDrawingState) {
|
||||
self.transitionState = transitionState
|
||||
self.drawingState = drawingState
|
||||
self.backgroundImage = backgroundImage
|
||||
self.overlayForeground = overlayForeground
|
||||
self.cutout = cutout
|
||||
self.appearanceState = appearanceState
|
||||
|
||||
super.init()
|
||||
}
|
||||
@ -601,65 +685,86 @@ private final class SemanticStatusNodeDrawingState: NSObject {
|
||||
private final class SemanticStatusNodeTransitionContext {
|
||||
let startTime: Double
|
||||
let duration: Double
|
||||
let previousStateContext: SemanticStatusNodeStateContext
|
||||
let previousStateContext: SemanticStatusNodeStateContext?
|
||||
let previousAppearanceContext: SemanticStatusNodeAppearanceContext?
|
||||
let completion: () -> Void
|
||||
|
||||
init(startTime: Double, duration: Double, previousStateContext: SemanticStatusNodeStateContext, completion: @escaping () -> Void) {
|
||||
init(startTime: Double, duration: Double, previousStateContext: SemanticStatusNodeStateContext?, previousAppearanceContext: SemanticStatusNodeAppearanceContext?, completion: @escaping () -> Void) {
|
||||
self.startTime = startTime
|
||||
self.duration = duration
|
||||
self.previousStateContext = previousStateContext
|
||||
self.previousAppearanceContext = previousAppearanceContext
|
||||
self.completion = completion
|
||||
}
|
||||
}
|
||||
|
||||
public final class SemanticStatusNode: ASControlNode {
|
||||
final class Cutout {
|
||||
|
||||
}
|
||||
|
||||
public var backgroundNodeColor: UIColor {
|
||||
didSet {
|
||||
if !self.backgroundNodeColor.isEqual(oldValue) {
|
||||
get {
|
||||
return self.appearanceContext.background
|
||||
}
|
||||
set {
|
||||
if !self.appearanceContext.background.isEqual(newValue) {
|
||||
self.appearanceContext = self.appearanceContext.withUpdatedBackground(newValue)
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var foregroundNodeColor: UIColor {
|
||||
didSet {
|
||||
if !self.foregroundNodeColor.isEqual(oldValue) {
|
||||
get {
|
||||
return self.appearanceContext.foreground
|
||||
}
|
||||
set {
|
||||
if !self.appearanceContext.foreground.isEqual(newValue) {
|
||||
self.appearanceContext = self.appearanceContext.withUpdatedForeground(newValue)
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var overlayForegroundNodeColor: UIColor? {
|
||||
didSet {
|
||||
if !(self.overlayForegroundNodeColor?.isEqual(oldValue) ?? true) {
|
||||
get {
|
||||
return self.appearanceContext.overlayForeground
|
||||
}
|
||||
set {
|
||||
if !(self.appearanceContext.overlayForeground?.isEqual(newValue) ?? false) {
|
||||
self.appearanceContext = self.appearanceContext.withUpdatedOverlayForeground(newValue)
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let hollow: Bool
|
||||
public var cutout: CGRect? {
|
||||
get {
|
||||
return self.appearanceContext.cutout
|
||||
}
|
||||
set {
|
||||
if self.appearanceContext.cutout != newValue {
|
||||
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: self.appearanceContext, completion: {})
|
||||
self.appearanceContext = self.appearanceContext.withUpdatedCutout(newValue)
|
||||
|
||||
self.updateAnimations()
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var animator: ConstantDisplayLinkAnimator?
|
||||
|
||||
private var hasState: Bool = false
|
||||
public private(set) var state: SemanticStatusNodeState
|
||||
private var transtionContext: SemanticStatusNodeTransitionContext?
|
||||
private var transitionContext: SemanticStatusNodeTransitionContext?
|
||||
private var stateContext: SemanticStatusNodeStateContext
|
||||
private var appearanceContext: SemanticStatusNodeAppearanceContext
|
||||
|
||||
private var disposable: Disposable?
|
||||
private var backgroundNodeImage: UIImage?
|
||||
|
||||
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, hollow: Bool = false) {
|
||||
self.backgroundNodeColor = backgroundNodeColor
|
||||
self.foregroundNodeColor = foregroundNodeColor
|
||||
self.overlayForegroundNodeColor = overlayForegroundNodeColor
|
||||
self.hollow = hollow
|
||||
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, cutout: CGRect? = nil) {
|
||||
self.state = .none
|
||||
self.stateContext = self.state.context(current: nil)
|
||||
self.appearanceContext = SemanticStatusNodeAppearanceContext(background: backgroundNodeColor, foreground: foregroundNodeColor, backgroundImage: nil, overlayForeground: overlayForegroundNodeColor, cutout: cutout)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -667,14 +772,22 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
self.displaysAsynchronously = true
|
||||
|
||||
if let image = image {
|
||||
let start = CACurrentMediaTime()
|
||||
self.disposable = (image
|
||||
|> deliverOnMainQueue).start(next: { [weak self] transform in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets()))
|
||||
self?.backgroundNodeImage = context?.generateImage()
|
||||
self?.setNeedsDisplay()
|
||||
|
||||
let previousAppearanceContext = strongSelf.appearanceContext
|
||||
strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage())
|
||||
|
||||
if CACurrentMediaTime() - start > 0.2 {
|
||||
strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {})
|
||||
strongSelf.updateAnimations()
|
||||
}
|
||||
strongSelf.setNeedsDisplay()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -687,9 +800,9 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
var animate = false
|
||||
let timestamp = CACurrentMediaTime()
|
||||
|
||||
if let transtionContext = self.transtionContext {
|
||||
if let transtionContext = self.transitionContext {
|
||||
if transtionContext.startTime + transtionContext.duration < timestamp {
|
||||
self.transtionContext = nil
|
||||
self.transitionContext = nil
|
||||
transtionContext.completion()
|
||||
} else {
|
||||
animate = true
|
||||
@ -724,13 +837,13 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
self.hasState = true
|
||||
animated = false
|
||||
}
|
||||
if self.state != state {
|
||||
if self.state != state || self.appearanceContext.cutout != cutout {
|
||||
self.state = state
|
||||
let previousStateContext = self.stateContext
|
||||
self.stateContext = self.state.context(current: self.stateContext)
|
||||
|
||||
if animated && previousStateContext !== self.stateContext {
|
||||
self.transtionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: previousStateContext, completion: completion)
|
||||
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: previousStateContext, previousAppearanceContext: nil, completion: completion)
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
@ -745,15 +858,23 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
var transitionState: SemanticStatusNodeTransitionDrawingState?
|
||||
var transitionFraction: CGFloat = 1.0
|
||||
if let transitionContext = self.transtionContext {
|
||||
var appearanceTransitionFraction: CGFloat = 1.0
|
||||
|
||||
if let transitionContext = self.transitionContext {
|
||||
let timestamp = CACurrentMediaTime()
|
||||
var t = CGFloat((timestamp - transitionContext.startTime) / transitionContext.duration)
|
||||
t = min(1.0, max(0.0, t))
|
||||
transitionFraction = t
|
||||
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext.drawingState(transitionFraction: 1.0 - t))
|
||||
|
||||
if let _ = transitionContext.previousStateContext {
|
||||
transitionFraction = t
|
||||
}
|
||||
if let _ = transitionContext.previousAppearanceContext {
|
||||
appearanceTransitionFraction = t
|
||||
}
|
||||
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext?.drawingState(transitionFraction: 1.0 - t), appearanceState: transitionContext.previousAppearanceContext?.drawingState(transitionFraction: 1.0 - t))
|
||||
}
|
||||
|
||||
return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, hollow: self.hollow, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), backgroundImage: self.backgroundNodeImage, overlayForeground: self.overlayForegroundNodeColor, cutout: nil)
|
||||
return SemanticStatusNodeDrawingState(transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), appearanceState: self.appearanceContext.drawingState(transitionFraction: appearanceTransitionFraction))
|
||||
}
|
||||
|
||||
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||
@ -768,32 +889,20 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
guard let parameters = parameters as? SemanticStatusNodeDrawingState else {
|
||||
return
|
||||
}
|
||||
|
||||
var foregroundColor = parameters.foreground
|
||||
if let backgroundImage = parameters.backgroundImage?.cgImage {
|
||||
context.saveGState()
|
||||
context.translateBy(x: 0.0, y: bounds.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.draw(backgroundImage, in: bounds)
|
||||
context.restoreGState()
|
||||
|
||||
if let overlayForegroundColor = parameters.overlayForeground {
|
||||
foregroundColor = overlayForegroundColor
|
||||
} else {
|
||||
foregroundColor = .white
|
||||
}
|
||||
} else {
|
||||
context.setFillColor(parameters.background.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: bounds.size))
|
||||
|
||||
if let transitionAppearanceState = parameters.transitionState?.appearanceState {
|
||||
transitionAppearanceState.drawBackground(context: context, size: bounds.size)
|
||||
}
|
||||
if let transitionState = parameters.transitionState {
|
||||
transitionState.drawingState.draw(context: context, size: bounds.size, foregroundColor: foregroundColor)
|
||||
parameters.appearanceState.drawBackground(context: context, size: bounds.size)
|
||||
|
||||
if let transitionDrawingState = parameters.transitionState?.drawingState {
|
||||
transitionDrawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.appearanceState.effectiveForegroundColor)
|
||||
}
|
||||
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: foregroundColor)
|
||||
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.appearanceState.effectiveForegroundColor)
|
||||
|
||||
if parameters.hollow {
|
||||
context.setBlendMode(.clear)
|
||||
context.fillEllipse(in: bounds.insetBy(dx: 8.0, dy: 8.0))
|
||||
if let transitionAppearanceState = parameters.transitionState?.appearanceState {
|
||||
transitionAppearanceState.drawForeground(context: context, size: bounds.size)
|
||||
}
|
||||
parameters.appearanceState.drawForeground(context: context, size: bounds.size)
|
||||
}
|
||||
}
|
||||
|
@ -133,14 +133,15 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
self.currentContent = content
|
||||
|
||||
if content.hasProgress {
|
||||
let statusFrame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
|
||||
if self.statusNode == nil {
|
||||
let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear, hollow: true)
|
||||
let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear, cutout: statusFrame.insetBy(dx: 8.0, dy: 8.0))
|
||||
self.statusNode = statusNode
|
||||
self.contentContainer.insertSubnode(statusNode, belowSubnode: self.contentNode)
|
||||
statusNode.transitionToState(.progress(value: nil, cancelEnabled: false, appearance: SemanticStatusNodeState.ProgressAppearance(inset: 4.0, lineWidth: 3.0)), animated: false, completion: {})
|
||||
}
|
||||
if let statusNode = self.statusNode {
|
||||
statusNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
|
||||
statusNode.frame = statusFrame
|
||||
if transition.isAnimated {
|
||||
statusNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||
statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
@ -146,12 +146,15 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
|
||||
if let _ = map.heading {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
if let _ = map.liveProximityNotificationRadius {
|
||||
flags |= 1 << 3
|
||||
}
|
||||
var geoFlags: Int32 = 0
|
||||
if let _ = map.accuracyRadius {
|
||||
geoFlags |= 1 << 0
|
||||
}
|
||||
if let liveBroadcastingTimeout = map.liveBroadcastingTimeout {
|
||||
input = .inputMediaGeoLive(flags: flags, geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })), heading: map.heading, period: liveBroadcastingTimeout, proximityNotificationRadius: nil)
|
||||
input = .inputMediaGeoLive(flags: flags, geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })), heading: map.heading, period: liveBroadcastingTimeout, proximityNotificationRadius: map.liveProximityNotificationRadius.flatMap({ Int32($0) }))
|
||||
} else if let venue = map.venue {
|
||||
input = .inputMediaVenue(geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })), title: venue.title, address: venue.address ?? "", provider: venue.provider ?? "", venueId: venue.id ?? "", venueType: venue.type ?? "")
|
||||
} else {
|
||||
|
@ -255,7 +255,7 @@ private func requestEditMessageInternal(postbox: Postbox, network: Network, stat
|
||||
}
|
||||
}
|
||||
|
||||
public func requestEditLiveLocation(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId, coordinate: (latitude: Double, longitude: Double, accuracyRadius: Int32?)?, heading: Int32?) -> Signal<Void, NoError> {
|
||||
public func requestEditLiveLocation(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId, stop: Bool, coordinate: (latitude: Double, longitude: Double, accuracyRadius: Int32?)?, heading: Int32?, proximityNotificationRadius: Int32?) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> (Api.InputPeer, TelegramMediaMap)? in
|
||||
guard let inputPeer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) else {
|
||||
return nil
|
||||
@ -275,19 +275,33 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan
|
||||
return .complete()
|
||||
}
|
||||
let inputMedia: Api.InputMedia
|
||||
if let coordinate = coordinate, let liveBroadcastingTimeout = media.liveBroadcastingTimeout {
|
||||
if let liveBroadcastingTimeout = media.liveBroadcastingTimeout, !stop {
|
||||
var flags: Int32 = 1 << 1
|
||||
if let _ = media.heading {
|
||||
let inputGeoPoint: Api.InputGeoPoint
|
||||
if let coordinate = coordinate {
|
||||
var geoFlags: Int32 = 0
|
||||
if let _ = coordinate.accuracyRadius {
|
||||
geoFlags |= 1 << 0
|
||||
}
|
||||
inputGeoPoint = .inputGeoPoint(flags: geoFlags, lat: coordinate.latitude, long: coordinate.longitude, accuracyRadius: coordinate.accuracyRadius.flatMap({ Int32($0) }))
|
||||
} else {
|
||||
var geoFlags: Int32 = 0
|
||||
if let _ = media.accuracyRadius {
|
||||
geoFlags |= 1 << 0
|
||||
}
|
||||
inputGeoPoint = .inputGeoPoint(flags: geoFlags, lat: media.latitude, long: media.longitude, accuracyRadius: media.accuracyRadius.flatMap({ Int32($0) }))
|
||||
}
|
||||
if let _ = heading {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
var geoFlags: Int32 = 0
|
||||
if let _ = coordinate.accuracyRadius {
|
||||
geoFlags |= 1 << 0
|
||||
if let _ = proximityNotificationRadius {
|
||||
flags |= 1 << 3
|
||||
}
|
||||
inputMedia = .inputMediaGeoLive(flags: flags, geoPoint: .inputGeoPoint(flags: geoFlags, lat: coordinate.latitude, long: coordinate.longitude, accuracyRadius: coordinate.accuracyRadius.flatMap({ Int32($0) })), heading: heading, period: liveBroadcastingTimeout, proximityNotificationRadius: nil)
|
||||
inputMedia = .inputMediaGeoLive(flags: flags, geoPoint: inputGeoPoint, heading: heading, period: liveBroadcastingTimeout, proximityNotificationRadius: proximityNotificationRadius)
|
||||
} else {
|
||||
inputMedia = .inputMediaGeoLive(flags: 1 << 0, geoPoint: .inputGeoPoint(flags: 0, lat: media.latitude, long: media.longitude, accuracyRadius: nil), heading: nil, period: nil, proximityNotificationRadius: nil)
|
||||
}
|
||||
|
||||
return network.request(Api.functions.messages.editMessage(flags: 1 << 14, peer: inputPeer, id: messageId.id, message: nil, media: inputMedia, replyMarkup: nil, entities: nil, scheduleDate: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
@ -297,7 +311,7 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan
|
||||
if let updates = updates {
|
||||
stateManager.addUpdates(updates)
|
||||
}
|
||||
if coordinate == nil {
|
||||
if coordinate == nil && proximityNotificationRadius == nil {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
|
@ -77,6 +77,7 @@ public func chatBubbleActionButtonImage(fillColor: UIColor, strokeColor: UIColor
|
||||
|
||||
public final class PrincipalThemeEssentialGraphics {
|
||||
public let chatMessageBackgroundIncomingMaskImage: UIImage
|
||||
public let chatMessageBackgroundIncomingExtractedMaskImage: UIImage
|
||||
public let chatMessageBackgroundIncomingImage: UIImage
|
||||
public let chatMessageBackgroundIncomingExtractedImage: UIImage
|
||||
public let chatMessageBackgroundIncomingOutlineImage: UIImage
|
||||
@ -110,6 +111,7 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
public let chatMessageBackgroundIncomingMergedSideHighlightedImage: UIImage
|
||||
|
||||
public let chatMessageBackgroundOutgoingMaskImage: UIImage
|
||||
public let chatMessageBackgroundOutgoingExtractedMaskImage: UIImage
|
||||
public let chatMessageBackgroundOutgoingImage: UIImage
|
||||
public let chatMessageBackgroundOutgoingExtractedImage: UIImage
|
||||
public let chatMessageBackgroundOutgoingOutlineImage: UIImage
|
||||
@ -248,12 +250,14 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
let emptyImage = UIImage()
|
||||
if preview {
|
||||
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
@ -350,6 +354,7 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.radialIndicatorFileIconOutgoing = emptyImage
|
||||
} else {
|
||||
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
@ -378,6 +383,7 @@ public final class PrincipalThemeEssentialGraphics {
|
||||
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||
|
||||
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||
|
@ -28,7 +28,7 @@ func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThe
|
||||
case .Side:
|
||||
image = graphics.chatMessageBackgroundIncomingMergedSideMaskImage
|
||||
case .Extracted:
|
||||
image = nil
|
||||
image = graphics.chatMessageBackgroundIncomingExtractedMaskImage
|
||||
}
|
||||
case let .outgoing(mergeType):
|
||||
switch mergeType {
|
||||
@ -47,7 +47,7 @@ func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThe
|
||||
case .Side:
|
||||
image = graphics.chatMessageBackgroundOutgoingMergedSideMaskImage
|
||||
case .Extracted:
|
||||
image = nil
|
||||
image = graphics.chatMessageBackgroundOutgoingExtractedMaskImage
|
||||
}
|
||||
}
|
||||
return image
|
||||
|
@ -214,10 +214,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
let contentMessageStableId: UInt32
|
||||
let sourceNode: ContextExtractedContentContainingNode
|
||||
let containerNode: ContextControllerSourceNode
|
||||
var backgroundWallpaperNode: ChatMessageBubbleBackdrop?
|
||||
var backgroundNode: ChatMessageBackground?
|
||||
var selectionBackgroundNode: ASDisplayNode?
|
||||
|
||||
var currentParams: (size: CGSize, contentOrigin: CGPoint, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, Bool?, selectionInsets: UIEdgeInsets)?
|
||||
private var currentParams: (size: CGSize, contentOrigin: CGPoint, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, mediaBox: MediaBox, messageSelection: Bool?, selectionInsets: UIEdgeInsets)?
|
||||
|
||||
init(contentMessageStableId: UInt32) {
|
||||
self.contentMessageStableId = contentMessageStableId
|
||||
@ -226,7 +227,31 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
}
|
||||
|
||||
func willUpdateIsExtractedToContextPreview(isExtractedToContextPreview: Bool, transition: ContainedViewLayoutTransition) {
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
fileprivate func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
guard let backgroundWallpaperNode = self.backgroundWallpaperNode else {
|
||||
return
|
||||
}
|
||||
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + backgroundWallpaperNode.frame.minX, y: rect.minY + backgroundWallpaperNode.frame.minY), size: rect.size)
|
||||
backgroundWallpaperNode.update(rect: mappedRect, within: containerSize)
|
||||
}
|
||||
|
||||
fileprivate func applyAbsoluteOffset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
guard let backgroundWallpaperNode = self.backgroundWallpaperNode else {
|
||||
return
|
||||
}
|
||||
backgroundWallpaperNode.offset(value: value, animationCurve: animationCurve, duration: duration)
|
||||
}
|
||||
|
||||
fileprivate func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
guard let backgroundWallpaperNode = self.backgroundWallpaperNode else {
|
||||
return
|
||||
}
|
||||
backgroundWallpaperNode.offsetSpring(value: value, duration: duration, damping: damping)
|
||||
}
|
||||
|
||||
fileprivate func willUpdateIsExtractedToContextPreview(isExtractedToContextPreview: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if isExtractedToContextPreview {
|
||||
var offset: CGFloat = 0.0
|
||||
var inset: CGFloat = 0.0
|
||||
@ -242,34 +267,57 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
|
||||
if let _ = self.backgroundNode {
|
||||
} else if let currentParams = self.currentParams {
|
||||
let backgroundWallpaperNode = ChatMessageBubbleBackdrop()
|
||||
backgroundWallpaperNode.alpha = 0.0
|
||||
|
||||
let backgroundNode = ChatMessageBackground()
|
||||
backgroundNode.alpha = 0.0
|
||||
|
||||
backgroundNode.setType(type: type, highlighted: false, graphics: currentParams.graphics, maskMode: false, hasWallpaper: currentParams.presentationData.theme.wallpaper.hasWallpaper, transition: .immediate)
|
||||
|
||||
self.sourceNode.contentNode.insertSubnode(backgroundNode, at: 0)
|
||||
self.sourceNode.contentNode.insertSubnode(backgroundWallpaperNode, at: 0)
|
||||
|
||||
self.backgroundWallpaperNode = backgroundWallpaperNode
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
transition.updateAlpha(node: backgroundNode, alpha: 1.0)
|
||||
transition.updateAlpha(node: backgroundWallpaperNode, alpha: 1.0)
|
||||
|
||||
backgroundNode.setType(type: type, highlighted: false, graphics: currentParams.graphics, maskMode: true, hasWallpaper: currentParams.presentationData.theme.wallpaper.hasWallpaper, transition: .immediate)
|
||||
backgroundWallpaperNode.setType(type: type, theme: currentParams.presentationData.theme, mediaBox: currentParams.mediaBox, essentialGraphics: currentParams.graphics, maskMode: true)
|
||||
}
|
||||
|
||||
if let currentParams = self.currentParams {
|
||||
let backgroundFrame = CGRect(x: currentParams.contentOrigin.x + offset, y: 0.0, width: currentParams.size.width + inset, height: currentParams.size.height)
|
||||
self.backgroundNode?.updateLayout(size: backgroundFrame.size, transition: .immediate)
|
||||
self.backgroundNode?.frame = backgroundFrame
|
||||
self.backgroundWallpaperNode?.frame = backgroundFrame
|
||||
|
||||
if let (rect, containerSize) = self.absoluteRect {
|
||||
let mappedRect = CGRect(origin: CGPoint(x: rect.minX + backgroundFrame.minX, y: rect.minY + backgroundFrame.minY), size: rect.size)
|
||||
self.backgroundWallpaperNode?.update(rect: mappedRect, within: containerSize)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
self.backgroundNode = nil
|
||||
transition.updateAlpha(node: backgroundNode, alpha: 0.0, completion: { [weak backgroundNode] _ in
|
||||
backgroundNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
if let backgroundWallpaperNode = self.backgroundWallpaperNode {
|
||||
self.backgroundWallpaperNode = nil
|
||||
transition.updateAlpha(node: backgroundWallpaperNode, alpha: 0.0, completion: { [weak backgroundWallpaperNode] _ in
|
||||
backgroundWallpaperNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
} else if let backgroundNode = self.backgroundNode {
|
||||
self.backgroundNode = nil
|
||||
transition.updateAlpha(node: backgroundNode, alpha: 0.0, completion: { [weak backgroundNode] _ in
|
||||
backgroundNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func isExtractedToContextPreviewUpdated(_ isExtractedToContextPreview: Bool) {
|
||||
fileprivate func isExtractedToContextPreviewUpdated(_ isExtractedToContextPreview: Bool) {
|
||||
}
|
||||
|
||||
func update(size: CGSize, contentOrigin: CGPoint, selectionInsets: UIEdgeInsets, index: Int, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, messageSelection: Bool?) {
|
||||
self.currentParams = (size, contentOrigin, presentationData, graphics, backgroundType, messageSelection, selectionInsets)
|
||||
fileprivate func update(size: CGSize, contentOrigin: CGPoint, selectionInsets: UIEdgeInsets, index: Int, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, mediaBox: MediaBox, messageSelection: Bool?) {
|
||||
self.currentParams = (size, contentOrigin, presentationData, graphics, backgroundType, mediaBox, messageSelection, selectionInsets)
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
var incoming: Bool = false
|
||||
@ -2314,9 +2362,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
|
||||
container?.isExtractedToContextPreviewUpdated(isExtractedToContextPreview)
|
||||
|
||||
// if !isExtractedToContextPreview, let (rect, size) = strongSelf.absoluteRect {
|
||||
// strongSelf.updateAbsoluteRect(rect, within: size)
|
||||
// }
|
||||
if !isExtractedToContextPreview, let (rect, size) = strongSelf.absoluteRect {
|
||||
container?.updateAbsoluteRect(relativeFrame.offsetBy(dx: rect.minX, dy: rect.minY), within: size)
|
||||
}
|
||||
|
||||
for contentNode in strongSelf.contentNodes {
|
||||
if contentNode.supernode === strongContextSourceNode.contentNode {
|
||||
@ -2325,23 +2373,23 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
contextSourceNode.updateAbsoluteRect = { [weak strongSelf] rect, size in
|
||||
guard let strongSelf = strongSelf, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||
contextSourceNode.updateAbsoluteRect = { [weak strongSelf, weak container, weak contextSourceNode] rect, size in
|
||||
guard let strongSelf = strongSelf, let strongContextSourceNode = contextSourceNode, strongContextSourceNode.isExtractedToContextPreview else {
|
||||
return
|
||||
}
|
||||
// strongSelf.updateAbsoluteRectInternal(rect, within: size)
|
||||
container?.updateAbsoluteRect(relativeFrame.offsetBy(dx: rect.minX, dy: rect.minY), within: size)
|
||||
}
|
||||
contextSourceNode.applyAbsoluteOffset = { [weak strongSelf] value, animationCurve, duration in
|
||||
guard let strongSelf = strongSelf, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||
contextSourceNode.applyAbsoluteOffset = { [weak strongSelf, weak container, weak contextSourceNode] value, animationCurve, duration in
|
||||
guard let strongSelf = strongSelf, let strongContextSourceNode = contextSourceNode, strongContextSourceNode.isExtractedToContextPreview else {
|
||||
return
|
||||
}
|
||||
// strongSelf.applyAbsoluteOffsetInternal(value: value, animationCurve: animationCurve, duration: duration)
|
||||
container?.applyAbsoluteOffset(value: value, animationCurve: animationCurve, duration: duration)
|
||||
}
|
||||
contextSourceNode.applyAbsoluteOffsetSpring = { [weak strongSelf] value, duration, damping in
|
||||
guard let strongSelf = strongSelf, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||
contextSourceNode.applyAbsoluteOffsetSpring = { [weak strongSelf, weak container, weak contextSourceNode] value, duration, damping in
|
||||
guard let strongSelf = strongSelf, let strongContextSourceNode = contextSourceNode, strongContextSourceNode.isExtractedToContextPreview else {
|
||||
return
|
||||
}
|
||||
// strongSelf.applyAbsoluteOffsetSpringInternal(value: value, duration: duration, damping: damping)
|
||||
container?.applyAbsoluteOffsetSpring(value: value, duration: duration, damping: damping)
|
||||
}
|
||||
|
||||
strongSelf.contentContainers.append(container)
|
||||
@ -2371,7 +2419,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
selectionInsets.bottom = groupOverlap / 2.0
|
||||
}
|
||||
|
||||
contentContainer?.update(size: relativeFrame.size, contentOrigin: contentOrigin, selectionInsets: selectionInsets, index: index, presentationData: item.presentationData, graphics: graphics, backgroundType: backgroundType, messageSelection: itemSelection)
|
||||
contentContainer?.update(size: relativeFrame.size, contentOrigin: contentOrigin, selectionInsets: selectionInsets, index: index, presentationData: item.presentationData, graphics: graphics, backgroundType: backgroundType, mediaBox: item.context.account.postbox.mediaBox, messageSelection: itemSelection)
|
||||
|
||||
index += 1
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ private struct FetchControls {
|
||||
|
||||
final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
private var selectionNode: FileMessageSelectionNode?
|
||||
private var cutoutNode: ASDisplayNode?
|
||||
|
||||
private let titleNode: TextNode
|
||||
private let descriptionNode: TextNode
|
||||
@ -974,34 +973,13 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
let cutoutFrame = streamingCacheStatusFrame.insetBy(dx: -(1.0 + UIScreenPixel), dy: -(1.0 + UIScreenPixel)).offsetBy(dx: progressFrame.minX - 6.0, dy: progressFrame.minY)
|
||||
|
||||
|
||||
if streamingState == .none && self.selectionNode == nil {
|
||||
if let cutoutNode = self.cutoutNode {
|
||||
self.cutoutNode = nil
|
||||
if animated {
|
||||
cutoutNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) { [weak cutoutNode] _ in
|
||||
cutoutNode?.removeFromSupernode()
|
||||
}
|
||||
} else {
|
||||
cutoutNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
self.statusNode?.cutout = nil
|
||||
} else if let statusNode = self.statusNode, (self.iconNode?.isHidden ?? true) {
|
||||
if let _ = self.cutoutNode {
|
||||
} else {
|
||||
let cutoutNode = ASImageNode()
|
||||
cutoutNode.displaysAsynchronously = false
|
||||
cutoutNode.displayWithoutProcessing = true
|
||||
cutoutNode.image = generateFilledCircleImage(diameter: 23.0, color: messageTheme.bubble.withWallpaper.fill)
|
||||
|
||||
self.cutoutNode = cutoutNode
|
||||
self.insertSubnode(cutoutNode, aboveSubnode: statusNode)
|
||||
|
||||
cutoutNode.frame = streamingCacheStatusFrame.insetBy(dx: -(1.0 + UIScreenPixel), dy: -(1.0 + UIScreenPixel))
|
||||
|
||||
if animated {
|
||||
cutoutNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
self.statusNode?.cutout = cutoutFrame
|
||||
}
|
||||
|
||||
if let (expandedString, compactString, font) = downloadingStrings {
|
||||
|
Loading…
x
Reference in New Issue
Block a user