Merge commit '8aca1f28fb3faf48a85feb33216ce673a4ad438a'

This commit is contained in:
Ali 2022-05-29 02:09:28 +04:00
commit 0dd4f9d438
35 changed files with 460 additions and 159 deletions

View File

@ -7530,8 +7530,6 @@ Sorry for the inconvenience.";
"StickerPack.Share" = "Share Stickers";
"StickerPack.CopyLink" = "Copy Link";
"StickerPack.PremiumStickers_1" = "+%@ PREMIUM STICKER";
"StickerPack.PremiumStickers_any" = "+%@ PREMIUM STICKERS";
"Stickers.PremiumStickers" = "Premium Stickers";
@ -7546,11 +7544,11 @@ Sorry for the inconvenience.";
"Chat.MultipleTypingPair" = "%@ and %@";
"Chat.MultipleTypingMore" = "%@ and %@ others";
"DialogList.ExtendedPinLimitError" = "Sorry, you can't pin more than **%1$@** chats to the top. Unpin some of the currently pinned ones or subscribe to **Telegram Premium** to double the limit to **%2$@** chats.";
"Group.Username.RemoveExistingUsernamesOrExtendInfo" = "You have reserved too many public links. Try revoking a link from an older group or channel, or upgrade to **Telegram Premium** to double the limit to **%@** public links.";
"Group.Username.RemoveExistingUsernamesFinalInfo" = "You have reserved too many public links. Try revoking the link from an older group or channel, or create a private one instead.";
"OldChannels.TooManyCommunitiesText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels.";
"OldChannels.TooManyCommunitiesFinalText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one.";
"OldChannels.LeaveCommunities_1" = "Leave %@ Community";
"OldChannels.LeaveCommunities_any" = "Leave %@ Communities";
@ -7559,11 +7557,19 @@ Sorry for the inconvenience.";
"Premium.IncreaseLimit" = "Increase Limit";
"Premium.MaxFoldersCountText" = "You have reached the limit of **%1$@** folders. You can double the limit to **%2$@** folders by subscribing to **Telegram Premium**.";
"Premium.MaxChatsInFolderCountText" = "Sorry, you can't add more than **%1$@** chats to a folder. You can increase this limit to **%2$@** by upgrading to **Telegram Premium**.";
"Premium.MaxChatsInFolderText" = "Sorry, you can't add more than **%1$@** chats to a folder. You can increase this limit to **%2$@** by upgrading to **Telegram Premium**.";
"Premium.MaxChatsInFolderFinalText" = "Sorry, you can't add more than **%@** chats to a folder.";
"Premium.MaxFileSizeText" = "Double this limit to %@ per file by subscribing to **Telegram Premium**.";
"Premium.MaxFileSizeFinalText" = "The document can't be sent, because it is larger than **%@**.";
"Premium.MaxPinsText" = "Sorry, you can't pin more than **%1$@** chats to the top. Unpin some of the currently pinned ones or subscribe to **Telegram Premium** to double the limit to **%2$@** chats.";
"Premium.MaxPinsFinalText" = "Sorry, you can't pin more than **@** chats to the top. Unpin some of the currently pinned ones.";
"Premium.MaxPinsFinalText" = "Sorry, you can't pin more than **@** chats to the top.";
"Premium.MaxFavedStickersTitle" = "The Limit of %@ Stickers Reached";
"Premium.MaxFavedStickersText" = "An older sticker was replaced with this one. You can [increase the limit]() to %@ stickers.";
"Premium.MaxFavedStickersFinalText" = "An older sticker was replaced with this one.";
"Premium.MaxSavedGifsTitle" = "The Limit of %@ GIFs Reached";
"Premium.MaxSavedGifsText" = "An older GIF was replaced with this one. You can [increase the limit]() to %@ GIFS.";
"Premium.MaxSavedGifsFinalText" = "An older GIF was replaced with this one.";
"Premium.MaxAccountsText" = "You have reached the limit of **%@** connected accounts. You can free one place by subscribing to **Telegram Premium**.";
"Premium.Free" = "Free";
@ -7636,3 +7642,5 @@ Sorry for the inconvenience.";
"SponsoredMessageMenu.Hide" = "Hide";
"ChatListFolder.MaxChatsInFolder" = "Sorry, you can't add more than %d chats to a folder.";
"Conversation.SaveGif" = "Save GIF";

View File

