diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index ce0ab57c8c..18991c0acb 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11718,6 +11718,11 @@ Sorry for the inconvenience."; "Monetization.Withdraw.EnterPassword.Text" = "Please enter your Two-Step Verification password to complete this action."; "Monetization.Withdraw.EnterPassword.Done" = "Proceed"; +"Monetization.Withdraw.SecurityCheck" = "Security Check"; +"Monetization.Withdraw.SecurityRequirements" = "Balance withdrawals are available if:\n\n• 2-Step verification was enabled for your account more than **7 days** ago.\n\n• You have logged in on this device more than **24 hours** ago."; +"Monetization.Withdraw.ComeBackLater" = "\n\nPlease come back later."; +"Monetization.Withdraw.SetupTwoStepAuth" = "Enable 2-Step Verification"; + "Monetization.TransactionInfo.Proceeds" = "Proceeds from Ads displayed in"; "Monetization.TransactionInfo.Withdrawal" = "Balance Withdrawal via %@"; "Monetization.TransactionInfo.Pending" = "Pending"; diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 96dc08b168..0b8c24b9b9 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -298,6 +298,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.scrollingArea = SparseItemGridScrollingArea() self.cameraWrapperView = UIView() + self.cameraWrapperView.clipsToBounds = true self.cameraActivateAreaNode = AccessibilityAreaNode() self.cameraActivateAreaNode.accessibilityLabel = "Camera" @@ -1554,14 +1555,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { transition.updateFrame(view: self.cameraWrapperView, frame: cameraRect) let screenWidth = min(layout.deviceMetrics.screenSize.width, layout.deviceMetrics.screenSize.height) - let cameraFullSize = cameraRect.size.aspectFilled(CGSize(width: screenWidth, height: screenWidth)) - let cameraScale = cameraRect.width / cameraFullSize.width + let cameraFullSize = CGSize(width: screenWidth, height: floorToScreenPixels(layout.size.width * 1.77778)) + let cameraScale = cameraRect.height / cameraFullSize.height cameraView.bounds = CGRect(origin: .zero, size: cameraFullSize) cameraView.center = CGPoint(x: cameraRect.size.width / 2.0, y: cameraRect.size.height / 2.0) cameraView.transform = CGAffineTransform(scaleX: cameraScale, y: cameraScale) - - transition.updateFrame(view: cameraView, frame: CGRect(origin: .zero, size: cameraRect.size)) + } else { transition.updateFrame(view: cameraView, frame: cameraRect) } diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index b59bb9763e..f693400079 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1540,6 +1540,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD let dataPromise = Promise(nil) let messagesPromise = Promise(nil) + let withdrawalDisposable = MetaDisposable() + actionsDisposable.add(withdrawalDisposable) + let storiesPromise = Promise() let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId) @@ -2120,15 +2123,18 @@ public func channelStatsController(context: AccountContext, updatedPresentationD } } requestWithdrawImpl = { - let controller = revenueWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, present: { c, _ in - presentImpl?(c) - }, completion: { [weak revenueContext] url in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) - - revenueContext?.reload() - }) - presentImpl?(controller) + withdrawalDisposable.set((context.engine.peers.checkChannelRevenueWithdrawalAvailability() + |> deliverOnMainQueue).start(error: { error in + let controller = revenueWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, initialError: error, present: { c, _ in + presentImpl?(c) + }, completion: { [weak revenueContext] url in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) + + revenueContext?.reload() + }) + presentImpl?(controller) + })) } openTransactionImpl = { transaction in let _ = (peer.get() diff --git a/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift b/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift index eb1808648d..77755aa3ec 100644 --- a/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift +++ b/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift @@ -6,8 +6,10 @@ import TelegramPresentationData import PresentationDataUtils import PeerInfoUI import AccountContext +import PasswordSetupUI +import Markdown -func revenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { +func confirmRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } var dismissImpl: (() -> Void)? @@ -69,3 +71,42 @@ func revenueWithdrawalController(context: AccountContext, updatedPresentationDat return controller } + + +func revenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, initialError: RequestRevenueWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + let theme = AlertControllerTheme(presentationData: presentationData) + + var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + + var text = presentationData.strings.Monetization_Withdraw_SecurityRequirements + let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 + + var actions: [TextAlertAction] = [] + switch initialError { + case .requestPassword: + return confirmRevenueWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, present: present, completion: completion) + case .twoStepAuthTooFresh, .authSessionTooFresh: + text = text + presentationData.strings.Monetization_Withdraw_ComeBackLater + actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + case .twoStepAuthMissing: + actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: { + let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in + if shouldDismiss { + controller.dismiss() + } + }) + present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] + default: + title = nil + text = presentationData.strings.Login_UnknownError + actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + } + + let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) + + return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) +} diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 2fd87adc12..3aa9509d4f 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -1924,24 +1924,59 @@ public class CameraScreen: ViewController { case .changed: if case .none = self.cameraState.recording { if case .compact = layout.metrics.widthClass { - if (translation.x < -10.0 || self.isDismissing) && self.hasAppeared { - self.isDismissing = true - let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width - controller.updateTransitionProgress(transitionFraction, transition: .immediate) - } else if translation.y < -10.0 && abs(translation.y) > abs(translation.x) { - controller.presentGallery(fromGesture: true) - gestureRecognizer.isEnabled = false - gestureRecognizer.isEnabled = true + switch controller.mode { + case .story: + if (translation.x < -10.0 || self.isDismissing) && self.hasAppeared { + self.isDismissing = true + let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width + controller.updateTransitionProgress(transitionFraction, transition: .immediate) + } else if translation.y < -10.0 && abs(translation.y) > abs(translation.x) { + controller.presentGallery(fromGesture: true) + gestureRecognizer.isEnabled = false + gestureRecognizer.isEnabled = true + } + case .sticker: + if (abs(translation.y) > 10.0 || self.isDismissing) && self.hasAppeared { + self.containerView.layer.sublayerTransform = CATransform3DMakeTranslation(0.0, translation.y, 0.0) + if !self.isDismissing { + controller.statusBar.updateStatusBarStyle(.Ignore, animated: true) + self.isDismissing = true + ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut).updateAlpha(layer: self.backgroundView.layer, alpha: 0.0) + } + } } } } case .ended, .cancelled: if self.isDismissing { - let velocity = gestureRecognizer.velocity(in: self.view) - let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width - controller.completeWithTransitionProgress(transitionFraction, velocity: abs(velocity.x), dismissing: true) - - self.isDismissing = false + switch controller.mode { + case .story: + let velocity = gestureRecognizer.velocity(in: self.view) + let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width + controller.completeWithTransitionProgress(transitionFraction, velocity: abs(velocity.x), dismissing: true) + + self.isDismissing = false + case .sticker: + let velocity = gestureRecognizer.velocity(in: self.view) + let transitionFraction = translation.y / self.frame.height + if abs(transitionFraction) > 0.3 || abs(velocity.y) > 1000.0 { + self.containerView.layer.sublayerTransform = CATransform3DIdentity + self.mainPreviewView.center = self.previewContainerView.center.offsetBy(dx: 0.0, dy: translation.y) + + if let view = self.componentHost.view { + view.center = view.center.offsetBy(dx: 0.0, dy: translation.y) + } + + controller.requestDismiss(animated: true, interactive: true) + } else { + ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut).updateAlpha(layer: self.backgroundView.layer, alpha: 1.0) + controller.statusBar.updateStatusBarStyle(.White, animated: true) + + ContainedViewLayoutTransition.animated(duration: 0.3, curve: .spring).updateSublayerTransformOffset(layer: self.containerView.layer, offset: .zero) + } + + self.isDismissing = false + } } default: break @@ -2095,7 +2130,12 @@ public class CameraScreen: ViewController { self.requestUpdateLayout(hasAppeared: true, transition: .immediate) }) - self.mainPreviewView.layer.animateBounds(from: self.mainPreviewView.bounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + var sourceBounds = self.mainPreviewView.bounds + if let holder = controller.holder { + sourceBounds = CGRect(origin: .zero, size: holder.parentView.frame.size.aspectFitted(sourceBounds.size)) + } + + self.mainPreviewView.layer.animateBounds(from: sourceBounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) let sourceScale = self.mainPreviewView.layer.value(forKeyPath: "transform.scale.x") as? CGFloat ?? 1.0 self.mainPreviewView.transform = CGAffineTransform.identity @@ -2154,7 +2194,7 @@ public class CameraScreen: ViewController { self.mainPreviewView.layer.animateBounds(from: self.mainPreviewView.bounds, to: CGRect(origin: .zero, size: self.previewContainerView.frame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) let targetScale = destinationInnerFrame.width / self.previewContainerView.frame.width -// self.mainPreviewView.transform = CGAffineTransform.identity + self.mainPreviewView.transform = CGAffineTransform(scaleX: targetScale, y: targetScale) self.mainPreviewView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) } @@ -3006,8 +3046,10 @@ public class CameraScreen: ViewController { }) } else { if !interactive { - if let navigationController = self.navigationController as? NavigationController { - navigationController.updateRootContainerTransitionOffset(self.node.frame.width, transition: .immediate) + if case .story = self.mode { + if let navigationController = self.navigationController as? NavigationController { + navigationController.updateRootContainerTransitionOffset(self.node.frame.width, transition: .immediate) + } } } self.updateTransitionProgress(0.0, transition: .animated(duration: 0.4, curve: .spring), completion: { [weak self] in @@ -3060,9 +3102,11 @@ public class CameraScreen: ViewController { return true }) - if let navigationController = self.navigationController as? NavigationController { - let offsetX = floorToScreenPixels(transitionFraction * self.node.frame.width) - navigationController.updateRootContainerTransitionOffset(offsetX, transition: transition) + if case .story = self.mode { + if let navigationController = self.navigationController as? NavigationController { + let offsetX = floorToScreenPixels(transitionFraction * self.node.frame.width) + navigationController.updateRootContainerTransitionOffset(offsetX, transition: transition) + } } } @@ -3103,7 +3147,9 @@ public class CameraScreen: ViewController { self.ignoreStatusBar = false self.updateTransitionProgress(1.0, transition: .animated(duration: 0.4, curve: .spring), completion: { [weak self] in if let self, let navigationController = self.navigationController as? NavigationController { - navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate) + if case .story = self.mode { + navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate) + } } }) } @@ -3113,7 +3159,9 @@ public class CameraScreen: ViewController { self.updateStatusBarAppearance() self.updateTransitionProgress(1.0, transition: .animated(duration: 0.4, curve: .spring), completion: { [weak self] in if let self, let navigationController = self.navigationController as? NavigationController { - navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate) + if case .story = self.mode { + navigationController.updateRootContainerTransitionOffset(0.0, transition: .immediate) + } self.node.requestUpdateLayout(hasAppeared: true, transition: .immediate) self.transitionedIn() }