From 59c65198de5fe0ffffdee867a856e776f882f8d3 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 12 Feb 2024 17:23:30 -0400 Subject: [PATCH 1/7] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 6 +- submodules/Camera/Sources/Camera.swift | 8 +- submodules/Camera/Sources/CameraOutput.swift | 20 +++- .../Sources/PremiumBoostLevelsScreen.swift | 97 +++++++++++++------ .../Sources/MessageContentKind.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 30 +++--- ...ChatMessageGiveawayBubbleContentNode.swift | 3 +- .../GroupStickerPackSetupController.swift | 5 - .../Sources/MessageInputPanelComponent.swift | 12 ++- .../Sources/VideoMessageCameraScreen.swift | 6 +- versions.json | 2 +- 11 files changed, 128 insertions(+), 63 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 143fb6f956..3799e62f52 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10988,8 +10988,8 @@ Sorry for the inconvenience."; "ChannelBoost.MaxLevelReached.Text" = "**%1$@** reached **Level %2$@**."; "ChannelBoost.MoreBoostsNeeded.Boosted.Text" = "%@ needed to unlock new features."; -"ChannelBoost.MoreBoostsNeeded.Boosted.Level.Text" = "This channel reached **%@** and unlocked new features."; -"GroupBoost.MoreBoostsNeeded.Boosted.Level.Text" = "This group reached **%@** and unlocked new features."; +"ChannelBoost.MoreBoostsNeeded.Boosted.Level.Text" = "This channel reached **Level %@** and unlocked new features."; +"GroupBoost.MoreBoostsNeeded.Boosted.Level.Text" = "This group reached **Level %@** and unlocked new features."; "ContactList.Context.Delete" = "Delete Contact"; "ContactList.Context.Select" = "Select"; @@ -11303,6 +11303,4 @@ Sorry for the inconvenience."; "Channel.AdminLog.MessageChangedGroupEmojiPack" = "%@ changed group emoji pack"; "Channel.AdminLog.MessageRemovedGroupEmojiPack" = "%@ removed group emoji pack"; -"Group.Appearance.EmojiPackUpdated" = "Group emoji pack updated."; - "Attachment.BoostToUnlock" = "Boost to Unlock"; diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index 5e2e736e8a..b92bbb9320 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -555,7 +555,7 @@ private final class CameraContext { } } - public func startRecording() -> Signal { + public func startRecording() -> Signal { guard let mainDeviceContext = self.mainDeviceContext else { return .complete() } @@ -829,7 +829,7 @@ public final class Camera { } } - public func startRecording() -> Signal { + public func startRecording() -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.queue.async { @@ -1082,3 +1082,7 @@ public struct CameraRecordingData { public let duration: Double public let filePath: String } + +public enum CameraRecordingError { + case audioInitializationError +} diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index 6f5c21942a..1402a00fa4 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -83,6 +83,8 @@ final class CameraOutput: NSObject { let colorSpace: CGColorSpace let isVideoMessage: Bool + var hasAudio: Bool = false + let photoOutput = AVCapturePhotoOutput() let videoOutput = AVCaptureVideoDataOutput() let audioOutput = AVCaptureAudioDataOutput() @@ -141,9 +143,15 @@ final class CameraOutput: NSObject { } else { Logger.shared.log("Camera", "Can't add video output") } - if audio, session.session.canAddOutput(self.audioOutput) { - session.session.addOutput(self.audioOutput) - self.audioOutput.setSampleBufferDelegate(self, queue: self.audioQueue) + if audio { + if session.session.canAddOutput(self.audioOutput) { + self.hasAudio = true + session.session.addOutput(self.audioOutput) + self.audioOutput.setSampleBufferDelegate(self, queue: self.audioQueue) + } else { + Logger.shared.log("Camera", "Can't add audio output") + print("error") + } } if photo, session.session.canAddOutput(self.photoOutput) { if session.hasMultiCam { @@ -302,7 +310,7 @@ final class CameraOutput: NSObject { private var currentMode: RecorderMode = .default private var recordingCompletionPipe = ValuePipe() - func startRecording(mode: RecorderMode, position: Camera.Position? = nil, orientation: AVCaptureVideoOrientation, additionalOutput: CameraOutput? = nil) -> Signal { + func startRecording(mode: RecorderMode, position: Camera.Position? = nil, orientation: AVCaptureVideoOrientation, additionalOutput: CameraOutput? = nil) -> Signal { guard self.videoRecorder == nil else { return .complete() } @@ -345,6 +353,10 @@ final class CameraOutput: NSObject { } let audioSettings = self.audioOutput.recommendedAudioSettingsForAssetWriter(writingTo: .mp4) ?? [:] + if self.hasAudio && audioSettings.isEmpty { + Logger.shared.log("Camera", "Audio settings are empty on recording start") + return .fail(.audioInitializationError) + } let outputFileName = NSUUID().uuidString let outputFilePath = NSTemporaryDirectory() + outputFileName + ".mp4" diff --git a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift index b1f13ca380..4077c014b9 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift @@ -402,13 +402,15 @@ private final class SheetContent: CombinedComponent { let mode: PremiumBoostLevelsScreen.Mode let status: ChannelBoostStatus? let boostState: InternalBoostState.DisplayData? - + let initialized: Bool + let boost: () -> Void let copyLink: (String) -> Void let dismiss: () -> Void let openStats: (() -> Void)? let openGift: (() -> Void)? let openPeer: ((EnginePeer) -> Void)? + let updated: () -> Void init(context: AccountContext, theme: PresentationTheme, @@ -419,12 +421,14 @@ private final class SheetContent: CombinedComponent { mode: PremiumBoostLevelsScreen.Mode, status: ChannelBoostStatus?, boostState: InternalBoostState.DisplayData?, + initialized: Bool, boost: @escaping () -> Void, copyLink: @escaping (String) -> Void, dismiss: @escaping () -> Void, openStats: (() -> Void)?, openGift: (() -> Void)?, - openPeer: ((EnginePeer) -> Void)? + openPeer: ((EnginePeer) -> Void)?, + updated: @escaping () -> Void ) { self.context = context self.theme = theme @@ -435,12 +439,14 @@ private final class SheetContent: CombinedComponent { self.mode = mode self.status = status self.boostState = boostState + self.initialized = initialized self.boost = boost self.copyLink = copyLink self.dismiss = dismiss self.openStats = openStats self.openGift = openGift self.openPeer = openPeer + self.updated = updated } static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool { @@ -468,6 +474,9 @@ private final class SheetContent: CombinedComponent { if lhs.boostState != rhs.boostState { return false } + if lhs.initialized != rhs.initialized { + return false + } return true } @@ -479,35 +488,33 @@ private final class SheetContent: CombinedComponent { private(set) var memberPeer: EnginePeer? private var disposable: Disposable? - private var memberDisposable: Disposable? - init(context: AccountContext, peerId: EnginePeer.Id, userId: EnginePeer.Id?) { + init(context: AccountContext, peerId: EnginePeer.Id, userId: EnginePeer.Id?, updated: @escaping () -> Void) { super.init() - self.disposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).startStrict(next: { [weak self] peer in + var peerIds: [EnginePeer.Id] = [peerId] + if let userId { + peerIds.append(userId) + } + + self.disposable = (context.engine.data.get( + EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) + ) |> deliverOnMainQueue).startStrict(next: { [weak self] peers in guard let self else { return } - self.peer = peer - self.updated() + if let maybePeer = peers[peerId] { + self.peer = maybePeer + } + if let userId, let maybePeer = peers[userId] { + self.memberPeer = maybePeer + } + updated() }) - - if let userId { - self.memberDisposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: userId)) - |> deliverOnMainQueue).startStrict(next: { [weak self] peer in - guard let self else { - return - } - self.memberPeer = peer - self.updated() - }) - } } deinit { self.disposable?.dispose() - self.memberDisposable?.dispose() } } @@ -516,7 +523,7 @@ private final class SheetContent: CombinedComponent { if case let .user(mode) = mode, case let .groupPeer(peerId, _) = mode { userId = peerId } - return State(context: self.context, peerId: self.peerId, userId: userId) + return State(context: self.context, peerId: self.peerId, userId: userId, updated: self.updated) } static var body: Body { @@ -636,7 +643,11 @@ private final class SheetContent: CombinedComponent { } else { let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining)) if myBoostCount > 0 { - textString = strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string + if remaining == 0 { + textString = isGroup ? strings.GroupBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level + 1)").string : strings.ChannelBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level + 1)").string + } else { + textString = strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string + } } else { textString = strings.ChannelBoost_MoreBoostsNeeded_Text(peerName, boostsString).string } @@ -674,7 +685,7 @@ private final class SheetContent: CombinedComponent { let boostsString = strings.ChannelBoost_MoreBoostsNeeded_Boosts(Int32(remaining)) if myBoostCount > 0 { if remaining == 0 { - textString = isGroup ? strings.GroupBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level)").string : strings.ChannelBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level)").string + textString = isGroup ? strings.GroupBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level + 1)").string : strings.ChannelBoost_MoreBoostsNeeded_Boosted_Level_Text("\(level + 1)").string } else { textString = strings.ChannelBoost_MoreBoostsNeeded_Boosted_Text(boostsString).string } @@ -1267,6 +1278,8 @@ private final class BoostLevelsContainerComponent: CombinedComponent { var cachedStatsImage: (UIImage, PresentationTheme)? var cachedCloseImage: (UIImage, PresentationTheme)? + var initialized = false + private var disposable: Disposable? private(set) var peer: EnginePeer? @@ -1279,7 +1292,6 @@ private final class BoostLevelsContainerComponent: CombinedComponent { return } self.peer = peer - self.updated() updated() }) } @@ -1325,6 +1337,7 @@ private final class BoostLevelsContainerComponent: CombinedComponent { if let isGroup { component.externalState.isGroup = isGroup + let updated = component.updated let scroll = scroll.update( component: ScrollComponent( content: AnyComponent( @@ -1338,12 +1351,17 @@ private final class BoostLevelsContainerComponent: CombinedComponent { mode: component.mode, status: component.status, boostState: component.boostState, + initialized: state.initialized, boost: component.boost, copyLink: component.copyLink, dismiss: component.dismiss, openStats: component.openStats, openGift: component.openGift, - openPeer: component.openPeer + openPeer: component.openPeer, + updated: { [weak state] in + state?.initialized = true + updated() + } ) ), externalState: externalScrollState, @@ -1757,9 +1775,16 @@ public class PremiumBoostLevelsScreen: ViewController { self.controller?.updateModalStyleOverlayTransitionFactor(0.0, transition: positionTransition) } + + func requestLayout(transition: Transition) { + guard let layout = self.currentLayout else { + return + } + self.containerLayoutUpdated(layout: layout, forceUpdate: true, transition: transition) + } private var dismissOffset: CGFloat? - func containerLayoutUpdated(layout: ContainerViewLayout, transition: Transition) { + func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, transition: Transition) { guard !self.isDismissing else { return } @@ -1824,7 +1849,7 @@ public class PremiumBoostLevelsScreen: ViewController { effectiveExpanded = true } - self.updated(transition: transition) + self.updated(transition: transition, forceUpdate: forceUpdate) let contentHeight = self.containerExternalState.contentHeight if contentHeight > 0.0 && contentHeight < 400.0, let view = self.footerView.componentView as? FooterComponent.View { @@ -1856,7 +1881,7 @@ public class PremiumBoostLevelsScreen: ViewController { } private var boostState: InternalBoostState.DisplayData? - func updated(transition: Transition) { + func updated(transition: Transition, forceUpdate: Bool = false) { guard let controller = self.controller else { return } @@ -1895,11 +1920,12 @@ public class PremiumBoostLevelsScreen: ViewController { openGift: controller.openGift, openPeer: controller.openPeer, updated: { [weak self] in - self?.controller?.requestLayout(transition: .immediate) + self?.requestLayout(transition: .immediate) } ) ), environment: {}, + forceUpdate: forceUpdate, containerSize: self.containerView.bounds.size ) self.contentView.frame = CGRect(origin: .zero, size: contentSize) @@ -2200,6 +2226,7 @@ public class PremiumBoostLevelsScreen: ViewController { let canBoostAgain = premiumConfiguration.boostsPerGiftCount > 0 let presentationData = self.presentationData let forceDark = controller.forceDark + let boostStatusUpdated = controller.boostStatusUpdated if let _ = status?.nextLevelBoosts { if let availableBoost = self.availableBoosts.first { @@ -2266,15 +2293,23 @@ public class PremiumBoostLevelsScreen: ViewController { ).startStandalone(next: { boostStatus, myBoostStatus in dismissReplaceImpl?() + if let boostStatus { + boostStatusUpdated(boostStatus) + } + let levelsController = PremiumBoostLevelsScreen( context: context, peerId: peerId, mode: mode, - status: status, + status: boostStatus, myBoostStatus: myBoostStatus, replacedBoosts: (Int32(slots.count), sourcePeers), - openStats: nil, openGift: nil, openPeer: openPeer, forceDark: forceDark + openStats: nil, + openGift: nil, + openPeer: openPeer, + forceDark: forceDark ) + levelsController.boostStatusUpdated = boostStatusUpdated if let navigationController { navigationController.pushViewController(levelsController, animated: true) } diff --git a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift index 31e3f7ee57..00668606e8 100644 --- a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift +++ b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift @@ -378,7 +378,7 @@ public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil } case .story: return .story - case .giveaway: + case .giveaway, .giveawayResults: return .giveaway case let .webpage(webpage): if let message, message.text.isEmpty, case let .Loaded(content) = webpage.content { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index d5e4205c45..40e309e644 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2184,19 +2184,21 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let attributedString: NSAttributedString var adminBadgeString: NSAttributedString? var boostBadgeString: NSAttributedString? - if let authorRank = authorRank { - let string: String - switch authorRank { + if incoming { + if let authorRank = authorRank { + let string: String + switch authorRank { case .owner: string = item.presentationData.strings.Conversation_Owner case .admin: string = item.presentationData.strings.Conversation_Admin case let .custom(rank): string = rank.trimmingEmojis + } + adminBadgeString = NSAttributedString(string: " \(string)", font: inlineBotPrefixFont, textColor: messageTheme.secondaryTextColor) + } else if authorIsChannel, case .peer = item.chatLocation { + adminBadgeString = NSAttributedString(string: " \(item.presentationData.strings.Channel_Status)", font: inlineBotPrefixFont, textColor: messageTheme.secondaryTextColor) } - adminBadgeString = NSAttributedString(string: " \(string)", font: inlineBotPrefixFont, textColor: messageTheme.secondaryTextColor) - } else if authorIsChannel, case .peer = item.chatLocation { - adminBadgeString = NSAttributedString(string: " \(item.presentationData.strings.Channel_Status)", font: inlineBotPrefixFont, textColor: messageTheme.secondaryTextColor) } var viaSuffix: NSAttributedString? @@ -2240,9 +2242,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var boostCount: Int = 0 - for attribute in item.message.attributes { - if let attribute = attribute as? BoostCountMessageAttribute { - boostCount = attribute.count + if incoming { + for attribute in item.message.attributes { + if let attribute = attribute as? BoostCountMessageAttribute { + boostCount = attribute.count + } } } @@ -3181,9 +3185,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var boostCount: Int = 0 - for attribute in item.message.attributes { - if let attribute = attribute as? BoostCountMessageAttribute { - boostCount = attribute.count + if incoming { + for attribute in item.message.attributes { + if let attribute = attribute as? BoostCountMessageAttribute { + boostCount = attribute.count + } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index 5c46c0dd0c..aa150aa342 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -433,7 +433,8 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode, } else if let giveawayResults { dateTextString = NSAttributedString(string: giveawayResults.winnersCount > 1 ? item.presentationData.strings.Chat_Giveaway_Message_WinnersInfo_Many : item.presentationData.strings.Chat_Giveaway_Message_WinnersInfo_One, font: textFont, textColor: textColor) } - let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none, hidesHeaders: true) + let hideHeaders = item.message.forwardInfo == nil + let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: hideHeaders, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none, hidesHeaders: hideHeaders) return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in let sideInsets = layoutConstants.text.bubbleInsets.right * 2.0 diff --git a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackSetupController.swift b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackSetupController.swift index 3c0dd74cdc..3f12c31673 100644 --- a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackSetupController.swift +++ b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackSetupController.swift @@ -350,11 +350,6 @@ public func groupStickerPackSetupController(context: AccountContext, updatedPres if let completion { completionImpl = { value in completion(value) - if let _ = value { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: presentationData.strings.Group_Appearance_EmojiPackUpdated, cancel: nil, destructive: false), elevatedLayout: false, action: { _ in return true }) - presentControllerImpl?(controller, nil) - } } } diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 91476b1fee..bf8fd6f11a 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -92,10 +92,12 @@ public final class MessageInputPanelComponent: Component { enum Kind { case text case premiumRequired + case boostRequired } case text(String) case premiumRequired(title: String, subtitle: String, action: () -> Void) + case boostRequired(title: String, subtitle: String, action: () -> Void) var kind: Kind { switch self { @@ -103,6 +105,8 @@ public final class MessageInputPanelComponent: Component { return .text case .premiumRequired: return .premiumRequired + case .boostRequired: + return .boostRequired } } @@ -120,6 +124,12 @@ public final class MessageInputPanelComponent: Component { } else { return false } + case let .boostRequired(title, subtitle, _): + if case .boostRequired(title, subtitle, _) = rhs { + return true + } else { + return false + } } } } @@ -1093,7 +1103,7 @@ public final class MessageInputPanelComponent: Component { contents = AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString(string: text, font: Font.regular(17.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.3))) )) - case let .premiumRequired(title, subtitle, action): + case let .premiumRequired(title, subtitle, action), let .boostRequired(title, subtitle, action): leftAlignment = true contents = AnyComponent(PlainButtonComponent( content: AnyComponent(VStack([ diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 8d6d2e45fc..9667631c9d 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -272,6 +272,10 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent { controller.onStop() } } + }, error: { [weak self] _ in + if let self, let controller = self.getController() { + controller.completion(nil, nil, nil) + } })) } @@ -1686,7 +1690,7 @@ public class VideoMessageCameraScreen: ViewController { self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: audioSessionType, activate: { [weak self] _ in if let self { - Queue.mainQueue().async { + Queue.mainQueue().after(0.05) { self.node.setupCamera() } } diff --git a/versions.json b/versions.json index 0050a3689f..e422f9cd49 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "10.7.2", + "app": "10.8", "bazel": "7.0.2", "xcode": "15.2", "macos": "13.0" From d65af3310120dc6766ae54bfb57d87f4d7c136e3 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 12 Feb 2024 17:29:45 -0400 Subject: [PATCH 2/7] Various fixes --- submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift index 4077c014b9..9102055055 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift @@ -1695,7 +1695,7 @@ public class PremiumBoostLevelsScreen: ViewController { otherText = presentationData.strings.ReassignBoost_OtherGroupsAndChannels(Int32(sourcePeers.count)) } let text = presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), otherText).string - let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: text, round: false, undoText: nil), elevatedLayout: false, position: .top, action: { _ in return true }) + let undoController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "BoostReplace", scale: 0.066, colors: [:], title: nil, text: text, customUndoText: nil, timeout: 4.0), elevatedLayout: false, position: .top, action: { _ in return true }) controller.present(undoController, in: .current) } } From 42cc67ade7dda41468950133df384c27cfe4c0ee Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 12 Feb 2024 18:48:48 -0400 Subject: [PATCH 3/7] Group stories --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../TelegramEngine/Data/PeersData.swift | 56 +++++++++ .../Data/TelegramEngineData.swift | 113 ++++++++++++++++++ .../Sources/StoryChatContent.swift | 55 ++++++--- .../Sources/StoryContent.swift | 14 ++- .../StoryItemSetContainerComponent.swift | 99 ++++++++++++--- 6 files changed, 305 insertions(+), 35 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 3799e62f52..90e52d4d2c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11304,3 +11304,6 @@ Sorry for the inconvenience."; "Channel.AdminLog.MessageRemovedGroupEmojiPack" = "%@ removed group emoji pack"; "Attachment.BoostToUnlock" = "Boost to Unlock"; + +"Story.GroupCommentingRestrictedPlaceholder" = "Comments restricted"; +"Story.GroupCommentingRestrictedPlaceholderAction" = "Boost to unlock"; diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index da715c2d31..08f63d5fab 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -879,6 +879,62 @@ public extension TelegramEngine.EngineData.Item { } } + public struct BoostsToUnrestrict: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Int32? + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedChannelData { + return cachedData.boostsToUnrestrict + } else { + return nil + } + } + } + + public struct AppliedBoosts: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Int32? + + fileprivate var id: EnginePeer.Id + public var mapKey: EnginePeer.Id { + return self.id + } + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedChannelData { + return cachedData.appliedBoosts + } else { + return nil + } + } + } + public struct MessageReadStatsAreHidden: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = Bool? diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift index de74d882fb..b72ee5fa4b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift @@ -391,6 +391,119 @@ public extension TelegramEngine { } } + public func subscribe< + T0: TelegramEngineDataItem, + T1: TelegramEngineDataItem, + T2: TelegramEngineDataItem, + T3: TelegramEngineDataItem, + T4: TelegramEngineDataItem, + T5: TelegramEngineDataItem, + T6: TelegramEngineDataItem, + T7: TelegramEngineDataItem + >( + _ t0: T0, + _ t1: T1, + _ t2: T2, + _ t3: T3, + _ t4: T4, + _ t5: T5, + _ t6: T6, + _ t7: T7 + ) -> Signal< + ( + T0.Result, + T1.Result, + T2.Result, + T3.Result, + T4.Result, + T5.Result, + T6.Result, + T7.Result + ), + NoError> { + return self._subscribe(items: [ + t0 as! AnyPostboxViewDataItem, + t1 as! AnyPostboxViewDataItem, + t2 as! AnyPostboxViewDataItem, + t3 as! AnyPostboxViewDataItem, + t4 as! AnyPostboxViewDataItem, + t5 as! AnyPostboxViewDataItem, + t6 as! AnyPostboxViewDataItem, + t7 as! AnyPostboxViewDataItem + ]) + |> map { results -> (T0.Result, T1.Result, T2.Result, T3.Result, T4.Result, T5.Result, T6.Result, T7.Result) in + return ( + results[0] as! T0.Result, + results[1] as! T1.Result, + results[2] as! T2.Result, + results[3] as! T3.Result, + results[4] as! T4.Result, + results[5] as! T5.Result, + results[6] as! T6.Result, + results[7] as! T7.Result + ) + } + } + + public func subscribe< + T0: TelegramEngineDataItem, + T1: TelegramEngineDataItem, + T2: TelegramEngineDataItem, + T3: TelegramEngineDataItem, + T4: TelegramEngineDataItem, + T5: TelegramEngineDataItem, + T6: TelegramEngineDataItem, + T7: TelegramEngineDataItem, + T8: TelegramEngineDataItem + >( + _ t0: T0, + _ t1: T1, + _ t2: T2, + _ t3: T3, + _ t4: T4, + _ t5: T5, + _ t6: T6, + _ t7: T7, + _ t8: T8 + ) -> Signal< + ( + T0.Result, + T1.Result, + T2.Result, + T3.Result, + T4.Result, + T5.Result, + T6.Result, + T7.Result, + T8.Result + ), + NoError> { + return self._subscribe(items: [ + t0 as! AnyPostboxViewDataItem, + t1 as! AnyPostboxViewDataItem, + t2 as! AnyPostboxViewDataItem, + t3 as! AnyPostboxViewDataItem, + t4 as! AnyPostboxViewDataItem, + t5 as! AnyPostboxViewDataItem, + t6 as! AnyPostboxViewDataItem, + t7 as! AnyPostboxViewDataItem, + t8 as! AnyPostboxViewDataItem + ]) + |> map { results -> (T0.Result, T1.Result, T2.Result, T3.Result, T4.Result, T5.Result, T6.Result, T7.Result, T8.Result) in + return ( + results[0] as! T0.Result, + results[1] as! T1.Result, + results[2] as! T2.Result, + results[3] as! T3.Result, + results[4] as! T4.Result, + results[5] as! T5.Result, + results[6] as! T6.Result, + results[7] as! T7.Result, + results[8] as! T8.Result + ) + } + } + public func get< T0: TelegramEngineDataItem, T1: TelegramEngineDataItem diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index d76bb2a168..1146bf3101 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -216,7 +216,9 @@ public final class StoryContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: false, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: nil, + appliedBoosts: nil ) } else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData { additionalPeerData = StoryContentContextState.AdditionalPeerData( @@ -225,7 +227,9 @@ public final class StoryContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: cachedChannelData.flags.contains(.canViewStats), isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: cachedChannelData.boostsToUnrestrict, + appliedBoosts: cachedChannelData.appliedBoosts ) } else { additionalPeerData = StoryContentContextState.AdditionalPeerData( @@ -234,18 +238,21 @@ public final class StoryContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: false, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: nil, + appliedBoosts: nil ) } - } - else { + } else { additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: true, areVoiceMessagesAvailable: true, presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: false, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: nil, + appliedBoosts: nil ) } let state = stateView.value?.get(Stories.PeerState.self) @@ -1165,7 +1172,9 @@ public final class SingleStoryContentContextImpl: StoryContentContext { TelegramEngine.EngineData.Item.Peer.CanViewStats(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global(), - TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: storyId.peerId) + TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: storyId.peerId), + TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict(id: storyId.peerId), + TelegramEngine.EngineData.Item.Peer.AppliedBoosts(id: storyId.peerId) ), item |> mapToSignal { item -> Signal<(Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]), NoError> in return context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]) in @@ -1236,7 +1245,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { return } - let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging) = data + let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts) = data let (item, peers, allEntityFiles, forwardInfoStories) = itemAndPeers guard let peer else { @@ -1251,7 +1260,9 @@ public final class SingleStoryContentContextImpl: StoryContentContext { presence: presence, canViewStats: canViewStats, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: boostsToUnrestrict, + appliedBoosts: appliedBoosts ) for (storyId, story) in forwardInfoStories { @@ -1447,7 +1458,9 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global(), - TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: peerId) + TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: peerId), + TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict(id: peerId), + TelegramEngine.EngineData.Item.Peer.AppliedBoosts(id: peerId) ), listContext.state, self.focusedIdUpdated.get(), @@ -1458,7 +1471,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { return } - let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging) = data + let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts) = data guard let peer else { return @@ -1472,7 +1485,9 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { presence: presence, canViewStats: canViewStats, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: boostsToUnrestrict, + appliedBoosts: appliedBoosts ) self.listState = state @@ -2389,7 +2404,9 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: false, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: nil, + appliedBoosts: nil ) } else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData { additionalPeerData = StoryContentContextState.AdditionalPeerData( @@ -2398,7 +2415,9 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: cachedChannelData.flags.contains(.canViewStats), isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: cachedChannelData.boostsToUnrestrict, + appliedBoosts: cachedChannelData.appliedBoosts ) } else { additionalPeerData = StoryContentContextState.AdditionalPeerData( @@ -2407,7 +2426,9 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: false, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: nil, + appliedBoosts: nil ) } } @@ -2418,7 +2439,9 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { presence: peerPresence.flatMap { EnginePeer.Presence($0) }, canViewStats: false, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, - preferHighQualityStories: preferHighQualityStories + preferHighQualityStories: preferHighQualityStories, + boostsToUnrestrict: nil, + appliedBoosts: nil ) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index 4afd4c85dc..3b4f6bbf43 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -147,6 +147,8 @@ public final class StoryContentContextState { public let canViewStats: Bool public let isPremiumRequiredForMessaging: Bool public let preferHighQualityStories: Bool + public let boostsToUnrestrict: Int32? + public let appliedBoosts: Int32? public init( isMuted: Bool, @@ -154,7 +156,9 @@ public final class StoryContentContextState { presence: EnginePeer.Presence?, canViewStats: Bool, isPremiumRequiredForMessaging: Bool, - preferHighQualityStories: Bool + preferHighQualityStories: Bool, + boostsToUnrestrict: Int32?, + appliedBoosts: Int32? ) { self.isMuted = isMuted self.areVoiceMessagesAvailable = areVoiceMessagesAvailable @@ -162,6 +166,8 @@ public final class StoryContentContextState { self.canViewStats = canViewStats self.isPremiumRequiredForMessaging = isPremiumRequiredForMessaging self.preferHighQualityStories = preferHighQualityStories + self.boostsToUnrestrict = boostsToUnrestrict + self.appliedBoosts = appliedBoosts } public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool { @@ -183,6 +189,12 @@ public final class StoryContentContextState { if lhs.preferHighQualityStories != rhs.preferHighQualityStories { return false } + if lhs.boostsToUnrestrict != rhs.boostsToUnrestrict { + return false + } + if lhs.appliedBoosts != rhs.appliedBoosts { + return false + } return true } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index ced2a7cf69..14aea4a93b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1668,7 +1668,13 @@ public final class StoryItemSetContainerComponent: Component { case .broadcast: displayFooter = true case .group: - displayFooter = false + var canBypassRestrictions = false + if let appliedBoosts = component.slice.additionalPeerData.appliedBoosts, let boostsToUnrestrict = component.slice.additionalPeerData.boostsToUnrestrict { + canBypassRestrictions = appliedBoosts >= boostsToUnrestrict + } + if let bannedSendText = channel.hasBannedPermission(.banSendText, ignoreDefault: canBypassRestrictions), bannedSendText.1 || component.slice.additionalPeerData.boostsToUnrestrict == nil { + displayFooter = true + } } } else if component.slice.peer.id == component.context.account.peerId { displayFooter = true @@ -2744,9 +2750,42 @@ public final class StoryItemSetContainerComponent: Component { inputPanelTransition = .immediate } + var canBypassRestrictions = false + if let appliedBoosts = component.slice.additionalPeerData.appliedBoosts, let boostsToUnrestrict = component.slice.additionalPeerData.boostsToUnrestrict { + canBypassRestrictions = appliedBoosts >= boostsToUnrestrict + } + + var isChannel = false + var isGroup = false + var showMessageInputPanel = true + var isGroupCommentRestricted = false + if case let .channel(channel) = component.slice.peer { + switch channel.info { + case .broadcast: + isChannel = true + showMessageInputPanel = false + case .group: + if let bannedSendText = channel.hasBannedPermission(.banSendText, ignoreDefault: canBypassRestrictions) { + if bannedSendText.1 || component.slice.additionalPeerData.boostsToUnrestrict == nil { + showMessageInputPanel = false + } else { + isGroupCommentRestricted = true + } + } + isGroup = true + } + } else { + showMessageInputPanel = component.slice.peer.id != component.context.account.peerId + } + var isUnsupported = false var disabledPlaceholder: MessageInputPanelComponent.DisabledPlaceholder? - if component.slice.additionalPeerData.isPremiumRequiredForMessaging { + + if isGroupCommentRestricted { + disabledPlaceholder = .boostRequired(title: component.strings.Story_GroupCommentingRestrictedPlaceholder, subtitle: component.strings.Story_GroupCommentingRestrictedPlaceholderAction, action: { [weak self] in + self?.presentBoostToUnrestrict() + }) + } else if component.slice.additionalPeerData.isPremiumRequiredForMessaging { disabledPlaceholder = .premiumRequired(title: component.strings.Story_MessagingRestrictedPlaceholder(component.slice.peer.compactDisplayTitle).string, subtitle: component.strings.Story_MessagingRestrictedPlaceholderAction, action: { [weak self] in self?.presentPremiumRequiredForMessaging() }) @@ -2757,21 +2796,6 @@ public final class StoryItemSetContainerComponent: Component { disabledPlaceholder = .text(component.strings.Story_FooterReplyUnavailable) } - var isChannel = false - var isGroup = false - var showMessageInputPanel = true - if case let .channel(channel) = component.slice.peer { - switch channel.info { - case .broadcast: - isChannel = true - showMessageInputPanel = false - case .group: - isGroup = true - } - } else { - showMessageInputPanel = component.slice.peer.id != component.context.account.peerId - } - let inputPlaceholder: MessageInputPanelComponent.Placeholder if let stealthModeTimeout = component.stealthModeTimeout { let minutes = Int(stealthModeTimeout / 60) @@ -5673,7 +5697,7 @@ public final class StoryItemSetContainerComponent: Component { let controller = PremiumIntroScreen(context: component.context, source: .settings, forceDark: true) self.sendMessageContext.actionSheet = controller - controller.wasDismissed = { [weak self, weak controller]in + controller.wasDismissed = { [weak self, weak controller] in guard let self else { return } @@ -5688,6 +5712,45 @@ public final class StoryItemSetContainerComponent: Component { component.controller()?.push(controller) } + private func presentBoostToUnrestrict() { + guard let component = self.component, let boostsToUnrestrict = component.slice.additionalPeerData.boostsToUnrestrict else { + return + } + + HapticFeedback().impact() + + let _ = combineLatest(queue: Queue.mainQueue(), + component.context.engine.peers.getChannelBoostStatus(peerId: component.slice.peer.id), + component.context.engine.peers.getMyBoostStatus() + ).startStandalone(next: { [weak self] boostStatus, myBoostStatus in + guard let self, let component = self.component, let boostStatus, let myBoostStatus else { + return + } + let boostController = PremiumBoostLevelsScreen( + context: component.context, + peerId: component.slice.peer.id, + mode: .user(mode: .unrestrict(Int(boostsToUnrestrict))), + status: boostStatus, + myBoostStatus: myBoostStatus, + forceDark: true + ) + boostController.disposed = { [weak self, weak boostController] in + guard let self else { + return + } + + if self.sendMessageContext.actionSheet === boostController { + self.sendMessageContext.actionSheet = nil + } + self.updateIsProgressPaused() + } + self.sendMessageContext.actionSheet = boostController + + self.updateIsProgressPaused() + component.controller()?.push(boostController) + }) + } + private func presentStoriesUpgradeScreen(source: PremiumSource) { guard let component = self.component else { return From 77969650f84d91e99c31ce3b61d0cfedd4cda596 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 12 Feb 2024 18:54:31 -0400 Subject: [PATCH 4/7] Various fixes --- submodules/Camera/Sources/CameraOutput.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index 1402a00fa4..276feb37a3 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -144,13 +144,12 @@ final class CameraOutput: NSObject { Logger.shared.log("Camera", "Can't add video output") } if audio { + self.hasAudio = true if session.session.canAddOutput(self.audioOutput) { - self.hasAudio = true session.session.addOutput(self.audioOutput) self.audioOutput.setSampleBufferDelegate(self, queue: self.audioQueue) } else { Logger.shared.log("Camera", "Can't add audio output") - print("error") } } if photo, session.session.canAddOutput(self.photoOutput) { From e73247c065efb34f3de73e4c31b1227cdce4b3c6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 12 Feb 2024 19:15:42 -0400 Subject: [PATCH 5/7] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 ++-- .../Sources/MessageInputPanelComponent.swift | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 90e52d4d2c..1bd2b687b6 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11305,5 +11305,5 @@ Sorry for the inconvenience."; "Attachment.BoostToUnlock" = "Boost to Unlock"; -"Story.GroupCommentingRestrictedPlaceholder" = "Comments restricted"; -"Story.GroupCommentingRestrictedPlaceholderAction" = "Boost to unlock"; +"Story.GroupCommentingRestrictedPlaceholder" = "Comments restricted. Boost the group to unlock."; +"Story.GroupCommentingRestrictedPlaceholderAction" = "Learn More..."; diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index bf8fd6f11a..c4b9b0d6c1 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -1105,15 +1105,17 @@ public final class MessageInputPanelComponent: Component { )) case let .premiumRequired(title, subtitle, action), let .boostRequired(title, subtitle, action): leftAlignment = true + + let text = NSMutableAttributedString(attributedString: NSAttributedString()) + text.append(NSAttributedString(string: "\(title) ", font: Font.regular(13.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.3))) + text.append(NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: component.theme.list.itemAccentColor)) + contents = AnyComponent(PlainButtonComponent( - content: AnyComponent(VStack([ - AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: title, font: Font.regular(13.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.3))) - ))), - AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: component.theme.list.itemAccentColor)) - ))) - ], alignment: .left, spacing: 1.0)), + content: AnyComponent(MultilineTextComponent( + text: .plain(text), + maximumNumberOfLines: 0, + lineSpacing: 0.1 + )), effectAlignment: .center, action: { action() From 089e818e00067a44fdb2217148d4435c053099e3 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 12 Feb 2024 20:42:39 -0400 Subject: [PATCH 6/7] Various fixes --- .../PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 32e2d8050f..406865a1bc 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -926,10 +926,6 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: presentationData.strings.Settings_Premium, icon: PresentationResourcesSettings.premium, action: { interaction.openSettings(.premium) })) - //TODO:localize - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 101, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Telegram Business", icon: PresentationResourcesSettings.chatFolders, action: { - interaction.openSettings(.businessSetup) - })) items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 102, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.Settings_PremiumGift, icon: PresentationResourcesSettings.premiumGift, action: { interaction.openSettings(.premiumGift) })) From 20ae8cc819898633f36d338b79df01f684762953 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 13 Feb 2024 06:42:22 -0400 Subject: [PATCH 7/7] Various fixes --- Telegram/Telegram-iOS/en.lproj/Localizable.strings | 2 +- .../StatisticsUI/Sources/ChannelStatsController.swift | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 1bd2b687b6..a647271629 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11043,7 +11043,7 @@ Sorry for the inconvenience."; "ChannelBoost.Table.Group.Wallpaper_any" = "%@ Group Backgrounds"; "ChannelBoost.Table.Group.CustomWallpaper" = "Custom Group Background"; -"Premium.Group.BoostByGiftDescription" = "Boost your group by gifting your subscribers Telegram Premium. [Get boosts >]()"; +"Premium.Group.BoostByGiftDescription" = "Boost your group by gifting your members Telegram Premium. [Get boosts >]()"; "Conversation.BoostGroup" = "BOOST"; diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 57ca6ab5b5..2c8c6cd556 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1519,7 +1519,11 @@ public func channelStatsController(context: AccountContext, updatedPresentationD peerId: peerId, mode: .owner(subject: nil), status: boostStatus, - myBoostStatus: myBoostStatus + myBoostStatus: myBoostStatus, + openGift: { [weak controller] in + let giveawayController = createGiveawayController(context: context, peerId: peerId, subject: .generic) + controller?.push(giveawayController) + } ) boostController.boostStatusUpdated = { boostStatus in boostDataPromise.set(.single(boostStatus))