@ -712,6 +712,8 @@ public protocol SharedAccountContext: AnyObject {
func makeChatQrCodeScreen(context: AccountContext, peer: Peer) -> ViewController
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController
func navigateToCurrentCall()
var hasOngoingCall: ValuePromise<Bool> { get }
var immediateHasOngoingCall: Bool { get }
@ -723,6 +725,24 @@ public protocol SharedAccountContext: AnyObject {
func beginNewAuth(testingEnvironment: Bool)
}
public enum PremiumIntroSource {
case settings
case stickers
case reactions
case ads
case upload
case groupsAndChannels
case pinnedChats
case publicLinks
case savedGifs
case savedStickers
case folders
case chatsPerFolder
case accounts
case deeplink(String?)
case profile(PeerId)
}
#if ENABLE_WALLET
private final class TonInstanceData {
var config: String?

View File

@ -332,7 +332,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
if let peer = peer {
return self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.UserLimits.init(isPremium: peer.isPremium))
|> map { limits in
return limits.maxCaptionLengthCount
return limits.maxCaptionLength
}
} else {
return .complete()

View File

@ -6,9 +6,9 @@ import ViewControllerComponent
public final class SheetComponentEnvironment: Equatable {
public let isDisplaying: Bool
public let dismiss: () -> Void
public let dismiss: (Bool) -> Void
public init(isDisplaying: Bool, dismiss: @escaping () -> Void) {
public init(isDisplaying: Bool, dismiss: @escaping (Bool) -> Void) {
self.isDisplaying = isDisplaying
self.dismiss = dismiss
}
@ -55,7 +55,7 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
private let contentView: ComponentHostView<ChildEnvironmentType>
private var previousIsDisplaying: Bool = false
private var dismiss: (() -> Void)?
private var dismiss: ((Bool) -> Void)?
override init(frame: CGRect) {
self.dimView = UIView()
@ -78,8 +78,9 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
super.init(frame: frame)
self.addSubview(self.dimView)
self.scrollView.delegate = self
self.addSubview(self.dimView)
self.scrollView.addSubview(self.backgroundView)
self.scrollView.addSubview(self.contentView)
self.addSubview(self.scrollView)
@ -93,17 +94,39 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
@objc private func dimViewTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.dismiss?()
self.dismiss?(true)
}
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
}
private var scrollingOut = false
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let contentOffset = (scrollView.contentOffset.y + scrollView.contentInset.top - scrollView.contentSize.height) * -1.0
let dismissalOffset = scrollView.contentSize.height - scrollView.contentInset.top + scrollView.contentSize.height
let delta = dismissalOffset - contentOffset
let initialVelocity = !delta.isZero ? velocity.y / delta : 0.0
targetContentOffset.pointee = scrollView.contentOffset
if velocity.y > 300.0 {
self.animateOut(initialVelocity: initialVelocity, completion: {
self.dismiss?(false)
})
} else {
if contentOffset < scrollView.contentSize.height * 0.333 {
scrollView.setContentOffset(CGPoint(x: 0.0, y: scrollView.contentSize.height - scrollView.contentInset.top), animated: true)
} else {
self.animateOut(initialVelocity: initialVelocity, completion: {
self.dismiss?(false)
})
}
}
}
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffset = (scrollView.contentOffset.y + scrollView.contentInset.top - scrollView.contentSize.height) * -1.0
if contentOffset >= scrollView.contentSize.height {
self.dismiss?(false)
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -114,14 +137,38 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
return super.hitTest(point, with: event)
}
private func animateOut(completion: @escaping () -> Void) {
self.isUserInteractionEnabled = false
self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.bounds.height - self.scrollView.contentInset.top), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
completion()
private func animateIn() {
self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
let targetPosition = self.scrollView.center
self.scrollView.center = targetPosition.offsetBy(dx: 0.0, dy: self.scrollView.contentSize.height)
transition.animateView(allowUserInteraction: true, {
self.scrollView.center = targetPosition
})
}
private func animateOut(initialVelocity: CGFloat? = nil, completion: @escaping () -> Void) {
self.isUserInteractionEnabled = false
self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
if let initialVelocity = initialVelocity {
let transition = ContainedViewLayoutTransition.animated(duration: 0.35, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
let contentOffset = (self.scrollView.contentOffset.y + self.scrollView.contentInset.top - self.scrollView.contentSize.height) * -1.0
let dismissalOffset = self.scrollView.contentSize.height
let delta = dismissalOffset - contentOffset
transition.updatePosition(layer: self.scrollView.layer, position: CGPoint(x: self.scrollView.center.x, y: self.scrollView.center.y + delta), completion: { _ in
completion()
})
} else {
self.scrollView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.scrollView.contentSize.height), duration: 0.25, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
completion()
})
}
}
func update(component: SheetComponent<ChildEnvironmentType>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
component.animateOut.connect { [weak self] completion in
guard let strongSelf = self else {
@ -147,15 +194,14 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
containerSize: CGSize(width: availableSize.width, height: .greatestFiniteMagnitude)
)
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
transition.setFrame(view: self.contentView, frame: CGRect(origin: .zero, size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
self.scrollView.contentSize = contentSize
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height), left: 0.0, bottom: 0.0, right: 0.0)
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height) + contentSize.height, left: 0.0, bottom: 0.0, right: 0.0)
if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) {
self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.scrollView.layer.animatePosition(from: CGPoint(x: 0.0, y: availableSize.height - self.scrollView.contentInset.top), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true, completion: nil)
self.animateIn()
} else if !environment[SheetComponentEnvironment.self].value.isDisplaying, self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateOutTransition.self) {
self.animateOut(completion: {})
}

