mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
10326f1fac
commit
4b41660767
@ -7615,3 +7615,6 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Settings.Terms_URL" = "https://telegram.org/tos";
|
||||
"Settings.PrivacyPolicy_URL" = "https://telegram.org/privacy";
|
||||
|
||||
"Stickers.PremiumPackInfoText" = "This pack contains premium stickers like this one.";
|
||||
"Stickers.PremiumPackView" = "View";
|
||||
|
@ -258,6 +258,7 @@ public enum ResolvedUrl {
|
||||
case importStickers
|
||||
case startAttach(peerId: PeerId, payload: String?)
|
||||
case invoice(slug: String, invoice: TelegramMediaInvoice)
|
||||
case premiumOffer(reference: String?)
|
||||
}
|
||||
|
||||
public enum NavigateToChatKeepStack {
|
||||
|
@ -569,7 +569,7 @@ public class AttachmentController: ViewController {
|
||||
ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear).updateAlpha(node: self.dim, alpha: 1.0)
|
||||
|
||||
let targetPosition = self.container.position
|
||||
let startPosition = targetPosition.offsetBy(dx: 0.0, dy: self.bounds.height)
|
||||
let startPosition = targetPosition.offsetBy(dx: 0.0, dy: layout.size.height)
|
||||
|
||||
self.container.position = startPosition
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||
@ -785,7 +785,9 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
|
||||
let controllers = self.currentControllers
|
||||
containerTransition.updateFrame(node: self.container, frame: CGRect(origin: CGPoint(), size: containerRect.size))
|
||||
if !self.animating {
|
||||
containerTransition.updateFrame(node: self.container, frame: CGRect(origin: CGPoint(), size: containerRect.size))
|
||||
}
|
||||
|
||||
let containerLayout = containerLayout.withUpdatedIntrinsicInsets(containerInsets)
|
||||
|
||||
|
@ -331,7 +331,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
||||
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
|
||||
let premiumScreen = PremiumIntroScreen(context: context)
|
||||
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
|
||||
replaceImpl?(premiumScreen)
|
||||
})
|
||||
chatListController?.push(controller)
|
||||
|
@ -1355,7 +1355,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if data.includePeers.peers.count >= limit {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .chatsInFolder, count: Int32(data.includePeers.peers.count), action: {
|
||||
let controller = PremiumIntroScreen(context: context)
|
||||
let controller = PremiumIntroScreen(context: context, source: .chatsPerFolder)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
|
@ -299,7 +299,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
||||
if filters.count >= limit {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(filters.count), action: {
|
||||
let controller = PremiumIntroScreen(context: context)
|
||||
let controller = PremiumIntroScreen(context: context, source: .folders)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
|
@ -830,30 +830,44 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
}, setItemPinned: { [weak self] itemId, _ in
|
||||
let location: TogglePeerChatPinnedLocation
|
||||
if let chatListFilter = chatListFilter {
|
||||
location = .filter(chatListFilter.id)
|
||||
} else {
|
||||
location = .group(groupId._asGroup())
|
||||
}
|
||||
let _ = (context.engine.peers.toggleItemPinned(location: location, itemId: itemId)
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
if let strongSelf = self {
|
||||
switch result {
|
||||
case .done:
|
||||
break
|
||||
case let .limitExceeded(count, _):
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
|
||||
let premiumScreen = PremiumIntroScreen(context: context)
|
||||
replaceImpl?(premiumScreen)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
strongSelf.push?(controller)
|
||||
}
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
let isPremium = peer?.isPremium ?? false
|
||||
let location: TogglePeerChatPinnedLocation
|
||||
if let chatListFilter = chatListFilter {
|
||||
location = .filter(chatListFilter.id)
|
||||
} else {
|
||||
location = .group(groupId._asGroup())
|
||||
}
|
||||
let _ = (context.engine.peers.toggleItemPinned(location: location, itemId: itemId)
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
if let strongSelf = self {
|
||||
switch result {
|
||||
case .done:
|
||||
break
|
||||
case let .limitExceeded(count, limit):
|
||||
if isPremium {
|
||||
let text: String
|
||||
if chatListFilter != nil {
|
||||
text = strongSelf.currentState.presentationData.strings.DialogList_UnknownPinLimitError
|
||||
} else {
|
||||
text = strongSelf.currentState.presentationData.strings.DialogList_PinLimitError("\(limit)").string
|
||||
}
|
||||
strongSelf.presentAlert?(text)
|
||||
} else {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .pins, count: Int32(count), action: {
|
||||
let premiumScreen = PremiumIntroScreen(context: context, source: .pinnedChats)
|
||||
replaceImpl?(premiumScreen)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
strongSelf.push?(controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}, setPeerMuted: { [weak self] peerId, _ in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -121,6 +121,14 @@ private final class HapticFeedbackImpl {
|
||||
}
|
||||
}
|
||||
|
||||
func warning() {
|
||||
if let notificationGenerator = self.notificationGenerator {
|
||||
notificationGenerator.notificationOccurred(.warning)
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@objc dynamic func f() {
|
||||
}
|
||||
}
|
||||
@ -205,6 +213,14 @@ public final class HapticFeedback {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func warning() {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.withImpl { impl in
|
||||
impl.warning()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
|
@ -361,6 +361,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
private func updateContainers(layout rawLayout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.isUpdatingContainers = true
|
||||
|
||||
if let badgeNode = self.badgeNode, let image = badgeNode.image {
|
||||
badgeNode.isHidden = !rawLayout.deviceMetrics.hasTopNotch || rawLayout.size.width > rawLayout.size.height
|
||||
badgeNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((rawLayout.size.width - image.size.width) / 2.0), y: 6.0), size: image.size)
|
||||
}
|
||||
|
||||
var layout = rawLayout
|
||||
|
||||
if self.ignoreInputHeight {
|
||||
@ -631,6 +636,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
var topVisibleModalContainerWithStatusBar: NavigationModalContainer?
|
||||
var visibleModalCount = 0
|
||||
var topModalIsFlat = false
|
||||
var topFlatModalHasProgress = false
|
||||
let isLandscape = layout.orientation == .landscape
|
||||
var hasVisibleStandaloneModal = false
|
||||
var topModalDismissProgress: CGFloat = 0.0
|
||||
@ -659,6 +665,17 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
effectiveModalTransition = 1.0
|
||||
}
|
||||
|
||||
if navigationLayout.modal[i].isFlat, let lastController = navigationLayout.modal[i].controllers.last {
|
||||
lastController.modalStyleOverlayTransitionFactorUpdated = { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateContainersNonReentrant(transition: transition)
|
||||
}
|
||||
modalStyleOverlayTransitionFactor = max(modalStyleOverlayTransitionFactor, lastController.modalStyleOverlayTransitionFactor)
|
||||
topFlatModalHasProgress = modalStyleOverlayTransitionFactor > 0.0
|
||||
}
|
||||
|
||||
containerTransition.updateFrame(node: modalContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
modalContainer.update(layout: modalContainer.isFlat ? overlayLayout : layout, controllers: navigationLayout.modal[i].controllers, coveredByModalTransition: effectiveModalTransition, transition: containerTransition)
|
||||
|
||||
@ -862,9 +879,15 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
let visibleRootModalDismissProgress: CGFloat
|
||||
var additionalModalFrameProgress: CGFloat
|
||||
if visibleModalCount == 1 {
|
||||
effectiveRootModalDismissProgress = (topModalIsFlat || isLandscape) ? 1.0 : topModalDismissProgress
|
||||
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
|
||||
additionalModalFrameProgress = 0.0
|
||||
if topFlatModalHasProgress {
|
||||
effectiveRootModalDismissProgress = 0.0
|
||||
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
|
||||
additionalModalFrameProgress = 1.0 - topModalDismissProgress
|
||||
} else {
|
||||
effectiveRootModalDismissProgress = ((topModalIsFlat && !topFlatModalHasProgress) || isLandscape) ? 1.0 : topModalDismissProgress
|
||||
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
|
||||
additionalModalFrameProgress = 0.0
|
||||
}
|
||||
} else if visibleModalCount >= 2 {
|
||||
effectiveRootModalDismissProgress = 0.0
|
||||
visibleRootModalDismissProgress = topModalDismissProgress
|
||||
@ -929,7 +952,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
let maxScale: CGFloat
|
||||
let maxOffset: CGFloat
|
||||
if topModalIsFlat || isLandscape {
|
||||
if (topModalIsFlat && !topFlatModalHasProgress) || isLandscape {
|
||||
maxScale = 1.0
|
||||
maxOffset = 0.0
|
||||
} else if visibleModalCount <= 1 {
|
||||
@ -1219,8 +1242,16 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.displayNode.addSubnode(inCallStatusBar)
|
||||
}
|
||||
}
|
||||
|
||||
let badgeNode = ASImageNode()
|
||||
badgeNode.displaysAsynchronously = false
|
||||
badgeNode.image = UIImage(bundleImageName: "Components/BadgeTest")
|
||||
self.badgeNode = badgeNode
|
||||
self.displayNode.addSubnode(badgeNode)
|
||||
}
|
||||
|
||||
private var badgeNode: ASImageNode?
|
||||
|
||||
public func pushViewController(_ controller: ViewController) {
|
||||
self.pushViewController(controller, completion: {})
|
||||
}
|
||||
|
@ -1256,8 +1256,12 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
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)
|
||||
}
|
||||
}))
|
||||
} else if file.mimeType.hasPrefix("image/") || file.mimeType.hasPrefix("video/") {
|
||||
} else if file.mimeType.hasPrefix("image/") {
|
||||
preferredAction = .saveToCameraRoll
|
||||
actionCompletionText = strongSelf.presentationData.strings.Gallery_ImageSaved
|
||||
} else if file.mimeType.hasPrefix("video/") {
|
||||
preferredAction = .saveToCameraRoll
|
||||
actionCompletionText = strongSelf.presentationData.strings.Gallery_VideoSaved
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1335,6 +1339,12 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
shareController.dismissed = { [weak self] _ in
|
||||
self?.interacting?(false)
|
||||
}
|
||||
shareController.actionCompleted = { [weak self, weak shareController] in
|
||||
if let strongSelf = self, let shareController = shareController, shareController.actionIsMediaSaving {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: presentationData.strings.Gallery_ImageSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
|
||||
}
|
||||
}
|
||||
shareController.completed = { [weak self] peerIds in
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Peer] in
|
||||
|
@ -356,6 +356,13 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
case .translate:
|
||||
if let parentController = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
let controller = TranslateScreen(context: strongSelf.context, text: string, fromLanguage: nil)
|
||||
controller.pushController = { [weak parentController] c in
|
||||
(parentController?.navigationController as? NavigationController)?._keepModalDismissProgress = true
|
||||
parentController?.push(c)
|
||||
}
|
||||
controller.presentController = { [weak parentController] c in
|
||||
parentController?.present(c, in: .window(.root))
|
||||
}
|
||||
parentController.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
@ -1096,6 +1096,13 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if canTranslate {
|
||||
actions.append(ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuTranslate, accessibilityLabel: strings.Conversation_ContextMenuTranslate), action: { [weak self] in
|
||||
let controller = TranslateScreen(context: context, text: text, fromLanguage: language)
|
||||
controller.pushController = { [weak self] c in
|
||||
(self?.controller?.navigationController as? NavigationController)?._keepModalDismissProgress = true
|
||||
self?.controller?.push(c)
|
||||
}
|
||||
controller.presentController = { [weak self] c in
|
||||
self?.controller?.present(c, in: .window(.root))
|
||||
}
|
||||
self?.present(controller, nil)
|
||||
}))
|
||||
}
|
||||
|
@ -1630,7 +1630,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
|
||||
if hasNamesToRevoke && selectedType == .publicChannel {
|
||||
footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {
|
||||
let controller = PremiumIntroScreen(context: context)
|
||||
let controller = PremiumIntroScreen(context: context, source: .publicLinks)
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa
|
||||
if state.selectedPeers.count > 0 {
|
||||
leaveActionImpl?()
|
||||
} else {
|
||||
let controller = PremiumIntroScreen(context: context)
|
||||
let controller = PremiumIntroScreen(context: context, source: .groupsAndChannels)
|
||||
pushImpl?(controller)
|
||||
}
|
||||
})
|
||||
|
@ -3,11 +3,13 @@ import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ViewControllerComponent
|
||||
import AccountContext
|
||||
import SolidRoundedButtonComponent
|
||||
import MultilineTextComponent
|
||||
import PresentationDataUtils
|
||||
import PrefixSectionGroupComponent
|
||||
import BundleIconComponent
|
||||
import SolidRoundedButtonComponent
|
||||
@ -17,6 +19,236 @@ import ConfettiEffect
|
||||
import TextFormat
|
||||
import InstantPageCache
|
||||
|
||||
public enum PremiumSource {
|
||||
case settings
|
||||
case stickers
|
||||
case reactions
|
||||
case ads
|
||||
case groupsAndChannels
|
||||
case pinnedChats
|
||||
case publicLinks
|
||||
case savedGifs
|
||||
case savedStickers
|
||||
case folders
|
||||
case chatsPerFolder
|
||||
case accounts
|
||||
|
||||
var identifier: String {
|
||||
switch self {
|
||||
case .settings:
|
||||
return "settings"
|
||||
case .stickers:
|
||||
return "premium_stickers"
|
||||
case .reactions:
|
||||
return "unique_reactions"
|
||||
case .ads:
|
||||
return "no_ads"
|
||||
case .groupsAndChannels:
|
||||
return "double_limits__channels"
|
||||
case .pinnedChats:
|
||||
return "double_limits__dialog_pinned"
|
||||
case .publicLinks:
|
||||
return "double_limits__channels_public"
|
||||
case .savedGifs:
|
||||
return "double_limits__saved_gifs"
|
||||
case .savedStickers:
|
||||
return "double_limits__stickers_faved"
|
||||
case .folders:
|
||||
return "double_limits__dialog_filters"
|
||||
case .chatsPerFolder:
|
||||
return "double_limits__dialog_filters_chats"
|
||||
case .accounts:
|
||||
return "double_limits__accounts"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum PremiumPerk: CaseIterable {
|
||||
case doubleLimits
|
||||
case moreUpload
|
||||
case fasterDownload
|
||||
case voiceToText
|
||||
case noAds
|
||||
case uniqueReactions
|
||||
case premiumStickers
|
||||
case advancedChatManagement
|
||||
case profileBadge
|
||||
case animatedUserpics
|
||||
|
||||
static var allCases: [PremiumPerk] {
|
||||
return [
|
||||
.doubleLimits,
|
||||
.moreUpload,
|
||||
.fasterDownload,
|
||||
.voiceToText,
|
||||
.noAds,
|
||||
.uniqueReactions,
|
||||
.premiumStickers,
|
||||
.advancedChatManagement,
|
||||
.profileBadge,
|
||||
.animatedUserpics
|
||||
]
|
||||
}
|
||||
|
||||
init?(identifier: String) {
|
||||
for perk in PremiumPerk.allCases {
|
||||
if perk.identifier == identifier {
|
||||
self = perk
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var identifier: String {
|
||||
switch self {
|
||||
case .doubleLimits:
|
||||
return "double_limits"
|
||||
case .moreUpload:
|
||||
return "more_upload"
|
||||
case .fasterDownload:
|
||||
return "faster_download"
|
||||
case .voiceToText:
|
||||
return "voice_to_text"
|
||||
case .noAds:
|
||||
return "no_ads"
|
||||
case .uniqueReactions:
|
||||
return "unique_reactions"
|
||||
case .premiumStickers:
|
||||
return "premium_stickers"
|
||||
case .advancedChatManagement:
|
||||
return "advanced_chat_management"
|
||||
case .profileBadge:
|
||||
return "profile_badge"
|
||||
case .animatedUserpics:
|
||||
return "animated_userpics"
|
||||
}
|
||||
}
|
||||
|
||||
func title(strings: PresentationStrings) -> String {
|
||||
switch self {
|
||||
case .doubleLimits:
|
||||
return strings.Premium_DoubledLimits
|
||||
case .moreUpload:
|
||||
return strings.Premium_UploadSize
|
||||
case .fasterDownload:
|
||||
return strings.Premium_FasterSpeed
|
||||
case .voiceToText:
|
||||
return strings.Premium_VoiceToText
|
||||
case .noAds:
|
||||
return strings.Premium_NoAds
|
||||
case .uniqueReactions:
|
||||
return strings.Premium_Reactions
|
||||
case .premiumStickers:
|
||||
return strings.Premium_Stickers
|
||||
case .advancedChatManagement:
|
||||
return strings.Premium_ChatManagement
|
||||
case .profileBadge:
|
||||
return strings.Premium_Badge
|
||||
case .animatedUserpics:
|
||||
return strings.Premium_Avatar
|
||||
}
|
||||
}
|
||||
|
||||
func subtitle(strings: PresentationStrings) -> String {
|
||||
switch self {
|
||||
case .doubleLimits:
|
||||
return strings.Premium_DoubledLimitsInfo
|
||||
case .moreUpload:
|
||||
return strings.Premium_UploadSizeInfo
|
||||
case .fasterDownload:
|
||||
return strings.Premium_FasterSpeedInfo
|
||||
case .voiceToText:
|
||||
return strings.Premium_VoiceToTextInfo
|
||||
case .noAds:
|
||||
return strings.Premium_NoAdsInfo
|
||||
case .uniqueReactions:
|
||||
return strings.Premium_ReactionsInfo
|
||||
case .premiumStickers:
|
||||
return strings.Premium_StickersInfo
|
||||
case .advancedChatManagement:
|
||||
return strings.Premium_ChatManagementInfo
|
||||
case .profileBadge:
|
||||
return strings.Premium_BadgeInfo
|
||||
case .animatedUserpics:
|
||||
return strings.Premium_AvatarInfo
|
||||
}
|
||||
}
|
||||
|
||||
var iconName: String {
|
||||
switch self {
|
||||
case .doubleLimits:
|
||||
return "Premium/Perk/Limits"
|
||||
case .moreUpload:
|
||||
return "Premium/Perk/Upload"
|
||||
case .fasterDownload:
|
||||
return "Premium/Perk/Speed"
|
||||
case .voiceToText:
|
||||
return "Premium/Perk/Voice"
|
||||
case .noAds:
|
||||
return "Premium/Perk/NoAds"
|
||||
case .uniqueReactions:
|
||||
return "Premium/Perk/Reactions"
|
||||
case .premiumStickers:
|
||||
return "Premium/Perk/Stickers"
|
||||
case .advancedChatManagement:
|
||||
return "Premium/Perk/Chat"
|
||||
case .profileBadge:
|
||||
return "Premium/Perk/Badge"
|
||||
case .animatedUserpics:
|
||||
return "Premium/Perk/Avatar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PremiumIntroConfiguration {
|
||||
static var defaultValue: PremiumIntroConfiguration {
|
||||
return PremiumIntroConfiguration(perks: [
|
||||
.doubleLimits,
|
||||
.moreUpload,
|
||||
.fasterDownload,
|
||||
.voiceToText,
|
||||
.noAds,
|
||||
.uniqueReactions,
|
||||
.premiumStickers,
|
||||
.advancedChatManagement,
|
||||
.profileBadge,
|
||||
.animatedUserpics
|
||||
])
|
||||
}
|
||||
|
||||
let perks: [PremiumPerk]
|
||||
|
||||
fileprivate init(perks: [PremiumPerk]) {
|
||||
self.perks = perks
|
||||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> PremiumIntroConfiguration {
|
||||
if let data = appConfiguration.data, let values = data["premium_promo_order"] as? [String] {
|
||||
var perks: [PremiumPerk] = []
|
||||
for value in values {
|
||||
if let perk = PremiumPerk(identifier: value) {
|
||||
if !perks.contains(perk) {
|
||||
perks.append(perk)
|
||||
} else {
|
||||
perks = []
|
||||
break
|
||||
}
|
||||
} else {
|
||||
perks = []
|
||||
break
|
||||
}
|
||||
}
|
||||
if perks.count < 4 {
|
||||
perks = PremiumIntroConfiguration.defaultValue.perks
|
||||
}
|
||||
return PremiumIntroConfiguration(perks: perks)
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class SectionGroupComponent: Component {
|
||||
public final class Item: Equatable {
|
||||
public let content: AnyComponentWithIdentity<Empty>
|
||||
@ -495,6 +727,40 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
|
||||
private var disposable: Disposable?
|
||||
var configuration = PremiumIntroConfiguration.defaultValue
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.context = context
|
||||
|
||||
super.init()
|
||||
|
||||
self.disposable = (context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> map { view -> AppConfiguration in
|
||||
let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
|
||||
return appConfiguration
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] appConfiguration in
|
||||
if let strongSelf = self {
|
||||
strongSelf.configuration = PremiumIntroConfiguration.with(appConfiguration: appConfiguration)
|
||||
strongSelf.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State(context: self.context)
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let overscroll = Child(Rectangle.self)
|
||||
let fade = Child(RoundedRectangle.self)
|
||||
@ -510,6 +776,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
|
||||
let scrollEnvironment = context.environment[ScrollChildEnvironment.self].value
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let state = context.state
|
||||
|
||||
let theme = environment.theme
|
||||
let strings = environment.strings
|
||||
@ -576,230 +843,52 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
size.height += text.size.height
|
||||
size.height += 21.0
|
||||
|
||||
let gradientColors: [(UIColor, UIColor)] = [
|
||||
(UIColor(rgb: 0xF28528), UIColor(rgb: 0xEF7633)),
|
||||
(UIColor(rgb: 0xEA5F43), UIColor(rgb: 0xE7504E)),
|
||||
(UIColor(rgb: 0xDE4768), UIColor(rgb: 0xD54D82)),
|
||||
(UIColor(rgb: 0xDE4768), UIColor(rgb: 0xD54D82)),
|
||||
(UIColor(rgb: 0xC654A8), UIColor(rgb: 0xBE5AC2)),
|
||||
(UIColor(rgb: 0xAF62E9), UIColor(rgb: 0xA668FF)),
|
||||
(UIColor(rgb: 0x9674FF), UIColor(rgb: 0x8C7DFF)),
|
||||
(UIColor(rgb: 0x9674FF), UIColor(rgb: 0x8C7DFF)),
|
||||
(UIColor(rgb: 0x7B88FF), UIColor(rgb: 0x7091FF)),
|
||||
(UIColor(rgb: 0x609DFF), UIColor(rgb: 0x56A5FF))
|
||||
]
|
||||
|
||||
var items: [SectionGroupComponent.Item] = []
|
||||
|
||||
var i = 0
|
||||
for perk in state.configuration.perks {
|
||||
let iconBackgroundColors = gradientColors[i]
|
||||
items.append(SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: perk.identifier,
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: perk.iconName,
|
||||
iconBackgroundColors: [
|
||||
iconBackgroundColors.0,
|
||||
iconBackgroundColors.1
|
||||
],
|
||||
title: perk.title(strings: strings),
|
||||
titleColor: titleColor,
|
||||
subtitle: perk.subtitle(strings: strings),
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
))
|
||||
i += 1
|
||||
}
|
||||
|
||||
let section = section.update(
|
||||
component: SectionGroupComponent(
|
||||
items: [
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "limits",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Limits",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0xF28528),
|
||||
UIColor(rgb: 0xEF7633)
|
||||
],
|
||||
title: strings.Premium_DoubledLimits,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_DoubledLimitsInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "upload",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Upload",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0xEA5F43),
|
||||
UIColor(rgb: 0xE7504E)
|
||||
],
|
||||
title: strings.Premium_UploadSize,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_UploadSizeInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "speed",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Speed",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0xDE4768),
|
||||
UIColor(rgb: 0xD54D82)
|
||||
],
|
||||
title: strings.Premium_FasterSpeed,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_FasterSpeedInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "voice",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Voice",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0xDE4768),
|
||||
UIColor(rgb: 0xD54D82)
|
||||
],
|
||||
title: strings.Premium_VoiceToText,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_VoiceToTextInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "noAds",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/NoAds",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0xC654A8),
|
||||
UIColor(rgb: 0xBE5AC2)
|
||||
],
|
||||
title: strings.Premium_NoAds,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_NoAdsInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "reactions",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Reactions",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0xAF62E9),
|
||||
UIColor(rgb: 0xA668FF)
|
||||
],
|
||||
title: strings.Premium_Reactions,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_ReactionsInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "stickers",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Stickers",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0x9674FF),
|
||||
UIColor(rgb: 0x8C7DFF)
|
||||
],
|
||||
title: strings.Premium_Stickers,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_StickersInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "chat",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Chat",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0x9674FF),
|
||||
UIColor(rgb: 0x8C7DFF)
|
||||
],
|
||||
title: strings.Premium_ChatManagement,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_ChatManagementInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "badge",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Badge",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0x7B88FF),
|
||||
UIColor(rgb: 0x7091FF)
|
||||
],
|
||||
title: strings.Premium_Badge,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_BadgeInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
SectionGroupComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: "avatar",
|
||||
component: AnyComponent(
|
||||
PerkComponent(
|
||||
iconName: "Premium/Perk/Avatar",
|
||||
iconBackgroundColors: [
|
||||
UIColor(rgb: 0x609DFF),
|
||||
UIColor(rgb: 0x56A5FF)
|
||||
],
|
||||
title: strings.Premium_Avatar,
|
||||
titleColor: titleColor,
|
||||
subtitle: strings.Premium_AvatarInfo,
|
||||
subtitleColor: subtitleColor,
|
||||
arrowColor: arrowColor
|
||||
)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
|
||||
}
|
||||
),
|
||||
],
|
||||
items: items,
|
||||
backgroundColor: environment.theme.list.itemBlocksBackgroundColor,
|
||||
selectionColor: environment.theme.list.itemHighlightedBackgroundColor,
|
||||
separatorColor: environment.theme.list.itemBlocksSeparatorColor
|
||||
@ -808,6 +897,241 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
availableSize: CGSize(width: availableWidth - sideInsets, height: .greatestFiniteMagnitude),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
// let section = section.update(
|
||||
// component: SectionGroupComponent(
|
||||
// items: [
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "limits",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Limits",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0xF28528),
|
||||
// UIColor(rgb: 0xEF7633)
|
||||
// ],
|
||||
// title: strings.Premium_DoubledLimits,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_DoubledLimitsInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "upload",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Upload",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0xEA5F43),
|
||||
// UIColor(rgb: 0xE7504E)
|
||||
// ],
|
||||
// title: strings.Premium_UploadSize,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_UploadSizeInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "speed",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Speed",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0xDE4768),
|
||||
// UIColor(rgb: 0xD54D82)
|
||||
// ],
|
||||
// title: strings.Premium_FasterSpeed,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_FasterSpeedInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "voice",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Voice",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0xDE4768),
|
||||
// UIColor(rgb: 0xD54D82)
|
||||
// ],
|
||||
// title: strings.Premium_VoiceToText,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_VoiceToTextInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "noAds",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/NoAds",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0xC654A8),
|
||||
// UIColor(rgb: 0xBE5AC2)
|
||||
// ],
|
||||
// title: strings.Premium_NoAds,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_NoAdsInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "reactions",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Reactions",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0xAF62E9),
|
||||
// UIColor(rgb: 0xA668FF)
|
||||
// ],
|
||||
// title: strings.Premium_Reactions,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_ReactionsInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "stickers",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Stickers",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0x9674FF),
|
||||
// UIColor(rgb: 0x8C7DFF)
|
||||
// ],
|
||||
// title: strings.Premium_Stickers,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_StickersInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "chat",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Chat",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0x9674FF),
|
||||
// UIColor(rgb: 0x8C7DFF)
|
||||
// ],
|
||||
// title: strings.Premium_ChatManagement,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_ChatManagementInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "badge",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Badge",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0x7B88FF),
|
||||
// UIColor(rgb: 0x7091FF)
|
||||
// ],
|
||||
// title: strings.Premium_Badge,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_BadgeInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// SectionGroupComponent.Item(
|
||||
// AnyComponentWithIdentity(
|
||||
// id: "avatar",
|
||||
// component: AnyComponent(
|
||||
// PerkComponent(
|
||||
// iconName: "Premium/Perk/Avatar",
|
||||
// iconBackgroundColors: [
|
||||
// UIColor(rgb: 0x609DFF),
|
||||
// UIColor(rgb: 0x56A5FF)
|
||||
// ],
|
||||
// title: strings.Premium_Avatar,
|
||||
// titleColor: titleColor,
|
||||
// subtitle: strings.Premium_AvatarInfo,
|
||||
// subtitleColor: subtitleColor,
|
||||
// arrowColor: arrowColor
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// action: {
|
||||
//
|
||||
// }
|
||||
// ),
|
||||
// ],
|
||||
// backgroundColor: environment.theme.list.itemBlocksBackgroundColor,
|
||||
// selectionColor: environment.theme.list.itemHighlightedBackgroundColor,
|
||||
// separatorColor: environment.theme.list.itemBlocksSeparatorColor
|
||||
// ),
|
||||
// environment: {},
|
||||
// availableSize: CGSize(width: availableWidth - sideInsets, height: .greatestFiniteMagnitude),
|
||||
// transition: context.transition
|
||||
// )
|
||||
context.add(section
|
||||
.position(CGPoint(x: availableWidth / 2.0, y: size.height + section.size.height / 2.0))
|
||||
.clipsToBounds(true)
|
||||
@ -1276,7 +1600,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
public init(context: AccountContext, modal: Bool = true) {
|
||||
public init(context: AccountContext, modal: Bool = true, reference: String? = nil, source: PremiumSource? = nil) {
|
||||
self.context = context
|
||||
|
||||
var updateInProgressImpl: ((Bool) -> Void)?
|
||||
|
@ -108,7 +108,7 @@ public final class PremiumReactionsScreen: ViewController {
|
||||
self.proceedButton.pressed = { [weak self] in
|
||||
if let strongSelf = self, let controller = strongSelf.controller, let navigationController = controller.navigationController {
|
||||
strongSelf.animateOut()
|
||||
navigationController.pushViewController(PremiumIntroScreen(context: controller.context), animated: true)
|
||||
navigationController.pushViewController(PremiumIntroScreen(context: controller.context, source: .reactions), animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,6 +317,7 @@ public final class ShareController: ViewController {
|
||||
private let accountActiveDisposable = MetaDisposable()
|
||||
|
||||
private var defaultAction: ShareControllerAction?
|
||||
public private(set) var actionIsMediaSaving = false
|
||||
|
||||
public var actionCompleted: (() -> Void)?
|
||||
public var dismissed: ((Bool) -> Void)?
|
||||
@ -391,6 +392,7 @@ public final class ShareController: ViewController {
|
||||
break
|
||||
case let .image(representations):
|
||||
if case .saveToCameraRoll = preferredAction {
|
||||
self.actionIsMediaSaving = true
|
||||
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Gallery_SaveImage, action: { [weak self] in
|
||||
self?.saveToCameraRoll(representations: representations)
|
||||
self?.actionCompleted?()
|
||||
@ -406,6 +408,7 @@ public final class ShareController: ViewController {
|
||||
isVideo = file.isVideo
|
||||
}
|
||||
if case .saveToCameraRoll = preferredAction, canSave {
|
||||
self.actionIsMediaSaving = true
|
||||
self.defaultAction = ShareControllerAction(title: isVideo ? self.presentationData.strings.Gallery_SaveVideo : self.presentationData.strings.Gallery_SaveImage, action: { [weak self] in
|
||||
self?.saveToCameraRoll(mediaReference: mediaReference)
|
||||
self?.actionCompleted?()
|
||||
@ -413,6 +416,7 @@ public final class ShareController: ViewController {
|
||||
}
|
||||
case let .messages(messages):
|
||||
if case .saveToCameraRoll = preferredAction {
|
||||
self.actionIsMediaSaving = true
|
||||
self.defaultAction = ShareControllerAction(title: self.presentationData.strings.Preview_SaveToCameraRoll, action: { [weak self] in
|
||||
self?.saveToCameraRoll(messages: messages)
|
||||
self?.actionCompleted?()
|
||||
|
@ -262,10 +262,14 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
|
||||
let bounds = self.bounds
|
||||
let boundsSide = min(bounds.size.width - 14.0, bounds.size.height - 14.0)
|
||||
let boundingSize = CGSize(width: boundsSide, height: boundsSide)
|
||||
var boundingSize = CGSize(width: boundsSide, height: boundsSide)
|
||||
|
||||
if let (_, item) = self.currentState {
|
||||
if let item = item, let dimensions = item.file.dimensions?.cgSize {
|
||||
if item.file.isPremiumSticker {
|
||||
boundingSize = CGSize(width: boundingSize.width * 1.1, height: boundingSize.width * 1.1)
|
||||
}
|
||||
|
||||
let imageSize = dimensions.aspectFitted(boundingSize)
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floor((bounds.size.width - imageSize.width) / 2.0), y: (bounds.size.height - imageSize.height) / 2.0), size: imageSize)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
|
@ -751,7 +751,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|
||||
let gridFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top + titleAreaInset), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - titleAreaInset))
|
||||
|
||||
let itemsPerRow = 4
|
||||
let itemsPerRow = 5
|
||||
let fillingWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
|
||||
let itemWidth = floor(fillingWidth / CGFloat(itemsPerRow))
|
||||
let gridLeftInset = floor((layout.size.width - fillingWidth) / 2.0)
|
||||
|
@ -65,7 +65,25 @@ public struct PresentationResourcesItemList {
|
||||
|
||||
public static func verifiedPeerIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.itemListVerifiedPeerIcon.rawValue, { theme in
|
||||
return UIImage(bundleImageName: "Item List/PeerVerifiedIcon")?.precomposed()
|
||||
if let backgroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconForeground") {
|
||||
return generateImage(backgroundImage.size, contextGenerator: { size, context in
|
||||
if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage {
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.saveGState()
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: backgroundCgImage)
|
||||
|
||||
context.setFillColor(theme.chatList.unreadBadgeActiveBackgroundColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.restoreGState()
|
||||
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: foregroundCgImage)
|
||||
context.setFillColor(theme.chatList.unreadBadgeActiveTextColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}, opaque: false)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "premiumbadge_16 (1).pdf",
|
||||
"filename" : "premiumbadge_16 (2).pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
@ -10,7 +10,7 @@ stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 1.050293 1.510986 cm
|
||||
1.000000 0.000000 -0.000000 1.000000 1.050293 2.010986 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
6.588397 2.211904 m
|
||||
3.455913 0.292931 l
|
Binary file not shown.
Before Width: | Height: | Size: 538 B |
Binary file not shown.
Before Width: | Height: | Size: 781 B |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "verifybadge1_16.pdf",
|
||||
"filename" : "verifybadge1_16 (1).pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
@ -0,0 +1,107 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 3.000000 3.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 8.400000 m
|
||||
0.000000 8.960052 0.000000 9.240079 0.108993 9.453991 c
|
||||
0.204867 9.642153 0.357847 9.795134 0.546009 9.891006 c
|
||||
0.759921 10.000000 1.039948 10.000000 1.600000 10.000000 c
|
||||
8.400000 10.000000 l
|
||||
8.960052 10.000000 9.240079 10.000000 9.453991 9.891006 c
|
||||
9.642153 9.795134 9.795134 9.642153 9.891006 9.453991 c
|
||||
10.000000 9.240079 10.000000 8.960052 10.000000 8.400000 c
|
||||
10.000000 1.600000 l
|
||||
10.000000 1.039948 10.000000 0.759921 9.891006 0.546009 c
|
||||
9.795134 0.357847 9.642153 0.204866 9.453991 0.108994 c
|
||||
9.240079 0.000000 8.960052 0.000000 8.400000 0.000000 c
|
||||
1.600000 0.000000 l
|
||||
1.039948 0.000000 0.759921 0.000000 0.546009 0.108994 c
|
||||
0.357847 0.204866 0.204867 0.357847 0.108993 0.546009 c
|
||||
0.000000 0.759921 0.000000 1.039948 0.000000 1.600000 c
|
||||
0.000000 8.400000 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
0.707107 0.707107 -0.707107 0.707107 10.928923 -1.999968 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 12.542089 m
|
||||
0.000000 13.102142 0.000000 13.382169 0.108993 13.596081 c
|
||||
0.204867 13.784243 0.357847 13.937223 0.546009 14.033096 c
|
||||
0.759921 14.142090 1.039948 14.142090 1.600000 14.142090 c
|
||||
8.400000 14.142090 l
|
||||
8.960052 14.142090 9.240079 14.142090 9.453991 14.033096 c
|
||||
9.642153 13.937223 9.795134 13.784243 9.891006 13.596081 c
|
||||
10.000000 13.382169 10.000000 13.102142 10.000000 12.542089 c
|
||||
10.000000 5.742090 l
|
||||
10.000000 5.182037 10.000000 4.902011 9.891006 4.688099 c
|
||||
9.795134 4.499937 9.642153 4.346956 9.453991 4.251083 c
|
||||
9.240079 4.142090 8.960052 4.142090 8.400000 4.142090 c
|
||||
1.600000 4.142090 l
|
||||
1.039948 4.142090 0.759921 4.142090 0.546009 4.251083 c
|
||||
0.357847 4.346956 0.204867 4.499937 0.108993 4.688099 c
|
||||
0.000000 4.902011 0.000000 5.182037 0.000000 5.742090 c
|
||||
0.000000 12.542089 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1811
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001901 00000 n
|
||||
0000001924 00000 n
|
||||
0000002097 00000 n
|
||||
0000002171 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2230
|
||||
%%EOF
|
@ -1,111 +0,0 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 1.300293 1.044312 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
-0.030118 6.492163 m
|
||||
0.081165 6.149670 0.378177 5.852658 0.972203 5.258633 c
|
||||
1.449829 4.781006 l
|
||||
1.449829 4.105689 l
|
||||
1.449829 3.265610 1.449829 2.845571 1.613319 2.524703 c
|
||||
1.757129 2.242459 1.986600 2.012989 2.268843 1.869179 c
|
||||
2.589711 1.705688 3.009750 1.705688 3.849829 1.705688 c
|
||||
4.525146 1.705688 l
|
||||
5.002711 1.228124 l
|
||||
5.596736 0.634098 5.893749 0.337086 6.236242 0.225803 c
|
||||
6.537507 0.127916 6.862028 0.127916 7.163293 0.225803 c
|
||||
7.505786 0.337086 7.802798 0.634098 8.396824 1.228124 c
|
||||
8.874389 1.705688 l
|
||||
9.549829 1.705688 l
|
||||
10.389908 1.705688 10.809947 1.705688 11.130815 1.869179 c
|
||||
11.413058 2.012989 11.642529 2.242459 11.786339 2.524703 c
|
||||
11.949829 2.845571 11.949829 3.265610 11.949829 4.105688 c
|
||||
11.949829 4.781129 l
|
||||
12.427332 5.258632 l
|
||||
12.427341 5.258640 l
|
||||
13.021361 5.852661 13.318372 6.149672 13.429653 6.492163 c
|
||||
13.527540 6.793428 13.527540 7.117949 13.429653 7.419214 c
|
||||
13.318372 7.761705 13.021361 8.058716 12.427344 8.652733 c
|
||||
12.427333 8.652744 l
|
||||
11.949829 9.130249 l
|
||||
11.949829 9.805689 l
|
||||
11.949829 10.645767 11.949829 11.065806 11.786339 11.386674 c
|
||||
11.642529 11.668918 11.413058 11.898388 11.130815 12.042198 c
|
||||
10.809947 12.205688 10.389908 12.205688 9.549829 12.205688 c
|
||||
8.874389 12.205688 l
|
||||
8.396824 12.683253 l
|
||||
7.802798 13.277279 7.505786 13.574291 7.163293 13.685574 c
|
||||
6.862028 13.783461 6.537507 13.783461 6.236242 13.685574 c
|
||||
5.893750 13.574291 5.596738 13.277280 5.002716 12.683257 c
|
||||
5.002711 12.683253 l
|
||||
4.525146 12.205688 l
|
||||
3.849829 12.205688 l
|
||||
3.009750 12.205688 2.589711 12.205688 2.268843 12.042198 c
|
||||
1.986600 11.898388 1.757129 11.668918 1.613319 11.386674 c
|
||||
1.449829 11.065806 1.449829 10.645767 1.449829 9.805689 c
|
||||
1.449829 9.130371 l
|
||||
0.972203 8.652744 l
|
||||
0.972199 8.652741 l
|
||||
0.378176 8.058718 0.081164 7.761706 -0.030118 7.419214 c
|
||||
-0.128005 7.117949 -0.128005 6.793428 -0.030118 6.492163 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1960
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002050 00000 n
|
||||
0000002073 00000 n
|
||||
0000002246 00000 n
|
||||
0000002320 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2379
|
||||
%%EOF
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "verifybadge2_16.pdf",
|
||||
"filename" : "verifybadge2_16 (1).pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
@ -0,0 +1,76 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 4.625000 5.061890 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
6.530330 4.926815 m
|
||||
6.823223 4.633921 6.823223 4.159048 6.530330 3.866154 c
|
||||
3.030330 0.366154 l
|
||||
2.737437 0.073261 2.262563 0.073261 1.969670 0.366154 c
|
||||
0.219670 2.116154 l
|
||||
-0.073223 2.409048 -0.073223 2.883921 0.219670 3.176815 c
|
||||
0.512563 3.469708 0.987437 3.469708 1.280330 3.176815 c
|
||||
2.500000 1.957145 l
|
||||
5.469670 4.926815 l
|
||||
5.762563 5.219707 6.237437 5.219707 6.530330 4.926815 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
510
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000600 00000 n
|
||||
0000000622 00000 n
|
||||
0000000795 00000 n
|
||||
0000000869 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
928
|
||||
%%EOF
|
@ -1,92 +0,0 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 5.375000 4.311890 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.530330 3.926815 m
|
||||
0.237437 4.219707 -0.237437 4.219707 -0.530330 3.926815 c
|
||||
-0.823223 3.633921 -0.823223 3.159048 -0.530330 2.866154 c
|
||||
0.530330 3.926815 l
|
||||
h
|
||||
1.750000 1.646484 m
|
||||
1.219670 1.116154 l
|
||||
1.512563 0.823261 1.987437 0.823261 2.280330 1.116154 c
|
||||
1.750000 1.646484 l
|
||||
h
|
||||
5.780330 4.616154 m
|
||||
6.073223 4.909048 6.073223 5.383921 5.780330 5.676815 c
|
||||
5.487437 5.969707 5.012563 5.969707 4.719670 5.676815 c
|
||||
5.780330 4.616154 l
|
||||
h
|
||||
-0.530330 2.866154 m
|
||||
1.219670 1.116154 l
|
||||
2.280330 2.176815 l
|
||||
0.530330 3.926815 l
|
||||
-0.530330 2.866154 l
|
||||
h
|
||||
2.280330 1.116154 m
|
||||
5.780330 4.616154 l
|
||||
4.719670 5.676815 l
|
||||
1.219670 2.176815 l
|
||||
2.280330 1.116154 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
762
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000852 00000 n
|
||||
0000000874 00000 n
|
||||
0000001047 00000 n
|
||||
0000001121 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1180
|
||||
%%EOF
|
BIN
submodules/TelegramUI/Images.xcassets/Components/BadgeTEst.imageset/AppBadge@3x.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Components/BadgeTEst.imageset/AppBadge@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -6,17 +6,16 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ChannelVerifiedIconSmall@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "AppBadge@3x.png",
|
||||
"idiom" : "universal",
|
||||
"filename" : "ChannelVerifiedIconSmall@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
12
submodules/TelegramUI/Images.xcassets/Peer Info/VerifiedIconBackground.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/VerifiedIconBackground.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "verifybadge1_20.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
107
submodules/TelegramUI/Images.xcassets/Peer Info/VerifiedIconBackground.imageset/verifybadge1_20.pdf
vendored
Normal file
107
submodules/TelegramUI/Images.xcassets/Peer Info/VerifiedIconBackground.imageset/verifybadge1_20.pdf
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 1.625244 1.404785 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.020125 7.977180 m
|
||||
0.168502 7.520524 0.564519 7.124507 1.356553 6.332473 c
|
||||
1.812012 5.877014 l
|
||||
1.812012 5.232715 l
|
||||
1.812012 4.112610 1.812012 3.552557 2.029999 3.124734 c
|
||||
2.221745 2.748409 2.527707 2.442449 2.904031 2.250702 c
|
||||
3.331854 2.032715 3.891907 2.032715 5.012012 2.032715 c
|
||||
5.656311 2.032715 l
|
||||
6.111846 1.577180 l
|
||||
6.903880 0.785147 7.299897 0.389130 7.756554 0.240753 c
|
||||
8.158240 0.110237 8.590935 0.110237 8.992621 0.240753 c
|
||||
9.449278 0.389130 9.845295 0.785147 10.637329 1.577180 c
|
||||
11.092864 2.032715 l
|
||||
11.737012 2.032715 l
|
||||
12.857117 2.032715 13.417170 2.032715 13.844993 2.250702 c
|
||||
14.221317 2.442449 14.527278 2.748409 14.719025 3.124734 c
|
||||
14.937012 3.552557 14.937012 4.112610 14.937012 5.232715 c
|
||||
14.937012 5.876863 l
|
||||
15.392622 6.332473 l
|
||||
16.184656 7.124507 16.580673 7.520524 16.729050 7.977180 c
|
||||
16.859566 8.378868 16.859566 8.811562 16.729050 9.213249 c
|
||||
16.580673 9.669906 16.184656 10.065923 15.392622 10.857956 c
|
||||
14.937012 11.313566 l
|
||||
14.937012 11.957715 l
|
||||
14.937012 13.077820 14.937012 13.637873 14.719025 14.065696 c
|
||||
14.527278 14.442020 14.221317 14.747981 13.844993 14.939728 c
|
||||
13.417170 15.157715 12.857117 15.157715 11.737012 15.157715 c
|
||||
11.092864 15.157715 l
|
||||
10.637329 15.613250 l
|
||||
9.845295 16.405283 9.449278 16.801300 8.992621 16.949677 c
|
||||
8.590935 17.080193 8.158240 17.080193 7.756554 16.949677 c
|
||||
7.299897 16.801300 6.903880 16.405283 6.111846 15.613250 c
|
||||
5.656311 15.157715 l
|
||||
5.012012 15.157715 l
|
||||
3.891907 15.157715 3.331854 15.157715 2.904031 14.939728 c
|
||||
2.527707 14.747981 2.221745 14.442020 2.029999 14.065696 c
|
||||
1.812012 13.637873 1.812012 13.077820 1.812012 11.957715 c
|
||||
1.812012 11.313416 l
|
||||
1.356553 10.857956 l
|
||||
0.564519 10.065923 0.168502 9.669906 0.020125 9.213249 c
|
||||
-0.110391 8.811562 -0.110391 8.378868 0.020125 7.977180 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1888
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 20.000000 20.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001978 00000 n
|
||||
0000002001 00000 n
|
||||
0000002174 00000 n
|
||||
0000002248 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
2307
|
||||
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Peer Info/VerifiedIconForeground.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/VerifiedIconForeground.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "verifybadge2_20.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 6.718750 5.801514 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.530330 4.364315 m
|
||||
0.237437 4.657207 -0.237437 4.657207 -0.530330 4.364315 c
|
||||
-0.823223 4.071421 -0.823223 3.596547 -0.530330 3.303654 c
|
||||
0.530330 4.364315 l
|
||||
h
|
||||
2.187500 1.646484 m
|
||||
1.657170 1.116154 l
|
||||
1.950063 0.823261 2.424937 0.823261 2.717830 1.116154 c
|
||||
2.187500 1.646484 l
|
||||
h
|
||||
7.092830 5.491154 m
|
||||
7.385724 5.784048 7.385724 6.258921 7.092830 6.551815 c
|
||||
6.799937 6.844707 6.325063 6.844707 6.032170 6.551815 c
|
||||
7.092830 5.491154 l
|
||||
h
|
||||
-0.530330 3.303654 m
|
||||
1.657170 1.116154 l
|
||||
2.717830 2.176814 l
|
||||
0.530330 4.364315 l
|
||||
-0.530330 3.303654 l
|
||||
h
|
||||
2.717830 1.116154 m
|
||||
7.092830 5.491154 l
|
||||
6.032170 6.551815 l
|
||||
1.657170 2.176814 l
|
||||
2.717830 1.116154 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
762
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 20.000000 20.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000852 00000 n
|
||||
0000000874 00000 n
|
||||
0000001047 00000 n
|
||||
0000001121 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1180
|
||||
%%EOF
|
@ -3125,6 +3125,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if !onlyHaptic {
|
||||
strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected()
|
||||
}
|
||||
}, displayPremiumStickerTooltip: { [weak self] file, message in
|
||||
self?.displayPremiumStickerTooltip(file: file, message: message)
|
||||
}, openPeerContextMenu: { [weak self] peer, messageId, node, rect, gesture in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -3358,7 +3360,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(false))).start(next: { [weak self] responded in
|
||||
if let strongSelf = self {
|
||||
if !responded {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, title: nil, text: strongSelf.presentationData.strings.Conversation_InteractiveEmojiSyncTip(EnginePeer(peer).compactDisplayTitle).string), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, title: nil, text: strongSelf.presentationData.strings.Conversation_InteractiveEmojiSyncTip(EnginePeer(peer).compactDisplayTitle).string, undoText: nil), elevatedLayout: false, action: { _ in return false }), in: .current)
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementInteractiveEmojiSyncTip(accountManager: strongSelf.context.sharedContext.accountManager, timestamp: currentTimestamp).start()
|
||||
}
|
||||
@ -3486,7 +3488,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
controller.navigationPresentation = .flatModal
|
||||
strongSelf.push(controller)
|
||||
// strongSelf.present(controller, in: .window(.root))
|
||||
strongSelf.currentMenuWebAppController = controller
|
||||
} else if simple {
|
||||
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestSimpleWebView(botId: peerId, url: url, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme))
|
||||
@ -7952,12 +7953,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self {
|
||||
switch result {
|
||||
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), elevatedLayout: true, action: { _ in return false }), with: nil)
|
||||
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 .limitExceeded:
|
||||
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("5").string, text: strongSelf.presentationData.strings.Premium_MaxFavedStickersText("10").string), elevatedLayout: true, action: { [weak self] action in
|
||||
strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("5").string, text: strongSelf.presentationData.strings.Premium_MaxFavedStickersText("10").string, undoText: nil), elevatedLayout: true, action: { [weak self] action in
|
||||
if let strongSelf = self {
|
||||
if case .info = action {
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context)
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .savedStickers)
|
||||
strongSelf.push(controller)
|
||||
return true
|
||||
}
|
||||
@ -11176,7 +11177,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
let present = {
|
||||
strongSelf.present(attachmentController, in: .window(.root))
|
||||
attachmentController.navigationPresentation = .flatModal
|
||||
strongSelf.push(attachmentController)
|
||||
// strongSelf.present(attachmentController, in: .window(.root))
|
||||
strongSelf.attachmentController = attachmentController
|
||||
}
|
||||
|
||||
@ -12353,6 +12356,51 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return transformEnqueueMessages(messages, silentPosting: silentPosting)
|
||||
}
|
||||
|
||||
private func displayPremiumStickerTooltip(file: TelegramMediaFile, message: Message) {
|
||||
var currentOverlayController: UndoOverlayController?
|
||||
|
||||
self.window?.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
currentOverlayController = controller
|
||||
}
|
||||
})
|
||||
self.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
currentOverlayController = controller
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if let currentOverlayController = currentOverlayController {
|
||||
if case .sticker = currentOverlayController.content {
|
||||
return
|
||||
}
|
||||
currentOverlayController.dismissWithCommitAction()
|
||||
}
|
||||
|
||||
var stickerPackReference: StickerPackReference?
|
||||
for attribute in file.attributes {
|
||||
if case let .Sticker(_, packReference, _) = attribute, let packReference = packReference {
|
||||
stickerPackReference = packReference
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let stickerPackReference = stickerPackReference {
|
||||
let _ = (self.context.engine.stickers.loadedStickerPack(reference: stickerPackReference, forceActualized: false)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] stickerPack in
|
||||
if let strongSelf = self, case let .result(info, _, _) = stickerPack {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, title: info.title, text: strongSelf.presentationData.strings.Stickers_PremiumPackInfoText, undoText: strongSelf.presentationData.strings.Stickers_PremiumPackView), elevatedLayout: false, action: { [weak self] action in
|
||||
if let strongSelf = self, action == .undo {
|
||||
let _ = strongSelf.controllerInteraction?.openMessage(message, .default)
|
||||
}
|
||||
return false
|
||||
}), in: .current)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func displayDiceTooltip(dice: TelegramMediaDice) {
|
||||
guard let _ = dice.value else {
|
||||
return
|
||||
|
@ -118,6 +118,7 @@ public final class ChatControllerInteraction {
|
||||
let displayPsa: (String, ASDisplayNode) -> Void
|
||||
let displayDiceTooltip: (TelegramMediaDice) -> Void
|
||||
let animateDiceSuccess: (Bool) -> Void
|
||||
let displayPremiumStickerTooltip: (TelegramMediaFile, Message) -> Void
|
||||
let openPeerContextMenu: (Peer, MessageId?, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
||||
let openMessageReplies: (MessageId, Bool, Bool) -> Void
|
||||
let openReplyThreadOriginalMessage: (Message) -> Void
|
||||
@ -218,6 +219,7 @@ public final class ChatControllerInteraction {
|
||||
displayPsa: @escaping (String, ASDisplayNode) -> Void,
|
||||
displayDiceTooltip: @escaping (TelegramMediaDice) -> Void,
|
||||
animateDiceSuccess: @escaping (Bool) -> Void,
|
||||
displayPremiumStickerTooltip: @escaping (TelegramMediaFile, Message) -> Void,
|
||||
openPeerContextMenu: @escaping (Peer, MessageId?, ASDisplayNode, CGRect, ContextGesture?) -> Void,
|
||||
openMessageReplies: @escaping (MessageId, Bool, Bool) -> Void,
|
||||
openReplyThreadOriginalMessage: @escaping (Message) -> Void,
|
||||
@ -304,6 +306,7 @@ public final class ChatControllerInteraction {
|
||||
self.openMessagePollResults = openMessagePollResults
|
||||
self.displayDiceTooltip = displayDiceTooltip
|
||||
self.animateDiceSuccess = animateDiceSuccess
|
||||
self.displayPremiumStickerTooltip = displayPremiumStickerTooltip
|
||||
self.openPeerContextMenu = openPeerContextMenu
|
||||
self.openMessageReplies = openMessageReplies
|
||||
self.openReplyThreadOriginalMessage = openReplyThreadOriginalMessage
|
||||
@ -362,6 +365,7 @@ public final class ChatControllerInteraction {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
|
@ -1723,10 +1723,21 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
if let item = self.item, self.imageNode.frame.contains(location) {
|
||||
if let _ = self.telegramFile {
|
||||
return .optionalAction({
|
||||
let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||
})
|
||||
if let file = self.telegramFile {
|
||||
if file.isPremiumSticker {
|
||||
return .optionalAction({
|
||||
if self.additionalAnimationNodes.isEmpty {
|
||||
self.playedPremiumStickerAnimation = false
|
||||
self.playPremiumStickerAnimation()
|
||||
} else {
|
||||
item.controllerInteraction.displayPremiumStickerTooltip(file, item.message)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return .optionalAction({
|
||||
let _ = item.controllerInteraction.openMessage(item.message, .default)
|
||||
})
|
||||
}
|
||||
} else if let dice = self.telegramDice {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.displayDiceTooltip(dice)
|
||||
|
@ -570,7 +570,13 @@ final class ChatQrCodeScreen: ViewController {
|
||||
var fileName: String {
|
||||
switch self {
|
||||
case let .peer(peer):
|
||||
return "t_me-\(peer.addressName ?? "")"
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
return "t_me-\(peer.addressName ?? "")"
|
||||
} else if let peer = peer as? TelegramUser {
|
||||
return "t_me-\(peer.phone ?? "")"
|
||||
} else {
|
||||
return "t_me-\(Int32.random(in: 0 ..< Int32.max))"
|
||||
}
|
||||
case let .messages(messages):
|
||||
if let message = messages.first, let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, message.id.namespace == Namespaces.Message.Cloud, let addressName = chatPeer.addressName, !addressName.isEmpty {
|
||||
return "t_me-\(addressName)-\(message.id.id)"
|
||||
@ -1510,9 +1516,16 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
self.codeStaticIconNode = nil
|
||||
}
|
||||
|
||||
let codeText: String
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
codeText = "@\(peer.addressName ?? "")".uppercased()
|
||||
} else {
|
||||
codeText = peer.debugDisplayTitle.uppercased()
|
||||
}
|
||||
|
||||
self.codeTextNode = ImmediateTextNode()
|
||||
self.codeTextNode.displaysAsynchronously = false
|
||||
self.codeTextNode.attributedText = NSAttributedString(string: "@\(peer.addressName ?? "")".uppercased(), font: Font.with(size: 23.0, design: .round, weight: .bold, traits: []), textColor: .black)
|
||||
self.codeTextNode.attributedText = NSAttributedString(string: codeText, font: Font.with(size: 23.0, design: .round, weight: .bold, traits: []), textColor: .black)
|
||||
self.codeTextNode.truncationMode = .byCharWrapping
|
||||
self.codeTextNode.maximumNumberOfLines = 2
|
||||
self.codeTextNode.textAlignment = .center
|
||||
@ -1547,8 +1560,17 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
self.addSubnode(codeAnimatedIconNode)
|
||||
}
|
||||
|
||||
let codeLink: String
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
codeLink = "https://t.me/\(peer.addressName ?? "")"
|
||||
} else if let peer = peer as? TelegramUser {
|
||||
codeLink = "https://t.me/+\(peer.phone ?? "")"
|
||||
} else {
|
||||
codeLink = ""
|
||||
}
|
||||
|
||||
let codeReadyPromise = ValuePromise<Bool>()
|
||||
self.codeImageNode.setSignal(qrCode(string: "https://t.me/\(peer.addressName ?? "")", color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q") |> beforeNext { [weak self] size, _ in
|
||||
self.codeImageNode.setSignal(qrCode(string: codeLink, color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q") |> beforeNext { [weak self] size, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
@ -518,6 +518,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
@ -958,6 +959,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
#endif
|
||||
case .settings:
|
||||
break
|
||||
case .premiumOffer:
|
||||
break
|
||||
case let .joinVoiceChat(peerId, invite):
|
||||
strongSelf.presentController(VoiceChatJoinScreen(context: strongSelf.context, peerId: peerId, invite: invite, join: { call in
|
||||
}), .window(.root), nil)
|
||||
|
@ -144,6 +144,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
|
@ -355,9 +355,9 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
||||
private func updateItemsLayout(width: CGFloat) {
|
||||
self.displayItems.removeAll()
|
||||
|
||||
let itemsPerRow = min(8, max(4, Int(width / 80)))
|
||||
let sideInset: CGFloat = 4.0
|
||||
let itemSpacing: CGFloat = 4.0
|
||||
let itemsPerRow = min(8, max(5, Int(width / 80)))
|
||||
let sideInset: CGFloat = 2.0
|
||||
let itemSpacing: CGFloat = 2.0
|
||||
let itemSize = floor((width - sideInset * 2.0 - itemSpacing * (CGFloat(itemsPerRow) - 1.0)) / CGFloat(itemsPerRow))
|
||||
|
||||
var columnIndex = 0
|
||||
|
@ -27,6 +27,7 @@ import PeerInfoUI
|
||||
import Markdown
|
||||
import WebUI
|
||||
import BotPaymentsUI
|
||||
import PremiumUI
|
||||
|
||||
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
||||
if case .default = navigation {
|
||||
@ -514,8 +515,19 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case let .premiumOffer(reference):
|
||||
dismissInput()
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
let isPremium = peer?.isPremium ?? false
|
||||
if !isPremium {
|
||||
let controller = PremiumIntroScreen(context: context, reference: reference)
|
||||
if let navigationController = navigationController {
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
}
|
||||
}
|
||||
})
|
||||
case let .joinVoiceChat(peerId, invite):
|
||||
dismissInput()
|
||||
if let navigationController = navigationController {
|
||||
|
@ -724,6 +724,20 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if parsedUrl.host == "premium_offer" {
|
||||
var reference: String?
|
||||
if let components = URLComponents(string: "/?" + query) {
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "ref" {
|
||||
reference = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleResolvedUrl(.premiumOffer(reference: reference))
|
||||
}
|
||||
} else {
|
||||
if parsedUrl.host == "importStickers" {
|
||||
@ -743,6 +757,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
handleResolvedUrl(.settings(section))
|
||||
}
|
||||
}
|
||||
} else if parsedUrl.host == "premium_offer" {
|
||||
handleResolvedUrl(.premiumOffer(reference: nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
|
@ -2312,15 +2312,36 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
} else if case .scam = credibilityIcon {
|
||||
image = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if case .verified = credibilityIcon {
|
||||
if let sourceImage = UIImage(bundleImageName: "Peer Info/VerifiedIcon") {
|
||||
image = generateImage(sourceImage.size, contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 7.0, dy: 7.0))
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.clip(to: CGRect(origin: CGPoint(), size: size), mask: sourceImage.cgImage!)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
})
|
||||
if let backgroundImage = UIImage(bundleImageName: "Peer Info/VerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Peer Info/VerifiedIconForeground") {
|
||||
image = generateImage(backgroundImage.size, contextGenerator: { size, context in
|
||||
if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage {
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.saveGState()
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: backgroundCgImage)
|
||||
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.restoreGState()
|
||||
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: foregroundCgImage)
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}, opaque: false)
|
||||
expandedImage = generateImage(backgroundImage.size, contextGenerator: { size, context in
|
||||
if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage {
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.saveGState()
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: backgroundCgImage)
|
||||
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 0.75).cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.restoreGState()
|
||||
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: foregroundCgImage)
|
||||
context.setBlendMode(.clear)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}, opaque: false)
|
||||
} else {
|
||||
image = nil
|
||||
}
|
||||
@ -2331,17 +2352,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
|
||||
|
||||
let colorsArray: [CGColor] = [
|
||||
UIColor(rgb: 0x6B93FF).cgColor,
|
||||
UIColor(rgb: 0x6B93FF).cgColor,
|
||||
UIColor(rgb: 0x976FFF).cgColor,
|
||||
UIColor(rgb: 0xE46ACE).cgColor,
|
||||
UIColor(rgb: 0xE46ACE).cgColor
|
||||
]
|
||||
var locations: [CGFloat] = [0.0, 0.35, 0.5, 0.65, 1.0]
|
||||
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
}, opaque: false)
|
||||
expandedImage = generateImage(sourceImage.size, contextGenerator: { size, context in
|
||||
|
@ -2303,6 +2303,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
@ -5576,6 +5577,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
actions.append(ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuTranslate, accessibilityLabel: presentationData.strings.Conversation_ContextMenuTranslate), action: { [weak self] in
|
||||
|
||||
let controller = TranslateScreen(context: context, text: text, fromLanguage: language)
|
||||
controller.pushController = { [weak self] c in
|
||||
(self?.controller?.navigationController as? NavigationController)?._keepModalDismissProgress = true
|
||||
self?.controller?.push(c)
|
||||
}
|
||||
controller.presentController = { [weak self] c in
|
||||
self?.controller?.present(c, in: .window(.root))
|
||||
}
|
||||
self?.controller?.present(controller, in: .window(.root))
|
||||
}))
|
||||
}
|
||||
@ -6204,7 +6212,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
case .language:
|
||||
push(LocalizationListController(context: self.context))
|
||||
case .premium:
|
||||
self.controller?.push(PremiumIntroScreen(context: self.context, modal: false))
|
||||
self.controller?.push(PremiumIntroScreen(context: self.context, modal: false, source: .settings))
|
||||
case .stickers:
|
||||
if let settings = self.data?.globalSettings {
|
||||
push(installedStickerPacksController(context: self.context, mode: .general, archivedPacks: settings.archivedStickerPacks, updatedPacks: { [weak self] packs in
|
||||
@ -7457,10 +7465,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false))
|
||||
} else {
|
||||
if self.isSettings {
|
||||
if let addressName = self.data?.peer?.addressName, !addressName.isEmpty {
|
||||
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
||||
}
|
||||
|
||||
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
||||
} else if peerInfoCanEdit(peer: self.data?.peer, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
|
||||
|
@ -1313,6 +1313,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
|
@ -34,7 +34,7 @@ public enum UndoOverlayContent {
|
||||
case voiceChatRecording(text: String)
|
||||
case voiceChatFlag(text: String)
|
||||
case voiceChatCanSpeak(text: String)
|
||||
case sticker(context: AccountContext, file: TelegramMediaFile, title: String?, text: String)
|
||||
case sticker(context: AccountContext, file: TelegramMediaFile, title: String?, text: String, undoText: String?)
|
||||
case copy(text: String)
|
||||
case mediaSaved(text: String)
|
||||
case paymentSent(currencyValue: String, itemTitle: String)
|
||||
|
@ -628,7 +628,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
case let .sticker(context, file, title, text):
|
||||
case let .sticker(context, file, title, text, customUndoText):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
@ -702,7 +702,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
isUserInteractionEnabled = true
|
||||
}
|
||||
|
||||
displayUndo = false
|
||||
if let customUndoText = customUndoText {
|
||||
undoText = customUndoText
|
||||
displayUndo = true
|
||||
} else {
|
||||
displayUndo = false
|
||||
}
|
||||
self.originalRemainingSeconds = 3
|
||||
|
||||
if let updatedFetchSignal = updatedFetchSignal {
|
||||
@ -869,12 +874,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
switch content {
|
||||
case .removedChat:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .sticker, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal:
|
||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal:
|
||||
if self.textNode.tapAttributeAction != nil {
|
||||
self.isUserInteractionEnabled = true
|
||||
} else {
|
||||
self.isUserInteractionEnabled = false
|
||||
}
|
||||
case let .sticker(_, _, _, _, undoText):
|
||||
self.isUserInteractionEnabled = undoText != nil
|
||||
case .dice:
|
||||
self.panelWrapperNode.clipsToBounds = true
|
||||
case .info:
|
||||
|
@ -427,6 +427,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
private var delayedScriptMessage: WKScriptMessage?
|
||||
private func handleScriptMessage(_ message: WKScriptMessage) {
|
||||
guard let controller = self.controller else {
|
||||
@ -487,7 +489,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
case "web_app_open_invoice":
|
||||
if let json = json, let slug = json["slug"] as? String {
|
||||
self.paymentDisposable = (context.engine.payments.fetchBotPaymentInvoice(source: .slug(slug))
|
||||
self.paymentDisposable = (self.context.engine.payments.fetchBotPaymentInvoice(source: .slug(slug))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<TelegramMediaInvoice?, NoError> in
|
||||
return .single(nil)
|
||||
@ -510,6 +512,51 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
})
|
||||
}
|
||||
case "web_app_open_link":
|
||||
if let json = json, let url = json["url"] as? String {
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
|
||||
self.webView?.lastTouchTimestamp = nil
|
||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
|
||||
}
|
||||
}
|
||||
case "web_app_setup_back_button":
|
||||
break
|
||||
case "web_app_trigger_haptic_feedback":
|
||||
if let json = json, let type = json["type"] as? String {
|
||||
switch type {
|
||||
case "impact":
|
||||
if let impactType = json["impact_style"] as? String {
|
||||
switch impactType {
|
||||
case "light":
|
||||
self.hapticFeedback.impact(.light)
|
||||
case "medium":
|
||||
self.hapticFeedback.impact(.medium)
|
||||
case "heavy":
|
||||
self.hapticFeedback.impact(.heavy)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case "notification":
|
||||
if let notificationType = json["notification_type"] as? String {
|
||||
switch notificationType {
|
||||
case "success":
|
||||
self.hapticFeedback.success()
|
||||
case "error":
|
||||
self.hapticFeedback.error()
|
||||
case "warning":
|
||||
self.hapticFeedback.warning()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case "selection_change":
|
||||
self.hapticFeedback.tap()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ final class WebAppWebView: WKWebView {
|
||||
self.sendEvent(name: "viewport_changed", data: data)
|
||||
}
|
||||
|
||||
var lastTouchTimestamp: Double?
|
||||
private(set) var didTouchOnce = false
|
||||
var onFirstTouch: () -> Void = {}
|
||||
|
||||
@ -161,6 +162,7 @@ final class WebAppWebView: WKWebView {
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
self.lastTouchTimestamp = CACurrentMediaTime()
|
||||
if result != nil && !self.didTouchOnce {
|
||||
self.didTouchOnce = true
|
||||
self.onFirstTouch()
|
||||
|
Loading…
x
Reference in New Issue
Block a user