diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 6ba0d96480..5896b6126b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10614,3 +10614,7 @@ Sorry for the inconvenience."; "MediaEditor.Shortcut.Location" = "Location"; "MediaEditor.Shortcut.Reaction" = "Reaction"; "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."; diff --git a/Tests/CallUITest/Sources/ViewController.swift b/Tests/CallUITest/Sources/ViewController.swift index c17d759f73..1dcd95c9c1 100644 --- a/Tests/CallUITest/Sources/ViewController.swift +++ b/Tests/CallUITest/Sources/ViewController.swift @@ -142,6 +142,8 @@ public final class ViewController: UIViewController { self.callState.lifecycleState = .terminated(PrivateCallScreen.State.TerminatedState(duration: 82.0)) self.callState.remoteVideo = nil self.callState.localVideo = nil + self.callState.isMicrophoneMuted = false + self.callState.isRemoteBatteryLow = false self.update(transition: .spring(duration: 0.4)) } callScreenView.backAction = { [weak self] in @@ -151,6 +153,12 @@ public final class ViewController: UIViewController { self.callState.isMicrophoneMuted = !self.callState.isMicrophoneMuted 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) { diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index cbdc55b4a2..a5b8eefe16 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -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) { switch self { case .immediate: diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 65deb106b9..36c9486c3e 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -925,3 +925,68 @@ public func drawSvgPath(_ context: CGContext, path: StaticString, strokeOnMove: } } } + +public func convertSvgPath(_ path: StaticString) throws -> CGPath { + var index: UnsafePointer = 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 +} diff --git a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift index 512945b3ac..cbc3d644cf 100644 --- a/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift +++ b/submodules/InAppPurchaseManager/Sources/InAppPurchaseManager.swift @@ -575,6 +575,8 @@ private final class PendingInAppPurchaseState: Codable { case additionalPeerIds case countries case onlyNewSubscribers + case showWinners + case prizeDescription case randomId case untilDate } @@ -593,7 +595,7 @@ private final class PendingInAppPurchaseState: Codable { case restore case gift(peerId: 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 { 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) }, countries: try container.decodeIfPresent([String].self, forKey: .countries) ?? [], 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), 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(peerIds.map { $0.toInt64() }, forKey: .peers) 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(boostPeer.toInt64(), forKey: .boostPeer) try container.encode(additionalPeerIds.map { $0.toInt64() }, forKey: .additionalPeerIds) try container.encode(countries, forKey: .countries) 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(untilDate, forKey: .untilDate) } @@ -669,8 +675,8 @@ private final class PendingInAppPurchaseState: Codable { self = .gift(peerId: peerId) case let .giftCode(peerIds, boostPeer, _, _): self = .giftCode(peerIds: peerIds, boostPeer: boostPeer) - case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate, _, _): - self = .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate) + case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, 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) case let .giftCode(peerIds, boostPeer): return .giftCode(peerIds: peerIds, boostPeer: boostPeer, currency: currency, amount: amount) - case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, randomId, untilDate): - return .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount) + case let .giveaway(boostPeer, additionalPeerIds, countries, onlyNewSubscribers, showWinners, prizeDescription, randomId, untilDate): + return .giveaway(boostPeer: boostPeer, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate, currency: currency, amount: amount) } } } diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h index 4ea98cdf5f..ed78e3cbef 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGModernConversationInputMicButton.h @@ -88,5 +88,6 @@ - (void)_commitLocked; - (void)setHidesPanelOnLock; +- (UIView *)createLockPanelView; @end diff --git a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m index bf73ed470e..7161bb7314 100644 --- a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m +++ b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m @@ -115,7 +115,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius UIImageView *_innerIconView; UIView *_lockPanelWrapperView; - UIImageView *_lockPanelView; + UIView *_lockPanelView; UIImageView *_lockArrowView; TGModernConversationInputLockView *_lockView; UIImage *_previousIcon; @@ -265,7 +265,9 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius if (!update) 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)); _lockView.color = self.pallete.lockColor; @@ -341,6 +343,13 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius 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 { if (!_locked) { _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)]; [[_presentation view] addSubview:_lockPanelWrapperView]; - _lockPanelView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 72.0f)]; - _lockPanelView.userInteractionEnabled = true; - _lockPanelView.image = [self panelBackgroundImage]; + _lockPanelView = [self createLockPanelView]; [_lockPanelWrapperView addSubview:_lockPanelView]; diff --git a/submodules/PremiumUI/Sources/CreateGiveawayController.swift b/submodules/PremiumUI/Sources/CreateGiveawayController.swift index a54f4aeac0..fb22a17f21 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayController.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayController.swift @@ -53,6 +53,8 @@ private enum CreateGiveawaySection: Int32 { case subscriptions case channels case users + case winners + case prizeDescription case time case duration } @@ -92,6 +94,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { case usersNew(PresentationTheme, String, String, Bool) 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 timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool) case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?, Bool, Bool) @@ -113,6 +123,10 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { return CreateGiveawaySection.channels.rawValue case .usersHeader, .usersAll, .usersNew, .usersInfo: 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: return CreateGiveawaySection.time.rawValue case .durationHeader, .duration, .durationInfo: @@ -154,20 +168,32 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { return 104 case .usersInfo: return 105 - case .timeHeader: + case .winnersHeader: return 106 - case .timeExpiryDate: + case .winners: return 107 - case .timeCustomPicker: + case .winnersInfo: return 108 - case .timeInfo: + case .prizeDescriptionHeader: return 109 - case .durationHeader: + case .prizeDescription: 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, _, _, _, _, _, _, _): - return 111 + index + return 117 + index case .durationInfo: - return 120 + return 130 } } @@ -269,7 +295,42 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { } else { 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): if case let .timeHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -423,6 +484,30 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { }) case let .usersInfo(_, text): 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): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) 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(.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(.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)) @@ -680,11 +773,13 @@ private struct CreateGiveawayControllerState: Equatable { var mode: Mode var subscriptions: Int32 - var channels: [EnginePeer.Id] - var peers: [EnginePeer.Id] + var channels: [EnginePeer.Id] = [] + var peers: [EnginePeer.Id] = [] var selectedMonths: Int32? - var countries: [String] - var onlyNewEligible: Bool + var countries: [String] = [] + var onlyNewEligible: Bool = false + var showWinners: Bool = false + var prizeDescription: String = "" var time: Int32 var pickingExpiryTime = false var pickingExpiryDate = false @@ -722,7 +817,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio let minDate = currentTime + 60 * 30 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 stateValue = Atomic(value: initialState) @@ -948,7 +1043,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio let quantity: Int32 switch state.mode { 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 case .gift: purpose = .giftCode(peerIds: state.peers, boostPeer: peerId, currency: currency, amount: amount) @@ -1040,7 +1135,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio 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: { if let controller, let navigationController = controller.navigationController as? NavigationController { var controllers = navigationController.viewControllers diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 212ceef5d5..637f870dc3 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -414,7 +414,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1251549527] = { return Api.InputStickeredMedia.parse_inputStickeredMediaPhoto($0) } dict[1634697192] = { return Api.InputStorePaymentPurpose.parse_inputStorePaymentGiftPremium($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[1012306921] = { return Api.InputTheme.parse_inputTheme($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[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($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[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($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[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($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[1891958275] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) } dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) } diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index 242e60ab2b..a118bfc4c2 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -604,7 +604,7 @@ public extension Api { indirect enum InputStorePaymentPurpose: TypeConstructorDescription { case inputStorePaymentGiftPremium(userId: Api.InputUser, 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) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { @@ -631,9 +631,9 @@ public extension Api { serializeString(currency, buffer: buffer, boxed: false) serializeInt64(amount, buffer: buffer, boxed: false) 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 { - buffer.appendInt32(2090038758) + buffer.appendInt32(369444042) } serializeInt32(flags, buffer: buffer, boxed: false) boostPeer.serialize(buffer, true) @@ -647,6 +647,7 @@ public extension Api { for item in countriesIso2! { 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) serializeInt32(untilDate, 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)]) 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)]) - case .inputStorePaymentPremiumGiveaway(let flags, let boostPeer, let additionalPeers, let countriesIso2, 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)]) + 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), ("prizeDescription", prizeDescription as Any), ("randomId", randomId as Any), ("untilDate", untilDate as Any), ("currency", currency as Any), ("amount", amount as Any)]) case .inputStorePaymentPremiumSubscription(let flags): return ("inputStorePaymentPremiumSubscription", [("flags", flags as Any)]) } @@ -735,24 +736,27 @@ public extension Api { if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { _4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) } } - var _5: Int64? - _5 = reader.readInt64() - var _6: Int32? - _6 = reader.readInt32() - var _7: String? - _7 = parseString(reader) - var _8: Int64? - _8 = reader.readInt64() + var _5: String? + if Int(_1!) & Int(1 << 4) != 0 {_5 = parseString(reader) } + var _6: Int64? + _6 = reader.readInt64() + var _7: Int32? + _7 = reader.readInt32() + var _8: String? + _8 = parseString(reader) + var _9: Int64? + _9 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != 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 _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiveaway(flags: _1!, boostPeer: _2!, additionalPeers: _3, countriesIso2: _4, randomId: _5!, untilDate: _6!, currency: _7!, amount: _8!) + let _c9 = _9 != nil + 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 { return nil diff --git a/submodules/TelegramApi/Sources/Api13.swift b/submodules/TelegramApi/Sources/Api13.swift index 6ff5682360..32be77f86c 100644 --- a/submodules/TelegramApi/Sources/Api13.swift +++ b/submodules/TelegramApi/Sources/Api13.swift @@ -697,7 +697,7 @@ public extension Api { case messageMediaGame(game: Api.Game) case messageMediaGeo(geo: Api.GeoPoint) 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 messageMediaPhoto(flags: Int32, photo: Api.Photo?, ttlSeconds: Int32?) case messageMediaPoll(poll: Api.Poll, results: Api.PollResults) @@ -762,9 +762,9 @@ public extension Api { serializeInt32(period, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)} 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 { - buffer.appendInt32(1478887012) + buffer.appendInt32(-626162256) } serializeInt32(flags, buffer: buffer, boxed: false) buffer.appendInt32(481674261) @@ -777,6 +777,7 @@ public extension Api { for item in countriesIso2! { 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(months, buffer: buffer, boxed: false) serializeInt32(untilDate, buffer: buffer, boxed: false) @@ -862,8 +863,8 @@ public extension Api { return ("messageMediaGeo", [("geo", geo as Any)]) 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)]) - case .messageMediaGiveaway(let flags, let channels, let countriesIso2, 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)]) + 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), ("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): 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): @@ -1007,20 +1008,23 @@ public extension Api { if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) } } - var _4: Int32? - _4 = reader.readInt32() + var _4: String? + if Int(_1!) & Int(1 << 3) != 0 {_4 = parseString(reader) } var _5: Int32? _5 = reader.readInt32() var _6: Int32? _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != 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 _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.MessageMedia.messageMediaGiveaway(flags: _1!, channels: _2!, countriesIso2: _3, quantity: _4!, months: _5!, untilDate: _6!) + let _c7 = _7 != nil + 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 { return nil diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 40fbe24d59..1e5e8bca16 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -911,7 +911,7 @@ public extension Api.payments { public extension Api.payments { enum GiveawayInfo: TypeConstructorDescription { 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) { 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 << 4) != 0 {serializeString(disallowedCountry!, buffer: buffer, boxed: false)} 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 { - buffer.appendInt32(13456752) + buffer.appendInt32(-1966612121) } serializeInt32(flags, 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(winnersCount, 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 } } @@ -943,8 +948,8 @@ public extension Api.payments { switch self { 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)]) - case .giveawayInfoResults(let flags, let startDate, let giftCodeSlug, let finishDate, let winnersCount, let activatedCount): - 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)]) + 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), ("winners", winners as Any)]) } } @@ -984,14 +989,19 @@ public extension Api.payments { _5 = reader.readInt32() var _6: Int32? _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 _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, finishDate: _4!, winnersCount: _5!, activatedCount: _6!) + let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil + 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 { return nil diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift index 5fe09436a1..bc489da5e9 100644 --- a/submodules/TelegramCallsUI/Sources/CallController.swift +++ b/submodules/TelegramCallsUI/Sources/CallController.swift @@ -136,7 +136,7 @@ public final class CallController: ViewController { override public func loadDisplayNode() { 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 { 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) } diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift index 53d8dbbbb5..4a4db0bd29 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift @@ -29,8 +29,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP private let callScreen: PrivateCallScreen private var callScreenState: PrivateCallScreen.State? - private var shouldStayHiddenUntilConnection: Bool = false - private var callStartTimestamp: Double? private var callState: PresentationCallState? @@ -67,7 +65,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, - shouldStayHiddenUntilConnection: Bool = false, easyDebugAccess: Bool, call: PresentationCall ) { @@ -80,8 +77,6 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP self.containerView = UIView() self.callScreen = PrivateCallScreen() - self.shouldStayHiddenUntilConnection = shouldStayHiddenUntilConnection - super.init() self.view.addSubview(self.containerView) @@ -123,6 +118,12 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP } self.back?() } + self.callScreen.closeAction = { [weak self] in + guard let self else { + return + } + self.dismissedInteractively?() + } self.callScreenState = PrivateCallScreen.State( lifecycleState: .connecting, @@ -130,7 +131,8 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP shortName: " ", avatarImage: nil, audioOutput: .internalSpeaker, - isMicrophoneMuted: false, + isLocalAudioMuted: false, + isRemoteAudioMuted: false, localVideo: nil, remoteVideo: nil, isRemoteBatteryLow: false @@ -145,8 +147,8 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP return } self.isMuted = isMuted - if callScreenState.isMicrophoneMuted != isMuted { - callScreenState.isMicrophoneMuted = isMuted + if callScreenState.isLocalAudioMuted != isMuted { + callScreenState.isLocalAudioMuted = isMuted self.callScreenState = callScreenState self.update(transition: .animated(duration: 0.3, curve: .spring)) } @@ -373,6 +375,13 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP callScreenState.isRemoteBatteryLow = false } + switch callState.remoteAudioState { + case .muted: + callScreenState.isRemoteAudioMuted = true + case .active: + callScreenState.isRemoteAudioMuted = false + } + if self.callScreenState != callScreenState { self.callScreenState = callScreenState self.update(transition: .animated(duration: 0.35, curve: .spring)) @@ -393,6 +402,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP return } callScreenState.name = peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) + callScreenState.shortName = peer.compactDisplayTitle if self.currentPeer?.smallProfileImage != peer.smallProfileImage { self.peerAvatarDisposable?.dispose() @@ -460,16 +470,14 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP self.containerView.layer.removeAnimation(forKey: "scale") 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.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } + 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) } } func animateOut(completion: @escaping () -> Void) { 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.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in 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.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( size: layout.size, insets: layout.insets(options: [.statusBar]), diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 4a060a7438..1da5c970ba 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -422,12 +422,15 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI case let .messageMediaStory(flags, peerId, id, _): let isMention = (flags & (1 << 1)) != 0 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 = [] if (apiFlags & (1 << 0)) != 0 { 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) } } diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 89018bee15..eced7585d4 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 167 + return 168 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaGiveaway.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaGiveaway.swift index 7a9ef7e304..46723467ff 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaGiveaway.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaGiveaway.swift @@ -9,6 +9,7 @@ public final class TelegramMediaGiveaway: Media, Equatable { } public static let onlyNewSubscribers = Flags(rawValue: 1 << 0) + public static let showWinners = Flags(rawValue: 1 << 1) } public var id: MediaId? { @@ -24,14 +25,16 @@ public final class TelegramMediaGiveaway: Media, Equatable { public let quantity: Int32 public let months: Int32 public let untilDate: Int32 - - public init(flags: Flags, channelPeerIds: [PeerId], countries: [String], quantity: Int32, months: Int32, untilDate: Int32) { + public let prizeDescription: String? + + public init(flags: Flags, channelPeerIds: [PeerId], countries: [String], quantity: Int32, months: Int32, untilDate: Int32, prizeDescription: String?) { self.flags = flags self.channelPeerIds = channelPeerIds self.countries = countries self.quantity = quantity self.months = months self.untilDate = untilDate + self.prizeDescription = prizeDescription } public init(decoder: PostboxDecoder) { @@ -41,6 +44,7 @@ public final class TelegramMediaGiveaway: Media, Equatable { self.quantity = decoder.decodeInt32ForKey("qty", orElse: 0) self.months = decoder.decodeInt32ForKey("mts", orElse: 0) self.untilDate = decoder.decodeInt32ForKey("unt", orElse: 0) + self.prizeDescription = decoder.decodeOptionalStringForKey("des") } public func encode(_ encoder: PostboxEncoder) { @@ -50,6 +54,11 @@ public final class TelegramMediaGiveaway: Media, Equatable { encoder.encodeInt32(self.quantity, forKey: "qty") encoder.encodeInt32(self.months, forKey: "mts") 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 { @@ -78,6 +87,9 @@ public final class TelegramMediaGiveaway: Media, Equatable { if self.untilDate != other.untilDate { return false } + if self.prizeDescription != other.prizeDescription { + return false + } return true } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift index 4731a335db..57f7f623d8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/AppStore.swift @@ -16,7 +16,7 @@ public enum AppStoreTransactionPurpose { case restore case gift(peerId: 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 { @@ -59,7 +59,7 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran 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 in guard let peer = transaction.getPeer(boostPeerId), let apiBoostPeer = apiInputPeer(peer) else { return .complete() @@ -68,6 +68,9 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran if onlyNewSubscribers { flags |= (1 << 0) } + if showWinners { + flags |= (1 << 0) + } var additionalPeers: [Api.InputPeer] = [] if !additionalPeerIds.isEmpty { flags |= (1 << 1) @@ -80,7 +83,10 @@ private func apiInputStorePaymentPurpose(account: Account, purpose: AppStoreTran if !countries.isEmpty { 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 } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift index 24bada7af3..12911592de 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/BotPaymentForm.swift @@ -7,7 +7,7 @@ import TelegramApi public enum BotPaymentInvoiceSource { case message(MessageId) 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) case let .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 { return nil } @@ -222,6 +222,9 @@ private func _internal_parseInputInvoice(transaction: Transaction, source: BotPa if onlyNewSubscribers { flags |= (1 << 0) } + if showWinners { + flags |= (1 << 3) + } var additionalPeers: [Api.InputPeer] = [] if !additionalPeerIds.isEmpty { flags |= (1 << 1) @@ -234,7 +237,11 @@ private func _internal_parseInputInvoice(transaction: Transaction, source: BotPa if !countries.isEmpty { 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 @@ -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) - 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 case let .Id(id) = message.id { receiptMessageId = id diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift index 784a06086e..27afdcf2aa 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift @@ -120,7 +120,7 @@ func _internal_getPremiumGiveawayInfo(account: Account, peerId: EnginePeer.Id, m } else { 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 if (flags & (1 << 1)) != 0 { status = .refunded @@ -201,18 +201,19 @@ public enum LaunchPrepaidGiveawayError { case generic } -func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, randomId: Int64, untilDate: Int32) -> Signal { +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 { return account.postbox.transaction { transaction -> Signal in var flags: Int32 = 0 if onlyNewSubscribers { flags |= (1 << 0) } - + if showWinners { + flags |= (1 << 3) + } var inputPeer: Api.InputPeer? if let peer = transaction.getPeer(peerId), let apiPeer = apiInputPeer(peer) { inputPeer = apiPeer } - var additionalPeers: [Api.InputPeer] = [] if !additionalPeerIds.isEmpty { flags |= (1 << 1) @@ -222,15 +223,16 @@ func _internal_launchPrepaidGiveaway(account: Account, peerId: EnginePeer.Id, id } } } - if !countries.isEmpty { flags |= (1 << 2) } - + if let _ = prizeDescription { + flags |= (1 << 4) + } guard let inputPeer = inputPeer else { 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 return .generic } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index d275bc2f26..07cbd5e7ff 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -62,8 +62,8 @@ public extension TelegramEngine { 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 { - return _internal_launchPrepaidGiveaway(account: self.account, peerId: peerId, id: id, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, randomId: randomId, untilDate: untilDate) + public func launchPrepaidGiveaway(peerId: EnginePeer.Id, id: Int64, additionalPeerIds: [EnginePeer.Id], countries: [String], onlyNewSubscribers: Bool, showWinners: Bool, prizeDescription: String?, randomId: Int64, untilDate: Int32) -> Signal { + return _internal_launchPrepaidGiveaway(account: self.account, peerId: peerId, id: id, additionalPeerIds: additionalPeerIds, countries: countries, onlyNewSubscribers: onlyNewSubscribers, showWinners: showWinners, prizeDescription: prizeDescription, randomId: randomId, untilDate: untilDate) } } } diff --git a/submodules/TelegramCore/Sources/WebpagePreview.swift b/submodules/TelegramCore/Sources/WebpagePreview.swift index 17e64c6a2d..229e69deca 100644 --- a/submodules/TelegramCore/Sources/WebpagePreview.swift +++ b/submodules/TelegramCore/Sources/WebpagePreview.swift @@ -3,7 +3,11 @@ import Postbox import SwiftSignalKit import TelegramApi import MtProtoKit - +import LinkPresentation +#if os(iOS) +import UIKit +#endif +import CoreServices public enum WebpagePreviewResult: Equatable { public struct Result: Equatable { @@ -15,8 +19,8 @@ public enum WebpagePreviewResult: Equatable { case result(Result?) } -public func webpagePreview(account: Account, urls: [String], webpageId: MediaId? = nil) -> Signal { - return webpagePreviewWithProgress(account: account, urls: urls) +public func webpagePreview(account: Account, urls: [String], webpageId: MediaId? = nil, forPeerId: PeerId? = nil) -> Signal { + return webpagePreviewWithProgress(account: account, urls: urls, webpageId: webpageId, forPeerId: forPeerId) |> mapToSignal { next -> Signal in if case let .result(result) = next { return .single(.result(result)) @@ -35,7 +39,7 @@ public func normalizedWebpagePreviewUrl(url: String) -> String { return url } -public func webpagePreviewWithProgress(account: Account, urls: [String], webpageId: MediaId? = nil) -> Signal { +public func webpagePreviewWithProgress(account: Account, urls: [String], webpageId: MediaId? = nil, forPeerId: PeerId? = nil) -> Signal { return account.postbox.transaction { transaction -> Signal in if let webpageId = webpageId, let webpage = transaction.getMedia(webpageId) as? TelegramMediaWebpage, let url = webpage.content.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))) } 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) |> `catch` { _ -> Signal, NoError> in return .single(.result(.messageMediaEmpty)) diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/BUILD b/submodules/TelegramUI/Components/Calls/CallScreen/BUILD index 6d202c1f72..ac35b7c0f2 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/BUILD +++ b/submodules/TelegramUI/Components/Calls/CallScreen/BUILD @@ -67,6 +67,7 @@ swift_library( "//submodules/SSignalKit/SwiftSignalKit", "//submodules/TelegramUI/Components/AnimatedTextComponent", "//submodules/AppBundle", + "//submodules/UIKitRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift index c1a20242eb..0e86f8f821 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift @@ -58,8 +58,10 @@ final class ButtonGroupView: OverlayMaskContainerView { private var buttons: [Button]? private var buttonViews: [Button.Content.Key: ContentOverlayButton] = [:] - private var noticeViews: [AnyHashable: NoticeView] = [:] + private var closeButtonView: CloseButtonView? + + var closePressed: (() -> Void)? override init(frame: CGRect) { super.init(frame: frame) @@ -79,7 +81,7 @@ final class ButtonGroupView: OverlayMaskContainerView { 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 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) + 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 { let title: String let image: UIImage? @@ -213,9 +255,10 @@ final class ButtonGroupView: OverlayMaskContainerView { Transition.immediate.setScale(view: buttonView, scale: 0.001) buttonView.alpha = 0.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))) buttonView.update(size: CGSize(width: buttonSize, height: buttonSize), image: image, isSelected: isActive, isDestructive: isDestructive, title: title, transition: buttonTransition) buttonX += buttonSize + buttonSpacing diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CloseButtonView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CloseButtonView.swift new file mode 100644 index 0000000000..110814ead6 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CloseButtonView.swift @@ -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) + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift index 12d62e5104..e02f4c9d00 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift @@ -63,8 +63,7 @@ final class ContentOverlayButton: HighlightTrackingButton, OverlayMaskContainerV let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width if highlighted { - self.layer.removeAnimation(forKey: "opacity") - self.layer.removeAnimation(forKey: "transform") + self.layer.removeAnimation(forKey: "sublayerTransform") let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut)) transition.setScale(layer: self.layer, scale: topScale) } else { diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/EmojiExpandedInfoView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/EmojiExpandedInfoView.swift index 3a0cf981bd..f2b0ffd7ec 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/EmojiExpandedInfoView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/EmojiExpandedInfoView.swift @@ -5,10 +5,10 @@ import ComponentFlow final class EmojiExpandedInfoView: OverlayMaskContainerView { private struct Params: Equatable { - var constrainedWidth: CGFloat + var width: CGFloat - init(constrainedWidth: CGFloat) { - self.constrainedWidth = constrainedWidth + init(width: CGFloat) { + self.width = width } } @@ -129,8 +129,8 @@ final class EmojiExpandedInfoView: OverlayMaskContainerView { return nil } - func update(constrainedWidth: CGFloat, transition: Transition) -> CGSize { - let params = Params(constrainedWidth: constrainedWidth) + func update(width: CGFloat, transition: Transition) -> CGSize { + let params = Params(width: width) if let currentLayout = self.currentLayout, currentLayout.params == params { return currentLayout.size } @@ -142,16 +142,12 @@ final class EmojiExpandedInfoView: OverlayMaskContainerView { private func update(params: Params, transition: Transition) -> CGSize { let buttonHeight: CGFloat = 56.0 - var constrainedWidth = params.constrainedWidth - constrainedWidth = min(constrainedWidth, 300.0) + 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) + 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 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)) diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift index 53ada01185..84beb5570c 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/PrivateCallVideoLayer.swift @@ -198,8 +198,8 @@ final class PrivateCallVideoLayer: MetalEngineSubjectLayer, MetalEngineSubject { encoder.setFragmentTexture(blurredTexture, index: 0) - var brightness: Float = 1.0 - var saturation: Float = 1.2 + var brightness: Float = 0.7 + var saturation: Float = 1.3 var overlay: SIMD4 = SIMD4(1.0, 1.0, 1.0, 0.2) encoder.setFragmentBytes(&brightness, length: 4, index: 0) encoder.setFragmentBytes(&saturation, length: 4, index: 1) diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RatingView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RatingView.swift new file mode 100644 index 0000000000..6b54934313 --- /dev/null +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RatingView.swift @@ -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 + } +} diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RoundedCornersView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RoundedCornersView.swift index 664616101a..ecd961fbc1 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RoundedCornersView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/RoundedCornersView.swift @@ -5,17 +5,16 @@ import ComponentFlow final class RoundedCornersView: UIImageView { private let color: UIColor + private let smoothCorners: Bool + private var currentCornerRadius: CGFloat? private var cornerImage: UIImage? - init(color: UIColor) { + init(color: UIColor, smoothCorners: Bool = false) { self.color = color + self.smoothCorners = smoothCorners super.init(image: nil) - - if #available(iOS 13.0, *) { - self.layer.cornerCurve = .circular - } } required init?(coder: NSCoder) { @@ -26,10 +25,23 @@ final class RoundedCornersView: UIImageView { guard let cornerRadius = self.currentCornerRadius else { 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 { 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.clipsToBounds = false @@ -52,6 +64,14 @@ final class RoundedCornersView: UIImageView { if let previousCornerRadius, self.layer.animation(forKey: "cornerRadius") == nil { 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 guard let self, completed else { return diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift index b67bebc28c..71a785965c 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift @@ -100,7 +100,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView { public var shortName: String public var avatarImage: UIImage? public var audioOutput: AudioOutput - public var isMicrophoneMuted: Bool + public var isLocalAudioMuted: Bool + public var isRemoteAudioMuted: Bool public var localVideo: VideoSource? public var remoteVideo: VideoSource? public var isRemoteBatteryLow: Bool @@ -111,7 +112,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView { shortName: String, avatarImage: UIImage?, audioOutput: AudioOutput, - isMicrophoneMuted: Bool, + isLocalAudioMuted: Bool, + isRemoteAudioMuted: Bool, localVideo: VideoSource?, remoteVideo: VideoSource?, isRemoteBatteryLow: Bool @@ -121,7 +123,8 @@ public final class PrivateCallScreen: OverlayMaskContainerView { self.shortName = shortName self.avatarImage = avatarImage self.audioOutput = audioOutput - self.isMicrophoneMuted = isMicrophoneMuted + self.isLocalAudioMuted = isLocalAudioMuted + self.isRemoteAudioMuted = isRemoteAudioMuted self.localVideo = localVideo self.remoteVideo = remoteVideo self.isRemoteBatteryLow = isRemoteBatteryLow @@ -143,7 +146,10 @@ public final class PrivateCallScreen: OverlayMaskContainerView { if lhs.audioOutput != rhs.audioOutput { return false } - if lhs.isMicrophoneMuted != rhs.isMicrophoneMuted { + if lhs.isLocalAudioMuted != rhs.isLocalAudioMuted { + return false + } + if lhs.isRemoteAudioMuted != rhs.isRemoteAudioMuted { return false } if lhs.localVideo !== rhs.localVideo { @@ -224,6 +230,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView { public var microhoneMuteAction: (() -> Void)? public var endCallAction: (() -> Void)? public var backAction: (() -> Void)? + public var closeAction: (() -> Void)? public override init(frame: CGRect) { self.overlayContentsView = UIView() @@ -264,10 +271,6 @@ public final class PrivateCallScreen: OverlayMaskContainerView { self.avatarTransformLayer.addSublayer(self.avatarLayer) 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.overlayContentsView.mask = self.maskContents @@ -310,6 +313,13 @@ public final class PrivateCallScreen: OverlayMaskContainerView { } self.backAction?() } + + self.buttonGroupView.closePressed = { [weak self] in + guard let self else { + return + } + self.closeAction?() + } } public required init?(coder: NSCoder) { @@ -497,6 +507,13 @@ public final class PrivateCallScreen: OverlayMaskContainerView { 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)] = [] if self.swapLocalAndRemoteVideo { if let activeLocalVideoSource = self.activeLocalVideoSource { @@ -554,7 +571,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView { } 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 { return } @@ -584,9 +601,12 @@ public final class PrivateCallScreen: OverlayMaskContainerView { } 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")) } + 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 { 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")) } - 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? 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) 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)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index 3222c8b419..b06d955ff9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -33,6 +33,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode private let prizeTitleNode: TextNode private let prizeTextNode: TextNode + private let additionalPrizeTitleNode: TextNode + private let additionalPrizeTextNode: TextNode + private let participantsTitleNode: TextNode private let participantsTextNode: TextNode @@ -81,6 +84,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode self.prizeTitleNode = TextNode() self.prizeTextNode = TextNode() + self.additionalPrizeTitleNode = TextNode() + self.additionalPrizeTextNode = TextNode() + self.participantsTitleNode = TextNode() self.participantsTextNode = TextNode() @@ -101,6 +107,8 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode self.addSubnode(self.prizeTitleNode) self.addSubnode(self.prizeTextNode) + self.addSubnode(self.additionalPrizeTitleNode) + self.addSubnode(self.additionalPrizeTextNode) self.addSubnode(self.participantsTitleNode) self.addSubnode(self.participantsTextNode) self.addSubnode(self.countriesTextNode) @@ -181,6 +189,9 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode let makePrizeTitleLayout = TextNode.asyncLayout(self.prizeTitleNode) 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 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) var prizeTextString: NSAttributedString? + var additionalPrizeTitleString: NSAttributedString? + var additionalPrizeTextString: NSAttributedString? if let giveaway { prizeTextString = parseMarkdownIntoAttributedString(item.presentationData.strings.Chat_Giveaway_Message_PrizeText( item.presentationData.strings.Chat_Giveaway_Message_Subscriptions(giveaway.quantity), @@ -244,6 +257,11 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode return ("URL", url) } ), 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) @@ -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 (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 (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, prizeTextLayout.size.width) + maxContentWidth = max(maxContentWidth, additionalPrizeTitleLayout.size.width) + maxContentWidth = max(maxContentWidth, additionalPrizeTextLayout.size.width) maxContentWidth = max(maxContentWidth, participantsTitleLayout.size.width) maxContentWidth = max(maxContentWidth, participantsTextLayout.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) + if additionalPrizeTextLayout.size.height > 0.0 { + layoutSize.height += additionalPrizeTitleLayout.size.height + additionalPrizeTextLayout.size.height + 7.0 + } if countriesTextLayout.size.height > 0.0 { layoutSize.height += countriesTextLayout.size.height + 7.0 } @@ -473,10 +500,12 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode strongSelf.giveaway = giveaway strongSelf.updateVisibility() - + let _ = badgeTextApply() let _ = prizeTitleApply() let _ = prizeTextApply() + let _ = additionalPrizeTitleApply() + let _ = additionalPrizeTextApply() let _ = participantsTitleApply() let _ = participantsTextApply() let _ = countriesTextApply() @@ -502,12 +531,19 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode } originY += 112.0 - + 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 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 + 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) 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) diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift index 446862b82c..822eacf1e8 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/ChatTextInputMediaRecordingButton.swift @@ -445,6 +445,15 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM (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() { self.isEnabled = false 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) + } + } +} diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift index 637cd20049..06a736e3f5 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift @@ -65,6 +65,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock { [ "Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor, "Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor, + "Rectangle 2.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor, "Path.Path.Обводка 1": theme.chat.inputPanel.panelControlAccentColor, "Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor ].forEach { key, value in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index a3a0913996..eca8b8c05e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -97,6 +97,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { let contextSourceNode: ContextReferenceContentNode private let textNode: ImmediateTextNode private let iconNode: ASImageNode + private let backIconLayer: SimpleShapeLayer private var animationNode: MoreIconNode? private let backgroundNode: NavigationBackgroundNode @@ -117,6 +118,15 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.iconNode.displaysAsynchronously = false 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) 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.textNode) self.contextSourceNode.addSubnode(self.iconNode) + self.contextSourceNode.layer.addSublayer(self.backIconLayer) self.addSubnode(self.containerNode) @@ -146,13 +157,43 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { 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.backgroundNode.updateColor(color: backgroundColor, transition: transition) transition.updateTintColor(layer: self.textNode.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 { transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor) @@ -184,9 +225,9 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { var animationState: MoreIconNodeState = .more switch key { case .back: - text = "" + text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.thinBackArrowImage + icon = NavigationBar.backArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text @@ -270,11 +311,19 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { } let inset: CGFloat = 0.0 + var textInset: CGFloat = 0.0 + switch key { + case .back: + textInset += 11.0 + default: + break + } let resultSize: CGSize - let textFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - textSize.height) / 2.0)), size: textSize) - self.textNode.frame = textFrame + let textFrame = CGRect(origin: CGPoint(x: inset + textInset, y: floor((height - textSize.height) / 2.0)), size: textSize) + self.textNode.position = textFrame.center + self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size) if let animationNode = self.animationNode { 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) resultSize = size } 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) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index c12dd1e360..18b3dd3f19 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -36,18 +36,21 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { private var backgroundContentColor: UIColor = .clear private var contentsColor: UIColor = .white + private var canBeExpanded: Bool = false 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.contentsColor = contentsColor 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 { - 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.alpha = 0.0 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 { transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) @@ -202,7 +205,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) 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 { buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index c6bb1f3716..18af6ed860 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -553,6 +553,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let navigationContentsAccentColor: UIColor let navigationContentsPrimaryColor: UIColor let navigationContentsSecondaryColor: UIColor + let navigationContentsCanBeExpanded: Bool let contentButtonBackgroundColor: UIColor let contentButtonForegroundColor: UIColor @@ -640,6 +641,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { navigationContentsAccentColor = collapsedHeaderNavigationContentsAccentColor navigationContentsPrimaryColor = collapsedHeaderNavigationContentsPrimaryColor navigationContentsSecondaryColor = collapsedHeaderNavigationContentsSecondaryColor + navigationContentsCanBeExpanded = true + contentButtonBackgroundColor = collapsedHeaderContentButtonBackgroundColor contentButtonForegroundColor = collapsedHeaderContentButtonForegroundColor @@ -651,6 +654,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { contentButtonBackgroundColor = expandedAvatarContentButtonBackgroundColor contentButtonForegroundColor = expandedAvatarContentButtonForegroundColor + navigationContentsCanBeExpanded = false + headerButtonBackgroundColor = expandedAvatarHeaderButtonBackgroundColor } else { let effectiveTransitionFraction: CGFloat = innerBackgroundTransitionFraction < 0.5 ? 0.0 : 1.0 @@ -659,6 +664,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { navigationContentsPrimaryColor = regularNavigationContentsPrimaryColor.mixedWith(collapsedHeaderNavigationContentsPrimaryColor, 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) contentButtonForegroundColor = regularContentButtonForegroundColor//.mixedWith(collapsedHeaderContentButtonForegroundColor, alpha: effectiveTransitionFraction) @@ -775,7 +786,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { 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.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition) diff --git a/submodules/TelegramUI/Resources/Animations/Lock.json b/submodules/TelegramUI/Resources/Animations/Lock.json index 8fd257e106..24dfabb379 100644 --- a/submodules/TelegramUI/Resources/Animations/Lock.json +++ b/submodules/TelegramUI/Resources/Animations/Lock.json @@ -1 +1 @@ -{"v":"5.6.5","fr":60,"ip":0,"op":30,"w":240,"h":360,"nm":"Lock2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[100]},{"t":23,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":13,"s":[100,100,100]},{"t":23,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.29,0],[0,0],[0,-1.29],[0,0],[1.28,0],[0,0],[0,1.28],[0,0]],"o":[[0,0],[1.28,0],[0,0],[0,1.28],[0,0],[-1.29,0],[0,0],[0,-1.29]],"v":[[-4.995,-6.335],[5.005,-6.335],[7.335,-3.995],[7.335,4.005],[5.005,6.335],[-4.995,6.335],[-7.335,4.005],[-7.335,-3.995]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.968627510819,0.968627510819,0.968627510819,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[0]},{"t":23,"s":[90]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[120]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[120]},{"t":23,"s":[120]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[150]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[200]},{"t":23,"s":[180]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":13,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true}]},{"t":23,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-8],[5,-8],[8,-5],[8,5],[5,8],[-5,8],[-8,5],[-8,-5]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647059,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.494117647059,0.898039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"t":10,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[30,-32,0],"to":[0.015,12,0],"ti":[-0.012,-7.718,0]},{"t":15,"s":[30.088,39.998,0]}],"ix":2},"a":{"a":0,"k":[30,36,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647059,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path 4","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.498,"y":1},"o":{"x":0.921,"y":0},"t":0,"s":[0,132,0],"to":[-0.018,-13.122,0],"ti":[0.001,0.644,0]},{"t":16,"s":[-0.62,8.043,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.753,3],[0,-3],[0.753,3]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647059,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.12.1","fr":60,"ip":0,"op":30,"w":240,"h":360,"nm":"Lock2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[0]},{"t":23,"s":[90]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[120]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[120]},{"t":23,"s":[120]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[150]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[200]},{"t":23,"s":[180]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":13,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true}]},{"t":23,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-8],[5,-8],[8,-5],[8,5],[5,8],[-5,8],[-8,5],[-8,-5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":13,"s":[{"i":[[0,0],[0,-1.66],[0,0],[-1.66,0],[0,0],[0,1.66],[0,0],[1.66,0]],"o":[[-1.66,0],[0,0],[0,1.66],[0,0],[1.66,0],[0,0],[0,-1.66],[0,0]],"v":[[-5,-7],[-8,-4],[-8,4],[-5,7],[5,7],[8,4],[8,-4],[5,-7]],"c":true}]},{"t":23,"s":[{"i":[[0,0],[0,-1.66],[0,0],[-1.66,0],[0,0],[0,1.66],[0,0],[1.66,0]],"o":[[-1.66,0],[0,0],[0,1.66],[0,0],[1.66,0],[0,0],[0,-1.66],[0,0]],"v":[[-5,-8],[-8,-5],[-8,5],[-5,8],[5,8],[8,5],[8,-5],[5,-8]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":10,"s":[97,97]},{"t":20,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":30,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Path","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"t":10,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[30,-32,0],"to":[0.015,12,0],"ti":[-0.012,-7.718,0]},{"t":15,"s":[30.088,39.998,0]}],"ix":2,"l":2},"a":{"a":0,"k":[30,36,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[7]},{"t":15,"s":[50]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[-1.809]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[90]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.214]},"t":6,"s":[88.872]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[79]},{"t":13,"s":[50]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":13,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path 4","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.498,"y":1},"o":{"x":0.921,"y":0},"t":0,"s":[0,132,0],"to":[-0.018,-13.122,0],"ti":[0.001,0.644,0]},{"t":16,"s":[-0.62,8.043,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.753,3],[0,-3],[0.753,3]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[0]},{"t":12,"s":[50]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[100]},{"t":12,"s":[50]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":30,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/LockWait.json b/submodules/TelegramUI/Resources/Animations/LockWait.json index c46209d092..7462cc0dc4 100644 --- a/submodules/TelegramUI/Resources/Animations/LockWait.json +++ b/submodules/TelegramUI/Resources/Animations/LockWait.json @@ -1 +1 @@ -{"v":"5.5.9","fr":60,"ip":0,"op":120,"w":240,"h":360,"nm":"Lock1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,282,0],"to":[0,-1.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[120,272,0],"to":[0,-0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[120,277,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[120,272,0],"to":[0,0.833,0],"ti":[0,-1.667,0]},{"t":120,"s":[120,282,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745098,0.96862745098,0.96862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":75,"s":[0]},{"t":120,"s":[10]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[150,118,0],"to":[0,1.667,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[150,128,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[150,123,0],"to":[0,0,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[150,128,0],"to":[0,-0.833,0],"ti":[0,1.667,0]},{"t":120,"s":[150,118,0]}],"ix":2},"a":{"a":0,"k":[30,36,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.12.1","fr":60,"ip":0,"op":120,"w":240,"h":360,"nm":"Lock1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,282,0],"to":[0,-1.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[120,272,0],"to":[0,-0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[120,277,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[120,272,0],"to":[0,0.833,0],"ti":[0,-1.667,0]},{"t":120,"s":[120,282,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745285,0.96862745285,0.96862745285,1],"ix":4},"o":{"a":0,"k":0,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":120,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Rectangle","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745285,0.96862745285,0.96862745285,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":120,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path","tt":2,"tp":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":75,"s":[0]},{"t":120,"s":[10]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[150,118,0],"to":[0,1.667,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[150,128,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[150,123,0],"to":[0,0,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[150,128,0],"to":[0,-0.833,0],"ti":[0,1.667,0]},{"t":120,"s":[150,118,0]}],"ix":2,"l":2},"a":{"a":0,"k":[30,36,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index 2964864ea0..c82be4bbb9 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -844,7 +844,7 @@ private func chatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASD 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] { progress?.set(.single(false)) diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index 9f6beaff47..2b7777d409 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -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() var inScope = true var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)? @@ -301,7 +301,7 @@ func updateChatPresentationInterfaceStateImpl( let isEditingMedia: Bool = updatedChatPresentationInterfaceState.editMessageState?.content != .plaintext 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() var inScope = true var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)? diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 2e54746eb9..0a7c110a2c 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -794,7 +794,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return false } switch action.action { - case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults: + case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults, .customText: for attribute in message.attributes { 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))) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift index 29b95179ec..ead219146a 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift @@ -510,7 +510,7 @@ struct UrlPreviewState { 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 { if currentQuery != nil { return (nil, .single({ _ in return nil })) @@ -522,7 +522,7 @@ func urlPreviewStateForInputText(_ inputText: NSAttributedString?, context: Acco let detectedUrls = detectUrls(inputText) if detectedUrls != (currentQuery?.detectedUrls ?? []) { 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 guard case let .result(webpageResult) = result else { return .complete() diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 7e6749f8e7..1210fe7148 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -132,8 +132,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { private var groupCallDisposable: Disposable? private var callController: CallController? + private var call: PresentationCall? public let hasOngoingCall = ValuePromise(false) private let callState = Promise(nil) + private var awaitingCallConnectionDisposable: Disposable? private var groupCallController: VoiceChatController? public var currentGroupCallController: ViewController? { @@ -741,26 +743,49 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.callDisposable = (callManager.currentCallSignal |> deliverOnMainQueue).start(next: { [weak self] call in - if let strongSelf = self { - if call !== strongSelf.callController?.call { - strongSelf.callController?.dismiss() - strongSelf.callController = nil - strongSelf.hasOngoingCall.set(false) + guard let self else { + return + } + + 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 { - mainWindow.hostView.containerView.endEditing(true) - let callController = CallController(sharedContext: strongSelf, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild) - strongSelf.callController = callController - strongSelf.mainWindow?.present(callController, on: .calls) - strongSelf.callState.set(call.state - |> map(Optional.init)) - strongSelf.hasOngoingCall.set(true) - setNotificationCall(call) - } else { - strongSelf.callState.set(.single(nil)) - strongSelf.hasOngoingCall.set(false) - setNotificationCall(nil) + if !call.isOutgoing && call.isIntegratedWithCallKit { + self.awaitingCallConnectionDisposable = (call.state + |> filter { state in + switch state.state { + case .ringing: + return false + default: + return true + } + } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] _ in + 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.groupCallDisposable?.dispose() self.callStateDisposable?.dispose() + self.awaitingCallConnectionDisposable?.dispose() } 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() { let sandbox: Bool #if DEBUG diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 8f41ea2654..8f2f1b9020 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 8f41ea265404dea86f2444a47343993ccdc3a64e +Subproject commit 8f2f1b90209b014071453079c1f28e115ee8de12 diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 52269bb863..e218f55f5f 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -312,7 +312,7 @@ public final class WebAppController: ViewController, AttachmentContainable { self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor } - let webView = WebAppWebView() + let webView = WebAppWebView(account: context.account) webView.alpha = 0.0 webView.navigationDelegate = self webView.uiDelegate = self @@ -418,8 +418,6 @@ public final class WebAppController: ViewController, AttachmentContainable { } }) }) - - self.setupWebView() } deinit { @@ -434,6 +432,8 @@ public final class WebAppController: ViewController, AttachmentContainable { override func didLoad() { super.didLoad() + self.setupWebView() + guard let webView = self.webView else { return } diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index b3262d1262..ee016c6db0 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -3,6 +3,7 @@ import UIKit import Display import WebKit import SwiftSignalKit +import TelegramCore private let findActiveElementY = """ function getOffset(el) { @@ -91,8 +92,22 @@ function disconnectObserver() { final class WebAppWebView: WKWebView { var handleScriptMessage: (WKScriptMessage) -> Void = { _ in } - init() { + init(account: Account) { 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() var handleScriptMessageImpl: ((WKScriptMessage) -> Void)?