View File

@ -1249,9 +1249,32 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Preview_SaveGif, action: { [weak self] in
if let strongSelf = self {
let message = messages[0]
let _ = addSavedGif(postbox: strongSelf.context.account.postbox, fileReference: .message(message: MessageReference(message._asMessage()), media: file)).start()
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
let context = strongSelf.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controllerInteraction = strongSelf.controllerInteraction
let _ = (toggleGifSaved(account: context.account, fileReference: .message(message: MessageReference(message._asMessage()), media: file), saved: true)
|> deliverOnMainQueue).start(next: { result in
switch result {
case .generic:
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
let text: String
if limit == premiumLimit {
text = presentationData.strings.Premium_MaxSavedGifsFinalText
} else {
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
}
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: true, animateInAsReplacement: true, action: { action in
if case .info = action {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs)
controllerInteraction?.pushController(controller)
return true
}
return false
}), nil)
}
})
}
}))
} else if file.mimeType.hasPrefix("image/") {
@ -1421,9 +1444,31 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
if file.isAnimated {
preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Preview_SaveGif, action: { [weak self] in
if let strongSelf = self {
let _ = addSavedGif(postbox: strongSelf.context.account.postbox, fileReference: .webPage(webPage: WebpageReference(webPage), media: file)).start()
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
let context = strongSelf.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controllerInteraction = strongSelf.controllerInteraction
let _ = (toggleGifSaved(account: context.account, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), saved: true)
|> deliverOnMainQueue).start(next: { result in
switch result {
case .generic:
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
let text: String
if limit == premiumLimit {
text = presentationData.strings.Premium_MaxSavedGifsFinalText
} else {
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
}
controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: true, animateInAsReplacement: true, action: { action in
if case .info = action {
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs)
controllerInteraction?.pushController(controller)
return true
}
return false
}), nil)
}
})
}
}))
} else if file.mimeType.hasPrefix("image/") || file.mimeType.hasPrefix("video/") {

View File

@ -1002,7 +1002,10 @@ public class GalleryController: ViewController, StandalonePresentableController
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, dismissController: { [weak self] in
}, pushController: { [weak self] c in
self?.baseNavigationController?.pushViewController(c)
self?.dismiss(forceAway: true)
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { [weak self] controller, ready in
if let strongSelf = self {

View File

@ -7,12 +7,14 @@ import Postbox
public final class GalleryControllerInteraction {
public let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void
public let pushController: (ViewController) -> Void
public let dismissController: () -> Void
public let replaceRootController: (ViewController, Promise<Bool>?) -> Void
public let editMedia: (MessageId) -> Void
public init(presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, dismissController: @escaping () -> Void, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, editMedia: @escaping (MessageId) -> Void) {
public init(presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping (ViewController) -> Void, dismissController: @escaping () -> Void, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, editMedia: @escaping (MessageId) -> Void) {
self.presentController = presentController
self.pushController = pushController
self.dismissController = dismissController
self.replaceRootController = replaceRootController
self.editMedia = editMedia

View File

@ -204,6 +204,7 @@ public final class SecretMediaPreviewController: ViewController {
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, pushController: { _ in
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { _, _ in

View File

@ -362,6 +362,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, pushController: { _ in
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { [weak self] controller, ready in

View File

@ -176,6 +176,7 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, pushController: { _ in
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { [weak self] controller, ready in

View File

@ -207,12 +207,18 @@ private final class AddPaymentMethodSheetComponent: CombinedComponent {
environment
SheetComponentEnvironment(
isDisplaying: environment.value.isVisible,
dismiss: {
animateOut.invoke(Action { _ in
dismiss: { animated in
if animated {
animateOut.invoke(Action { _ in
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
} else {
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
}
}
)
},

View File

@ -565,6 +565,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, pushController: { _ in
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { [weak self] controller, ready in

View File

@ -60,6 +60,7 @@ private final class PhoneView: UIView {
self.contentContainerView.clipsToBounds = true
self.contentContainerView.backgroundColor = .darkGray
self.contentContainerView.layer.cornerRadius = 10.0
self.contentContainerView.layer.allowsGroupOpacity = true
self.overlayView = UIView()
self.overlayView.backgroundColor = .black
@ -230,22 +231,26 @@ final class PhoneDemoComponent: Component {
var mappedPosition = environment[DemoPageEnvironment.self].position
mappedPosition *= abs(mappedPosition)
let scale: CGFloat = availableSize.width / 390.0
let phoneX = mappedPosition * 50.0 * scale
let phoneY: CGFloat
switch component.position {
case .top:
phoneY = phoneSize.height / 2.0 + 24.0 + abs(mappedPosition) * 24.0
phoneY = availableSize.height + (-phoneSize.height / 2.0 + 24.0 + 149.0 + abs(mappedPosition) * 24.0) * scale
case .bottom:
phoneY = availableSize.height - phoneSize.height / 2.0 - 24.0 - abs(mappedPosition) * 24.0
phoneY = (-149.0 + phoneSize.height / 2.0 - 24.0 - abs(mappedPosition) * 24.0) * scale
}
let isVisible = environment[DemoPageEnvironment.self].isDisplaying
let isCentral = environment[DemoPageEnvironment.self].isCentral
self.isCentral = isCentral
self.phoneView.center = CGPoint(x: availableSize.width / 2.0 + mappedPosition * 50.0, y: phoneY)
self.phoneView.center = CGPoint(x: availableSize.width / 2.0 + phoneX, y: phoneY)
self.phoneView.screenRotation = mappedPosition * -0.7
var perspective = CATransform3DIdentity
var perspective = CATransform3DMakeScale(scale, scale, 1.0)
perspective.m34 = mappedPosition / 50.0
self.phoneView.layer.transform = CATransform3DRotate(perspective, 0.1, 0, 1, 0)

View File

@ -855,7 +855,7 @@ private final class DemoSheetContent: CombinedComponent {
transition: context.transition
)
var contentHeight: CGFloat = context.availableSize.width + 154.0
var contentHeight: CGFloat = context.availableSize.width + 146.0
if case .other = component.source {
contentHeight -= 40.0
}
@ -865,7 +865,9 @@ private final class DemoSheetContent: CombinedComponent {
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
)
let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom)
let bottomPanelPadding: CGFloat = 12.0
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + bottomInset)
return contentSize
}
@ -933,12 +935,18 @@ private final class DemoSheetComponent: CombinedComponent {
environment
SheetComponentEnvironment(
isDisplaying: environment.value.isVisible,
dismiss: {
animateOut.invoke(Action { _ in
dismiss: { animated in
if animated {
animateOut.invoke(Action { _ in
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
} else {
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
}
}
)
},

View File

@ -698,7 +698,7 @@ private final class LimitSheetContent: CombinedComponent {
let premiumLimit = state.premiumLimits.maxFolderChatsCount
iconName = "Premium/Chat"
badgeText = "\(component.count)"
string = strings.Premium_MaxChatsInFolderCountText("\(limit)", "\(premiumLimit)").string
string = strings.Premium_MaxChatsInFolderText("\(limit)", "\(premiumLimit)").string
defaultValue = component.count > limit ? "\(limit)" : ""
premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)"
badgePosition = CGFloat(component.count) / CGFloat(premiumLimit)
@ -909,12 +909,18 @@ private final class LimitSheetComponent: CombinedComponent {
environment
SheetComponentEnvironment(
isDisplaying: environment.value.isVisible,
dismiss: {
animateOut.invoke(Action { _ in
dismiss: { animated in
if animated {
animateOut.invoke(Action { _ in
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
} else {
if let controller = controller() {
controller.dismiss(completion: nil)
}
})
}
}
)
},

View File

@ -223,7 +223,7 @@ private class StickersCarouselNode: ASDisplayNode, UIScrollViewDelegate {
init(context: AccountContext, stickers: [TelegramMediaFile]) {
self.context = context
self.stickers = Array(stickers.shuffled().prefix(14))
self.stickers = stickers
self.scrollNode = ASScrollNode()
self.tapNode = ASDisplayNode()

View File

@ -357,6 +357,7 @@ public class WallpaperGalleryController: ViewController {
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, pushController: { _ in
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { controller, ready in

View File

@ -1,69 +0,0 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramPresentationData
import AppBundle
final class StickerPackPreviewPremiumHeaderItem: GridItem {
let theme: PresentationTheme
let strings: PresentationStrings
let count: Int32
let section: GridSection? = nil
let fillsRowWithHeight: (CGFloat, Bool)? = (15.0, true)
init(theme: PresentationTheme, strings: PresentationStrings, count: Int32) {
self.theme = theme
self.strings = strings
self.count = count
}
func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode {
let node = StickerPackPreviewPremiumHeaderItemNode()
node.setup(theme: self.theme, strings: self.strings, count: self.count)
return node
}
func update(node: GridItemNode) {
guard let node = node as? StickerPackPreviewPremiumHeaderItemNode else {
assertionFailure()
return
}
node.setup(theme: self.theme, strings: self.strings, count: self.count)
}
}
final class StickerPackPreviewPremiumHeaderItemNode: GridItemNode {
private var currentState: (PresentationTheme, PresentationStrings, Int32)?
let labelNode: ImmediateTextNode
override init() {
self.labelNode = ImmediateTextNode()
self.labelNode.displaysAsynchronously = false
self.labelNode.isUserInteractionEnabled = false
super.init()
self.addSubnode(self.labelNode)
}
func setup(theme: PresentationTheme, strings: PresentationStrings, count: Int32) {
if self.currentState?.0 !== theme || self.currentState?.1 !== strings || self.currentState?.2 != count {
self.labelNode.attributedText = NSAttributedString(string: strings.StickerPack_PremiumStickers(count), font: Font.medium(12.0), textColor: theme.actionSheet.controlAccentColor)
self.currentState = (theme, strings, count)
}
}
override func layout() {
super.layout()
let bounds = self.bounds
let textSize = self.labelNode.updateLayout(bounds.size)
let textFrame = CGRect(origin: CGPoint(x: floor((bounds.width - textSize.width) / 2.0), y: floor((bounds.height - textSize.height) / 2.0)), size: textSize)
self.labelNode.frame = textFrame
}
}

View File

@ -18,14 +18,11 @@ import PremiumUI
private enum StickerPackPreviewGridEntry: Comparable, Identifiable {
case sticker(index: Int, stableId: Int, stickerItem: StickerPackItem?, isEmpty: Bool, isPremium: Bool, isLocked: Bool)
case premiumHeader(index: Int, stableId: Int, count: Int32)
var stableId: Int {
switch self {
case let .sticker(_, stableId, _, _, _, _):
return stableId
case let .premiumHeader(_, stableId, _):
return stableId
}
}
@ -33,8 +30,6 @@ private enum StickerPackPreviewGridEntry: Comparable, Identifiable {
switch self {
case let .sticker(index, _, _, _, _, _):
return index
case let .premiumHeader(index, _, _):
return index
}
}
@ -46,8 +41,6 @@ private enum StickerPackPreviewGridEntry: Comparable, Identifiable {
switch self {
case let .sticker(_, _, stickerItem, isEmpty, isPremium, isLocked):
return StickerPackPreviewGridItem(account: account, stickerItem: stickerItem, interaction: interaction, theme: theme, isPremium: isPremium, isLocked: isLocked, isEmpty: isEmpty)
case let .premiumHeader(_, _, count):
return StickerPackPreviewPremiumHeaderItem(theme: theme, strings: strings, count: count)
}
}
}

View File

@ -30,11 +30,11 @@ public func getIsGifSaved(transaction: Transaction, mediaId: MediaId) -> Bool {
return false
}
public func addSavedGif(postbox: Postbox, fileReference: FileMediaReference) -> Signal<Void, NoError> {
public func addSavedGif(postbox: Postbox, fileReference: FileMediaReference, limit: Int = 200) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Void in
if let resource = fileReference.media.resource as? CloudDocumentMediaResource {
if let entry = CodableEntry(RecentMediaItem(fileReference.media)) {
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(fileReference.media.fileId).rawValue, contents: entry), removeTailIfCountExceeds: 200)
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(fileReference.media.fileId).rawValue, contents: entry), removeTailIfCountExceeds: limit)
}
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .add(id: resource.fileId, accessHash: resource.accessHash, fileReference: fileReference))
}
@ -52,3 +52,48 @@ public func removeSavedGif(postbox: Postbox, mediaId: MediaId) -> Signal<Void, N
}
}
}
public enum SavedGifResult {
case generic
case limitExceeded(Int32, Int32)
}
public func toggleGifSaved(account: Account, fileReference: FileMediaReference, saved: Bool) -> Signal<SavedGifResult, NoError> {
if saved {
return account.postbox.transaction { transaction -> Signal<SavedGifResult, NoError> in
let isPremium = transaction.getPeer(account.peerId)?.isPremium ?? false
let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs)
let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue
let limitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
let premiumLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: true)
let result: SavedGifResult
if isPremium && items.count >= premiumLimitsConfiguration.maxSavedGifCount {
result = .limitExceeded(premiumLimitsConfiguration.maxSavedGifCount, premiumLimitsConfiguration.maxSavedGifCount)
} else if !isPremium && items.count >= limitsConfiguration.maxSavedGifCount {
result = .limitExceeded(limitsConfiguration.maxSavedGifCount, premiumLimitsConfiguration.maxSavedGifCount)
} else {
result = .generic
}
return addSavedGif(postbox: account.postbox, fileReference: fileReference, limit: Int(isPremium ? premiumLimitsConfiguration.maxSavedGifCount : limitsConfiguration.maxSavedGifCount))
|> map { _ -> SavedGifResult in
return .generic
}
|> filter { _ in
return false
}
|> then(
.single(result)
)
}
|> switchToLatest
} else {
return removeSavedSticker(postbox: account.postbox, mediaId: fileReference.media.fileId)
|> map { _ -> SavedGifResult in
return .generic
}
}
}

View File

@ -37,7 +37,7 @@ public func getIsStickerSaved(transaction: Transaction, fileId: MediaId) -> Bool
}
}
public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMediaFile) -> Signal<Void, AddSavedStickerError> {
public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMediaFile, limit: Int = 5) -> Signal<Void, AddSavedStickerError> {
return postbox.transaction { transaction -> Signal<Void, AddSavedStickerError> in
for attribute in file.attributes {
if case let .Sticker(_, maybePackReference, _) = attribute, let packReference = maybePackReference {
@ -107,10 +107,10 @@ public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMe
} |> mapError { _ -> AddSavedStickerError in } |> switchToLatest
}
public func addSavedSticker(transaction: Transaction, file: TelegramMediaFile, stringRepresentations: [String]) {
public func addSavedSticker(transaction: Transaction, file: TelegramMediaFile, stringRepresentations: [String], limit: Int = 5) {
if let resource = file.resource as? CloudDocumentMediaResource {
if let entry = CodableEntry(SavedStickerItem(file: file, stringRepresentations: stringRepresentations)) {
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: entry), removeTailIfCountExceeds: 5)
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: entry), removeTailIfCountExceeds: limit)
}
addSynchronizeSavedStickersOperation(transaction: transaction, operation: .add(id: resource.fileId, accessHash: resource.accessHash, fileReference: .standalone(media: file)))
}

View File

@ -9,8 +9,9 @@ public struct UserLimitsConfiguration: Equatable {
public let maxFavedStickerCount: Int32
public let maxFoldersCount: Int32
public let maxFolderChatsCount: Int32
public let maxCaptionLengthCount: Int32
public let maxCaptionLength: Int32
public let maxUploadFileParts: Int32
public let maxAboutLength: Int32
public let maxAnimatedEmojisInText: Int32
public static var defaultValue: UserLimitsConfiguration {
@ -22,8 +23,9 @@ public struct UserLimitsConfiguration: Equatable {
maxFavedStickerCount: 5,
maxFoldersCount: 10,
maxFolderChatsCount: 100,
maxCaptionLengthCount: 1024,
maxCaptionLength: 1024,
maxUploadFileParts: 4000,
maxAboutLength: 70,
maxAnimatedEmojisInText: 10
)
}
@ -36,8 +38,9 @@ public struct UserLimitsConfiguration: Equatable {
maxFavedStickerCount: Int32,
maxFoldersCount: Int32,
maxFolderChatsCount: Int32,
maxCaptionLengthCount: Int32,
maxCaptionLength: Int32,
maxUploadFileParts: Int32,
maxAboutLength: Int32,
maxAnimatedEmojisInText: Int32
) {
self.maxPinnedChatCount = maxPinnedChatCount
@ -47,8 +50,9 @@ public struct UserLimitsConfiguration: Equatable {
self.maxFavedStickerCount = maxFavedStickerCount
self.maxFoldersCount = maxFoldersCount
self.maxFolderChatsCount = maxFolderChatsCount
self.maxCaptionLengthCount = maxCaptionLengthCount
self.maxCaptionLength = maxCaptionLength
self.maxUploadFileParts = maxUploadFileParts
self.maxAboutLength = maxAboutLength
self.maxAnimatedEmojisInText = maxAnimatedEmojisInText
}
}
@ -81,8 +85,9 @@ extension UserLimitsConfiguration {
self.maxFavedStickerCount = getValue("stickers_faved_limit", orElse: defaultValue.maxFavedStickerCount)
self.maxFoldersCount = getValue("dialog_filters_limit", orElse: defaultValue.maxFoldersCount)
self.maxFolderChatsCount = getValue("dialog_filters_chats_limit", orElse: defaultValue.maxFolderChatsCount)
self.maxCaptionLengthCount = getValue("caption_length_limit", orElse: defaultValue.maxCaptionLengthCount)
self.maxCaptionLength = getValue("caption_length_limit", orElse: defaultValue.maxCaptionLength)
self.maxUploadFileParts = getValue("upload_max_fileparts", orElse: defaultValue.maxUploadFileParts)
self.maxAboutLength = getValue("about_length_limit", orElse: defaultValue.maxAboutLength)
self.maxAnimatedEmojisInText = getGeneralValue("message_animated_emoji_max", orElse: defaultValue.maxAnimatedEmojisInText)
}
}

View File

@ -58,8 +58,9 @@ public enum EngineConfiguration {
public let maxFavedStickerCount: Int32
public let maxFoldersCount: Int32
public let maxFolderChatsCount: Int32
public let maxCaptionLengthCount: Int32
public let maxCaptionLength: Int32
public let maxUploadFileParts: Int32
public let maxAboutLength: Int32
public let maxAnimatedEmojisInText: Int32
public static var defaultValue: UserLimits {
@ -74,8 +75,9 @@ public enum EngineConfiguration {
maxFavedStickerCount: Int32,
maxFoldersCount: Int32,
maxFolderChatsCount: Int32,
maxCaptionLengthCount: Int32,
maxCaptionLength: Int32,
maxUploadFileParts: Int32,
maxAboutLength: Int32,
maxAnimatedEmojisInText: Int32
) {
self.maxPinnedChatCount = maxPinnedChatCount
@ -85,8 +87,9 @@ public enum EngineConfiguration {
self.maxFavedStickerCount = maxFavedStickerCount
self.maxFoldersCount = maxFoldersCount
self.maxFolderChatsCount = maxFolderChatsCount
self.maxCaptionLengthCount = maxCaptionLengthCount
self.maxCaptionLength = maxCaptionLength
self.maxUploadFileParts = maxUploadFileParts
self.maxAboutLength = maxAboutLength
self.maxAnimatedEmojisInText = maxAnimatedEmojisInText
}
}
@ -142,8 +145,9 @@ public extension EngineConfiguration.UserLimits {
maxFavedStickerCount: userLimitsConfiguration.maxFavedStickerCount,
maxFoldersCount: userLimitsConfiguration.maxFoldersCount,
maxFolderChatsCount: userLimitsConfiguration.maxFolderChatsCount,
maxCaptionLengthCount: userLimitsConfiguration.maxCaptionLengthCount,
maxCaptionLength: userLimitsConfiguration.maxCaptionLength,
maxUploadFileParts: userLimitsConfiguration.maxUploadFileParts,
maxAboutLength: userLimitsConfiguration.maxAboutLength,
maxAnimatedEmojisInText: userLimitsConfiguration.maxAnimatedEmojisInText
)
}

View File

@ -16,9 +16,18 @@ func _internal_toggleStickerSaved(postbox: Postbox, network: Network, accountPee
let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue
let limitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
let premiumLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
let premiumLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: true)
return addSavedSticker(postbox: postbox, network: network, file: file)
let result: SavedStickerResult
if isPremium && items.count >= premiumLimitsConfiguration.maxFavedStickerCount {
result = .limitExceeded(premiumLimitsConfiguration.maxFavedStickerCount, premiumLimitsConfiguration.maxFavedStickerCount)
} else if !isPremium && items.count >= limitsConfiguration.maxFavedStickerCount {
result = .limitExceeded(limitsConfiguration.maxFavedStickerCount, premiumLimitsConfiguration.maxFavedStickerCount)
} else {
result = .generic
}
return addSavedSticker(postbox: postbox, network: network, file: file, limit: Int(isPremium ? premiumLimitsConfiguration.maxFavedStickerCount : limitsConfiguration.maxFavedStickerCount))
|> map { _ -> SavedStickerResult in
return .generic
}
@ -26,7 +35,7 @@ func _internal_toggleStickerSaved(postbox: Postbox, network: Network, accountPee
return false
}
|> then(
items.count == limitsConfiguration.maxFavedStickerCount && !isPremium ? .single(.limitExceeded(limitsConfiguration.maxFavedStickerCount, premiumLimitsConfiguration.maxFavedStickerCount)) : .single(.generic)
.single(result)
)
}
|> castError(AddSavedStickerError.self)

View File

@ -7982,7 +7982,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case .generic:
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: nil, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: true, action: { _ in return false }), with: nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: true, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: true, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)

View File

@ -1213,10 +1213,33 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
if let file = media as? TelegramMediaFile, !chatPresentationInterfaceState.copyProtectionEnabled && !message.isCopyProtected() {
if file.isVideo {
if file.isAnimated && !file.isVideoSticker {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_LinkDialogSave, icon: { theme in
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveGif, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
let _ = addSavedGif(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file)).start()
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (toggleGifSaved(account: context.account, fileReference: .message(message: MessageReference(message), media: file), saved: true)
|> deliverOnMainQueue).start(next: { result in
switch result {
case .generic:
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
let text: String
if limit == premiumLimit {
text = presentationData.strings.Premium_MaxSavedGifsFinalText
} else {
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
}
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if case .info = action {
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
controllerInteraction.navigationController()?.pushViewController(controller)
return true
}
return false
}), nil)
}
})
f(.default)
})))
}

View File

@ -1487,9 +1487,32 @@ final class ChatMediaInputNode: ChatInputNode {
guard let strongSelf = self else {
return
}
let _ = addSavedGif(postbox: strongSelf.context.account.postbox, fileReference: file.file).start()
strongSelf.controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), nil)
let context = strongSelf.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controllerInteraction = strongSelf.controllerInteraction
let _ = (toggleGifSaved(account: context.account, fileReference: file.file, saved: true)
|> deliverOnMainQueue).start(next: { result in
switch result {
case .generic:
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
let text: String
if limit == premiumLimit {
text = presentationData.strings.Premium_MaxSavedGifsFinalText
} else {
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
}
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if case .info = action {
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
controllerInteraction.navigationController()?.pushViewController(controller)
return true
}
return false
}), nil)
}
})
})))
}
@ -1581,7 +1604,13 @@ final class ChatMediaInputNode: ChatInputNode {
case .generic:
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)
@ -1727,7 +1756,13 @@ final class ChatMediaInputNode: ChatInputNode {
case .generic:
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)

View File

@ -502,7 +502,13 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
case .generic:
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)
@ -583,7 +589,13 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
case .generic:
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)

View File

@ -193,10 +193,32 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
guard let strongSelf = self else {
return
}
let _ = addSavedGif(postbox: strongSelf.context.account.postbox, fileReference: .standalone(media: file)).start()
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: strongSelf.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), nil)
let context = strongSelf.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let interfaceInteraction = strongSelf.interfaceInteraction
let _ = (toggleGifSaved(account: context.account, fileReference: .standalone(media: file), saved: true)
|> deliverOnMainQueue).start(next: { result in
switch result {
case .generic:
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
let text: String
if limit == premiumLimit {
text = presentationData.strings.Premium_MaxSavedGifsFinalText
} else {
text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string
}
interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if case .info = action {
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
interfaceInteraction?.getNavigationController()?.pushViewController(controller)
return true
}
return false
}), nil)
}
})
})))
}
menuItems.append(.action(ContextMenuActionItem(text: strongSelf.strings.ShareMenu_Send, icon: { theme in

View File

@ -193,7 +193,13 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
case .generic:
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)

View File

@ -145,7 +145,13 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
case .generic:
strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)

View File

@ -25,6 +25,7 @@ import LocationUI
import AppLock
import WallpaperBackgroundNode
import InAppPurchaseManager
import PremiumUI
private final class AccountUserInterfaceInUseContext {
let subscribers = Bag<(Bool) -> Void>()
@ -1461,6 +1462,43 @@ public final class SharedAccountContextImpl: SharedAccountContext {
public func makePrivacyAndSecurityController(context: AccountContext) -> ViewController {
return SettingsUI.makePrivacyAndSecurityController(context: context)
}
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController {
let mappedSource: PremiumSource
switch source {
case .settings:
mappedSource = .settings
case .stickers:
mappedSource = .stickers
case .reactions:
mappedSource = .reactions
case .ads:
mappedSource = .ads
case .upload:
mappedSource = .upload
case .groupsAndChannels:
mappedSource = .groupsAndChannels
case .pinnedChats:
mappedSource = .pinnedChats
case .publicLinks:
mappedSource = .publicLinks
case .savedGifs:
mappedSource = .savedGifs
case .savedStickers:
mappedSource = .savedStickers
case .folders:
mappedSource = .folders
case .chatsPerFolder:
mappedSource = .chatsPerFolder
case .accounts:
mappedSource = .accounts
case let .deeplink(reference):
mappedSource = .deeplink(reference)
case let .profile(peerId):
mappedSource = .profile(peerId)
}
return PremiumIntroScreen(context: context, source: mappedSource)
}
}
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {

View File

@ -149,7 +149,13 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
case .generic:
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil)
case let .limitExceeded(limit, premiumLimit):
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string, undoText: nil), elevatedLayout: false, action: { [weak self] action in
let text: String
if limit == premiumLimit {
text = strongSelf.strings.Premium_MaxFavedStickersFinalText
} else {
text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string
}
strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: strongSelf.strings.Premium_MaxFavedStickersTitle("\(limit)").string, text: text, undoText: nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self {
if case .info = action {
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)

View File

@ -829,9 +829,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
}), textAlignment: .natural)
self.textNode.attributedText = attributedText
if text.contains("](") {
isUserInteractionEnabled = true
}
self.originalRemainingSeconds = isUserInteractionEnabled ? 5 : 3
self.textNode.maximumNumberOfLines = 5
displayUndo = false
self.originalRemainingSeconds = 3
case let .image(image, text):
self.avatarNode = nil
self.iconNode = ASImageNode()

View File

@ -219,6 +219,7 @@ class WebSearchGalleryController: ViewController {
if let strongSelf = self {
strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
}
}, pushController: { _ in
}, dismissController: { [weak self] in
self?.dismiss(forceAway: true)
}, replaceRootController: { [weak self] controller, ready in