mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
3df7111853
@ -10614,3 +10614,7 @@ Sorry for the inconvenience.";
|
|||||||
"MediaEditor.Shortcut.Location" = "Location";
|
"MediaEditor.Shortcut.Location" = "Location";
|
||||||
"MediaEditor.Shortcut.Reaction" = "Reaction";
|
"MediaEditor.Shortcut.Reaction" = "Reaction";
|
||||||
"MediaEditor.Shortcut.Audio" = "Audio";
|
"MediaEditor.Shortcut.Audio" = "Audio";
|
||||||
|
|
||||||
|
"BoostGift.WinnersTitle" = "WINNERS";
|
||||||
|
"BoostGift.Winners" = "Show Winners";
|
||||||
|
"BoostGift.WinnersInfo" = "Choose whether to make the list of winners public when the giveaway ends.";
|
||||||
|
@ -142,6 +142,8 @@ public final class ViewController: UIViewController {
|
|||||||
self.callState.lifecycleState = .terminated(PrivateCallScreen.State.TerminatedState(duration: 82.0))
|
self.callState.lifecycleState = .terminated(PrivateCallScreen.State.TerminatedState(duration: 82.0))
|
||||||
self.callState.remoteVideo = nil
|
self.callState.remoteVideo = nil
|
||||||
self.callState.localVideo = nil
|
self.callState.localVideo = nil
|
||||||
|
self.callState.isMicrophoneMuted = false
|
||||||
|
self.callState.isRemoteBatteryLow = false
|
||||||
self.update(transition: .spring(duration: 0.4))
|
self.update(transition: .spring(duration: 0.4))
|
||||||
}
|
}
|
||||||
callScreenView.backAction = { [weak self] in
|
callScreenView.backAction = { [weak self] in
|
||||||
@ -151,6 +153,12 @@ public final class ViewController: UIViewController {
|
|||||||
self.callState.isMicrophoneMuted = !self.callState.isMicrophoneMuted
|
self.callState.isMicrophoneMuted = !self.callState.isMicrophoneMuted
|
||||||
self.update(transition: .spring(duration: 0.4))
|
self.update(transition: .spring(duration: 0.4))
|
||||||
}
|
}
|
||||||
|
callScreenView.closeAction = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.callScreenView?.speakerAction?()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update(transition: Transition) {
|
private func update(transition: Transition) {
|
||||||
|
@ -1573,6 +1573,56 @@ public extension ContainedViewLayoutTransition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateLineWidth(layer: CAShapeLayer, lineWidth: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||||
|
if layer.lineWidth == lineWidth {
|
||||||
|
completion?(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case .immediate:
|
||||||
|
layer.removeAnimation(forKey: "lineWidth")
|
||||||
|
layer.lineWidth = lineWidth
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
case let .animated(duration, curve):
|
||||||
|
let fromLineWidth = layer.lineWidth
|
||||||
|
layer.lineWidth = lineWidth
|
||||||
|
layer.animate(from: fromLineWidth as NSNumber, to: lineWidth as NSNumber, keyPath: "lineWidth", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
||||||
|
result in
|
||||||
|
if let completion = completion {
|
||||||
|
completion(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStrokeColor(layer: CAShapeLayer, strokeColor: UIColor, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||||
|
if layer.strokeColor.flatMap(UIColor.init(cgColor:)) == strokeColor {
|
||||||
|
completion?(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case .immediate:
|
||||||
|
layer.removeAnimation(forKey: "strokeColor")
|
||||||
|
layer.strokeColor = strokeColor.cgColor
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
case let .animated(duration, curve):
|
||||||
|
let fromStrokeColor = layer.strokeColor ?? UIColor.clear.cgColor
|
||||||
|
layer.strokeColor = strokeColor.cgColor
|
||||||
|
layer.animate(from: fromStrokeColor, to: strokeColor.cgColor, keyPath: "strokeColor", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
|
||||||
|
result in
|
||||||
|
if let completion = completion {
|
||||||
|
completion(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func attachAnimation(view: UIView, id: String, completion: @escaping (Bool) -> Void) {
|
func attachAnimation(view: UIView, id: String, completion: @escaping (Bool) -> Void) {
|
||||||
switch self {
|
switch self {
|
||||||
case .immediate:
|
case .immediate:
|
||||||
|
@ -925,3 +925,68 @@ public func drawSvgPath(_ context: CGContext, path: StaticString, strokeOnMove:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func convertSvgPath(_ path: StaticString) throws -> CGPath {
|
||||||
|
var index: UnsafePointer<UInt8> = path.utf8Start
|
||||||
|
let end = path.utf8Start.advanced(by: path.utf8CodeUnitCount)
|
||||||
|
var currentPoint = CGPoint()
|
||||||
|
|
||||||
|
let result = CGMutablePath()
|
||||||
|
|
||||||
|
while index < end {
|
||||||
|
let c = index.pointee
|
||||||
|
index = index.successor()
|
||||||
|
|
||||||
|
if c == 77 { // M
|
||||||
|
let x = try readCGFloat(&index, end: end, separator: 44)
|
||||||
|
let y = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
|
||||||
|
//print("Move to \(x), \(y)")
|
||||||
|
currentPoint = CGPoint(x: x, y: y)
|
||||||
|
result.move(to: currentPoint)
|
||||||
|
} else if c == 76 { // L
|
||||||
|
let x = try readCGFloat(&index, end: end, separator: 44)
|
||||||
|
let y = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
|
||||||
|
//print("Line to \(x), \(y)")
|
||||||
|
currentPoint = CGPoint(x: x, y: y)
|
||||||
|
result.addLine(to: currentPoint)
|
||||||
|
} else if c == 72 { // H
|
||||||
|
let x = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
|
||||||
|
//print("Move to \(x), \(y)")
|
||||||
|
currentPoint = CGPoint(x: x, y: currentPoint.y)
|
||||||
|
result.addLine(to: currentPoint)
|
||||||
|
} else if c == 86 { // V
|
||||||
|
let y = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
|
||||||
|
//print("Move to \(x), \(y)")
|
||||||
|
currentPoint = CGPoint(x: currentPoint.x, y: y)
|
||||||
|
result.addLine(to: currentPoint)
|
||||||
|
} else if c == 67 { // C
|
||||||
|
let x1 = try readCGFloat(&index, end: end, separator: 44)
|
||||||
|
let y1 = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
let x2 = try readCGFloat(&index, end: end, separator: 44)
|
||||||
|
let y2 = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
let x = try readCGFloat(&index, end: end, separator: 44)
|
||||||
|
let y = try readCGFloat(&index, end: end, separator: 32)
|
||||||
|
|
||||||
|
currentPoint = CGPoint(x: x, y: y)
|
||||||
|
result.addCurve(to: currentPoint, control1: CGPoint(x: x1, y: y1), control2: CGPoint(x: x2, y: y2))
|
||||||
|
} else if c == 90 { // Z
|
||||||
|
if index != end && index.pointee != 32 {
|
||||||
|
throw ParsingError.Generic
|
||||||
|
}
|
||||||
|
} else if c == 83 { // S
|
||||||
|
if index != end && index.pointee != 32 {
|
||||||
|
throw ParsingError.Generic
|
||||||
|
}
|
||||||
|
} else if c == 32 { // space
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
throw ParsingError.Generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -575,6 +575,8 @@ private final class PendingInAppPurchaseState: Codable {
|
|||||||
case additionalPeerIds
|
case additionalPeerIds
|
||||||
case countries
|
case countries
|
||||||
case onlyNewSubscribers
|
case onlyNewSubscribers
|
||||||
|
case showWinners
|
||||||
|
case prizeDescription
|
||||||
case randomId
|
case randomId
|
||||||
case untilDate
|
case untilDate
|
||||||
}
|
}
|
||||||
@ -593,7 +595,7 @@ private final class PendingInAppPurchaseState: Codable {
|
|||||||
case restore
|
case restore
|
||||||
case gift(peerId: EnginePeer.Id)
|
case gift(peerId: EnginePeer.Id)
|
||||||
case giftCode(peerIds: [EnginePeer.Id], boostPeer: EnginePeer.Id?)
|
case giftCode(peerIds: [EnginePeer.Id], boostPeer: EnginePeer.Id?)
|
||||||
case giveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32)
|
case giveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32)
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
@ -621,6 +623,8 @@ private final class PendingInAppPurchaseState: Codable {
|
|||||||
additionalPeerIds: try container.decode([Int64].self, forKey: .randomId).map { EnginePeer.Id($0) },
|
additionalPeerIds: try container.decode([Int64].self, forKey: .randomId).map { EnginePeer.Id($0) },
|
||||||
countries: try container.decodeIfPresent([String].self, forKey: .countries) ?? [],
|
countries: try container.decodeIfPresent([String].self, forKey: .countries) ?? [],
|
||||||
onlyNewSubscribers: try container.decode(Bool.self, forKey: .onlyNewSubscribers),
|
onlyNewSubscribers: try container.decode(Bool.self, forKey: .onlyNewSubscribers),
|
||||||
|
showWinners: try container.decodeIfPresent(Bool.self, forKey: .showWinners) ?? false,
|
||||||
|
prizeDescription: try container.decodeIfPresent(String.self, forKey: .prizeDescription),
|
||||||
randomId: try container.decode(Int64.self, forKey: .randomId),
|
randomId: try container.decode(Int64.self, forKey: .randomId),
|
||||||
untilDate: try container.decode(Int32.self, forKey: .untilDate)
|
untilDate: try container.decode(Int32.self, forKey: .untilDate)
|
||||||
)
|
)
|
||||||
@ -646,12 +650,14 @@ private final class PendingInAppPurchaseState: Codable {
|
|||||||
try container.encode(PurposeType.giftCode.rawValue, forKey: .type)
|
try container.encode(PurposeType.giftCode.rawValue, forKey: .type)
|
||||||
try container.encode(peerIds.map { $0.toInt64() }, forKey: .peers)
|
try container.encode(peerIds.map { $0.toInt64() }, forKey: .peers)
|
||||||
try container.encodeIfPresent(boostPeer?.toInt64(), forKey: .boostPeer)
|
try container.encodeIfPresent(boostPeer?.toInt64(), forKey: .boostPeer)
|
||||||
case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate):
|
case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate):
|
||||||
try container.encode(PurposeType.giveaway.rawValue, forKey: .type)
|
try container.encode(PurposeType.giveaway.rawValue, forKey: .type)
|
||||||
try container.encode(boostPeer.toInt64(), forKey: .boostPeer)
|
try container.encode(boostPeer.toInt64(), forKey: .boostPeer)
|
||||||
try container.encode(additionalPeerIds.map { $0.toInt64() }, forKey: .additionalPeerIds)
|
try container.encode(additionalPeerIds.map { $0.toInt64() }, forKey: .additionalPeerIds)
|
||||||
try container.encode(countries, forKey: .countries)
|
try container.encode(countries, forKey: .countries)
|
||||||
try container.encode(onlyNewSubscribers, forKey: .onlyNewSubscribers)
|
try container.encode(onlyNewSubscribers, forKey: .onlyNewSubscribers)
|
||||||
|
try container.encode(showWinners, forKey: .showWinners)
|
||||||
|
try container.encode(prizeDescription, forKey: .prizeDescription)
|
||||||
try container.encode(randomId, forKey: .randomId)
|
try container.encode(randomId, forKey: .randomId)
|
||||||
try container.encode(untilDate, forKey: .untilDate)
|
try container.encode(untilDate, forKey: .untilDate)
|
||||||
}
|
}
|
||||||
@ -669,8 +675,8 @@ private final class PendingInAppPurchaseState: Codable {
|
|||||||
self = .gift(peerId: peerId)
|
self = .gift(peerId: peerId)
|
||||||
case let .giftCode(peerIds, boostPeer, _, _):
|
case let .giftCode(peerIds, boostPeer, _, _):
|
||||||
self = .giftCode(peerIds: peerIds, boostPeer: boostPeer)
|
self = .giftCode(peerIds: peerIds, boostPeer: boostPeer)
|
||||||
case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate, _, _):
|
case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, _, _):
|
||||||
self = .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate)
|
self = .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,8 +693,8 @@ private final class PendingInAppPurchaseState: Codable {
|
|||||||
return .gift(peerId: peerId, currency: currency, amount: amount)
|
return .gift(peerId: peerId, currency: currency, amount: amount)
|
||||||
case let .giftCode(peerIds, boostPeer):
|
case let .giftCode(peerIds, boostPeer):
|
||||||
return .giftCode(peerIds: peerIds, boostPeer: boostPeer, currency: currency, amount: amount)
|
return .giftCode(peerIds: peerIds, boostPeer: boostPeer, currency: currency, amount: amount)
|
||||||
case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate):
|
case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate):
|
||||||
return .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount)
|
return .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,5 +88,6 @@
|
|||||||
- (void)_commitLocked;
|
- (void)_commitLocked;
|
||||||
|
|
||||||
- (void)setHidesPanelOnLock;
|
- (void)setHidesPanelOnLock;
|
||||||
|
- (UIView *)createLockPanelView;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -115,7 +115,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
|||||||
UIImageView *_innerIconView;
|
UIImageView *_innerIconView;
|
||||||
|
|
||||||
UIView *_lockPanelWrapperView;
|
UIView *_lockPanelWrapperView;
|
||||||
UIImageView *_lockPanelView;
|
UIView *_lockPanelView;
|
||||||
UIImageView *_lockArrowView;
|
UIImageView *_lockArrowView;
|
||||||
TGModernConversationInputLockView *_lockView;
|
TGModernConversationInputLockView *_lockView;
|
||||||
UIImage *_previousIcon;
|
UIImage *_previousIcon;
|
||||||
@ -265,7 +265,9 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
|||||||
if (!update)
|
if (!update)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_lockPanelView.image = [self panelBackgroundImage];
|
if ([_lockPanelView isKindOfClass:[UIImageView class]]) {
|
||||||
|
((UIImageView *)_lockPanelView).image = [self panelBackgroundImage];
|
||||||
|
}
|
||||||
_lockArrowView.image = TGTintedImage(TGComponentsImageNamed(@"VideoRecordArrow"), self.pallete != nil ? self.pallete.lockColor : UIColorRGB(0x9597a0));
|
_lockArrowView.image = TGTintedImage(TGComponentsImageNamed(@"VideoRecordArrow"), self.pallete != nil ? self.pallete.lockColor : UIColorRGB(0x9597a0));
|
||||||
_lockView.color = self.pallete.lockColor;
|
_lockView.color = self.pallete.lockColor;
|
||||||
|
|
||||||
@ -341,6 +343,13 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
|||||||
return stopButtonImage;
|
return stopButtonImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (UIView *)createLockPanelView {
|
||||||
|
UIImageView *view = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)];
|
||||||
|
view.userInteractionEnabled = true;
|
||||||
|
view.image = [self panelBackgroundImage];
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)animateIn {
|
- (void)animateIn {
|
||||||
if (!_locked) {
|
if (!_locked) {
|
||||||
_lockView.lockness = 0.0f;
|
_lockView.lockness = 0.0f;
|
||||||
@ -373,9 +382,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
|||||||
_lockPanelWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)];
|
_lockPanelWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)];
|
||||||
[[_presentation view] addSubview:_lockPanelWrapperView];
|
[[_presentation view] addSubview:_lockPanelWrapperView];
|
||||||
|
|
||||||
_lockPanelView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)];
|
_lockPanelView = [self createLockPanelView];
|
||||||
_lockPanelView.userInteractionEnabled = true;
|
|
||||||
_lockPanelView.image = [self panelBackgroundImage];
|
|
||||||
|
|
||||||
[_lockPanelWrapperView addSubview:_lockPanelView];
|
[_lockPanelWrapperView addSubview:_lockPanelView];
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ private enum CreateGiveawaySection: Int32 {
|
|||||||
case subscriptions
|
case subscriptions
|
||||||
case channels
|
case channels
|
||||||
case users
|
case users
|
||||||
|
case winners
|
||||||
|
case prizeDescription
|
||||||
case time
|
case time
|
||||||
case duration
|
case duration
|
||||||
}
|
}
|
||||||
@ -92,6 +94,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
case usersNew(PresentationTheme, String, String, Bool)
|
case usersNew(PresentationTheme, String, String, Bool)
|
||||||
case usersInfo(PresentationTheme, String)
|
case usersInfo(PresentationTheme, String)
|
||||||
|
|
||||||
|
case winnersHeader(PresentationTheme, String)
|
||||||
|
case winners(PresentationTheme, String, Bool)
|
||||||
|
case winnersInfo(PresentationTheme, String)
|
||||||
|
|
||||||
|
case prizeDescriptionHeader(PresentationTheme, String)
|
||||||
|
case prizeDescription(PresentationTheme, String, String)
|
||||||
|
case prizeDescriptionInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case timeHeader(PresentationTheme, String)
|
case timeHeader(PresentationTheme, String)
|
||||||
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
||||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?, Bool, Bool)
|
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?, Bool, Bool)
|
||||||
@ -113,6 +123,10 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
return CreateGiveawaySection.channels.rawValue
|
return CreateGiveawaySection.channels.rawValue
|
||||||
case .usersHeader, .usersAll, .usersNew, .usersInfo:
|
case .usersHeader, .usersAll, .usersNew, .usersInfo:
|
||||||
return CreateGiveawaySection.users.rawValue
|
return CreateGiveawaySection.users.rawValue
|
||||||
|
case .winnersHeader, .winners, .winnersInfo:
|
||||||
|
return CreateGiveawaySection.winners.rawValue
|
||||||
|
case .prizeDescriptionHeader, .prizeDescription, .prizeDescriptionInfo:
|
||||||
|
return CreateGiveawaySection.prizeDescription.rawValue
|
||||||
case .timeHeader, .timeExpiryDate, .timeCustomPicker, .timeInfo:
|
case .timeHeader, .timeExpiryDate, .timeCustomPicker, .timeInfo:
|
||||||
return CreateGiveawaySection.time.rawValue
|
return CreateGiveawaySection.time.rawValue
|
||||||
case .durationHeader, .duration, .durationInfo:
|
case .durationHeader, .duration, .durationInfo:
|
||||||
@ -154,20 +168,32 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
return 104
|
return 104
|
||||||
case .usersInfo:
|
case .usersInfo:
|
||||||
return 105
|
return 105
|
||||||
case .timeHeader:
|
case .winnersHeader:
|
||||||
return 106
|
return 106
|
||||||
case .timeExpiryDate:
|
case .winners:
|
||||||
return 107
|
return 107
|
||||||
case .timeCustomPicker:
|
case .winnersInfo:
|
||||||
return 108
|
return 108
|
||||||
case .timeInfo:
|
case .prizeDescriptionHeader:
|
||||||
return 109
|
return 109
|
||||||
case .durationHeader:
|
case .prizeDescription:
|
||||||
return 110
|
return 110
|
||||||
|
case .prizeDescriptionInfo:
|
||||||
|
return 111
|
||||||
|
case .timeHeader:
|
||||||
|
return 112
|
||||||
|
case .timeExpiryDate:
|
||||||
|
return 113
|
||||||
|
case .timeCustomPicker:
|
||||||
|
return 114
|
||||||
|
case .timeInfo:
|
||||||
|
return 115
|
||||||
|
case .durationHeader:
|
||||||
|
return 116
|
||||||
case let .duration(index, _, _, _, _, _, _, _):
|
case let .duration(index, _, _, _, _, _, _, _):
|
||||||
return 111 + index
|
return 117 + index
|
||||||
case .durationInfo:
|
case .durationInfo:
|
||||||
return 120
|
return 130
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +295,42 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .winnersHeader(lhsTheme, lhsText):
|
||||||
|
if case let .winnersHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .winners(lhsTheme, lhsText, lhsValue):
|
||||||
|
if case let .winners(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .winnersInfo(lhsTheme, lhsText):
|
||||||
|
if case let .winnersInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .prizeDescriptionHeader(lhsTheme, lhsText):
|
||||||
|
if case let .prizeDescriptionHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .prizeDescription(lhsTheme, lhsPlaceholder, lhsValue):
|
||||||
|
if case let .prizeDescription(rhsTheme, rhsPlaceholder, rhsValue) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsValue == rhsValue {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .prizeDescriptionInfo(lhsTheme, lhsText):
|
||||||
|
if case let .prizeDescriptionInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
case let .timeHeader(lhsTheme, lhsText):
|
case let .timeHeader(lhsTheme, lhsText):
|
||||||
if case let .timeHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .timeHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
@ -423,6 +484,30 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .usersInfo(_, text):
|
case let .usersInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
|
case let .winnersHeader(_, text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
|
case let .winners(_, text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
|
arguments.updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.showWinners = value
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
})
|
||||||
|
case let .winnersInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
|
case let .prizeDescriptionHeader(_, text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
|
case let .prizeDescription(_, placeholder, value):
|
||||||
|
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: value, placeholder: placeholder, sectionId: self.section, textUpdated: { value in
|
||||||
|
arguments.updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.prizeDescription = value
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
}, action: {})
|
||||||
|
case let .prizeDescriptionInfo(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .timeHeader(_, text):
|
case let .timeHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .timeExpiryDate(theme, dateTimeFormat, value, active):
|
case let .timeExpiryDate(theme, dateTimeFormat, value, active):
|
||||||
@ -617,6 +702,14 @@ private func createGiveawayControllerEntries(
|
|||||||
entries.append(.usersNew(presentationData.theme, presentationData.strings.BoostGift_OnlyNewSubscribers, countriesText, state.onlyNewEligible))
|
entries.append(.usersNew(presentationData.theme, presentationData.strings.BoostGift_OnlyNewSubscribers, countriesText, state.onlyNewEligible))
|
||||||
entries.append(.usersInfo(presentationData.theme, presentationData.strings.BoostGift_LimitSubscribersInfo))
|
entries.append(.usersInfo(presentationData.theme, presentationData.strings.BoostGift_LimitSubscribersInfo))
|
||||||
|
|
||||||
|
entries.append(.winnersHeader(presentationData.theme, presentationData.strings.BoostGift_WinnersTitle.uppercased()))
|
||||||
|
entries.append(.winners(presentationData.theme, presentationData.strings.BoostGift_Winners, state.showWinners))
|
||||||
|
entries.append(.winnersInfo(presentationData.theme, presentationData.strings.BoostGift_WinnersInfo))
|
||||||
|
|
||||||
|
entries.append(.prizeDescriptionHeader(presentationData.theme, "Additional Prizes".uppercased()))
|
||||||
|
entries.append(.prizeDescription(presentationData.theme, "Prize Description (Optional)", state.prizeDescription))
|
||||||
|
entries.append(.prizeDescriptionInfo(presentationData.theme, "Provide description of any additional prizes you plan to award to the winners, in addition to Telegram Premium."))
|
||||||
|
|
||||||
entries.append(.timeHeader(presentationData.theme, presentationData.strings.BoostGift_DateTitle.uppercased()))
|
entries.append(.timeHeader(presentationData.theme, presentationData.strings.BoostGift_DateTitle.uppercased()))
|
||||||
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate, state.pickingExpiryDate, state.pickingExpiryTime))
|
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate, state.pickingExpiryDate, state.pickingExpiryTime))
|
||||||
entries.append(.timeInfo(presentationData.theme, presentationData.strings.BoostGift_DateInfo(presentationData.strings.BoostGift_DateInfoSubscribers(Int32(state.subscriptions))).string))
|
entries.append(.timeInfo(presentationData.theme, presentationData.strings.BoostGift_DateInfo(presentationData.strings.BoostGift_DateInfoSubscribers(Int32(state.subscriptions))).string))
|
||||||
@ -680,11 +773,13 @@ private struct CreateGiveawayControllerState: Equatable {
|
|||||||
|
|
||||||
var mode: Mode
|
var mode: Mode
|
||||||
var subscriptions: Int32
|
var subscriptions: Int32
|
||||||
var channels: [EnginePeer.Id]
|
var channels: [EnginePeer.Id] = []
|
||||||
var peers: [EnginePeer.Id]
|
var peers: [EnginePeer.Id] = []
|
||||||
var selectedMonths: Int32?
|
var selectedMonths: Int32?
|
||||||
var countries: [String]
|
var countries: [String] = []
|
||||||
var onlyNewEligible: Bool
|
var onlyNewEligible: Bool = false
|
||||||
|
var showWinners: Bool = false
|
||||||
|
var prizeDescription: String = ""
|
||||||
var time: Int32
|
var time: Int32
|
||||||
var pickingExpiryTime = false
|
var pickingExpiryTime = false
|
||||||
var pickingExpiryDate = false
|
var pickingExpiryDate = false
|
||||||
@ -722,7 +817,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
let minDate = currentTime + 60 * 30
|
let minDate = currentTime + 60 * 30
|
||||||
let maxDate = currentTime + context.userLimits.maxGiveawayPeriodSeconds
|
let maxDate = currentTime + context.userLimits.maxGiveawayPeriodSeconds
|
||||||
|
|
||||||
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, channels: [], peers: [], countries: [], onlyNewEligible: false, time: expiryTime)
|
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, time: expiryTime)
|
||||||
|
|
||||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: initialState)
|
let stateValue = Atomic(value: initialState)
|
||||||
@ -948,7 +1043,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
let quantity: Int32
|
let quantity: Int32
|
||||||
switch state.mode {
|
switch state.mode {
|
||||||
case .giveaway:
|
case .giveaway:
|
||||||
purpose = .giveaway(boostPeer: peerId, additionalPeerIds: state.channels.filter { $0 != peerId }, countries: state.countries, onlyNewSubscribers: state.onlyNewEligible, randomId: Int64.random(in: .min ..< .max), untilDate: state.time, currency: currency, amount: amount)
|
purpose = .giveaway(boostPeer: peerId, additionalPeerIds: state.channels.filter { $0 != peerId }, countries: state.countries, onlyNewSubscribers: state.onlyNewEligible, showWinners: state.showWinners, prizeDescription: state.prizeDescription.isEmpty ? nil : state.prizeDescription, randomId: Int64.random(in: .min ..< .max), untilDate: state.time, currency: currency, amount: amount)
|
||||||
quantity = selectedProduct.giftOption.storeQuantity
|
quantity = selectedProduct.giftOption.storeQuantity
|
||||||
case .gift:
|
case .gift:
|
||||||
purpose = .giftCode(peerIds: state.peers, boostPeer: peerId, currency: currency, amount: amount)
|
purpose = .giftCode(peerIds: state.peers, boostPeer: peerId, currency: currency, amount: amount)
|
||||||
@ -1040,7 +1135,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (context.engine.payments.launchPrepaidGiveaway(peerId: peerId, id: prepaidGiveaway.id, additionalPeerIds: state.channels.filter { $0 != peerId }, countries: state.countries, onlyNewSubscribers: state.onlyNewEligible, randomId: Int64.random(in: .min ..< .max), untilDate: state.time)
|
let _ = (context.engine.payments.launchPrepaidGiveaway(peerId: peerId, id: prepaidGiveaway.id, additionalPeerIds: state.channels.filter { $0 != peerId }, countries: state.countries, onlyNewSubscribers: state.onlyNewEligible, showWinners: state.showWinners, prizeDescription: state.prizeDescription.isEmpty ? nil : state.prizeDescription, randomId: Int64.random(in: .min ..< .max), untilDate: state.time)
|
||||||
|> deliverOnMainQueue).startStandalone(completed: {
|
|> deliverOnMainQueue).startStandalone(completed: {
|
||||||
if let controller, let navigationController = controller.navigationController as? NavigationController {
|
if let controller, let navigationController = controller.navigationController as? NavigationController {
|
||||||
var controllers = navigationController.viewControllers
|
var controllers = navigationController.viewControllers
|
||||||
|
@ -414,7 +414,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1251549527] = { return Api.InputStickeredMedia.parse_inputStickeredMediaPhoto($0) }
|
dict[1251549527] = { return Api.InputStickeredMedia.parse_inputStickeredMediaPhoto($0) }
|
||||||
dict[1634697192] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentGiftPremium($0) }
|
dict[1634697192] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentGiftPremium($0) }
|
||||||
dict[-1551868097] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumGiftCode($0) }
|
dict[-1551868097] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumGiftCode($0) }
|
||||||
dict[2090038758] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumGiveaway($0) }
|
dict[369444042] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumGiveaway($0) }
|
||||||
dict[-1502273946] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumSubscription($0) }
|
dict[-1502273946] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentPremiumSubscription($0) }
|
||||||
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
||||||
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
||||||
@ -542,7 +542,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
|
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
|
||||||
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
|
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
|
||||||
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
|
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
|
||||||
dict[1478887012] = { return Api.MessageMedia.parse_messageMediaGiveaway($0) }
|
dict[-626162256] = { return Api.MessageMedia.parse_messageMediaGiveaway($0) }
|
||||||
dict[-156940077] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
|
dict[-156940077] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
|
||||||
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
|
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
|
||||||
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
|
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
|
||||||
@ -1176,7 +1176,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1222446760] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) }
|
dict[-1222446760] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) }
|
||||||
dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) }
|
dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) }
|
||||||
dict[1130879648] = { return Api.payments.GiveawayInfo.parse_giveawayInfo($0) }
|
dict[1130879648] = { return Api.payments.GiveawayInfo.parse_giveawayInfo($0) }
|
||||||
dict[13456752] = { return Api.payments.GiveawayInfo.parse_giveawayInfoResults($0) }
|
dict[-1966612121] = { return Api.payments.GiveawayInfo.parse_giveawayInfoResults($0) }
|
||||||
dict[-1610250415] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
|
dict[-1610250415] = { return Api.payments.PaymentForm.parse_paymentForm($0) }
|
||||||
dict[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) }
|
dict[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) }
|
||||||
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
||||||
|
@ -604,7 +604,7 @@ public extension Api {
|
|||||||
indirect enum InputStorePaymentPurpose: TypeConstructorDescription {
|
indirect enum InputStorePaymentPurpose: TypeConstructorDescription {
|
||||||
case inputStorePaymentGiftPremium(userId: Api.InputUser, currency: String, amount: Int64)
|
case inputStorePaymentGiftPremium(userId: Api.InputUser, currency: String, amount: Int64)
|
||||||
case inputStorePaymentPremiumGiftCode(flags: Int32, users: [Api.InputUser], boostPeer: Api.InputPeer?, currency: String, amount: Int64)
|
case inputStorePaymentPremiumGiftCode(flags: Int32, users: [Api.InputUser], boostPeer: Api.InputPeer?, currency: String, amount: Int64)
|
||||||
case inputStorePaymentPremiumGiveaway(flags: Int32, boostPeer: Api.InputPeer, additionalPeers: [Api.InputPeer]?, countriesIso2: [String]?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64)
|
case inputStorePaymentPremiumGiveaway(flags: Int32, boostPeer: Api.InputPeer, additionalPeers: [Api.InputPeer]?, countriesIso2: [String]?, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64)
|
||||||
case inputStorePaymentPremiumSubscription(flags: Int32)
|
case inputStorePaymentPremiumSubscription(flags: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
@ -631,9 +631,9 @@ public extension Api {
|
|||||||
serializeString(currency, buffer: buffer, boxed: false)
|
serializeString(currency, buffer: buffer, boxed: false)
|
||||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
case .inputStorePaymentPremiumGiveaway(let flags, let boostPeer, let additionalPeers, let countriesIso2, let randomId, let untilDate, let currency, let amount):
|
case .inputStorePaymentPremiumGiveaway(let flags, let boostPeer, let additionalPeers, let countriesIso2, let prizeDescription, let randomId, let untilDate, let currency, let amount):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(2090038758)
|
buffer.appendInt32(369444042)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
boostPeer.serialize(buffer, true)
|
boostPeer.serialize(buffer, true)
|
||||||
@ -647,6 +647,7 @@ public extension Api {
|
|||||||
for item in countriesIso2! {
|
for item in countriesIso2! {
|
||||||
serializeString(item, buffer: buffer, boxed: false)
|
serializeString(item, buffer: buffer, boxed: false)
|
||||||
}}
|
}}
|
||||||
|
if Int(flags) & Int(1 << 4) != 0 {serializeString(prizeDescription!, buffer: buffer, boxed: false)}
|
||||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||||
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
||||||
serializeString(currency, buffer: buffer, boxed: false)
|
serializeString(currency, buffer: buffer, boxed: false)
|
||||||
@ -667,8 +668,8 @@ public extension Api {
|
|||||||
return ("inputStorePaymentGiftPremium", [("userId", userId as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
return ("inputStorePaymentGiftPremium", [("userId", userId as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||||
case .inputStorePaymentPremiumGiftCode(let flags, let users, let boostPeer, let currency, let amount):
|
case .inputStorePaymentPremiumGiftCode(let flags, let users, let boostPeer, let currency, let amount):
|
||||||
return ("inputStorePaymentPremiumGiftCode", [("flags", flags as Any), ("users", users as Any), ("boostPeer", boostPeer as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
return ("inputStorePaymentPremiumGiftCode", [("flags", flags as Any), ("users", users as Any), ("boostPeer", boostPeer as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||||
case .inputStorePaymentPremiumGiveaway(let flags, let boostPeer, let additionalPeers, let countriesIso2, let randomId, let untilDate, let currency, let amount):
|
case .inputStorePaymentPremiumGiveaway(let flags, let boostPeer, let additionalPeers, let countriesIso2, let prizeDescription, let randomId, let untilDate, let currency, let amount):
|
||||||
return ("inputStorePaymentPremiumGiveaway", [("flags", flags as Any), ("boostPeer", boostPeer as Any), ("additionalPeers", additionalPeers as Any), ("countriesIso2", countriesIso2 as Any), ("randomId", randomId as Any), ("untilDate", untilDate as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
return ("inputStorePaymentPremiumGiveaway", [("flags", flags as Any), ("boostPeer", boostPeer as Any), ("additionalPeers", additionalPeers as Any), ("countriesIso2", countriesIso2 as Any), ("prizeDescription", prizeDescription as Any), ("randomId", randomId as Any), ("untilDate", untilDate as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||||
case .inputStorePaymentPremiumSubscription(let flags):
|
case .inputStorePaymentPremiumSubscription(let flags):
|
||||||
return ("inputStorePaymentPremiumSubscription", [("flags", flags as Any)])
|
return ("inputStorePaymentPremiumSubscription", [("flags", flags as Any)])
|
||||||
}
|
}
|
||||||
@ -735,24 +736,27 @@ public extension Api {
|
|||||||
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||||
_4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
_4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||||
} }
|
} }
|
||||||
var _5: Int64?
|
var _5: String?
|
||||||
_5 = reader.readInt64()
|
if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) }
|
||||||
var _6: Int32?
|
var _6: Int64?
|
||||||
_6 = reader.readInt32()
|
_6 = reader.readInt64()
|
||||||
var _7: String?
|
var _7: Int32?
|
||||||
_7 = parseString(reader)
|
_7 = reader.readInt32()
|
||||||
var _8: Int64?
|
var _8: String?
|
||||||
_8 = reader.readInt64()
|
_8 = parseString(reader)
|
||||||
|
var _9: Int64?
|
||||||
|
_9 = reader.readInt64()
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = _7 != nil
|
let _c7 = _7 != nil
|
||||||
let _c8 = _8 != nil
|
let _c8 = _8 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
let _c9 = _9 != nil
|
||||||
return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiveaway(flags: _1!, boostPeer: _2!, additionalPeers: _3, countriesIso2: _4, randomId: _5!, untilDate: _6!, currency: _7!, amount: _8!)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||||
|
return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiveaway(flags: _1!, boostPeer: _2!, additionalPeers: _3, countriesIso2: _4, prizeDescription: _5, randomId: _6!, untilDate: _7!, currency: _8!, amount: _9!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -697,7 +697,7 @@ public extension Api {
|
|||||||
case messageMediaGame(game: Api.Game)
|
case messageMediaGame(game: Api.Game)
|
||||||
case messageMediaGeo(geo: Api.GeoPoint)
|
case messageMediaGeo(geo: Api.GeoPoint)
|
||||||
case messageMediaGeoLive(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32, proximityNotificationRadius: Int32?)
|
case messageMediaGeoLive(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32, proximityNotificationRadius: Int32?)
|
||||||
case messageMediaGiveaway(flags: Int32, channels: [Int64], countriesIso2: [String]?, quantity: Int32, months: Int32, untilDate: Int32)
|
case messageMediaGiveaway(flags: Int32, channels: [Int64], countriesIso2: [String]?, prizeDescription: String?, quantity: Int32, months: Int32, untilDate: Int32)
|
||||||
case messageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String, extendedMedia: Api.MessageExtendedMedia?)
|
case messageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String, extendedMedia: Api.MessageExtendedMedia?)
|
||||||
case messageMediaPhoto(flags: Int32, photo: Api.Photo?, ttlSeconds: Int32?)
|
case messageMediaPhoto(flags: Int32, photo: Api.Photo?, ttlSeconds: Int32?)
|
||||||
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
|
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
|
||||||
@ -762,9 +762,9 @@ public extension Api {
|
|||||||
serializeInt32(period, buffer: buffer, boxed: false)
|
serializeInt32(period, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .messageMediaGiveaway(let flags, let channels, let countriesIso2, let quantity, let months, let untilDate):
|
case .messageMediaGiveaway(let flags, let channels, let countriesIso2, let prizeDescription, let quantity, let months, let untilDate):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1478887012)
|
buffer.appendInt32(-626162256)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
@ -777,6 +777,7 @@ public extension Api {
|
|||||||
for item in countriesIso2! {
|
for item in countriesIso2! {
|
||||||
serializeString(item, buffer: buffer, boxed: false)
|
serializeString(item, buffer: buffer, boxed: false)
|
||||||
}}
|
}}
|
||||||
|
if Int(flags) & Int(1 << 3) != 0 {serializeString(prizeDescription!, buffer: buffer, boxed: false)}
|
||||||
serializeInt32(quantity, buffer: buffer, boxed: false)
|
serializeInt32(quantity, buffer: buffer, boxed: false)
|
||||||
serializeInt32(months, buffer: buffer, boxed: false)
|
serializeInt32(months, buffer: buffer, boxed: false)
|
||||||
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
serializeInt32(untilDate, buffer: buffer, boxed: false)
|
||||||
@ -862,8 +863,8 @@ public extension Api {
|
|||||||
return ("messageMediaGeo", [("geo", geo as Any)])
|
return ("messageMediaGeo", [("geo", geo as Any)])
|
||||||
case .messageMediaGeoLive(let flags, let geo, let heading, let period, let proximityNotificationRadius):
|
case .messageMediaGeoLive(let flags, let geo, let heading, let period, let proximityNotificationRadius):
|
||||||
return ("messageMediaGeoLive", [("flags", flags as Any), ("geo", geo as Any), ("heading", heading as Any), ("period", period as Any), ("proximityNotificationRadius", proximityNotificationRadius as Any)])
|
return ("messageMediaGeoLive", [("flags", flags as Any), ("geo", geo as Any), ("heading", heading as Any), ("period", period as Any), ("proximityNotificationRadius", proximityNotificationRadius as Any)])
|
||||||
case .messageMediaGiveaway(let flags, let channels, let countriesIso2, let quantity, let months, let untilDate):
|
case .messageMediaGiveaway(let flags, let channels, let countriesIso2, let prizeDescription, let quantity, let months, let untilDate):
|
||||||
return ("messageMediaGiveaway", [("flags", flags as Any), ("channels", channels as Any), ("countriesIso2", countriesIso2 as Any), ("quantity", quantity as Any), ("months", months as Any), ("untilDate", untilDate as Any)])
|
return ("messageMediaGiveaway", [("flags", flags as Any), ("channels", channels as Any), ("countriesIso2", countriesIso2 as Any), ("prizeDescription", prizeDescription as Any), ("quantity", quantity as Any), ("months", months as Any), ("untilDate", untilDate as Any)])
|
||||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam, let extendedMedia):
|
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam, let extendedMedia):
|
||||||
return ("messageMediaInvoice", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("receiptMsgId", receiptMsgId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("startParam", startParam as Any), ("extendedMedia", extendedMedia as Any)])
|
return ("messageMediaInvoice", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("receiptMsgId", receiptMsgId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("startParam", startParam as Any), ("extendedMedia", extendedMedia as Any)])
|
||||||
case .messageMediaPhoto(let flags, let photo, let ttlSeconds):
|
case .messageMediaPhoto(let flags, let photo, let ttlSeconds):
|
||||||
@ -1007,20 +1008,23 @@ public extension Api {
|
|||||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||||
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||||
} }
|
} }
|
||||||
var _4: Int32?
|
var _4: String?
|
||||||
_4 = reader.readInt32()
|
if Int(_1!) & Int(1 << 3) != 0 {_4 = parseString(reader) }
|
||||||
var _5: Int32?
|
var _5: Int32?
|
||||||
_5 = reader.readInt32()
|
_5 = reader.readInt32()
|
||||||
var _6: Int32?
|
var _6: Int32?
|
||||||
_6 = reader.readInt32()
|
_6 = reader.readInt32()
|
||||||
|
var _7: Int32?
|
||||||
|
_7 = reader.readInt32()
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
let _c7 = _7 != nil
|
||||||
return Api.MessageMedia.messageMediaGiveaway(flags: _1!, channels: _2!, countriesIso2: _3, quantity: _4!, months: _5!, untilDate: _6!)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||||
|
return Api.MessageMedia.messageMediaGiveaway(flags: _1!, channels: _2!, countriesIso2: _3, prizeDescription: _4, quantity: _5!, months: _6!, untilDate: _7!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -911,7 +911,7 @@ public extension Api.payments {
|
|||||||
public extension Api.payments {
|
public extension Api.payments {
|
||||||
enum GiveawayInfo: TypeConstructorDescription {
|
enum GiveawayInfo: TypeConstructorDescription {
|
||||||
case giveawayInfo(flags: Int32, startDate: Int32, joinedTooEarlyDate: Int32?, adminDisallowedChatId: Int64?, disallowedCountry: String?)
|
case giveawayInfo(flags: Int32, startDate: Int32, joinedTooEarlyDate: Int32?, adminDisallowedChatId: Int64?, disallowedCountry: String?)
|
||||||
case giveawayInfoResults(flags: Int32, startDate: Int32, giftCodeSlug: String?, finishDate: Int32, winnersCount: Int32, activatedCount: Int32)
|
case giveawayInfoResults(flags: Int32, startDate: Int32, giftCodeSlug: String?, finishDate: Int32, winnersCount: Int32, activatedCount: Int32, winners: [Api.User]?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -925,9 +925,9 @@ public extension Api.payments {
|
|||||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt64(adminDisallowedChatId!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 2) != 0 {serializeInt64(adminDisallowedChatId!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 4) != 0 {serializeString(disallowedCountry!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 4) != 0 {serializeString(disallowedCountry!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount):
|
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount, let winners):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(13456752)
|
buffer.appendInt32(-1966612121)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(startDate, buffer: buffer, boxed: false)
|
serializeInt32(startDate, buffer: buffer, boxed: false)
|
||||||
@ -935,6 +935,11 @@ public extension Api.payments {
|
|||||||
serializeInt32(finishDate, buffer: buffer, boxed: false)
|
serializeInt32(finishDate, buffer: buffer, boxed: false)
|
||||||
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
serializeInt32(winnersCount, buffer: buffer, boxed: false)
|
||||||
serializeInt32(activatedCount, buffer: buffer, boxed: false)
|
serializeInt32(activatedCount, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(winners!.count))
|
||||||
|
for item in winners! {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -943,8 +948,8 @@ public extension Api.payments {
|
|||||||
switch self {
|
switch self {
|
||||||
case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry):
|
case .giveawayInfo(let flags, let startDate, let joinedTooEarlyDate, let adminDisallowedChatId, let disallowedCountry):
|
||||||
return ("giveawayInfo", [("flags", flags as Any), ("startDate", startDate as Any), ("joinedTooEarlyDate", joinedTooEarlyDate as Any), ("adminDisallowedChatId", adminDisallowedChatId as Any), ("disallowedCountry", disallowedCountry as Any)])
|
return ("giveawayInfo", [("flags", flags as Any), ("startDate", startDate as Any), ("joinedTooEarlyDate", joinedTooEarlyDate as Any), ("adminDisallowedChatId", adminDisallowedChatId as Any), ("disallowedCountry", disallowedCountry as Any)])
|
||||||
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount):
|
case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount, let winners):
|
||||||
return ("giveawayInfoResults", [("flags", flags as Any), ("startDate", startDate as Any), ("giftCodeSlug", giftCodeSlug as Any), ("finishDate", finishDate as Any), ("winnersCount", winnersCount as Any), ("activatedCount", activatedCount as Any)])
|
return ("giveawayInfoResults", [("flags", flags as Any), ("startDate", startDate as Any), ("giftCodeSlug", giftCodeSlug as Any), ("finishDate", finishDate as Any), ("winnersCount", winnersCount as Any), ("activatedCount", activatedCount as Any), ("winners", winners as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,14 +989,19 @@ public extension Api.payments {
|
|||||||
_5 = reader.readInt32()
|
_5 = reader.readInt32()
|
||||||
var _6: Int32?
|
var _6: Int32?
|
||||||
_6 = reader.readInt32()
|
_6 = reader.readInt32()
|
||||||
|
var _7: [Api.User]?
|
||||||
|
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||||
|
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||||
|
} }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
|
||||||
return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||||
|
return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!, winners: _7)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -136,7 +136,7 @@ public final class CallController: ViewController {
|
|||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
if self.sharedContext.immediateExperimentalUISettings.callUIV2 {
|
if self.sharedContext.immediateExperimentalUISettings.callUIV2 {
|
||||||
self.displayNode = CallControllerNodeV2(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
|
self.displayNode = CallControllerNodeV2(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), easyDebugAccess: self.easyDebugAccess, call: self.call)
|
||||||
} else {
|
} else {
|
||||||
self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
|
self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
private let callScreen: PrivateCallScreen
|
private let callScreen: PrivateCallScreen
|
||||||
private var callScreenState: PrivateCallScreen.State?
|
private var callScreenState: PrivateCallScreen.State?
|
||||||
|
|
||||||
private var shouldStayHiddenUntilConnection: Bool = false
|
|
||||||
|
|
||||||
private var callStartTimestamp: Double?
|
private var callStartTimestamp: Double?
|
||||||
|
|
||||||
private var callState: PresentationCallState?
|
private var callState: PresentationCallState?
|
||||||
@ -67,7 +65,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
presentationData: PresentationData,
|
presentationData: PresentationData,
|
||||||
statusBar: StatusBar,
|
statusBar: StatusBar,
|
||||||
debugInfo: Signal<(String, String), NoError>,
|
debugInfo: Signal<(String, String), NoError>,
|
||||||
shouldStayHiddenUntilConnection: Bool = false,
|
|
||||||
easyDebugAccess: Bool,
|
easyDebugAccess: Bool,
|
||||||
call: PresentationCall
|
call: PresentationCall
|
||||||
) {
|
) {
|
||||||
@ -80,8 +77,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
self.containerView = UIView()
|
self.containerView = UIView()
|
||||||
self.callScreen = PrivateCallScreen()
|
self.callScreen = PrivateCallScreen()
|
||||||
|
|
||||||
self.shouldStayHiddenUntilConnection = shouldStayHiddenUntilConnection
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.view.addSubview(self.containerView)
|
self.view.addSubview(self.containerView)
|
||||||
@ -123,6 +118,12 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
}
|
}
|
||||||
self.back?()
|
self.back?()
|
||||||
}
|
}
|
||||||
|
self.callScreen.closeAction = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.dismissedInteractively?()
|
||||||
|
}
|
||||||
|
|
||||||
self.callScreenState = PrivateCallScreen.State(
|
self.callScreenState = PrivateCallScreen.State(
|
||||||
lifecycleState: .connecting,
|
lifecycleState: .connecting,
|
||||||
@ -130,7 +131,8 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
shortName: " ",
|
shortName: " ",
|
||||||
avatarImage: nil,
|
avatarImage: nil,
|
||||||
audioOutput: .internalSpeaker,
|
audioOutput: .internalSpeaker,
|
||||||
isMicrophoneMuted: false,
|
isLocalAudioMuted: false,
|
||||||
|
isRemoteAudioMuted: false,
|
||||||
localVideo: nil,
|
localVideo: nil,
|
||||||
remoteVideo: nil,
|
remoteVideo: nil,
|
||||||
isRemoteBatteryLow: false
|
isRemoteBatteryLow: false
|
||||||
@ -145,8 +147,8 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.isMuted = isMuted
|
self.isMuted = isMuted
|
||||||
if callScreenState.isMicrophoneMuted != isMuted {
|
if callScreenState.isLocalAudioMuted != isMuted {
|
||||||
callScreenState.isMicrophoneMuted = isMuted
|
callScreenState.isLocalAudioMuted = isMuted
|
||||||
self.callScreenState = callScreenState
|
self.callScreenState = callScreenState
|
||||||
self.update(transition: .animated(duration: 0.3, curve: .spring))
|
self.update(transition: .animated(duration: 0.3, curve: .spring))
|
||||||
}
|
}
|
||||||
@ -373,6 +375,13 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
callScreenState.isRemoteBatteryLow = false
|
callScreenState.isRemoteBatteryLow = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch callState.remoteAudioState {
|
||||||
|
case .muted:
|
||||||
|
callScreenState.isRemoteAudioMuted = true
|
||||||
|
case .active:
|
||||||
|
callScreenState.isRemoteAudioMuted = false
|
||||||
|
}
|
||||||
|
|
||||||
if self.callScreenState != callScreenState {
|
if self.callScreenState != callScreenState {
|
||||||
self.callScreenState = callScreenState
|
self.callScreenState = callScreenState
|
||||||
self.update(transition: .animated(duration: 0.35, curve: .spring))
|
self.update(transition: .animated(duration: 0.35, curve: .spring))
|
||||||
@ -393,6 +402,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
callScreenState.name = peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)
|
callScreenState.name = peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)
|
||||||
|
callScreenState.shortName = peer.compactDisplayTitle
|
||||||
|
|
||||||
if self.currentPeer?.smallProfileImage != peer.smallProfileImage {
|
if self.currentPeer?.smallProfileImage != peer.smallProfileImage {
|
||||||
self.peerAvatarDisposable?.dispose()
|
self.peerAvatarDisposable?.dispose()
|
||||||
@ -460,16 +470,14 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
self.containerView.layer.removeAnimation(forKey: "scale")
|
self.containerView.layer.removeAnimation(forKey: "scale")
|
||||||
self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
|
||||||
if !self.shouldStayHiddenUntilConnection {
|
self.containerView.layer.animateScale(from: 1.04, to: 1.0, duration: 0.3)
|
||||||
self.containerView.layer.animateScale(from: 1.04, to: 1.0, duration: 0.3)
|
self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateOut(completion: @escaping () -> Void) {
|
func animateOut(completion: @escaping () -> Void) {
|
||||||
self.statusBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
self.statusBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||||
if !self.shouldStayHiddenUntilConnection || self.containerView.alpha > 0.0 {
|
if self.containerView.alpha > 0.0 {
|
||||||
self.containerView.layer.allowsGroupOpacity = true
|
self.containerView.layer.allowsGroupOpacity = true
|
||||||
self.containerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
|
self.containerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
|
||||||
self?.containerView.layer.allowsGroupOpacity = false
|
self?.containerView.layer.allowsGroupOpacity = false
|
||||||
@ -499,7 +507,14 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
transition.updateFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: layout.size))
|
transition.updateFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
transition.updateFrame(view: self.callScreen, frame: CGRect(origin: CGPoint(), size: layout.size))
|
transition.updateFrame(view: self.callScreen, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
|
|
||||||
if let callScreenState = self.callScreenState {
|
if var callScreenState = self.callScreenState {
|
||||||
|
if case .terminated = callScreenState.lifecycleState {
|
||||||
|
callScreenState.isLocalAudioMuted = false
|
||||||
|
callScreenState.isRemoteAudioMuted = false
|
||||||
|
callScreenState.isRemoteBatteryLow = false
|
||||||
|
callScreenState.localVideo = nil
|
||||||
|
callScreenState.remoteVideo = nil
|
||||||
|
}
|
||||||
self.callScreen.update(
|
self.callScreen.update(
|
||||||
size: layout.size,
|
size: layout.size,
|
||||||
insets: layout.insets(options: [.statusBar]),
|
insets: layout.insets(options: [.statusBar]),
|
||||||
|
@ -422,12 +422,15 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
|||||||
case let .messageMediaStory(flags, peerId, id, _):
|
case let .messageMediaStory(flags, peerId, id, _):
|
||||||
let isMention = (flags & (1 << 1)) != 0
|
let isMention = (flags & (1 << 1)) != 0
|
||||||
return (TelegramMediaStory(storyId: StoryId(peerId: peerId.peerId, id: id), isMention: isMention), nil, nil, nil, nil)
|
return (TelegramMediaStory(storyId: StoryId(peerId: peerId.peerId, id: id), isMention: isMention), nil, nil, nil, nil)
|
||||||
case let .messageMediaGiveaway(apiFlags, channels, countries, quantity, months, untilDate):
|
case let .messageMediaGiveaway(apiFlags, channels, countries, prizeDescription, quantity, months, untilDate):
|
||||||
var flags: TelegramMediaGiveaway.Flags = []
|
var flags: TelegramMediaGiveaway.Flags = []
|
||||||
if (apiFlags & (1 << 0)) != 0 {
|
if (apiFlags & (1 << 0)) != 0 {
|
||||||
flags.insert(.onlyNewSubscribers)
|
flags.insert(.onlyNewSubscribers)
|
||||||
}
|
}
|
||||||
return (TelegramMediaGiveaway(flags: flags, channelPeerIds: channels.map { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, countries: countries ?? [], quantity: quantity, months: months, untilDate: untilDate), nil, nil, nil, nil)
|
if (apiFlags & (1 << 2)) != 0 {
|
||||||
|
flags.insert(.showWinners)
|
||||||
|
}
|
||||||
|
return (TelegramMediaGiveaway(flags: flags, channelPeerIds: channels.map { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, countries: countries ?? [], quantity: quantity, months: months, untilDate: untilDate, prizeDescription: prizeDescription), nil, nil, nil, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||||||
|
|
||||||
public class Serialization: NSObject, MTSerialization {
|
public class Serialization: NSObject, MTSerialization {
|
||||||
public func currentLayer() -> UInt {
|
public func currentLayer() -> UInt {
|
||||||
return 167
|
return 168
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseMessage(_ data: Data!) -> Any! {
|
public func parseMessage(_ data: Data!) -> Any! {
|
||||||
|
@ -9,6 +9,7 @@ public final class TelegramMediaGiveaway: Media, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static let onlyNewSubscribers = Flags(rawValue: 1 << 0)
|
public static let onlyNewSubscribers = Flags(rawValue: 1 << 0)
|
||||||
|
public static let showWinners = Flags(rawValue: 1 << 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var id: MediaId? {
|
public var id: MediaId? {
|
||||||
@ -24,14 +25,16 @@ public final class TelegramMediaGiveaway: Media, Equatable {
|
|||||||
public let quantity: Int32
|
public let quantity: Int32
|
||||||
public let months: Int32
|
public let months: Int32
|
||||||
public let untilDate: Int32
|
public let untilDate: Int32
|
||||||
|
public let prizeDescription: String?
|
||||||
public init(flags: Flags, channelPeerIds: [PeerId], countries: [String], quantity: Int32, months: Int32, untilDate: Int32) {
|
|
||||||
|
public init(flags: Flags, channelPeerIds: [PeerId], countries: [String], quantity: Int32, months: Int32, untilDate: Int32, prizeDescription: String?) {
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.channelPeerIds = channelPeerIds
|
self.channelPeerIds = channelPeerIds
|
||||||
self.countries = countries
|
self.countries = countries
|
||||||
self.quantity = quantity
|
self.quantity = quantity
|
||||||
self.months = months
|
self.months = months
|
||||||
self.untilDate = untilDate
|
self.untilDate = untilDate
|
||||||
|
self.prizeDescription = prizeDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -41,6 +44,7 @@ public final class TelegramMediaGiveaway: Media, Equatable {
|
|||||||
self.quantity = decoder.decodeInt32ForKey("qty", orElse: 0)
|
self.quantity = decoder.decodeInt32ForKey("qty", orElse: 0)
|
||||||
self.months = decoder.decodeInt32ForKey("mts", orElse: 0)
|
self.months = decoder.decodeInt32ForKey("mts", orElse: 0)
|
||||||
self.untilDate = decoder.decodeInt32ForKey("unt", orElse: 0)
|
self.untilDate = decoder.decodeInt32ForKey("unt", orElse: 0)
|
||||||
|
self.prizeDescription = decoder.decodeOptionalStringForKey("des")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -50,6 +54,11 @@ public final class TelegramMediaGiveaway: Media, Equatable {
|
|||||||
encoder.encodeInt32(self.quantity, forKey: "qty")
|
encoder.encodeInt32(self.quantity, forKey: "qty")
|
||||||
encoder.encodeInt32(self.months, forKey: "mts")
|
encoder.encodeInt32(self.months, forKey: "mts")
|
||||||
encoder.encodeInt32(self.untilDate, forKey: "unt")
|
encoder.encodeInt32(self.untilDate, forKey: "unt")
|
||||||
|
if let prizeDescription = self.prizeDescription {
|
||||||
|
encoder.encodeString(prizeDescription, forKey: "des")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "des")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isLikelyToBeUpdated() -> Bool {
|
public func isLikelyToBeUpdated() -> Bool {
|
||||||
@ -78,6 +87,9 @@ public final class TelegramMediaGiveaway: Media, Equatable {
|
|||||||
if self.untilDate != other.untilDate {
|
if self.untilDate != other.untilDate {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if self.prizeDescription != other.prizeDescription {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ public enum AppStoreTransactionPurpose {
|
|||||||
case restore
|
case restore
|
||||||
case gift(peerId: EnginePeer.Id, currency: String, amount: Int64)
|
case gift(peerId: EnginePeer.Id, currency: String, amount: Int64)
|
||||||
case giftCode(peerIds: [EnginePeer.Id], boostPeer: EnginePeer.Id?, currency: String, amount: Int64)
|
case giftCode(peerIds: [EnginePeer.Id], boostPeer: EnginePeer.Id?, currency: String, amount: Int64)
|
||||||
case giveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32, currency: String, amount: Int64)
|
case giveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTransactionPurpose) -> Signal<Api.InputStorePaymentPurpose, NoError> {
|
private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTransactionPurpose) -> Signal<Api.InputStorePaymentPurpose, NoError> {
|
||||||
@ -59,7 +59,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
|||||||
|
|
||||||
return .inputStorePaymentPremiumGiftCode(flags: flags, users: apiInputUsers, boostPeer: apiBoostPeer, currency: currency, amount: amount)
|
return .inputStorePaymentPremiumGiftCode(flags: flags, users: apiInputUsers, boostPeer: apiBoostPeer, currency: currency, amount: amount)
|
||||||
}
|
}
|
||||||
case let .giveaway(boostPeerId, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate, currency, amount):
|
case let .giveaway(boostPeerId, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, currency, amount):
|
||||||
return account.postbox.transaction { transaction -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
return account.postbox.transaction { transaction -> Signal<Api.InputStorePaymentPurpose, NoError> in
|
||||||
guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else {
|
guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else {
|
||||||
return .complete()
|
return .complete()
|
||||||
@ -68,6 +68,9 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
|||||||
if onlyNewSubscribers {
|
if onlyNewSubscribers {
|
||||||
flags |= (1 << 0)
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
|
if showWinners {
|
||||||
|
flags |= (1 << 0)
|
||||||
|
}
|
||||||
var additionalPeers: [Api.InputPeer] = []
|
var additionalPeers: [Api.InputPeer] = []
|
||||||
if !additionalPeerIds.isEmpty {
|
if !additionalPeerIds.isEmpty {
|
||||||
flags |= (1 << 1)
|
flags |= (1 << 1)
|
||||||
@ -80,7 +83,10 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran
|
|||||||
if !countries.isEmpty {
|
if !countries.isEmpty {
|
||||||
flags |= (1 << 2)
|
flags |= (1 << 2)
|
||||||
}
|
}
|
||||||
return .single(.inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: apiBoostPeer, additionalPeers: additionalPeers, countriesIso2: countries, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount))
|
if let _ = prizeDescription {
|
||||||
|
flags |= (1 << 4)
|
||||||
|
}
|
||||||
|
return .single(.inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: apiBoostPeer, additionalPeers: additionalPeers, countriesIso2: countries, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount))
|
||||||
}
|
}
|
||||||
|> switchToLatest
|
|> switchToLatest
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import TelegramApi
|
|||||||
public enum BotPaymentInvoiceSource {
|
public enum BotPaymentInvoiceSource {
|
||||||
case message(MessageId)
|
case message(MessageId)
|
||||||
case slug(String)
|
case slug(String)
|
||||||
case premiumGiveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, option: PremiumGiftCodeOption)
|
case premiumGiveaway(boostPeer: EnginePeer.Id, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32, currency: String, amount: Int64, option: PremiumGiftCodeOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ private func _internal_parseInputInvoice(transaction: Transaction, source: BotPa
|
|||||||
return .inputInvoiceMessage(peer: inputPeer, msgId: messageId.id)
|
return .inputInvoiceMessage(peer: inputPeer, msgId: messageId.id)
|
||||||
case let .slug(slug):
|
case let .slug(slug):
|
||||||
return .inputInvoiceSlug(slug: slug)
|
return .inputInvoiceSlug(slug: slug)
|
||||||
case let .premiumGiveaway(boostPeerId, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate, currency, amount, option):
|
case let .premiumGiveaway(boostPeerId, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate, currency, amount, option):
|
||||||
guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else {
|
guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -222,6 +222,9 @@ private func _internal_parseInputInvoice(transaction: Transaction, source: BotPa
|
|||||||
if onlyNewSubscribers {
|
if onlyNewSubscribers {
|
||||||
flags |= (1 << 0)
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
|
if showWinners {
|
||||||
|
flags |= (1 << 3)
|
||||||
|
}
|
||||||
var additionalPeers: [Api.InputPeer] = []
|
var additionalPeers: [Api.InputPeer] = []
|
||||||
if !additionalPeerIds.isEmpty {
|
if !additionalPeerIds.isEmpty {
|
||||||
flags |= (1 << 1)
|
flags |= (1 << 1)
|
||||||
@ -234,7 +237,11 @@ private func _internal_parseInputInvoice(transaction: Transaction, source: BotPa
|
|||||||
if !countries.isEmpty {
|
if !countries.isEmpty {
|
||||||
flags |= (1 << 2)
|
flags |= (1 << 2)
|
||||||
}
|
}
|
||||||
let input: Api.InputStorePaymentPurpose = .inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: apiBoostPeer, additionalPeers: additionalPeers, countriesIso2: countries, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount)
|
if let _ = prizeDescription {
|
||||||
|
flags |= (1 << 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputPurpose: Api.InputStorePaymentPurpose = .inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: apiBoostPeer, additionalPeers: additionalPeers, countriesIso2: countries, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount)
|
||||||
|
|
||||||
flags = 0
|
flags = 0
|
||||||
|
|
||||||
@ -247,7 +254,7 @@ private func _internal_parseInputInvoice(transaction: Transaction, source: BotPa
|
|||||||
|
|
||||||
let option: Api.PremiumGiftCodeOption = .premiumGiftCodeOption(flags: flags, users: option.users, months: option.months, storeProduct: option.storeProductId, storeQuantity: option.storeQuantity, currency: option.currency, amount: option.amount)
|
let option: Api.PremiumGiftCodeOption = .premiumGiftCodeOption(flags: flags, users: option.users, months: option.months, storeProduct: option.storeProductId, storeQuantity: option.storeQuantity, currency: option.currency, amount: option.amount)
|
||||||
|
|
||||||
return .inputInvoicePremiumGiftCode(purpose: input, option: option)
|
return .inputInvoicePremiumGiftCode(purpose: inputPurpose, option: option)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,7 +533,7 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .premiumGiveaway(_, _, _, _, randomId, _, _, _, _):
|
case let .premiumGiveaway(_, _, _, _, _, _, randomId, _, _, _, _):
|
||||||
if message.globallyUniqueId == randomId {
|
if message.globallyUniqueId == randomId {
|
||||||
if case let .Id(id) = message.id {
|
if case let .Id(id) = message.id {
|
||||||
receiptMessageId = id
|
receiptMessageId = id
|
||||||
|
@ -120,7 +120,7 @@ func _internal_getPremiumGiveawayInfo(account: Account, peerId: EnginePeer.Id, m
|
|||||||
} else {
|
} else {
|
||||||
return .ongoing(startDate: startDate, status: .notQualified)
|
return .ongoing(startDate: startDate, status: .notQualified)
|
||||||
}
|
}
|
||||||
case let .giveawayInfoResults(flags, startDate, giftCodeSlug, finishDate, winnersCount, activatedCount):
|
case let .giveawayInfoResults(flags, startDate, giftCodeSlug, finishDate, winnersCount, activatedCount, _):
|
||||||
let status: PremiumGiveawayInfo.ResultStatus
|
let status: PremiumGiveawayInfo.ResultStatus
|
||||||
if (flags & (1 << 1)) != 0 {
|
if (flags & (1 << 1)) != 0 {
|
||||||
status = .refunded
|
status = .refunded
|
||||||
@ -201,18 +201,19 @@ public enum LaunchPrepaidGiveawayError {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal<Never, LaunchPrepaidGiveawayError> {
|
func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32) -> Signal<Never, LaunchPrepaidGiveawayError> {
|
||||||
return account.postbox.transaction { transaction -> Signal<Never, LaunchPrepaidGiveawayError> in
|
return account.postbox.transaction { transaction -> Signal<Never, LaunchPrepaidGiveawayError> in
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if onlyNewSubscribers {
|
if onlyNewSubscribers {
|
||||||
flags |= (1 << 0)
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
|
if showWinners {
|
||||||
|
flags |= (1 << 3)
|
||||||
|
}
|
||||||
var inputPeer: Api.InputPeer?
|
var inputPeer: Api.InputPeer?
|
||||||
if let peer = transaction.getPeer(peerId), let apiPeer = apiInputPeer(peer) {
|
if let peer = transaction.getPeer(peerId), let apiPeer = apiInputPeer(peer) {
|
||||||
inputPeer = apiPeer
|
inputPeer = apiPeer
|
||||||
}
|
}
|
||||||
|
|
||||||
var additionalPeers: [Api.InputPeer] = []
|
var additionalPeers: [Api.InputPeer] = []
|
||||||
if !additionalPeerIds.isEmpty {
|
if !additionalPeerIds.isEmpty {
|
||||||
flags |= (1 << 1)
|
flags |= (1 << 1)
|
||||||
@ -222,15 +223,16 @@ func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !countries.isEmpty {
|
if !countries.isEmpty {
|
||||||
flags |= (1 << 2)
|
flags |= (1 << 2)
|
||||||
}
|
}
|
||||||
|
if let _ = prizeDescription {
|
||||||
|
flags |= (1 << 4)
|
||||||
|
}
|
||||||
guard let inputPeer = inputPeer else {
|
guard let inputPeer = inputPeer else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.payments.launchPrepaidGiveaway(peer: inputPeer, giveawayId: id, purpose: .inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: inputPeer, additionalPeers: additionalPeers, countriesIso2: countries, randomId: randomId, untilDate: untilDate, currency: "", amount: 0)))
|
return account.network.request(Api.functions.payments.launchPrepaidGiveaway(peer: inputPeer, giveawayId: id, purpose: .inputStorePaymentPremiumGiveaway(flags: flags, boostPeer: inputPeer, additionalPeers: additionalPeers, countriesIso2: countries, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: "", amount: 0)))
|
||||||
|> mapError { _ -> LaunchPrepaidGiveawayError in
|
|> mapError { _ -> LaunchPrepaidGiveawayError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,8 @@ public extension TelegramEngine {
|
|||||||
return _internal_getPremiumGiveawayInfo(account: self.account, peerId: peerId, messageId: messageId)
|
return _internal_getPremiumGiveawayInfo(account: self.account, peerId: peerId, messageId: messageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func launchPrepaidGiveaway(peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal<Never, LaunchPrepaidGiveawayError> {
|
public func launchPrepaidGiveaway(peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32) -> Signal<Never, LaunchPrepaidGiveawayError> {
|
||||||
return _internal_launchPrepaidGiveaway(account: self.account, peerId: peerId, id: id, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate)
|
return _internal_launchPrepaidGiveaway(account: self.account, peerId: peerId, id: id, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,11 @@ import Postbox
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramApi
|
import TelegramApi
|
||||||
import MtProtoKit
|
import MtProtoKit
|
||||||
|
import LinkPresentation
|
||||||
|
#if os(iOS)
|
||||||
|
import UIKit
|
||||||
|
#endif
|
||||||
|
import CoreServices
|
||||||
|
|
||||||
public enum WebpagePreviewResult: Equatable {
|
public enum WebpagePreviewResult: Equatable {
|
||||||
public struct Result: Equatable {
|
public struct Result: Equatable {
|
||||||
@ -15,8 +19,8 @@ public enum WebpagePreviewResult: Equatable {
|
|||||||
case result(Result?)
|
case result(Result?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func webpagePreview(account: Account, urls: [String], webpageId: MediaId? = nil) -> Signal<WebpagePreviewResult, NoError> {
|
public func webpagePreview(account: Account, urls: [String], webpageId: MediaId? = nil, forPeerId: PeerId? = nil) -> Signal<WebpagePreviewResult, NoError> {
|
||||||
return webpagePreviewWithProgress(account: account, urls: urls)
|
return webpagePreviewWithProgress(account: account, urls: urls, webpageId: webpageId, forPeerId: forPeerId)
|
||||||
|> mapToSignal { next -> Signal<WebpagePreviewResult, NoError> in
|
|> mapToSignal { next -> Signal<WebpagePreviewResult, NoError> in
|
||||||
if case let .result(result) = next {
|
if case let .result(result) = next {
|
||||||
return .single(.result(result))
|
return .single(.result(result))
|
||||||
@ -35,7 +39,7 @@ public func normalizedWebpagePreviewUrl(url: String) -> String {
|
|||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
public func webpagePreviewWithProgress(account: Account, urls: [String], webpageId: MediaId? = nil) -> Signal<WebpagePreviewWithProgressResult, NoError> {
|
public func webpagePreviewWithProgress(account: Account, urls: [String], webpageId: MediaId? = nil, forPeerId: PeerId? = nil) -> Signal<WebpagePreviewWithProgressResult, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Signal<WebpagePreviewWithProgressResult, NoError> in
|
return account.postbox.transaction { transaction -> Signal<WebpagePreviewWithProgressResult, NoError> in
|
||||||
if let webpageId = webpageId, let webpage = transaction.getMedia(webpageId) as? TelegramMediaWebpage, let url = webpage.content.url {
|
if let webpageId = webpageId, let webpage = transaction.getMedia(webpageId) as? TelegramMediaWebpage, let url = webpage.content.url {
|
||||||
var sourceUrl = url
|
var sourceUrl = url
|
||||||
@ -44,6 +48,108 @@ public func webpagePreviewWithProgress(account: Account, urls: [String], webpage
|
|||||||
}
|
}
|
||||||
return .single(.result(WebpagePreviewResult.Result(webpage: webpage, sourceUrl: sourceUrl)))
|
return .single(.result(WebpagePreviewResult.Result(webpage: webpage, sourceUrl: sourceUrl)))
|
||||||
} else {
|
} else {
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
if let forPeerId, forPeerId.namespace == Namespaces.Peer.SecretChat, let sourceUrl = urls.first, let url = URL(string: sourceUrl) {
|
||||||
|
let localHosts: [String] = [
|
||||||
|
"twitter.com",
|
||||||
|
"www.twitter.com",
|
||||||
|
"instagram.com",
|
||||||
|
"www.instagram.com",
|
||||||
|
"tiktok.com",
|
||||||
|
"www.tiktok.com"
|
||||||
|
]
|
||||||
|
if let host = url.host?.lowercased(), localHosts.contains(host) {
|
||||||
|
return Signal { subscriber in
|
||||||
|
subscriber.putNext(.progress(0.0))
|
||||||
|
|
||||||
|
let metadataProvider = LPMetadataProvider()
|
||||||
|
metadataProvider.shouldFetchSubresources = true
|
||||||
|
metadataProvider.startFetchingMetadata(for: url, completionHandler: { metadata, _ in
|
||||||
|
if let metadata = metadata {
|
||||||
|
let completeWithImage: (Data?) -> Void = { imageData in
|
||||||
|
var image: TelegramMediaImage?
|
||||||
|
if let imageData, let parsedImage = UIImage(data: imageData) {
|
||||||
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||||
|
account.postbox.mediaBox.storeResourceData(resource.id, data: imageData)
|
||||||
|
image = TelegramMediaImage(
|
||||||
|
imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)),
|
||||||
|
representations: [
|
||||||
|
TelegramMediaImageRepresentation(
|
||||||
|
dimensions: PixelDimensions(width: Int32(parsedImage.size.width), height: Int32(parsedImage.size.height)),
|
||||||
|
resource: resource,
|
||||||
|
progressiveSizes: [],
|
||||||
|
immediateThumbnailData: nil,
|
||||||
|
hasVideo: false,
|
||||||
|
isPersonal: false
|
||||||
|
)
|
||||||
|
],
|
||||||
|
immediateThumbnailData: nil,
|
||||||
|
reference: nil,
|
||||||
|
partialReference: nil,
|
||||||
|
flags: []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var webpageType: String?
|
||||||
|
if image != nil {
|
||||||
|
webpageType = "photo"
|
||||||
|
}
|
||||||
|
|
||||||
|
let webpage = TelegramMediaWebpage(
|
||||||
|
webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: Int64.random(in: Int64.min ... Int64.max)),
|
||||||
|
content: .Loaded(TelegramMediaWebpageLoadedContent(
|
||||||
|
url: sourceUrl,
|
||||||
|
displayUrl: metadata.url?.absoluteString ?? sourceUrl,
|
||||||
|
hash: 0,
|
||||||
|
type: webpageType,
|
||||||
|
websiteName: nil,
|
||||||
|
title: metadata.title,
|
||||||
|
text: metadata.value(forKey: "_summary") as? String,
|
||||||
|
embedUrl: nil,
|
||||||
|
embedType: nil,
|
||||||
|
embedSize: nil,
|
||||||
|
duration: nil,
|
||||||
|
author: nil,
|
||||||
|
isMediaLargeByDefault: true,
|
||||||
|
image: image,
|
||||||
|
file: nil,
|
||||||
|
story: nil,
|
||||||
|
attributes: [],
|
||||||
|
instantPage: nil
|
||||||
|
))
|
||||||
|
)
|
||||||
|
subscriber.putNext(.result(WebpagePreviewResult.Result(
|
||||||
|
webpage: webpage,
|
||||||
|
sourceUrl: sourceUrl
|
||||||
|
)))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let imageProvider = metadata.imageProvider {
|
||||||
|
imageProvider.loadFileRepresentation(forTypeIdentifier: kUTTypeImage as String, completionHandler: { imageUrl, _ in
|
||||||
|
guard let imageUrl, let imageData = try? Data(contentsOf: imageUrl) else {
|
||||||
|
completeWithImage(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completeWithImage(imageData)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
completeWithImage(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subscriber.putNext(.result(nil))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
metadataProvider.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return account.network.requestWithAdditionalInfo(Api.functions.messages.getWebPagePreview(flags: 0, message: urls.joined(separator: " "), entities: nil), info: .progress)
|
return account.network.requestWithAdditionalInfo(Api.functions.messages.getWebPagePreview(flags: 0, message: urls.joined(separator: " "), entities: nil), info: .progress)
|
||||||
|> `catch` { _ -> Signal<NetworkRequestResult<Api.MessageMedia>, NoError> in
|
|> `catch` { _ -> Signal<NetworkRequestResult<Api.MessageMedia>, NoError> in
|
||||||
return .single(.result(.messageMediaEmpty))
|
return .single(.result(.messageMediaEmpty))
|
||||||
|
@ -67,6 +67,7 @@ swift_library(
|
|||||||
"//submodules/SSignalKit/SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit",
|
||||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||||
"//submodules/AppBundle",
|
"//submodules/AppBundle",
|
||||||
|
"//submodules/UIKitRuntimeUtils",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -58,8 +58,10 @@ final class ButtonGroupView: OverlayMaskContainerView {
|
|||||||
|
|
||||||
private var buttons: [Button]?
|
private var buttons: [Button]?
|
||||||
private var buttonViews: [Button.Content.Key: ContentOverlayButton] = [:]
|
private var buttonViews: [Button.Content.Key: ContentOverlayButton] = [:]
|
||||||
|
|
||||||
private var noticeViews: [AnyHashable: NoticeView] = [:]
|
private var noticeViews: [AnyHashable: NoticeView] = [:]
|
||||||
|
private var closeButtonView: CloseButtonView?
|
||||||
|
|
||||||
|
var closePressed: (() -> Void)?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
@ -79,7 +81,7 @@ final class ButtonGroupView: OverlayMaskContainerView {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(size: CGSize, insets: UIEdgeInsets, controlsHidden: Bool, buttons: [Button], notices: [Notice], transition: Transition) -> CGFloat {
|
func update(size: CGSize, insets: UIEdgeInsets, minWidth: CGFloat, controlsHidden: Bool, displayClose: Bool, buttons: [Button], notices: [Notice], transition: Transition) -> CGFloat {
|
||||||
self.buttons = buttons
|
self.buttons = buttons
|
||||||
|
|
||||||
let buttonSize: CGFloat = 56.0
|
let buttonSize: CGFloat = 56.0
|
||||||
@ -163,6 +165,46 @@ final class ButtonGroupView: OverlayMaskContainerView {
|
|||||||
}
|
}
|
||||||
var buttonX: CGFloat = floor((size.width - buttonSize * CGFloat(buttons.count) - buttonSpacing * CGFloat(buttons.count - 1)) * 0.5)
|
var buttonX: CGFloat = floor((size.width - buttonSize * CGFloat(buttons.count) - buttonSpacing * CGFloat(buttons.count - 1)) * 0.5)
|
||||||
|
|
||||||
|
if displayClose {
|
||||||
|
let closeButtonView: CloseButtonView
|
||||||
|
var closeButtonTransition = transition
|
||||||
|
var animateIn = false
|
||||||
|
if let current = self.closeButtonView {
|
||||||
|
closeButtonView = current
|
||||||
|
} else {
|
||||||
|
closeButtonTransition = closeButtonTransition.withAnimation(.none)
|
||||||
|
animateIn = true
|
||||||
|
|
||||||
|
closeButtonView = CloseButtonView()
|
||||||
|
self.closeButtonView = closeButtonView
|
||||||
|
self.addSubview(closeButtonView)
|
||||||
|
closeButtonView.pressAction = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.closePressed?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let closeButtonSize = CGSize(width: minWidth, height: buttonSize)
|
||||||
|
closeButtonView.update(text: "Close", size: closeButtonSize, transition: closeButtonTransition)
|
||||||
|
closeButtonTransition.setFrame(view: closeButtonView, frame: CGRect(origin: CGPoint(x: floor((size.width - closeButtonSize.width) * 0.5), y: buttonY), size: closeButtonSize))
|
||||||
|
|
||||||
|
if animateIn && !transition.animation.isImmediate {
|
||||||
|
closeButtonView.animateIn()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let closeButtonView = self.closeButtonView {
|
||||||
|
self.closeButtonView = nil
|
||||||
|
if !transition.animation.isImmediate {
|
||||||
|
closeButtonView.animateOut(completion: { [weak closeButtonView] in
|
||||||
|
closeButtonView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
closeButtonView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for button in buttons {
|
for button in buttons {
|
||||||
let title: String
|
let title: String
|
||||||
let image: UIImage?
|
let image: UIImage?
|
||||||
@ -213,9 +255,10 @@ final class ButtonGroupView: OverlayMaskContainerView {
|
|||||||
Transition.immediate.setScale(view: buttonView, scale: 0.001)
|
Transition.immediate.setScale(view: buttonView, scale: 0.001)
|
||||||
buttonView.alpha = 0.0
|
buttonView.alpha = 0.0
|
||||||
transition.setScale(view: buttonView, scale: 1.0)
|
transition.setScale(view: buttonView, scale: 1.0)
|
||||||
transition.setAlpha(view: buttonView, alpha: 1.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transition.setAlpha(view: buttonView, alpha: displayClose ? 0.0 : 1.0)
|
||||||
|
|
||||||
buttonTransition.setFrame(view: buttonView, frame: CGRect(origin: CGPoint(x: buttonX, y: buttonY), size: CGSize(width: buttonSize, height: buttonSize)))
|
buttonTransition.setFrame(view: buttonView, frame: CGRect(origin: CGPoint(x: buttonX, y: buttonY), size: CGSize(width: buttonSize, height: buttonSize)))
|
||||||
buttonView.update(size: CGSize(width: buttonSize, height: buttonSize), image: image, isSelected: isActive, isDestructive: isDestructive, title: title, transition: buttonTransition)
|
buttonView.update(size: CGSize(width: buttonSize, height: buttonSize), image: image, isSelected: isActive, isDestructive: isDestructive, title: title, transition: buttonTransition)
|
||||||
buttonX += buttonSize + buttonSpacing
|
buttonX += buttonSize + buttonSpacing
|
||||||
|
@ -0,0 +1,185 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
import UIKitRuntimeUtils
|
||||||
|
|
||||||
|
final class CloseButtonView: HighlightTrackingButton, OverlayMaskContainerViewProtocol {
|
||||||
|
private struct Params: Equatable {
|
||||||
|
var text: String
|
||||||
|
var size: CGSize
|
||||||
|
|
||||||
|
init(text: String, size: CGSize) {
|
||||||
|
self.text = text
|
||||||
|
self.size = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let backdropBackgroundView: RoundedCornersView
|
||||||
|
private let backgroundView: RoundedCornersView
|
||||||
|
private let backgroundMaskView: UIView
|
||||||
|
private let backgroundClippingView: UIView
|
||||||
|
|
||||||
|
private let duration: Double = 5.0
|
||||||
|
private var fillTime: Double = 0.0
|
||||||
|
|
||||||
|
private let backgroundTextView: TextView
|
||||||
|
private let backgroundTextClippingView: UIView
|
||||||
|
|
||||||
|
private let textView: TextView
|
||||||
|
|
||||||
|
var pressAction: (() -> Void)?
|
||||||
|
|
||||||
|
private var params: Params?
|
||||||
|
private var updateDisplayLink: SharedDisplayLinkDriver.Link?
|
||||||
|
|
||||||
|
let maskContents: UIView
|
||||||
|
override static var layerClass: AnyClass {
|
||||||
|
return MirroringLayer.self
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.backdropBackgroundView = RoundedCornersView(color: .white, smoothCorners: true)
|
||||||
|
self.backdropBackgroundView.update(cornerRadius: 12.0, transition: .immediate)
|
||||||
|
|
||||||
|
self.backgroundView = RoundedCornersView(color: .white, smoothCorners: true)
|
||||||
|
self.backgroundView.update(cornerRadius: 12.0, transition: .immediate)
|
||||||
|
self.backgroundView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.backgroundMaskView = UIView()
|
||||||
|
self.backgroundMaskView.backgroundColor = .white
|
||||||
|
self.backgroundView.mask = self.backgroundMaskView
|
||||||
|
if let filter = makeLuminanceToAlphaFilter() {
|
||||||
|
self.backgroundMaskView.layer.filters = [filter]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.backgroundClippingView = UIView()
|
||||||
|
self.backgroundClippingView.clipsToBounds = true
|
||||||
|
self.backgroundClippingView.layer.cornerRadius = 12.0
|
||||||
|
|
||||||
|
self.backgroundTextClippingView = UIView()
|
||||||
|
self.backgroundTextClippingView.clipsToBounds = true
|
||||||
|
|
||||||
|
self.backgroundTextView = TextView()
|
||||||
|
self.textView = TextView()
|
||||||
|
|
||||||
|
self.maskContents = UIView()
|
||||||
|
|
||||||
|
self.maskContents.addSubview(self.backdropBackgroundView)
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
(self.layer as? MirroringLayer)?.targetLayer = self.maskContents.layer
|
||||||
|
|
||||||
|
self.backgroundTextClippingView.addSubview(self.backgroundTextView)
|
||||||
|
self.backgroundTextClippingView.isUserInteractionEnabled = false
|
||||||
|
self.addSubview(self.backgroundTextClippingView)
|
||||||
|
|
||||||
|
self.backgroundClippingView.addSubview(self.backgroundView)
|
||||||
|
self.backgroundClippingView.isUserInteractionEnabled = false
|
||||||
|
self.addSubview(self.backgroundClippingView)
|
||||||
|
|
||||||
|
self.backgroundMaskView.addSubview(self.textView)
|
||||||
|
|
||||||
|
self.internalHighligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self, self.bounds.width > 0.0 {
|
||||||
|
let topScale: CGFloat = (self.bounds.width - 8.0) / self.bounds.width
|
||||||
|
let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width
|
||||||
|
|
||||||
|
if highlighted {
|
||||||
|
self.layer.removeAnimation(forKey: "sublayerTransform")
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut))
|
||||||
|
transition.setScale(layer: self.layer, scale: topScale)
|
||||||
|
} else {
|
||||||
|
let t = self.layer.presentation()?.transform ?? layer.transform
|
||||||
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||||
|
|
||||||
|
let transition = Transition(animation: .none)
|
||||||
|
transition.setScale(layer: self.layer, scale: 1.0)
|
||||||
|
|
||||||
|
self.layer.animateScale(from: currentScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] completed in
|
||||||
|
guard let self, completed else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||||
|
|
||||||
|
(self.layer as? MirroringLayer)?.didEnterHierarchy = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.fillTime < self.duration && self.updateDisplayLink == nil {
|
||||||
|
self.updateDisplayLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.fillTime = min(self.duration, self.fillTime + deltaTime)
|
||||||
|
if let params = self.params {
|
||||||
|
self.update(params: params, transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.fillTime >= self.duration {
|
||||||
|
self.updateDisplayLink = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func pressed() {
|
||||||
|
self.pressAction?()
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateIn() {
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut(completion: @escaping () -> Void) {
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(text: String, size: CGSize, transition: Transition) {
|
||||||
|
let params = Params(text: text, size: size)
|
||||||
|
if self.params == params {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.params = params
|
||||||
|
self.update(params: params, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func update(params: Params, transition: Transition) {
|
||||||
|
let fillFraction: CGFloat = CGFloat(self.fillTime / self.duration)
|
||||||
|
|
||||||
|
let sideInset: CGFloat = 12.0
|
||||||
|
let textSize = self.textView.update(string: params.text, fontSize: 17.0, fontWeight: UIFont.Weight.semibold.rawValue, color: .black, constrainedWidth: params.size.width - sideInset * 2.0, transition: .immediate)
|
||||||
|
let _ = self.backgroundTextView.update(string: params.text, fontSize: 17.0, fontWeight: UIFont.Weight.semibold.rawValue, color: .white, constrainedWidth: params.size.width - sideInset * 2.0, transition: .immediate)
|
||||||
|
|
||||||
|
transition.setFrame(view: self.backdropBackgroundView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||||
|
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||||
|
transition.setFrame(view: self.backgroundMaskView, frame: CGRect(origin: CGPoint(), size: params.size))
|
||||||
|
|
||||||
|
let progressWidth: CGFloat = max(0.0, min(params.size.width, floorToScreenPixels(fillFraction * params.size.width)))
|
||||||
|
let backgroundClippingFrame = CGRect(origin: CGPoint(x: progressWidth, y: 0.0), size: CGSize(width: params.size.width - progressWidth, height: params.size.height))
|
||||||
|
transition.setPosition(view: self.backgroundClippingView, position: backgroundClippingFrame.center)
|
||||||
|
transition.setBounds(view: self.backgroundClippingView, bounds: CGRect(origin: CGPoint(x: backgroundClippingFrame.minX, y: 0.0), size: backgroundClippingFrame.size))
|
||||||
|
|
||||||
|
let backgroundTextClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: progressWidth, height: params.size.height))
|
||||||
|
transition.setPosition(view: self.backgroundTextClippingView, position: backgroundTextClippingFrame.center)
|
||||||
|
transition.setBounds(view: self.backgroundTextClippingView, bounds: CGRect(origin: CGPoint(), size: backgroundTextClippingFrame.size))
|
||||||
|
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: floor((params.size.width - textSize.width) * 0.5), y: floor((params.size.height - textSize.height) * 0.5)), size: textSize)
|
||||||
|
transition.setFrame(view: self.textView, frame: textFrame)
|
||||||
|
transition.setFrame(view: self.backgroundTextView, frame: textFrame)
|
||||||
|
}
|
||||||
|
}
|
@ -63,8 +63,7 @@ final class ContentOverlayButton: HighlightTrackingButton, OverlayMaskContainerV
|
|||||||
let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width
|
let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width
|
||||||
|
|
||||||
if highlighted {
|
if highlighted {
|
||||||
self.layer.removeAnimation(forKey: "opacity")
|
self.layer.removeAnimation(forKey: "sublayerTransform")
|
||||||
self.layer.removeAnimation(forKey: "transform")
|
|
||||||
let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut))
|
let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut))
|
||||||
transition.setScale(layer: self.layer, scale: topScale)
|
transition.setScale(layer: self.layer, scale: topScale)
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,10 +5,10 @@ import ComponentFlow
|
|||||||
|
|
||||||
final class EmojiExpandedInfoView: OverlayMaskContainerView {
|
final class EmojiExpandedInfoView: OverlayMaskContainerView {
|
||||||
private struct Params: Equatable {
|
private struct Params: Equatable {
|
||||||
var constrainedWidth: CGFloat
|
var width: CGFloat
|
||||||
|
|
||||||
init(constrainedWidth: CGFloat) {
|
init(width: CGFloat) {
|
||||||
self.constrainedWidth = constrainedWidth
|
self.width = width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,8 +129,8 @@ final class EmojiExpandedInfoView: OverlayMaskContainerView {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(constrainedWidth: CGFloat, transition: Transition) -> CGSize {
|
func update(width: CGFloat, transition: Transition) -> CGSize {
|
||||||
let params = Params(constrainedWidth: constrainedWidth)
|
let params = Params(width: width)
|
||||||
if let currentLayout = self.currentLayout, currentLayout.params == params {
|
if let currentLayout = self.currentLayout, currentLayout.params == params {
|
||||||
return currentLayout.size
|
return currentLayout.size
|
||||||
}
|
}
|
||||||
@ -142,16 +142,12 @@ final class EmojiExpandedInfoView: OverlayMaskContainerView {
|
|||||||
private func update(params: Params, transition: Transition) -> CGSize {
|
private func update(params: Params, transition: Transition) -> CGSize {
|
||||||
let buttonHeight: CGFloat = 56.0
|
let buttonHeight: CGFloat = 56.0
|
||||||
|
|
||||||
var constrainedWidth = params.constrainedWidth
|
let titleSize = self.titleView.update(string: self.title, fontSize: 16.0, fontWeight: 0.3, alignment: .center, color: .white, constrainedWidth: params.width - 16.0 * 2.0, transition: transition)
|
||||||
constrainedWidth = min(constrainedWidth, 300.0)
|
let textSize = self.textView.update(string: self.text, fontSize: 16.0, fontWeight: 0.0, alignment: .center, color: .white, constrainedWidth: params.width - 16.0 * 2.0, transition: transition)
|
||||||
|
|
||||||
let titleSize = self.titleView.update(string: self.title, fontSize: 16.0, fontWeight: 0.3, alignment: .center, color: .white, constrainedWidth: constrainedWidth - 16.0 * 2.0, transition: transition)
|
|
||||||
let textSize = self.textView.update(string: self.text, fontSize: 16.0, fontWeight: 0.0, alignment: .center, color: .white, constrainedWidth: constrainedWidth - 16.0 * 2.0, transition: transition)
|
|
||||||
|
|
||||||
let contentWidth: CGFloat = max(titleSize.width, textSize.width) + 26.0 * 2.0
|
|
||||||
let contentHeight = 78.0 + titleSize.height + 10.0 + textSize.height + 22.0 + buttonHeight
|
let contentHeight = 78.0 + titleSize.height + 10.0 + textSize.height + 22.0 + buttonHeight
|
||||||
|
|
||||||
let size = CGSize(width: contentWidth, height: contentHeight)
|
let size = CGSize(width: params.width, height: contentHeight)
|
||||||
|
|
||||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
@ -198,8 +198,8 @@ final class PrivateCallVideoLayer: MetalEngineSubjectLayer, MetalEngineSubject {
|
|||||||
|
|
||||||
encoder.setFragmentTexture(blurredTexture, index: 0)
|
encoder.setFragmentTexture(blurredTexture, index: 0)
|
||||||
|
|
||||||
var brightness: Float = 1.0
|
var brightness: Float = 0.7
|
||||||
var saturation: Float = 1.2
|
var saturation: Float = 1.3
|
||||||
var overlay: SIMD4<Float> = SIMD4<Float>(1.0, 1.0, 1.0, 0.2)
|
var overlay: SIMD4<Float> = SIMD4<Float>(1.0, 1.0, 1.0, 0.2)
|
||||||
encoder.setFragmentBytes(&brightness, length: 4, index: 0)
|
encoder.setFragmentBytes(&brightness, length: 4, index: 0)
|
||||||
encoder.setFragmentBytes(&saturation, length: 4, index: 1)
|
encoder.setFragmentBytes(&saturation, length: 4, index: 1)
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
|
||||||
|
final class RatingView: OverlayMaskContainerView {
|
||||||
|
private let backgroundView: RoundedCornersView
|
||||||
|
private let textContainer: UIView
|
||||||
|
private let textView: TextView
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.backgroundView = RoundedCornersView(color: .white)
|
||||||
|
self.textContainer = UIView()
|
||||||
|
self.textContainer.clipsToBounds = true
|
||||||
|
self.textView = TextView()
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.clipsToBounds = true
|
||||||
|
|
||||||
|
self.maskContents.addSubview(self.backgroundView)
|
||||||
|
|
||||||
|
self.textContainer.addSubview(self.textView)
|
||||||
|
self.addSubview(self.textContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateIn() {
|
||||||
|
let delay: Double = 0.2
|
||||||
|
|
||||||
|
self.layer.animateScale(from: 0.001, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
self.textView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: delay)
|
||||||
|
|
||||||
|
self.backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
self.backgroundView.layer.animateFrame(from: CGRect(origin: CGPoint(x: (self.bounds.width - self.bounds.height) * 0.5, y: 0.0), size: CGSize(width: self.bounds.height, height: self.bounds.height)), to: self.backgroundView.frame, duration: 0.5, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
|
self.textContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||||
|
self.textContainer.layer.cornerRadius = self.bounds.height * 0.5
|
||||||
|
self.textContainer.layer.animateFrame(from: CGRect(origin: CGPoint(x: (self.bounds.width - self.bounds.height) * 0.5, y: 0.0), size: CGSize(width: self.bounds.height, height: self.bounds.height)), to: self.textContainer.frame, duration: 0.5, delay: delay, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] completed in
|
||||||
|
guard let self, completed else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.textContainer.layer.cornerRadius = 0.0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut(completion: @escaping () -> Void) {
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
self.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(text: String, constrainedWidth: CGFloat, transition: Transition) -> CGSize {
|
||||||
|
let sideInset: CGFloat = 12.0
|
||||||
|
let verticalInset: CGFloat = 6.0
|
||||||
|
|
||||||
|
let textSize = self.textView.update(string: text, fontSize: 15.0, fontWeight: 0.0, color: .white, constrainedWidth: constrainedWidth - sideInset * 2.0, transition: .immediate)
|
||||||
|
let size = CGSize(width: textSize.width + sideInset * 2.0, height: textSize.height + verticalInset * 2.0)
|
||||||
|
|
||||||
|
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
self.backgroundView.update(cornerRadius: floor(size.height * 0.5), transition: transition)
|
||||||
|
|
||||||
|
transition.setFrame(view: self.textContainer, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
transition.setFrame(view: self.textView, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: textSize))
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
@ -5,17 +5,16 @@ import ComponentFlow
|
|||||||
|
|
||||||
final class RoundedCornersView: UIImageView {
|
final class RoundedCornersView: UIImageView {
|
||||||
private let color: UIColor
|
private let color: UIColor
|
||||||
|
private let smoothCorners: Bool
|
||||||
|
|
||||||
private var currentCornerRadius: CGFloat?
|
private var currentCornerRadius: CGFloat?
|
||||||
private var cornerImage: UIImage?
|
private var cornerImage: UIImage?
|
||||||
|
|
||||||
init(color: UIColor) {
|
init(color: UIColor, smoothCorners: Bool = false) {
|
||||||
self.color = color
|
self.color = color
|
||||||
|
self.smoothCorners = smoothCorners
|
||||||
|
|
||||||
super.init(image: nil)
|
super.init(image: nil)
|
||||||
|
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
self.layer.cornerCurve = .circular
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -26,10 +25,23 @@ final class RoundedCornersView: UIImageView {
|
|||||||
guard let cornerRadius = self.currentCornerRadius else {
|
guard let cornerRadius = self.currentCornerRadius else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let cornerImage = self.cornerImage, cornerImage.size.height == cornerRadius * 2.0 {
|
if self.smoothCorners {
|
||||||
|
let size = CGSize(width: cornerRadius * 2.0 + 10.0, height: cornerRadius * 2.0 + 10.0)
|
||||||
|
if let cornerImage = self.cornerImage, cornerImage.size == size {
|
||||||
|
} else {
|
||||||
|
self.cornerImage = generateImage(size, rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: cornerRadius).cgPath)
|
||||||
|
context.setFillColor(self.color.cgColor)
|
||||||
|
context.fillPath()
|
||||||
|
})?.stretchableImage(withLeftCapWidth: Int(cornerRadius) + 5, topCapHeight: Int(cornerRadius) + 5)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let size = CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0)
|
let size = CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0)
|
||||||
self.cornerImage = generateStretchableFilledCircleImage(diameter: size.width, color: self.color)
|
if let cornerImage = self.cornerImage, cornerImage.size == size {
|
||||||
|
} else {
|
||||||
|
self.cornerImage = generateStretchableFilledCircleImage(diameter: size.width, color: self.color)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.image = self.cornerImage
|
self.image = self.cornerImage
|
||||||
self.clipsToBounds = false
|
self.clipsToBounds = false
|
||||||
@ -52,6 +64,14 @@ final class RoundedCornersView: UIImageView {
|
|||||||
if let previousCornerRadius, self.layer.animation(forKey: "cornerRadius") == nil {
|
if let previousCornerRadius, self.layer.animation(forKey: "cornerRadius") == nil {
|
||||||
self.layer.cornerRadius = previousCornerRadius
|
self.layer.cornerRadius = previousCornerRadius
|
||||||
}
|
}
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
if self.smoothCorners {
|
||||||
|
self.layer.cornerCurve = .continuous
|
||||||
|
} else {
|
||||||
|
self.layer.cornerCurve = .circular
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
transition.setCornerRadius(layer: self.layer, cornerRadius: cornerRadius, completion: { [weak self] completed in
|
transition.setCornerRadius(layer: self.layer, cornerRadius: cornerRadius, completion: { [weak self] completed in
|
||||||
guard let self, completed else {
|
guard let self, completed else {
|
||||||
return
|
return
|
||||||
|
@ -100,7 +100,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
public var shortName: String
|
public var shortName: String
|
||||||
public var avatarImage: UIImage?
|
public var avatarImage: UIImage?
|
||||||
public var audioOutput: AudioOutput
|
public var audioOutput: AudioOutput
|
||||||
public var isMicrophoneMuted: Bool
|
public var isLocalAudioMuted: Bool
|
||||||
|
public var isRemoteAudioMuted: Bool
|
||||||
public var localVideo: VideoSource?
|
public var localVideo: VideoSource?
|
||||||
public var remoteVideo: VideoSource?
|
public var remoteVideo: VideoSource?
|
||||||
public var isRemoteBatteryLow: Bool
|
public var isRemoteBatteryLow: Bool
|
||||||
@ -111,7 +112,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
shortName: String,
|
shortName: String,
|
||||||
avatarImage: UIImage?,
|
avatarImage: UIImage?,
|
||||||
audioOutput: AudioOutput,
|
audioOutput: AudioOutput,
|
||||||
isMicrophoneMuted: Bool,
|
isLocalAudioMuted: Bool,
|
||||||
|
isRemoteAudioMuted: Bool,
|
||||||
localVideo: VideoSource?,
|
localVideo: VideoSource?,
|
||||||
remoteVideo: VideoSource?,
|
remoteVideo: VideoSource?,
|
||||||
isRemoteBatteryLow: Bool
|
isRemoteBatteryLow: Bool
|
||||||
@ -121,7 +123,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
self.shortName = shortName
|
self.shortName = shortName
|
||||||
self.avatarImage = avatarImage
|
self.avatarImage = avatarImage
|
||||||
self.audioOutput = audioOutput
|
self.audioOutput = audioOutput
|
||||||
self.isMicrophoneMuted = isMicrophoneMuted
|
self.isLocalAudioMuted = isLocalAudioMuted
|
||||||
|
self.isRemoteAudioMuted = isRemoteAudioMuted
|
||||||
self.localVideo = localVideo
|
self.localVideo = localVideo
|
||||||
self.remoteVideo = remoteVideo
|
self.remoteVideo = remoteVideo
|
||||||
self.isRemoteBatteryLow = isRemoteBatteryLow
|
self.isRemoteBatteryLow = isRemoteBatteryLow
|
||||||
@ -143,7 +146,10 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
if lhs.audioOutput != rhs.audioOutput {
|
if lhs.audioOutput != rhs.audioOutput {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.isMicrophoneMuted != rhs.isMicrophoneMuted {
|
if lhs.isLocalAudioMuted != rhs.isLocalAudioMuted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isRemoteAudioMuted != rhs.isRemoteAudioMuted {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.localVideo !== rhs.localVideo {
|
if lhs.localVideo !== rhs.localVideo {
|
||||||
@ -224,6 +230,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
public var microhoneMuteAction: (() -> Void)?
|
public var microhoneMuteAction: (() -> Void)?
|
||||||
public var endCallAction: (() -> Void)?
|
public var endCallAction: (() -> Void)?
|
||||||
public var backAction: (() -> Void)?
|
public var backAction: (() -> Void)?
|
||||||
|
public var closeAction: (() -> Void)?
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
self.overlayContentsView = UIView()
|
self.overlayContentsView = UIView()
|
||||||
@ -264,10 +271,6 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
self.avatarTransformLayer.addSublayer(self.avatarLayer)
|
self.avatarTransformLayer.addSublayer(self.avatarLayer)
|
||||||
self.layer.addSublayer(self.avatarTransformLayer)
|
self.layer.addSublayer(self.avatarTransformLayer)
|
||||||
|
|
||||||
/*let edgeTestLayer = EdgeTestLayer()
|
|
||||||
edgeTestLayer.frame = CGRect(origin: CGPoint(x: 20.0, y: 100.0), size: CGSize(width: 100.0, height: 100.0))
|
|
||||||
self.layer.addSublayer(edgeTestLayer)*/
|
|
||||||
|
|
||||||
self.addSubview(self.videoContainerBackgroundView)
|
self.addSubview(self.videoContainerBackgroundView)
|
||||||
|
|
||||||
self.overlayContentsView.mask = self.maskContents
|
self.overlayContentsView.mask = self.maskContents
|
||||||
@ -310,6 +313,13 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
}
|
}
|
||||||
self.backAction?()
|
self.backAction?()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.buttonGroupView.closePressed = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.closeAction?()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init?(coder: NSCoder) {
|
public required init?(coder: NSCoder) {
|
||||||
@ -497,6 +507,13 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
|
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(), size: params.size)
|
let backgroundFrame = CGRect(origin: CGPoint(), size: params.size)
|
||||||
|
|
||||||
|
let wideContentWidth: CGFloat
|
||||||
|
if params.size.width < 500.0 {
|
||||||
|
wideContentWidth = params.size.width - 44.0 * 2.0
|
||||||
|
} else {
|
||||||
|
wideContentWidth = 400.0
|
||||||
|
}
|
||||||
|
|
||||||
var activeVideoSources: [(VideoContainerView.Key, VideoSource)] = []
|
var activeVideoSources: [(VideoContainerView.Key, VideoSource)] = []
|
||||||
if self.swapLocalAndRemoteVideo {
|
if self.swapLocalAndRemoteVideo {
|
||||||
if let activeLocalVideoSource = self.activeLocalVideoSource {
|
if let activeLocalVideoSource = self.activeLocalVideoSource {
|
||||||
@ -554,7 +571,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
}
|
}
|
||||||
self.videoAction?()
|
self.videoAction?()
|
||||||
}),
|
}),
|
||||||
ButtonGroupView.Button(content: .microphone(isMuted: params.state.isMicrophoneMuted), action: { [weak self] in
|
ButtonGroupView.Button(content: .microphone(isMuted: params.state.isLocalAudioMuted), action: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -584,9 +601,12 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var notices: [ButtonGroupView.Notice] = []
|
var notices: [ButtonGroupView.Notice] = []
|
||||||
if params.state.isMicrophoneMuted {
|
if params.state.isLocalAudioMuted {
|
||||||
notices.append(ButtonGroupView.Notice(id: AnyHashable(0 as Int), text: "Your microphone is turned off"))
|
notices.append(ButtonGroupView.Notice(id: AnyHashable(0 as Int), text: "Your microphone is turned off"))
|
||||||
}
|
}
|
||||||
|
if params.state.isRemoteAudioMuted {
|
||||||
|
notices.append(ButtonGroupView.Notice(id: AnyHashable(0 as Int), text: "\(params.state.shortName)'s microphone is turned off"))
|
||||||
|
}
|
||||||
if params.state.remoteVideo != nil && params.state.localVideo == nil {
|
if params.state.remoteVideo != nil && params.state.localVideo == nil {
|
||||||
notices.append(ButtonGroupView.Notice(id: AnyHashable(1 as Int), text: "Your camera is turned off"))
|
notices.append(ButtonGroupView.Notice(id: AnyHashable(1 as Int), text: "Your camera is turned off"))
|
||||||
}
|
}
|
||||||
@ -594,7 +614,11 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
notices.append(ButtonGroupView.Notice(id: AnyHashable(2 as Int), text: "\(params.state.shortName)'s battery is low"))
|
notices.append(ButtonGroupView.Notice(id: AnyHashable(2 as Int), text: "\(params.state.shortName)'s battery is low"))
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentBottomInset = self.buttonGroupView.update(size: params.size, insets: params.insets, controlsHidden: currentAreControlsHidden, buttons: buttons, notices: notices, transition: transition)
|
var displayClose = false
|
||||||
|
if case .terminated = params.state.lifecycleState {
|
||||||
|
displayClose = true
|
||||||
|
}
|
||||||
|
let contentBottomInset = self.buttonGroupView.update(size: params.size, insets: params.insets, minWidth: wideContentWidth, controlsHidden: currentAreControlsHidden, displayClose: displayClose, buttons: buttons, notices: notices, transition: transition)
|
||||||
|
|
||||||
var expandedEmojiKeyRect: CGRect?
|
var expandedEmojiKeyRect: CGRect?
|
||||||
if self.isEmojiKeyExpanded {
|
if self.isEmojiKeyExpanded {
|
||||||
@ -632,7 +656,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let emojiExpandedInfoSize = emojiExpandedInfoView.update(constrainedWidth: params.size.width - (params.insets.left + 16.0) * 2.0, transition: emojiExpandedInfoTransition)
|
let emojiExpandedInfoSize = emojiExpandedInfoView.update(width: wideContentWidth, transition: emojiExpandedInfoTransition)
|
||||||
let emojiExpandedInfoFrame = CGRect(origin: CGPoint(x: floor((params.size.width - emojiExpandedInfoSize.width) * 0.5), y: params.insets.top + 73.0), size: emojiExpandedInfoSize)
|
let emojiExpandedInfoFrame = CGRect(origin: CGPoint(x: floor((params.size.width - emojiExpandedInfoSize.width) * 0.5), y: params.insets.top + 73.0), size: emojiExpandedInfoSize)
|
||||||
emojiExpandedInfoTransition.setPosition(view: emojiExpandedInfoView, position: CGPoint(x: emojiExpandedInfoFrame.minX + emojiExpandedInfoView.layer.anchorPoint.x * emojiExpandedInfoFrame.width, y: emojiExpandedInfoFrame.minY + emojiExpandedInfoView.layer.anchorPoint.y * emojiExpandedInfoFrame.height))
|
emojiExpandedInfoTransition.setPosition(view: emojiExpandedInfoView, position: CGPoint(x: emojiExpandedInfoFrame.minX + emojiExpandedInfoView.layer.anchorPoint.x * emojiExpandedInfoFrame.width, y: emojiExpandedInfoFrame.minY + emojiExpandedInfoView.layer.anchorPoint.y * emojiExpandedInfoFrame.height))
|
||||||
emojiExpandedInfoTransition.setBounds(view: emojiExpandedInfoView, bounds: CGRect(origin: CGPoint(), size: emojiExpandedInfoFrame.size))
|
emojiExpandedInfoTransition.setBounds(view: emojiExpandedInfoView, bounds: CGRect(origin: CGPoint(), size: emojiExpandedInfoFrame.size))
|
||||||
|
@ -33,6 +33,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
private let prizeTitleNode: TextNode
|
private let prizeTitleNode: TextNode
|
||||||
private let prizeTextNode: TextNode
|
private let prizeTextNode: TextNode
|
||||||
|
|
||||||
|
private let additionalPrizeTitleNode: TextNode
|
||||||
|
private let additionalPrizeTextNode: TextNode
|
||||||
|
|
||||||
private let participantsTitleNode: TextNode
|
private let participantsTitleNode: TextNode
|
||||||
private let participantsTextNode: TextNode
|
private let participantsTextNode: TextNode
|
||||||
|
|
||||||
@ -81,6 +84,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
self.prizeTitleNode = TextNode()
|
self.prizeTitleNode = TextNode()
|
||||||
self.prizeTextNode = TextNode()
|
self.prizeTextNode = TextNode()
|
||||||
|
|
||||||
|
self.additionalPrizeTitleNode = TextNode()
|
||||||
|
self.additionalPrizeTextNode = TextNode()
|
||||||
|
|
||||||
self.participantsTitleNode = TextNode()
|
self.participantsTitleNode = TextNode()
|
||||||
self.participantsTextNode = TextNode()
|
self.participantsTextNode = TextNode()
|
||||||
|
|
||||||
@ -101,6 +107,8 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
|
|
||||||
self.addSubnode(self.prizeTitleNode)
|
self.addSubnode(self.prizeTitleNode)
|
||||||
self.addSubnode(self.prizeTextNode)
|
self.addSubnode(self.prizeTextNode)
|
||||||
|
self.addSubnode(self.additionalPrizeTitleNode)
|
||||||
|
self.addSubnode(self.additionalPrizeTextNode)
|
||||||
self.addSubnode(self.participantsTitleNode)
|
self.addSubnode(self.participantsTitleNode)
|
||||||
self.addSubnode(self.participantsTextNode)
|
self.addSubnode(self.participantsTextNode)
|
||||||
self.addSubnode(self.countriesTextNode)
|
self.addSubnode(self.countriesTextNode)
|
||||||
@ -181,6 +189,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
let makePrizeTitleLayout = TextNode.asyncLayout(self.prizeTitleNode)
|
let makePrizeTitleLayout = TextNode.asyncLayout(self.prizeTitleNode)
|
||||||
let makePrizeTextLayout = TextNode.asyncLayout(self.prizeTextNode)
|
let makePrizeTextLayout = TextNode.asyncLayout(self.prizeTextNode)
|
||||||
|
|
||||||
|
let makeAdditionalPrizeTitleLayout = TextNode.asyncLayout(self.additionalPrizeTitleNode)
|
||||||
|
let makeAdditionalPrizeTextLayout = TextNode.asyncLayout(self.additionalPrizeTextNode)
|
||||||
|
|
||||||
let makeParticipantsTitleLayout = TextNode.asyncLayout(self.participantsTitleNode)
|
let makeParticipantsTitleLayout = TextNode.asyncLayout(self.participantsTitleNode)
|
||||||
let makeParticipantsTextLayout = TextNode.asyncLayout(self.participantsTextNode)
|
let makeParticipantsTextLayout = TextNode.asyncLayout(self.participantsTextNode)
|
||||||
|
|
||||||
@ -232,6 +243,8 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
|
|
||||||
let prizeTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_PrizeTitle, font: titleFont, textColor: textColor)
|
let prizeTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_PrizeTitle, font: titleFont, textColor: textColor)
|
||||||
var prizeTextString: NSAttributedString?
|
var prizeTextString: NSAttributedString?
|
||||||
|
var additionalPrizeTitleString: NSAttributedString?
|
||||||
|
var additionalPrizeTextString: NSAttributedString?
|
||||||
if let giveaway {
|
if let giveaway {
|
||||||
prizeTextString = parseMarkdownIntoAttributedString(item.presentationData.strings.Chat_Giveaway_Message_PrizeText(
|
prizeTextString = parseMarkdownIntoAttributedString(item.presentationData.strings.Chat_Giveaway_Message_PrizeText(
|
||||||
item.presentationData.strings.Chat_Giveaway_Message_Subscriptions(giveaway.quantity),
|
item.presentationData.strings.Chat_Giveaway_Message_Subscriptions(giveaway.quantity),
|
||||||
@ -244,6 +257,11 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
return ("URL", url)
|
return ("URL", url)
|
||||||
}
|
}
|
||||||
), textAlignment: .center)
|
), textAlignment: .center)
|
||||||
|
|
||||||
|
if let prizeDescription = giveaway.prizeDescription {
|
||||||
|
additionalPrizeTitleString = NSAttributedString(string: "Additional Prize", font: titleFont, textColor: textColor)
|
||||||
|
additionalPrizeTextString = NSAttributedString(string: prizeDescription, font: textFont, textColor: textColor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let participantsTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_ParticipantsTitle, font: titleFont, textColor: textColor)
|
let participantsTitleString = NSAttributedString(string: item.presentationData.strings.Chat_Giveaway_Message_ParticipantsTitle, font: titleFont, textColor: textColor)
|
||||||
@ -316,6 +334,10 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
|
|
||||||
let (prizeTextLayout, prizeTextApply) = makePrizeTextLayout(TextNodeLayoutArguments(attributedString: prizeTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (prizeTextLayout, prizeTextApply) = makePrizeTextLayout(TextNodeLayoutArguments(attributedString: prizeTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let (additionalPrizeTitleLayout, additionalPrizeTitleApply) = makeAdditionalPrizeTitleLayout(TextNodeLayoutArguments(attributedString: additionalPrizeTitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let (additionalPrizeTextLayout, additionalPrizeTextApply) = makeAdditionalPrizeTextLayout(TextNodeLayoutArguments(attributedString: additionalPrizeTextString, backgroundColor: nil, maximumNumberOfLines: 6, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let (participantsTitleLayout, participantsTitleApply) = makeParticipantsTitleLayout(TextNodeLayoutArguments(attributedString: participantsTitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (participantsTitleLayout, participantsTitleApply) = makeParticipantsTitleLayout(TextNodeLayoutArguments(attributedString: participantsTitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
let (participantsTextLayout, participantsTextApply) = makeParticipantsTextLayout(TextNodeLayoutArguments(attributedString: participantsTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (participantsTextLayout, participantsTextApply) = makeParticipantsTextLayout(TextNodeLayoutArguments(attributedString: participantsTextString, backgroundColor: nil, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
@ -423,6 +445,8 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
}
|
}
|
||||||
maxContentWidth = max(maxContentWidth, prizeTitleLayout.size.width)
|
maxContentWidth = max(maxContentWidth, prizeTitleLayout.size.width)
|
||||||
maxContentWidth = max(maxContentWidth, prizeTextLayout.size.width)
|
maxContentWidth = max(maxContentWidth, prizeTextLayout.size.width)
|
||||||
|
maxContentWidth = max(maxContentWidth, additionalPrizeTitleLayout.size.width)
|
||||||
|
maxContentWidth = max(maxContentWidth, additionalPrizeTextLayout.size.width)
|
||||||
maxContentWidth = max(maxContentWidth, participantsTitleLayout.size.width)
|
maxContentWidth = max(maxContentWidth, participantsTitleLayout.size.width)
|
||||||
maxContentWidth = max(maxContentWidth, participantsTextLayout.size.width)
|
maxContentWidth = max(maxContentWidth, participantsTextLayout.size.width)
|
||||||
maxContentWidth = max(maxContentWidth, dateTitleLayout.size.width)
|
maxContentWidth = max(maxContentWidth, dateTitleLayout.size.width)
|
||||||
@ -453,6 +477,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
|
|
||||||
var layoutSize = CGSize(width: boundingWidth, height: 49.0 + prizeTitleLayout.size.height + prizeTextLayout.size.height + participantsTitleLayout.size.height + participantsTextLayout.size.height + dateTitleLayout.size.height + dateTextLayout.size.height + buttonSize.height + buttonSpacing + 120.0)
|
var layoutSize = CGSize(width: boundingWidth, height: 49.0 + prizeTitleLayout.size.height + prizeTextLayout.size.height + participantsTitleLayout.size.height + participantsTextLayout.size.height + dateTitleLayout.size.height + dateTextLayout.size.height + buttonSize.height + buttonSpacing + 120.0)
|
||||||
|
|
||||||
|
if additionalPrizeTextLayout.size.height > 0.0 {
|
||||||
|
layoutSize.height += additionalPrizeTitleLayout.size.height + additionalPrizeTextLayout.size.height + 7.0
|
||||||
|
}
|
||||||
if countriesTextLayout.size.height > 0.0 {
|
if countriesTextLayout.size.height > 0.0 {
|
||||||
layoutSize.height += countriesTextLayout.size.height + 7.0
|
layoutSize.height += countriesTextLayout.size.height + 7.0
|
||||||
}
|
}
|
||||||
@ -473,10 +500,12 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
strongSelf.giveaway = giveaway
|
strongSelf.giveaway = giveaway
|
||||||
|
|
||||||
strongSelf.updateVisibility()
|
strongSelf.updateVisibility()
|
||||||
|
|
||||||
let _ = badgeTextApply()
|
let _ = badgeTextApply()
|
||||||
let _ = prizeTitleApply()
|
let _ = prizeTitleApply()
|
||||||
let _ = prizeTextApply()
|
let _ = prizeTextApply()
|
||||||
|
let _ = additionalPrizeTitleApply()
|
||||||
|
let _ = additionalPrizeTextApply()
|
||||||
let _ = participantsTitleApply()
|
let _ = participantsTitleApply()
|
||||||
let _ = participantsTextApply()
|
let _ = participantsTextApply()
|
||||||
let _ = countriesTextApply()
|
let _ = countriesTextApply()
|
||||||
@ -502,12 +531,19 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
originY += 112.0
|
originY += 112.0
|
||||||
|
|
||||||
strongSelf.prizeTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - prizeTitleLayout.size.width) / 2.0), y: originY), size: prizeTitleLayout.size)
|
strongSelf.prizeTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - prizeTitleLayout.size.width) / 2.0), y: originY), size: prizeTitleLayout.size)
|
||||||
originY += prizeTitleLayout.size.height + smallSpacing
|
originY += prizeTitleLayout.size.height + smallSpacing
|
||||||
strongSelf.prizeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - prizeTextLayout.size.width) / 2.0), y: originY), size: prizeTextLayout.size)
|
strongSelf.prizeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - prizeTextLayout.size.width) / 2.0), y: originY), size: prizeTextLayout.size)
|
||||||
originY += prizeTextLayout.size.height + largeSpacing
|
originY += prizeTextLayout.size.height + largeSpacing
|
||||||
|
|
||||||
|
if additionalPrizeTextLayout.size.height > 0.0 {
|
||||||
|
strongSelf.additionalPrizeTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - additionalPrizeTitleLayout.size.width) / 2.0), y: originY), size: additionalPrizeTitleLayout.size)
|
||||||
|
originY += additionalPrizeTitleLayout.size.height + smallSpacing
|
||||||
|
strongSelf.additionalPrizeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - additionalPrizeTextLayout.size.width) / 2.0), y: originY), size: additionalPrizeTextLayout.size)
|
||||||
|
originY += additionalPrizeTextLayout.size.height + largeSpacing
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.participantsTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - participantsTitleLayout.size.width) / 2.0), y: originY), size: participantsTitleLayout.size)
|
strongSelf.participantsTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - participantsTitleLayout.size.width) / 2.0), y: originY), size: participantsTitleLayout.size)
|
||||||
originY += participantsTitleLayout.size.height + smallSpacing
|
originY += participantsTitleLayout.size.height + smallSpacing
|
||||||
strongSelf.participantsTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - participantsTextLayout.size.width) / 2.0), y: originY), size: participantsTextLayout.size)
|
strongSelf.participantsTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layoutSize.width - participantsTextLayout.size.width) / 2.0), y: originY), size: participantsTextLayout.size)
|
||||||
|
@ -445,6 +445,15 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
|
|||||||
(self.micLockValue as? LockView)?.updateTheme(theme)
|
(self.micLockValue as? LockView)?.updateTheme(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func createLockPanelView() -> UIView! {
|
||||||
|
if self.hidesOnLock {
|
||||||
|
let view = WrapperBlurrredBackgroundView(frame: CGRect(origin: .zero, size: CGSize(width: 40.0, height: 72.0)))
|
||||||
|
return view
|
||||||
|
} else {
|
||||||
|
return super.createLockPanelView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func cancelRecording() {
|
public func cancelRecording() {
|
||||||
self.isEnabled = false
|
self.isEnabled = false
|
||||||
self.isEnabled = true
|
self.isEnabled = true
|
||||||
@ -572,3 +581,31 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class WrapperBlurrredBackgroundView: UIView {
|
||||||
|
let view: BlurredBackgroundView
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
let view = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.5), enableBlur: true)
|
||||||
|
view.frame = CGRect(origin: .zero, size: frame.size)
|
||||||
|
view.update(size: frame.size, cornerRadius: frame.width / 2.0, transition: .immediate)
|
||||||
|
self.view = view
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.addSubview(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override var frame: CGRect {
|
||||||
|
get {
|
||||||
|
return super.frame
|
||||||
|
} set {
|
||||||
|
super.frame = newValue
|
||||||
|
self.view.update(size: newValue.size, cornerRadius: newValue.width / 2.0, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -65,6 +65,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
|||||||
[
|
[
|
||||||
"Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
|
"Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
|
||||||
"Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
"Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||||
|
"Rectangle 2.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||||
"Path.Path.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
"Path.Path.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||||
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
||||||
].forEach { key, value in
|
].forEach { key, value in
|
||||||
|
@ -97,6 +97,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
let contextSourceNode: ContextReferenceContentNode
|
let contextSourceNode: ContextReferenceContentNode
|
||||||
private let textNode: ImmediateTextNode
|
private let textNode: ImmediateTextNode
|
||||||
private let iconNode: ASImageNode
|
private let iconNode: ASImageNode
|
||||||
|
private let backIconLayer: SimpleShapeLayer
|
||||||
private var animationNode: MoreIconNode?
|
private var animationNode: MoreIconNode?
|
||||||
private let backgroundNode: NavigationBackgroundNode
|
private let backgroundNode: NavigationBackgroundNode
|
||||||
|
|
||||||
@ -117,6 +118,15 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
self.iconNode.displaysAsynchronously = false
|
self.iconNode.displaysAsynchronously = false
|
||||||
self.iconNode.displayWithoutProcessing = true
|
self.iconNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.backIconLayer = SimpleShapeLayer()
|
||||||
|
self.backIconLayer.lineWidth = 3.0
|
||||||
|
self.backIconLayer.lineCap = .round
|
||||||
|
self.backIconLayer.lineJoin = .round
|
||||||
|
self.backIconLayer.strokeColor = UIColor.white.cgColor
|
||||||
|
self.backIconLayer.fillColor = nil
|
||||||
|
self.backIconLayer.isHidden = true
|
||||||
|
self.backIconLayer.path = try? convertSvgPath("M10.5,2 L1.5,11 L10.5,20 ")
|
||||||
|
|
||||||
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true)
|
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true)
|
||||||
|
|
||||||
super.init(pointerStyle: .insetRectangle(-8.0, 2.0))
|
super.init(pointerStyle: .insetRectangle(-8.0, 2.0))
|
||||||
@ -128,6 +138,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
self.contextSourceNode.addSubnode(self.backgroundNode)
|
self.contextSourceNode.addSubnode(self.backgroundNode)
|
||||||
self.contextSourceNode.addSubnode(self.textNode)
|
self.contextSourceNode.addSubnode(self.textNode)
|
||||||
self.contextSourceNode.addSubnode(self.iconNode)
|
self.contextSourceNode.addSubnode(self.iconNode)
|
||||||
|
self.contextSourceNode.layer.addSublayer(self.backIconLayer)
|
||||||
|
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.containerNode)
|
||||||
|
|
||||||
@ -146,13 +157,43 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
self.action?(self.contextSourceNode, nil)
|
self.action?(self.contextSourceNode, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, transition: ContainedViewLayoutTransition) {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
var boundingRect = self.bounds
|
||||||
|
if self.textNode.alpha != 0.0 {
|
||||||
|
boundingRect = boundingRect.union(self.textNode.frame)
|
||||||
|
}
|
||||||
|
boundingRect = boundingRect.insetBy(dx: -8.0, dy: -4.0)
|
||||||
|
if boundingRect.contains(point) {
|
||||||
|
return super.hitTest(self.bounds.center, with: event)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
self.contentsColor = contentsColor
|
self.contentsColor = contentsColor
|
||||||
|
|
||||||
self.backgroundNode.updateColor(color: backgroundColor, transition: transition)
|
self.backgroundNode.updateColor(color: backgroundColor, transition: transition)
|
||||||
|
|
||||||
transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor)
|
transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor)
|
||||||
transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor)
|
transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor)
|
||||||
|
transition.updateStrokeColor(layer: self.backIconLayer, strokeColor: self.contentsColor)
|
||||||
|
|
||||||
|
switch self.key {
|
||||||
|
case .back:
|
||||||
|
transition.updateAlpha(layer: self.textNode.layer, alpha: canBeExpanded ? 1.0 : 0.0)
|
||||||
|
transition.updateTransformScale(node: self.textNode, scale: canBeExpanded ? 1.0 : 0.001)
|
||||||
|
|
||||||
|
var iconTransform = CATransform3DIdentity
|
||||||
|
iconTransform = CATransform3DScale(iconTransform, canBeExpanded ? 1.0 : 0.8, canBeExpanded ? 1.0 : 0.8, 1.0)
|
||||||
|
iconTransform = CATransform3DTranslate(iconTransform, canBeExpanded ? -7.0 : 0.0, 0.0, 0.0)
|
||||||
|
transition.updateTransform(node: self.iconNode, transform: CATransform3DGetAffineTransform(iconTransform))
|
||||||
|
|
||||||
|
transition.updateTransform(layer: self.backIconLayer, transform: CATransform3DGetAffineTransform(iconTransform))
|
||||||
|
transition.updateLineWidth(layer: self.backIconLayer, lineWidth: canBeExpanded ? 3.0 : 2.075)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if let animationNode = self.animationNode {
|
if let animationNode = self.animationNode {
|
||||||
transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor)
|
transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor)
|
||||||
@ -184,9 +225,9 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
var animationState: MoreIconNodeState = .more
|
var animationState: MoreIconNodeState = .more
|
||||||
switch key {
|
switch key {
|
||||||
case .back:
|
case .back:
|
||||||
text = ""
|
text = presentationData.strings.Common_Back
|
||||||
accessibilityText = presentationData.strings.Common_Back
|
accessibilityText = presentationData.strings.Common_Back
|
||||||
icon = NavigationBar.thinBackArrowImage
|
icon = NavigationBar.backArrowImage(color: .white)
|
||||||
case .edit:
|
case .edit:
|
||||||
text = presentationData.strings.Common_Edit
|
text = presentationData.strings.Common_Edit
|
||||||
accessibilityText = text
|
accessibilityText = text
|
||||||
@ -270,11 +311,19 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inset: CGFloat = 0.0
|
let inset: CGFloat = 0.0
|
||||||
|
var textInset: CGFloat = 0.0
|
||||||
|
switch key {
|
||||||
|
case .back:
|
||||||
|
textInset += 11.0
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
let resultSize: CGSize
|
let resultSize: CGSize
|
||||||
|
|
||||||
let textFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - textSize.height) / 2.0)), size: textSize)
|
let textFrame = CGRect(origin: CGPoint(x: inset + textInset, y: floor((height - textSize.height) / 2.0)), size: textSize)
|
||||||
self.textNode.frame = textFrame
|
self.textNode.position = textFrame.center
|
||||||
|
self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size)
|
||||||
|
|
||||||
if let animationNode = self.animationNode {
|
if let animationNode = self.animationNode {
|
||||||
let animationSize = CGSize(width: 30.0, height: 30.0)
|
let animationSize = CGSize(width: 30.0, height: 30.0)
|
||||||
@ -286,7 +335,20 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|||||||
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
resultSize = size
|
resultSize = size
|
||||||
} else if let image = self.iconNode.image {
|
} else if let image = self.iconNode.image {
|
||||||
self.iconNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
|
let iconFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
|
||||||
|
self.iconNode.position = iconFrame.center
|
||||||
|
self.iconNode.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||||
|
|
||||||
|
if case .back = key {
|
||||||
|
self.backIconLayer.position = iconFrame.center
|
||||||
|
self.backIconLayer.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||||
|
|
||||||
|
self.iconNode.isHidden = true
|
||||||
|
self.backIconLayer.isHidden = false
|
||||||
|
} else {
|
||||||
|
self.iconNode.isHidden = false
|
||||||
|
self.backIconLayer.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
let size = CGSize(width: image.size.width + inset * 2.0, height: height)
|
let size = CGSize(width: image.size.width + inset * 2.0, height: height)
|
||||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
|
@ -36,18 +36,21 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
|||||||
|
|
||||||
private var backgroundContentColor: UIColor = .clear
|
private var backgroundContentColor: UIColor = .clear
|
||||||
private var contentsColor: UIColor = .white
|
private var contentsColor: UIColor = .white
|
||||||
|
private var canBeExpanded: Bool = false
|
||||||
|
|
||||||
var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?, ContextGesture?) -> Void)?
|
var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, transition: ContainedViewLayoutTransition) {
|
func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
self.backgroundContentColor = backgroundContentColor
|
self.backgroundContentColor = backgroundContentColor
|
||||||
self.contentsColor = contentsColor
|
self.contentsColor = contentsColor
|
||||||
|
|
||||||
for (_, button) in self.leftButtonNodes {
|
for (_, button) in self.leftButtonNodes {
|
||||||
button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: transition)
|
button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition)
|
||||||
|
transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? -8.0 : 0.0, y: 0.0))
|
||||||
}
|
}
|
||||||
for (_, button) in self.rightButtonNodes {
|
for (_, button) in self.rightButtonNodes {
|
||||||
button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: transition)
|
button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition)
|
||||||
|
transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? 8.0 : 0.0, y: 0.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +109,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
|||||||
buttonNode.frame = buttonFrame
|
buttonNode.frame = buttonFrame
|
||||||
buttonNode.alpha = 0.0
|
buttonNode.alpha = 0.0
|
||||||
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
||||||
buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: .immediate)
|
buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate)
|
||||||
} else {
|
} else {
|
||||||
transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
||||||
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
||||||
@ -202,7 +205,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
|||||||
}
|
}
|
||||||
let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction)
|
let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction)
|
||||||
if wasAdded {
|
if wasAdded {
|
||||||
buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, transition: .immediate)
|
buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate)
|
||||||
|
|
||||||
if key == .moreToSearch {
|
if key == .moreToSearch {
|
||||||
buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
|
buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
|
||||||
|
@ -553,6 +553,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
let navigationContentsAccentColor: UIColor
|
let navigationContentsAccentColor: UIColor
|
||||||
let navigationContentsPrimaryColor: UIColor
|
let navigationContentsPrimaryColor: UIColor
|
||||||
let navigationContentsSecondaryColor: UIColor
|
let navigationContentsSecondaryColor: UIColor
|
||||||
|
let navigationContentsCanBeExpanded: Bool
|
||||||
|
|
||||||
let contentButtonBackgroundColor: UIColor
|
let contentButtonBackgroundColor: UIColor
|
||||||
let contentButtonForegroundColor: UIColor
|
let contentButtonForegroundColor: UIColor
|
||||||
@ -640,6 +641,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
navigationContentsAccentColor = collapsedHeaderNavigationContentsAccentColor
|
navigationContentsAccentColor = collapsedHeaderNavigationContentsAccentColor
|
||||||
navigationContentsPrimaryColor = collapsedHeaderNavigationContentsPrimaryColor
|
navigationContentsPrimaryColor = collapsedHeaderNavigationContentsPrimaryColor
|
||||||
navigationContentsSecondaryColor = collapsedHeaderNavigationContentsSecondaryColor
|
navigationContentsSecondaryColor = collapsedHeaderNavigationContentsSecondaryColor
|
||||||
|
navigationContentsCanBeExpanded = true
|
||||||
|
|
||||||
contentButtonBackgroundColor = collapsedHeaderContentButtonBackgroundColor
|
contentButtonBackgroundColor = collapsedHeaderContentButtonBackgroundColor
|
||||||
contentButtonForegroundColor = collapsedHeaderContentButtonForegroundColor
|
contentButtonForegroundColor = collapsedHeaderContentButtonForegroundColor
|
||||||
|
|
||||||
@ -651,6 +654,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
contentButtonBackgroundColor = expandedAvatarContentButtonBackgroundColor
|
contentButtonBackgroundColor = expandedAvatarContentButtonBackgroundColor
|
||||||
contentButtonForegroundColor = expandedAvatarContentButtonForegroundColor
|
contentButtonForegroundColor = expandedAvatarContentButtonForegroundColor
|
||||||
|
|
||||||
|
navigationContentsCanBeExpanded = false
|
||||||
|
|
||||||
headerButtonBackgroundColor = expandedAvatarHeaderButtonBackgroundColor
|
headerButtonBackgroundColor = expandedAvatarHeaderButtonBackgroundColor
|
||||||
} else {
|
} else {
|
||||||
let effectiveTransitionFraction: CGFloat = innerBackgroundTransitionFraction < 0.5 ? 0.0 : 1.0
|
let effectiveTransitionFraction: CGFloat = innerBackgroundTransitionFraction < 0.5 ? 0.0 : 1.0
|
||||||
@ -659,6 +664,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
navigationContentsPrimaryColor = regularNavigationContentsPrimaryColor.mixedWith(collapsedHeaderNavigationContentsPrimaryColor, alpha: effectiveTransitionFraction)
|
navigationContentsPrimaryColor = regularNavigationContentsPrimaryColor.mixedWith(collapsedHeaderNavigationContentsPrimaryColor, alpha: effectiveTransitionFraction)
|
||||||
navigationContentsSecondaryColor = regularNavigationContentsSecondaryColor.mixedWith(collapsedHeaderNavigationContentsSecondaryColor, alpha: effectiveTransitionFraction)
|
navigationContentsSecondaryColor = regularNavigationContentsSecondaryColor.mixedWith(collapsedHeaderNavigationContentsSecondaryColor, alpha: effectiveTransitionFraction)
|
||||||
|
|
||||||
|
if peer?.profileColor != nil {
|
||||||
|
navigationContentsCanBeExpanded = effectiveTransitionFraction == 1.0
|
||||||
|
} else {
|
||||||
|
navigationContentsCanBeExpanded = true
|
||||||
|
}
|
||||||
|
|
||||||
contentButtonBackgroundColor = regularContentButtonBackgroundColor//.mixedWith(collapsedHeaderContentButtonBackgroundColor, alpha: effectiveTransitionFraction)
|
contentButtonBackgroundColor = regularContentButtonBackgroundColor//.mixedWith(collapsedHeaderContentButtonBackgroundColor, alpha: effectiveTransitionFraction)
|
||||||
contentButtonForegroundColor = regularContentButtonForegroundColor//.mixedWith(collapsedHeaderContentButtonForegroundColor, alpha: effectiveTransitionFraction)
|
contentButtonForegroundColor = regularContentButtonForegroundColor//.mixedWith(collapsedHeaderContentButtonForegroundColor, alpha: effectiveTransitionFraction)
|
||||||
|
|
||||||
@ -775,7 +786,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.titleExpandedCredibilityIconSize = expandedIconSize
|
self.titleExpandedCredibilityIconSize = expandedIconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, transition: navigationTransition)
|
self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, canBeExpanded: navigationContentsCanBeExpanded, transition: navigationTransition)
|
||||||
|
|
||||||
self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition)
|
self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition)
|
||||||
self.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition)
|
self.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition)
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -844,7 +844,7 @@ private func chatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASD
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (updatedUrlPreviewState, signal) = urlPreviewStateForInputText(NSAttributedString(string: url), context: selfController.context, currentQuery: nil), let updatedUrlPreviewState, let detectedUrl = updatedUrlPreviewState.detectedUrls.first {
|
if let (updatedUrlPreviewState, signal) = urlPreviewStateForInputText(NSAttributedString(string: url), context: selfController.context, currentQuery: nil, forPeerId: selfController.chatLocation.peerId), let updatedUrlPreviewState, let detectedUrl = updatedUrlPreviewState.detectedUrls.first {
|
||||||
if let webpage = webpageCache[detectedUrl] {
|
if let webpage = webpageCache[detectedUrl] {
|
||||||
progress?.set(.single(false))
|
progress?.set(.single(false))
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ func updateChatPresentationInterfaceStateImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (updatedUrlPreviewState, updatedUrlPreviewSignal) = urlPreviewStateForInputText(updatedChatPresentationInterfaceState.interfaceState.composeInputState.inputText, context: selfController.context, currentQuery: selfController.urlPreviewQueryState?.0) {
|
if let (updatedUrlPreviewState, updatedUrlPreviewSignal) = urlPreviewStateForInputText(updatedChatPresentationInterfaceState.interfaceState.composeInputState.inputText, context: selfController.context, currentQuery: selfController.urlPreviewQueryState?.0, forPeerId: selfController.chatLocation.peerId) {
|
||||||
selfController.urlPreviewQueryState?.1.dispose()
|
selfController.urlPreviewQueryState?.1.dispose()
|
||||||
var inScope = true
|
var inScope = true
|
||||||
var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)?
|
var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)?
|
||||||
@ -301,7 +301,7 @@ func updateChatPresentationInterfaceStateImpl(
|
|||||||
|
|
||||||
let isEditingMedia: Bool = updatedChatPresentationInterfaceState.editMessageState?.content != .plaintext
|
let isEditingMedia: Bool = updatedChatPresentationInterfaceState.editMessageState?.content != .plaintext
|
||||||
let editingUrlPreviewText: NSAttributedString? = isEditingMedia ? nil : updatedChatPresentationInterfaceState.interfaceState.editMessage?.inputState.inputText
|
let editingUrlPreviewText: NSAttributedString? = isEditingMedia ? nil : updatedChatPresentationInterfaceState.interfaceState.editMessage?.inputState.inputText
|
||||||
if let (updatedEditingUrlPreviewState, updatedEditingUrlPreviewSignal) = urlPreviewStateForInputText(editingUrlPreviewText, context: selfController.context, currentQuery: selfController.editingUrlPreviewQueryState?.0) {
|
if let (updatedEditingUrlPreviewState, updatedEditingUrlPreviewSignal) = urlPreviewStateForInputText(editingUrlPreviewText, context: selfController.context, currentQuery: selfController.editingUrlPreviewQueryState?.0, forPeerId: selfController.chatLocation.peerId) {
|
||||||
selfController.editingUrlPreviewQueryState?.1.dispose()
|
selfController.editingUrlPreviewQueryState?.1.dispose()
|
||||||
var inScope = true
|
var inScope = true
|
||||||
var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)?
|
var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)?
|
||||||
|
@ -794,7 +794,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch action.action {
|
switch action.action {
|
||||||
case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults:
|
case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults, .customText:
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
if let attribute = attribute as? ReplyMessageAttribute {
|
if let attribute = attribute as? ReplyMessageAttribute {
|
||||||
strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)))
|
strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)))
|
||||||
|
@ -510,7 +510,7 @@ struct UrlPreviewState {
|
|||||||
var detectedUrls: [String]
|
var detectedUrls: [String]
|
||||||
}
|
}
|
||||||
|
|
||||||
func urlPreviewStateForInputText(_ inputText: NSAttributedString?, context: AccountContext, currentQuery: UrlPreviewState?) -> (UrlPreviewState?, Signal<(TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?, NoError>)? {
|
func urlPreviewStateForInputText(_ inputText: NSAttributedString?, context: AccountContext, currentQuery: UrlPreviewState?, forPeerId: PeerId?) -> (UrlPreviewState?, Signal<(TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?, NoError>)? {
|
||||||
guard let _ = inputText else {
|
guard let _ = inputText else {
|
||||||
if currentQuery != nil {
|
if currentQuery != nil {
|
||||||
return (nil, .single({ _ in return nil }))
|
return (nil, .single({ _ in return nil }))
|
||||||
@ -522,7 +522,7 @@ func urlPreviewStateForInputText(_ inputText: NSAttributedString?, context: Acco
|
|||||||
let detectedUrls = detectUrls(inputText)
|
let detectedUrls = detectUrls(inputText)
|
||||||
if detectedUrls != (currentQuery?.detectedUrls ?? []) {
|
if detectedUrls != (currentQuery?.detectedUrls ?? []) {
|
||||||
if !detectedUrls.isEmpty {
|
if !detectedUrls.isEmpty {
|
||||||
return (UrlPreviewState(detectedUrls: detectedUrls), webpagePreview(account: context.account, urls: detectedUrls)
|
return (UrlPreviewState(detectedUrls: detectedUrls), webpagePreview(account: context.account, urls: detectedUrls, forPeerId: forPeerId)
|
||||||
|> mapToSignal { result -> Signal<(TelegramMediaWebpage, String)?, NoError> in
|
|> mapToSignal { result -> Signal<(TelegramMediaWebpage, String)?, NoError> in
|
||||||
guard case let .result(webpageResult) = result else {
|
guard case let .result(webpageResult) = result else {
|
||||||
return .complete()
|
return .complete()
|
||||||
|
@ -132,8 +132,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
private var groupCallDisposable: Disposable?
|
private var groupCallDisposable: Disposable?
|
||||||
|
|
||||||
private var callController: CallController?
|
private var callController: CallController?
|
||||||
|
private var call: PresentationCall?
|
||||||
public let hasOngoingCall = ValuePromise<Bool>(false)
|
public let hasOngoingCall = ValuePromise<Bool>(false)
|
||||||
private let callState = Promise<PresentationCallState?>(nil)
|
private let callState = Promise<PresentationCallState?>(nil)
|
||||||
|
private var awaitingCallConnectionDisposable: Disposable?
|
||||||
|
|
||||||
private var groupCallController: VoiceChatController?
|
private var groupCallController: VoiceChatController?
|
||||||
public var currentGroupCallController: ViewController? {
|
public var currentGroupCallController: ViewController? {
|
||||||
@ -741,26 +743,49 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
self.callDisposable = (callManager.currentCallSignal
|
self.callDisposable = (callManager.currentCallSignal
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||||
if let strongSelf = self {
|
guard let self else {
|
||||||
if call !== strongSelf.callController?.call {
|
return
|
||||||
strongSelf.callController?.dismiss()
|
}
|
||||||
strongSelf.callController = nil
|
|
||||||
strongSelf.hasOngoingCall.set(false)
|
if call !== self.call {
|
||||||
|
self.call = call
|
||||||
|
|
||||||
|
self.callController?.dismiss()
|
||||||
|
self.callController = nil
|
||||||
|
self.hasOngoingCall.set(false)
|
||||||
|
|
||||||
|
if let call {
|
||||||
|
self.callState.set(call.state
|
||||||
|
|> map(Optional.init))
|
||||||
|
self.hasOngoingCall.set(true)
|
||||||
|
setNotificationCall(call)
|
||||||
|
|
||||||
if let call = call {
|
if !call.isOutgoing && call.isIntegratedWithCallKit {
|
||||||
mainWindow.hostView.containerView.endEditing(true)
|
self.awaitingCallConnectionDisposable = (call.state
|
||||||
let callController = CallController(sharedContext: strongSelf, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
|> filter { state in
|
||||||
strongSelf.callController = callController
|
switch state.state {
|
||||||
strongSelf.mainWindow?.present(callController, on: .calls)
|
case .ringing:
|
||||||
strongSelf.callState.set(call.state
|
return false
|
||||||
|> map(Optional.init))
|
default:
|
||||||
strongSelf.hasOngoingCall.set(true)
|
return true
|
||||||
setNotificationCall(call)
|
}
|
||||||
} else {
|
}
|
||||||
strongSelf.callState.set(.single(nil))
|
|> take(1)
|
||||||
strongSelf.hasOngoingCall.set(false)
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
setNotificationCall(nil)
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.presentControllerWithCurrentCall()
|
||||||
|
})
|
||||||
|
} else{
|
||||||
|
self.presentControllerWithCurrentCall()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.callState.set(.single(nil))
|
||||||
|
self.hasOngoingCall.set(false)
|
||||||
|
self.awaitingCallConnectionDisposable?.dispose()
|
||||||
|
self.awaitingCallConnectionDisposable = nil
|
||||||
|
setNotificationCall(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -951,6 +976,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.callDisposable?.dispose()
|
self.callDisposable?.dispose()
|
||||||
self.groupCallDisposable?.dispose()
|
self.groupCallDisposable?.dispose()
|
||||||
self.callStateDisposable?.dispose()
|
self.callStateDisposable?.dispose()
|
||||||
|
self.awaitingCallConnectionDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var didPerformAccountSettingsImport = false
|
private var didPerformAccountSettingsImport = false
|
||||||
@ -1010,6 +1036,27 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentControllerWithCurrentCall() {
|
||||||
|
guard let call = self.call else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let currentCallController = self.callController {
|
||||||
|
if currentCallController.call === call {
|
||||||
|
self.navigateToCurrentCall()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
self.callController = nil
|
||||||
|
currentCallController.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mainWindow?.hostView.containerView.endEditing(true)
|
||||||
|
let callController = CallController(sharedContext: self, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||||
|
self.callController = callController
|
||||||
|
self.mainWindow?.present(callController, on: .calls)
|
||||||
|
}
|
||||||
|
|
||||||
public func updateNotificationTokensRegistration() {
|
public func updateNotificationTokensRegistration() {
|
||||||
let sandbox: Bool
|
let sandbox: Bool
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 8f41ea265404dea86f2444a47343993ccdc3a64e
|
Subproject commit 8f2f1b90209b014071453079c1f28e115ee8de12
|
@ -312,7 +312,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let webView = WebAppWebView()
|
let webView = WebAppWebView(account: context.account)
|
||||||
webView.alpha = 0.0
|
webView.alpha = 0.0
|
||||||
webView.navigationDelegate = self
|
webView.navigationDelegate = self
|
||||||
webView.uiDelegate = self
|
webView.uiDelegate = self
|
||||||
@ -418,8 +418,6 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
self.setupWebView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -434,6 +432,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
|
self.setupWebView()
|
||||||
|
|
||||||
guard let webView = self.webView else {
|
guard let webView = self.webView else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import UIKit
|
|||||||
import Display
|
import Display
|
||||||
import WebKit
|
import WebKit
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
private let findActiveElementY = """
|
private let findActiveElementY = """
|
||||||
function getOffset(el) {
|
function getOffset(el) {
|
||||||
@ -91,8 +92,22 @@ function disconnectObserver() {
|
|||||||
final class WebAppWebView: WKWebView {
|
final class WebAppWebView: WKWebView {
|
||||||
var handleScriptMessage: (WKScriptMessage) -> Void = { _ in }
|
var handleScriptMessage: (WKScriptMessage) -> Void = { _ in }
|
||||||
|
|
||||||
init() {
|
init(account: Account) {
|
||||||
let configuration = WKWebViewConfiguration()
|
let configuration = WKWebViewConfiguration()
|
||||||
|
|
||||||
|
let uuid: UUID
|
||||||
|
|
||||||
|
if let current = UserDefaults.standard.object(forKey: "TelegramWebStoreUUID_\(account.id.int64)") as? String {
|
||||||
|
uuid = UUID(uuidString: current)!
|
||||||
|
} else {
|
||||||
|
uuid = UUID()
|
||||||
|
UserDefaults.standard.set(uuid.uuidString, forKey: "TelegramWebStoreUUID_\(account.id.int64)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if #available(iOS 17.0, *) {
|
||||||
|
configuration.websiteDataStore = WKWebsiteDataStore(forIdentifier: uuid)
|
||||||
|
}
|
||||||
|
|
||||||
let contentController = WKUserContentController()
|
let contentController = WKUserContentController()
|
||||||
|
|
||||||
var handleScriptMessageImpl: ((WKScriptMessage) -> Void)?
|
var handleScriptMessageImpl: ((WKScriptMessage) -> Void)?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user