diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 81b2135018..0b3a8efe7a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10049,3 +10049,14 @@ Sorry for the inconvenience."; "Chat.ReplyStoryPrivateChannel" = "Private story"; "Message.ForwardedUnavailableStoryShort" = "Private Story\nFrom: %@"; + +"ChannelBoost.ReplaceBoost" = "You currently boost **%1$@**. Do you want to boost **%2$@** instead?"; + +"ChannelBoost.Error.BoostTooOftenTitle" = "Can't Boost Too Often"; +"ChannelBoost.Error.BoostTooOftenText" = "You can change the channel you boost only once a day. Next time you can boost is in **%@**."; +"ChannelBoost.Error.PremiumNeededTitle" = "Premium Needed"; +"ChannelBoost.Error.PremiumNeededText" = "Only **Telegram Premium** subscribers can boost channels. Do you want to subscribe to **Telegram Premium**?"; +"ChannelBoost.Error.GiftedPremiumNotAllowedTitle" = "Can't Boost with Gifted Premium"; +"ChannelBoost.Error.GiftedPremiumNotAllowedText" = "Because your **Telegram Premium** subscription was gifted to you, you can't use it to boost channels."; + +"Chat.ErrorFolderLinkExpired" = "The folder link has expired."; diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 9f7bc8ba75..ee31b02466 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -1525,6 +1525,8 @@ public class PremiumLimitScreen: ViewControllerComponentContainer { private var action: (() -> Bool)? private let openPeer: (EnginePeer) -> Void + private let hapticFeedback = HapticFeedback() + public init(context: AccountContext, subject: PremiumLimitScreen.Subject, count: Int32, forceDark: Bool = false, cancel: @escaping () -> Void = {}, action: @escaping () -> Bool, openPeer: @escaping (EnginePeer) -> Void = { _ in }) { self.context = context self.openPeer = openPeer @@ -1532,7 +1534,7 @@ public class PremiumLimitScreen: ViewControllerComponentContainer { var actionImpl: (() -> Bool)? super.init(context: context, component: LimitSheetComponent(context: context, subject: subject, count: count, cancel: {}, action: { return actionImpl?() ?? true - }, openPeer: openPeer), navigationBarAppearance: .none, theme: forceDark ? .dark : .default) + }, openPeer: openPeer), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default) self.navigationPresentation = .flatModal @@ -1564,6 +1566,8 @@ public class PremiumLimitScreen: ViewControllerComponentContainer { return true }, openPeer: self.openPeer) self.updateComponent(component: AnyComponent(component), transition: .easeInOut(duration: 0.2)) + + self.hapticFeedback.impact() self.view.addSubview(ConfettiView(frame: self.view.bounds)) } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index dcd7469c5b..6b9e550881 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3911,7 +3911,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let dropInteraction = UIDropInteraction(delegate: self) self.displayNode.view.addInteraction(dropInteraction) - Queue.mainQueue().after(1.0) { + Queue.mainQueue().after(0.4) { self.adminedChannels.set(.single([]) |> then(self.context.engine.peers.channelsForStories())) self.closeFriends.set(self.context.engine.data.get(TelegramEngine.EngineData.Item.Contacts.CloseFriends())) } @@ -4507,57 +4507,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } private var didComplete = false - func requestCompletion(animated: Bool, skipSendCheck: Bool = false) { + func requestCompletion(animated: Bool) { guard let mediaEditor = self.node.mediaEditor, let subject = self.node.subject, !self.didComplete else { return } - if !self.isEditingStory, !skipSendCheck, let sendAsPeerId = self.state.privacy.sendAsPeerId, sendAsPeerId.namespace == Namespaces.Peer.CloudChannel { - let _ = (self.context.engine.messages.checkStoriesUploadAvailability(target: .peer(sendAsPeerId)) - |> deliverOnMainQueue).start(next: { [weak self] status in - guard let self else { - return - } - switch status { - case .available: - self.requestCompletion(animated: animated, skipSendCheck: true) - case .channelBoostRequired: - let _ = combineLatest( - queue: Queue.mainQueue(), - self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: sendAsPeerId)), - self.context.engine.peers.getChannelBoostStatus(peerId: sendAsPeerId) - ).start(next: { [weak self] peer, status in - guard let self, let peer, let status else { - return - } - - let link: String - if let addressName = peer.addressName, !addressName.isEmpty { - link = "t.me/\(peer.addressName ?? "")?boost" - } else { - link = "t.me/c/\(peer.id.id._internalGetInt64Value())?boost" - } - - let controller = self.context.sharedContext.makePremiumLimitController(context: self.context, subject: .storiesChannelBoost(peer: peer, isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, boosted: false), count: Int32(status.boosts), forceDark: true, cancel: {}, action: { [weak self] in - guard let self else { - return true - } - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - self.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { _ in return false }), in: .current) - UIPasteboard.general.string = "https://\(link)" - return true - }) - self.push(controller) - - self.hapticFeedback.impact(.light) - }) - default: - break - } - }) - return - } - self.didComplete = true self.dismissAllTooltips() diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/BUILD b/submodules/TelegramUI/Components/ShareWithPeersScreen/BUILD index 4129b96171..f53e1721b9 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/BUILD +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/BUILD @@ -39,6 +39,7 @@ swift_library( "//submodules/TelegramUI/Components/SwitchComponent", "//submodules/TooltipUI", "//submodules/OverlayStatusController", + "//submodules/UndoUI", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index d9cff048ef..697cb71947 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -25,6 +25,7 @@ import TooltipUI import OverlayStatusController import Markdown import TelegramUIPreferences +import UndoUI final class ShareWithPeersScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -345,6 +346,8 @@ final class ShareWithPeersScreenComponent: Component { private var searchStateContext: ShareWithPeersScreen.StateContext? private var searchStateDisposable: Disposable? + private let postingAvailabilityDisposable = MetaDisposable() + private let hapticFeedback = HapticFeedback() private var effectiveStateValue: ShareWithPeersScreen.State? { @@ -1063,15 +1066,73 @@ final class ShareWithPeersScreenComponent: Component { selectionState: .none, hasNext: i < peers.count - 1, action: { [weak self] peer in - guard let self else { + guard let self, let component = self.component else { return } if isStories { let _ = self.presentSendAsPeer() } else { - self.hapticFeedback.impact(.light) - self.environment?.controller()?.dismiss() - self.component?.peerCompletion(peer.id) + if peer.id.namespace == Namespaces.Peer.CloudUser { + self.component?.peerCompletion(peer.id) + self.environment?.controller()?.dismiss() + + self.hapticFeedback.impact(.light) + } else { + self.postingAvailabilityDisposable.set((component.context.engine.messages.checkStoriesUploadAvailability(target: .peer(peer.id)) + |> deliverOnMainQueue).start(next: { [weak self] status in + guard let self, let component = self.component else { + return + } + switch status { + case .available: + component.peerCompletion(peer.id) + self.environment?.controller()?.dismiss() + case .channelBoostRequired: + let _ = combineLatest( + queue: Queue.mainQueue(), + component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)), + component.context.engine.peers.getChannelBoostStatus(peerId: peer.id) + ).start(next: { [weak self] peer, status in + guard let self, let component = self.component, let peer, let status else { + return + } + + let link: String + if let addressName = peer.addressName, !addressName.isEmpty { + link = "t.me/\(peer.addressName ?? "")?boost" + } else { + link = "t.me/c/\(peer.id.id._internalGetInt64Value())?boost" + } + + if let navigationController = self.environment?.controller()?.navigationController as? NavigationController { + if let previousController = navigationController.viewControllers.last as? ShareWithPeersScreen { + previousController.dismiss() + } + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let controller = component.context.sharedContext.makePremiumLimitController(context: component.context, subject: .storiesChannelBoost(peer: peer, isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, boosted: false), count: Int32(status.boosts), forceDark: true, cancel: {}, action: { [weak navigationController] in + UIPasteboard.general.string = "https://\(link)" + + if let previousController = navigationController?.viewControllers.reversed().first(where: { $0 is ShareWithPeersScreen}) as? ShareWithPeersScreen { + previousController.dismiss(completion: { [weak navigationController] in + Queue.mainQueue().justDispatch { + if let controller = navigationController?.viewControllers.last as? ViewController { + controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: true, position: .top, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + } + }) + } + return true + }) + navigationController.pushViewController(controller) + } + + self.hapticFeedback.impact(.light) + }) + default: + break + } + })) + } } } )), @@ -2826,9 +2887,10 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer { closeFriendsPeers: closeFriends, grayListPeers: grayListPeers ) + self.stateValue = state self.stateSubject.set(.single(state)) - + self.readySubject.set(true) }) case let .chats(isGrayList): diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index ddcdb02758..0dd67d5a3e 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -791,7 +791,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur let errorText: String switch error { case .generic: - errorText = "The folder link has expired." + errorText = presentationData.strings.Chat_ErrorFolderLinkExpired } present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) })) @@ -877,7 +877,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur case .ok: updateImpl?() case let .replace(previousPeer): - let text = "You currently boost **\(previousPeer.compactDisplayTitle)**. Do you want to boost **\(peer.compactDisplayTitle)** instead?" + let text = presentationData.strings.ChannelBoost_ReplaceBoost(previousPeer.compactDisplayTitle, peer.compactDisplayTitle).string let controller = replaceBoostConfirmationController(context: context, fromPeer: previousPeer, toPeer: peer, text: text, commit: { updateImpl?() }) @@ -895,16 +895,13 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur title = nil text = presentationData.strings.Login_UnknownError case let .floodWait(timeout): - title = "Can't Boost Too Often" + title = presentationData.strings.ChannelBoost_Error_BoostTooOftenTitle let valueText = timeIntervalString(strings: presentationData.strings, value: timeout, usage: .afterTime, preferLowerValue: false) - text = "You can change the channel you boost only once a day. Next time you can boost is in **\(valueText)**." + text = presentationData.strings.ChannelBoost_Error_BoostTooOftenText(valueText).string dismiss = true - case .peerBoostAlreadyActive: - title = "Already Boosted" - text = "You are already boosting this channel." case .premiumRequired: - title = "Premium Needed" - text = "Only **Telegram Premium** subscribers can boost channels. Do you want to subscribe to **Telegram Premium**?" + title = presentationData.strings.ChannelBoost_Error_PremiumNeededTitle + text = presentationData.strings.ChannelBoost_Error_PremiumNeededText actions = [ TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { @@ -914,9 +911,11 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur }) ] case .giftedPremiumNotAllowed: - title = "Can't Boost with Gifted Premium" - text = "Because your **Telegram Premium** subscription was gifted to you, you can't use it to boost channels." + title = presentationData.strings.ChannelBoost_Error_GiftedPremiumNotAllowedTitle + text = presentationData.strings.ChannelBoost_Error_GiftedPremiumNotAllowedText dismiss = true + case .peerBoostAlreadyActive: + return true } let controller = textAlertController(sharedContext: context.sharedContext, title: title, text: text, actions: actions, parseMarkdown: true)