mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Merge branch 'master' into stars-subscriptions
This commit is contained in:
commit
dc68eab568
@ -9587,6 +9587,7 @@ Sorry for the inconvenience.";
|
||||
"Story.ContextPrivacy.LabelEveryone" = "Everyone";
|
||||
"Story.Context.Privacy" = "Who Can See";
|
||||
"Story.Context.Edit" = "Edit Story";
|
||||
"Story.Context.EditCover" = "Edit Cover";
|
||||
"Story.Context.SaveToProfile" = "Save to Profile";
|
||||
"Story.Context.RemoveFromProfile" = "Remove from Profile";
|
||||
"Story.ToastRemovedFromProfileText" = "Story removed from your profile";
|
||||
@ -12563,6 +12564,7 @@ Sorry for the inconvenience.";
|
||||
"WebBrowser.AddressBar.RecentlyVisited.Clear" = "Clear";
|
||||
|
||||
"WebBrowser.AddressBar.Bookmarks" = "BOOKMARKS";
|
||||
"WebBrowser.AddressBar.ShowMore" = "Show More";
|
||||
|
||||
"WebBrowser.OpenLinksIn.Title" = "OPEN LINKS IN";
|
||||
"WebBrowser.AutoLogin" = "Auto-Login via Telegram";
|
||||
@ -12581,6 +12583,94 @@ Sorry for the inconvenience.";
|
||||
"WebBrowser.Exceptions.Create.Text" = "Enter a domain that you don't want to be opened in the in-app browser.";
|
||||
"WebBrowser.Exceptions.Create.Placeholder" = "Enter URL";
|
||||
|
||||
"WebBrowser.Exceptions.ClearConfirmation.Text" = "Are you sure you want to clear this list?";
|
||||
"WebBrowser.Exceptions.ClearConfirmation.Clear" = "Clear";
|
||||
|
||||
"WebBrowser.ClearCookies.ClearConfirmation.Text" = "Are you sure you want to clear cookies?";
|
||||
"WebBrowser.ClearCookies.ClearConfirmation.Clear" = "Clear";
|
||||
|
||||
"WebBrowser.ClearCache" = "Clear Cache";
|
||||
"WebBrowser.ClearCache.ClearConfirmation.Text" = "Are you sure you want to clear cookies?";
|
||||
"WebBrowser.ClearCache.ClearConfirmation.Clear" = "Clear";
|
||||
"WebBrowser.ClearCache.Succeed" = "Cookies cleared.";
|
||||
|
||||
|
||||
"WebBrowser.Done" = "Done";
|
||||
|
||||
"AccessDenied.LocationWeather" = "Telegram needs access to your location so that you can add the weather widget to your stories.\n\nPlease go to Settings > Privacy > Location Services and set Telegram to ON.";
|
||||
|
||||
"Story.Editor.TooltipWeatherLimitText" = "You can't add more than one weather sticker to a story.";
|
||||
|
||||
"WebBrowser.AddressPlaceholder" = "Enter URL";
|
||||
|
||||
"WebBrowser.Bookmarks.Title" = "Bookmarks";
|
||||
"WebBrowser.Bookmarks.BookmarkCurrent" = "Bookmark Current Page";
|
||||
|
||||
"Story.Privacy.ChooseCover" = "Choose Story Cover";
|
||||
"Story.Privacy.ChooseCoverInfo" = "Choose a frame from the story to show in your Profile.";
|
||||
"Story.Privacy.ChooseCoverChannelInfo" = "Choose a frame from the story to show in channel profile.";
|
||||
"Story.Privacy.ChooseCoverGroupInfo" = "Choose a frame from the story to show in group profile.";
|
||||
|
||||
"ChatList.Search.FilterApps" = "Apps";
|
||||
"ChatList.Search.SectionPopularApps" = "POPULAR APPS";
|
||||
"ChatList.Search.SectionRecentApps" = "APPS YOU USE";
|
||||
"ChatList.Search.Apps.Empty.Text" = "No Apps Found";
|
||||
|
||||
"VoiceChat.MicrophoneModes" = "Microphone Modes";
|
||||
|
||||
"MiniAppList.Title" = "Examples";
|
||||
"MiniAppList.ListSectionHeader" = "APPS THAT ACCEPT STARS";
|
||||
|
||||
"PeerInfo.PaneBotPreviews" = "Preview";
|
||||
"PeerInfo.OpenAppButton" = "Open App";
|
||||
"PeerInfo.AppFooterAdmin" = "By publishing this mini app, you agree to the [Telegram Terms of Service for Developers](https://telegram.org/privacy).";
|
||||
"PeerInfo.AppFooter" = "By launching this mini app, you agree to the [Terms of Service for Mini Apps](https://telegram.org/privacy).";
|
||||
"BotPreviews.MenuAddPreview" = "Add Preview";
|
||||
"BotPreviews.MenuReorder" = "Reorder";
|
||||
"BotPreviews.MenuDeleteLanguage" = "Delete %@";
|
||||
"BotPreviews.SubtitleLoading" = "loading";
|
||||
"BotPreviews.SubtitleEmpty" = "no preview added";
|
||||
"BotPreviews.SubtitleCount_1" = "1 preview";
|
||||
"BotPreviews.SubtitleCount_any" = "%d previews";
|
||||
"BotPreviews.SheetDeleteTitle_1" = "Delete 1 Preview?";
|
||||
"BotPreviews.SheetDeleteTitle_any" = "Delete %d Previews?";
|
||||
"BotPreviews.LanguageTab.Main" = "Main";
|
||||
"BotPreviews.LanguageTab.Add" = "+ Add Language";
|
||||
"BotPreviews.Empty.Title" = "No Preview";
|
||||
"BotPreviews.Empty.Text_1" = "Upload up to 1 screenshot and video demos for your mini app.";
|
||||
"BotPreviews.Empty.Text_any" = "Upload up to %d screenshots and video demos for your mini app.";
|
||||
"BotPreviews.Empty.Add" = "Add Preview";
|
||||
"BotPreviews.Empty.AddTranslation" = "Create a Translation";
|
||||
"BotPreviews.Empty.DeleteTranslation" = "Delete this Translation";
|
||||
"BotPreviews.Empty.Separator" = "or";
|
||||
"BotPreviews.AlertTooManyPreviews_1" = "You can add at most 1 preview.";
|
||||
"BotPreviews.AlertTooManyPreviews_any" = "You can add at most 1 previews.";
|
||||
"BotPreviews.DeleteTranslationAlert.Title" = "Delete Translation";
|
||||
"BotPreviews.DeleteTranslationAlert.Text" = "Are you sure you want to delete this translation?";
|
||||
"BotPreviews.TranslationFooter.Text" = "This preview will be displayed for all users who have %@ set as their language.";
|
||||
"BotPreviews.DefaultFooter.Text" = "This preview will be shown by default. You can also add translations into specific languages.";
|
||||
"BotPreviews.SelectLanguage.Title" = "Add a Translation";
|
||||
"BotPreview.ViewContextDelete" = "Delete Preview";
|
||||
|
||||
"WebBrowser.Download.Confirmation" = "Do you want to download \"%@\"?";
|
||||
"WebBrowser.Download.Download" = "Download";
|
||||
|
||||
"Story.Cover" = "Story Cover";
|
||||
"Story.SaveCover" = "Save Cover";
|
||||
|
||||
"WebBrowser.CopyLink" = "Copy Link";
|
||||
"WebBrowser.DeleteBookmark" = "Delete Bookmark";
|
||||
"WebBrowser.RemoveRecent" = "Remove from Recent";
|
||||
|
||||
"Stars.Intro.Transaction.Gift.Title" = "Received Gift";
|
||||
"Stars.Intro.Transaction.Gift.UnknownUser" = "Unknown User";
|
||||
|
||||
"WebApp.PrivacyPolicy" = "Privacy Policy";
|
||||
|
||||
"Conversation.OpenProfile" = "OPEN PROFILE";
|
||||
|
||||
"Stars.Intro.GiftStars" = "Gift Stars to Friends";
|
||||
|
||||
"MediaPicker.CreateSticker" = "Create a sticker from a photo";
|
||||
|
||||
"Stickers.CreateSticker" = "Create\nSticker";
|
||||
|
@ -836,6 +836,9 @@ public final class BotPreviewEditorTransitionOut {
|
||||
}
|
||||
}
|
||||
|
||||
public protocol MiniAppListScreenInitialData: AnyObject {
|
||||
}
|
||||
|
||||
public protocol SharedAccountContext: AnyObject {
|
||||
var sharedContainerPath: String { get }
|
||||
var basePath: String { get }
|
||||
@ -903,7 +906,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
selectedMessages: Signal<Set<MessageId>?, NoError>,
|
||||
mode: ChatHistoryListMode
|
||||
) -> ChatHistoryListNode
|
||||
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem
|
||||
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: ((UIView?, CGPoint?) -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem
|
||||
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||
func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||
func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController?
|
||||
@ -978,6 +981,8 @@ public protocol SharedAccountContext: AnyObject {
|
||||
|
||||
func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController
|
||||
|
||||
func makeStoryMediaEditorScreen(context: AccountContext, source: Any?, text: String?, link: (url: String, name: String?)?, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void) -> ViewController
|
||||
|
||||
func makeBotPreviewEditorScreen(context: AccountContext, source: Any?, target: Stories.PendingTarget, transitionArguments: (UIView, CGRect, UIImage?)?, transitionOut: @escaping () -> BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
||||
|
||||
func makeStickerEditorScreen(context: AccountContext, source: Any?, intro: Bool, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
||||
@ -1004,6 +1009,10 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
|
||||
func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController
|
||||
func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController
|
||||
|
||||
func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError>
|
||||
func makeMiniAppListScreen(context: AccountContext, initialData: MiniAppListScreenInitialData) -> ViewController
|
||||
|
||||
func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: EnginePeer, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool)
|
||||
|
||||
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
||||
|
@ -1187,4 +1187,6 @@ public protocol ChatHistoryListNode: ListView {
|
||||
func scrollToEndOfHistory()
|
||||
func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets)
|
||||
func messageInCurrentHistoryView(_ id: MessageId) -> Message?
|
||||
|
||||
var contentPositionChanged: (ListViewVisibleContentOffset) -> Void { get set }
|
||||
}
|
||||
|
@ -139,6 +139,7 @@ public struct PremiumConfiguration {
|
||||
showPremiumGiftInAttachMenu: false,
|
||||
showPremiumGiftInTextField: false,
|
||||
giveawayGiftsPurchaseAvailable: false,
|
||||
starsGiftsPurchaseAvailable: false,
|
||||
boostsPerGiftCount: 3,
|
||||
audioTransciptionTrialMaxDuration: 300,
|
||||
audioTransciptionTrialCount: 2,
|
||||
@ -165,6 +166,7 @@ public struct PremiumConfiguration {
|
||||
public let showPremiumGiftInAttachMenu: Bool
|
||||
public let showPremiumGiftInTextField: Bool
|
||||
public let giveawayGiftsPurchaseAvailable: Bool
|
||||
public let starsGiftsPurchaseAvailable: Bool
|
||||
public let boostsPerGiftCount: Int32
|
||||
public let audioTransciptionTrialMaxDuration: Int32
|
||||
public let audioTransciptionTrialCount: Int32
|
||||
@ -190,6 +192,7 @@ public struct PremiumConfiguration {
|
||||
showPremiumGiftInAttachMenu: Bool,
|
||||
showPremiumGiftInTextField: Bool,
|
||||
giveawayGiftsPurchaseAvailable: Bool,
|
||||
starsGiftsPurchaseAvailable: Bool,
|
||||
boostsPerGiftCount: Int32,
|
||||
audioTransciptionTrialMaxDuration: Int32,
|
||||
audioTransciptionTrialCount: Int32,
|
||||
@ -214,6 +217,7 @@ public struct PremiumConfiguration {
|
||||
self.showPremiumGiftInAttachMenu = showPremiumGiftInAttachMenu
|
||||
self.showPremiumGiftInTextField = showPremiumGiftInTextField
|
||||
self.giveawayGiftsPurchaseAvailable = giveawayGiftsPurchaseAvailable
|
||||
self.starsGiftsPurchaseAvailable = starsGiftsPurchaseAvailable
|
||||
self.boostsPerGiftCount = boostsPerGiftCount
|
||||
self.audioTransciptionTrialMaxDuration = audioTransciptionTrialMaxDuration
|
||||
self.audioTransciptionTrialCount = audioTransciptionTrialCount
|
||||
@ -246,6 +250,7 @@ public struct PremiumConfiguration {
|
||||
showPremiumGiftInAttachMenu: data["premium_gift_attach_menu_icon"] as? Bool ?? defaultValue.showPremiumGiftInAttachMenu,
|
||||
showPremiumGiftInTextField: data["premium_gift_text_field_icon"] as? Bool ?? defaultValue.showPremiumGiftInTextField,
|
||||
giveawayGiftsPurchaseAvailable: data["giveaway_gifts_purchase_available"] as? Bool ?? defaultValue.giveawayGiftsPurchaseAvailable,
|
||||
starsGiftsPurchaseAvailable: data["stars_gifts_enabled"] as? Bool ?? defaultValue.starsGiftsPurchaseAvailable,
|
||||
boostsPerGiftCount: get(data["boosts_per_sent_gift"]) ?? defaultValue.boostsPerGiftCount,
|
||||
audioTransciptionTrialMaxDuration: get(data["transcribe_audio_trial_duration_max"]) ?? defaultValue.audioTransciptionTrialMaxDuration,
|
||||
audioTransciptionTrialCount: get(data["transcribe_audio_trial_weekly_number"]) ?? defaultValue.audioTransciptionTrialCount,
|
||||
|
@ -1365,4 +1365,13 @@ public class AttachmentController: ViewController, MinimizableController {
|
||||
})
|
||||
return disposableSet
|
||||
}
|
||||
|
||||
public func makeContentSnapshotView() -> UIView? {
|
||||
let snapshotView = self.view.snapshotView(afterScreenUpdates: false)
|
||||
if let contentSnapshotView = self.mainController.makeContentSnapshotView() {
|
||||
contentSnapshotView.frame = contentSnapshotView.frame.offsetBy(dx: 0.0, dy: 64.0 + 56.0)
|
||||
snapshotView?.addSubview(contentSnapshotView)
|
||||
}
|
||||
return snapshotView
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ final class BotCheckoutWebInteractionController: ViewController {
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = BotCheckoutWebInteractionControllerNode(presentationData: self.presentationData, url: self.url, intent: self.intent)
|
||||
self.displayNode = BotCheckoutWebInteractionControllerNode(context: self.context, presentationData: self.presentationData, url: self.url, intent: self.intent)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
@ -4,6 +4,7 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
import WebKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
||||
private class WeakPaymentScriptMessageHandler: NSObject, WKScriptMessageHandler {
|
||||
private let f: (WKScriptMessage) -> ()
|
||||
@ -20,12 +21,14 @@ private class WeakPaymentScriptMessageHandler: NSObject, WKScriptMessageHandler
|
||||
}
|
||||
|
||||
final class BotCheckoutWebInteractionControllerNode: ViewControllerTracingNode, WKNavigationDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private let intent: BotCheckoutWebInteractionControllerIntent
|
||||
|
||||
private var webView: WKWebView?
|
||||
|
||||
init(presentationData: PresentationData, url: String, intent: BotCheckoutWebInteractionControllerIntent) {
|
||||
init(context: AccountContext, presentationData: PresentationData, url: String, intent: BotCheckoutWebInteractionControllerIntent) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.intent = intent
|
||||
|
||||
@ -143,9 +146,25 @@ final class BotCheckoutWebInteractionControllerNode: ViewControllerTracingNode,
|
||||
decisionHandler(.cancel)
|
||||
completion(true)
|
||||
} else {
|
||||
if let url = navigationAction.request.url, let scheme = url.scheme {
|
||||
let defaultSchemes: [String] = ["http", "https"]
|
||||
if !defaultSchemes.contains(scheme) {
|
||||
decisionHandler(.cancel)
|
||||
self.context.sharedContext.applicationBindings.openUrl(url.absoluteString)
|
||||
return
|
||||
}
|
||||
}
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
} else {
|
||||
if let url = navigationAction.request.url, let scheme = url.scheme {
|
||||
let defaultSchemes: [String] = ["http", "https"]
|
||||
if !defaultSchemes.contains(scheme) {
|
||||
decisionHandler(.cancel)
|
||||
self.context.sharedContext.applicationBindings.openUrl(url.absoluteString)
|
||||
return
|
||||
}
|
||||
}
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,15 @@ swift_library(
|
||||
"//submodules/Svg",
|
||||
"//submodules/PromptUI",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/PhotoResources",
|
||||
"//submodules/TelegramUI/Components/ChatControllerInteraction",
|
||||
"//submodules/ChatPresentationInterfaceState",
|
||||
"//submodules/UrlWhitelist",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode",
|
||||
"//submodules/SearchUI",
|
||||
"//submodules/SearchBarNode",
|
||||
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -3,25 +3,39 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
import UrlEscaping
|
||||
|
||||
final class AddressBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let metrics: LayoutMetrics
|
||||
let url: String
|
||||
let isSecure: Bool
|
||||
let isExpanded: Bool
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
metrics: LayoutMetrics,
|
||||
url: String,
|
||||
isSecure: Bool,
|
||||
isExpanded: Bool,
|
||||
performAction: ActionSlot<BrowserScreen.Action>
|
||||
) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.metrics = metrics
|
||||
self.url = url
|
||||
self.isSecure = isSecure
|
||||
self.isExpanded = isExpanded
|
||||
self.performAction = performAction
|
||||
}
|
||||
|
||||
@ -32,9 +46,18 @@ final class AddressBarContentComponent: Component {
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.metrics != rhs.metrics {
|
||||
return false
|
||||
}
|
||||
if lhs.url != rhs.url {
|
||||
return false
|
||||
}
|
||||
if lhs.isSecure != rhs.isSecure {
|
||||
return false
|
||||
}
|
||||
if lhs.isExpanded != rhs.isExpanded {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -43,12 +66,25 @@ final class AddressBarContentComponent: Component {
|
||||
override func textRect(forBounds bounds: CGRect) -> CGRect {
|
||||
return bounds.integral
|
||||
}
|
||||
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
var canBecomeFirstResponder = super.canBecomeFirstResponder
|
||||
if !canBecomeFirstResponder && self.alpha.isZero {
|
||||
canBecomeFirstResponder = true
|
||||
}
|
||||
return canBecomeFirstResponder
|
||||
}
|
||||
}
|
||||
|
||||
private struct Params: Equatable {
|
||||
var theme: PresentationTheme
|
||||
var strings: PresentationStrings
|
||||
var size: CGSize
|
||||
var isActive: Bool
|
||||
var title: String
|
||||
var isSecure: Bool
|
||||
var collapseFraction: CGFloat
|
||||
var isTablet: Bool
|
||||
|
||||
static func ==(lhs: Params, rhs: Params) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
@ -60,14 +96,28 @@ final class AddressBarContentComponent: Component {
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
if lhs.isActive != rhs.isActive {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.isSecure != rhs.isSecure {
|
||||
return false
|
||||
}
|
||||
if lhs.collapseFraction != rhs.collapseFraction {
|
||||
return false
|
||||
}
|
||||
if lhs.isTablet != rhs.isTablet {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let activated: (Bool) -> Void = { _ in }
|
||||
private let deactivated: (Bool) -> Void = { _ in }
|
||||
private let updateQuery: (String?) -> Void = { _ in }
|
||||
|
||||
|
||||
private let backgroundLayer: SimpleLayer
|
||||
|
||||
private let iconView: UIImageView
|
||||
@ -79,10 +129,11 @@ final class AddressBarContentComponent: Component {
|
||||
private let cancelButton: HighlightTrackingButton
|
||||
|
||||
private var placeholderContent = ComponentView<Empty>()
|
||||
private var titleContent = ComponentView<Empty>()
|
||||
|
||||
private var textFrame: CGRect?
|
||||
private var textField: TextField?
|
||||
|
||||
|
||||
private var tapRecognizer: UITapGestureRecognizer?
|
||||
|
||||
private var params: Params?
|
||||
@ -99,12 +150,12 @@ final class AddressBarContentComponent: Component {
|
||||
|
||||
self.clearIconView = UIImageView()
|
||||
self.clearIconButton = HighlightableButton()
|
||||
self.clearIconView.isHidden = true
|
||||
self.clearIconButton.isHidden = true
|
||||
self.clearIconView.isHidden = false
|
||||
self.clearIconButton.isHidden = false
|
||||
|
||||
self.cancelButtonTitle = ComponentView()
|
||||
self.cancelButton = HighlightTrackingButton()
|
||||
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.layer.addSublayer(self.backgroundLayer)
|
||||
@ -156,76 +207,50 @@ final class AddressBarContentComponent: Component {
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.activateTextInput()
|
||||
if case .ended = recognizer.state, let component = self.component, !component.isExpanded {
|
||||
component.performAction.invoke(.openAddressBar)
|
||||
}
|
||||
}
|
||||
|
||||
private func activateTextInput() {
|
||||
if self.textField == nil, let textFrame = self.textFrame {
|
||||
let backgroundFrame = self.backgroundLayer.frame
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
|
||||
|
||||
let textField = TextField(frame: textFieldFrame)
|
||||
textField.autocorrectionType = .no
|
||||
textField.returnKeyType = .search
|
||||
self.textField = textField
|
||||
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
textField.delegate = self
|
||||
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||
}
|
||||
|
||||
guard !(self.textField?.isFirstResponder ?? false) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.activated(true)
|
||||
|
||||
self.textField?.becomeFirstResponder()
|
||||
if let textField = self.textField {
|
||||
textField.becomeFirstResponder()
|
||||
Queue.mainQueue().after(0.3, {
|
||||
textField.selectAll(nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func deactivateTextInput() {
|
||||
self.textField?.endEditing(true)
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.updateQuery(nil)
|
||||
self.deactivated(self.textField?.isFirstResponder ?? false)
|
||||
|
||||
self.clearIconView.isHidden = true
|
||||
self.clearIconButton.isHidden = true
|
||||
|
||||
let textField = self.textField
|
||||
self.textField = nil
|
||||
|
||||
self.deactivated(textField?.isFirstResponder ?? false)
|
||||
|
||||
self.component?.performAction.invoke(.updateSearchActive(false))
|
||||
|
||||
if let textField {
|
||||
textField.resignFirstResponder()
|
||||
textField.removeFromSuperview()
|
||||
}
|
||||
self.component?.performAction.invoke(.closeAddressBar)
|
||||
}
|
||||
|
||||
@objc private func clearPressed() {
|
||||
self.updateQuery(nil)
|
||||
self.textField?.text = ""
|
||||
|
||||
self.clearIconView.isHidden = true
|
||||
self.clearIconButton.isHidden = true
|
||||
}
|
||||
|
||||
func deactivate() {
|
||||
if let text = self.textField?.text, !text.isEmpty {
|
||||
self.textField?.endEditing(true)
|
||||
} else {
|
||||
self.cancelPressed()
|
||||
guard let textField = self.textField else {
|
||||
return
|
||||
}
|
||||
textField.text = ""
|
||||
self.textFieldChanged(textField)
|
||||
}
|
||||
|
||||
|
||||
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
}
|
||||
|
||||
public func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
}
|
||||
|
||||
|
||||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
if let component = self.component {
|
||||
let finalUrl = explicitUrl(textField.text ?? "")
|
||||
component.performAction.invoke(.navigateTo(finalUrl, true))
|
||||
}
|
||||
textField.endEditing(true)
|
||||
return false
|
||||
}
|
||||
@ -237,38 +262,59 @@ final class AddressBarContentComponent: Component {
|
||||
self.clearIconButton.isHidden = text.isEmpty
|
||||
self.placeholderContent.view?.isHidden = !text.isEmpty
|
||||
|
||||
self.updateQuery(text)
|
||||
|
||||
self.component?.performAction.invoke(.updateSearchQuery(text))
|
||||
|
||||
if let params = self.params {
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, transition: .immediate)
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: AddressBarContentComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
|
||||
func update(component: AddressBarContentComponent, availableSize: CGSize, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
let collapseFraction = environment[BrowserNavigationBarEnvironment.self].fraction
|
||||
|
||||
let wasExpanded = self.component?.isExpanded ?? false
|
||||
self.component = component
|
||||
|
||||
self.update(theme: component.theme, strings: component.strings, size: availableSize, transition: transition)
|
||||
if !wasExpanded && component.isExpanded {
|
||||
self.activateTextInput()
|
||||
}
|
||||
if wasExpanded && !component.isExpanded {
|
||||
self.deactivateTextInput()
|
||||
}
|
||||
let isActive = self.textField?.isFirstResponder ?? false
|
||||
|
||||
var title: String = ""
|
||||
if let parsedUrl = URL(string: component.url) {
|
||||
title = parsedUrl.host ?? component.url
|
||||
if title.hasPrefix("www.") {
|
||||
title.removeSubrange(title.startIndex ..< title.index(title.startIndex, offsetBy: 4))
|
||||
}
|
||||
title = title.idnaDecoded ?? title
|
||||
}
|
||||
|
||||
self.update(theme: component.theme, strings: component.strings, size: availableSize, isActive: isActive, title: title.lowercased(), isSecure: component.isSecure, collapseFraction: collapseFraction, isTablet: component.metrics.isTablet, transition: transition)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, transition: ComponentTransition) {
|
||||
public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, isActive: Bool, title: String, isSecure: Bool, collapseFraction: CGFloat, isTablet: Bool, transition: ComponentTransition) {
|
||||
let params = Params(
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
size: size
|
||||
size: size,
|
||||
isActive: isActive,
|
||||
title: title,
|
||||
isSecure: isSecure,
|
||||
collapseFraction: collapseFraction,
|
||||
isTablet: isTablet
|
||||
)
|
||||
|
||||
if self.params == params {
|
||||
return
|
||||
}
|
||||
|
||||
let isActiveWithText = true
|
||||
let isActiveWithText = self.component?.isExpanded ?? false
|
||||
|
||||
if self.params?.theme !== theme {
|
||||
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Lock"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.iconView.tintColor = theme.rootController.navigationSearchBar.inputIconColor
|
||||
self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.clearIconView.tintColor = theme.rootController.navigationSearchBar.inputClearButtonColor
|
||||
@ -280,10 +326,9 @@ final class AddressBarContentComponent: Component {
|
||||
let inputHeight: CGFloat = 36.0
|
||||
let topInset: CGFloat = (size.height - inputHeight) / 2.0
|
||||
|
||||
let sideTextInset: CGFloat = sideInset + 4.0 + 17.0
|
||||
|
||||
self.backgroundLayer.backgroundColor = theme.rootController.navigationSearchBar.inputFillColor.cgColor
|
||||
self.backgroundLayer.cornerRadius = 10.5
|
||||
transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.0, min(1.0, 1.0 - collapseFraction * 1.5)))
|
||||
|
||||
let cancelTextSize = self.cancelButtonTitle.update(
|
||||
transition: .immediate,
|
||||
@ -299,42 +344,82 @@ final class AddressBarContentComponent: Component {
|
||||
let cancelButtonSpacing: CGFloat = 8.0
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight))
|
||||
if isActiveWithText {
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing
|
||||
}
|
||||
transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame)
|
||||
|
||||
transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height)))
|
||||
self.cancelButton.isUserInteractionEnabled = isActiveWithText && !isTablet
|
||||
|
||||
let textX: CGFloat = backgroundFrame.minX + sideTextInset
|
||||
let textX: CGFloat = backgroundFrame.minX + sideInset
|
||||
let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
||||
self.textFrame = textFrame
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + 5.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
transition.setFrame(view: self.iconView, frame: iconFrame)
|
||||
}
|
||||
|
||||
|
||||
let placeholderSize = self.placeholderContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.Common_Search, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
)
|
||||
if let placeholderContentView = self.placeholderContent.view {
|
||||
if placeholderContentView.superview == nil {
|
||||
placeholderContentView.alpha = 0.0
|
||||
placeholderContentView.isHidden = true
|
||||
self.addSubview(placeholderContentView)
|
||||
}
|
||||
let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.midY - placeholderSize.height / 2.0), size: placeholderSize)
|
||||
transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame)
|
||||
transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let titleSize = self.titleContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.rootController.navigationSearchBar.inputTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
truncationType: .end,
|
||||
maximumNumberOfLines: 1
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 36.0, height: size.height)
|
||||
)
|
||||
var titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.midX - titleSize.width / 2.0, y: backgroundFrame.midY - titleSize.height / 2.0), size: titleSize)
|
||||
if isSecure && !isActiveWithText {
|
||||
titleContentFrame.origin.x += 7.0
|
||||
}
|
||||
var titleSizeChanged = false
|
||||
if let titleContentView = self.titleContent.view {
|
||||
if titleContentView.superview == nil {
|
||||
self.addSubview(titleContentView)
|
||||
}
|
||||
if titleContentView.frame.width != titleContentFrame.size.width {
|
||||
titleSizeChanged = true
|
||||
}
|
||||
transition.setPosition(view: titleContentView, position: titleContentFrame.center)
|
||||
titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size)
|
||||
transition.setAlpha(view: titleContentView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: titleContentFrame.minX - image.size.width - 3.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
var iconTransition = transition
|
||||
if titleSizeChanged {
|
||||
iconTransition = .immediate
|
||||
}
|
||||
iconTransition.setFrame(view: self.iconView, frame: iconFrame)
|
||||
transition.setAlpha(view: self.iconView, alpha: isActiveWithText || !isSecure ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
if let image = self.clearIconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
transition.setFrame(view: self.clearIconView, frame: iconFrame)
|
||||
transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0))
|
||||
transition.setAlpha(view: self.clearIconView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
self.clearIconButton.isUserInteractionEnabled = isActiveWithText
|
||||
}
|
||||
|
||||
if let cancelButtonTitleComponentView = self.cancelButtonTitle.view {
|
||||
@ -343,12 +428,51 @@ final class AddressBarContentComponent: Component {
|
||||
cancelButtonTitleComponentView.isUserInteractionEnabled = false
|
||||
}
|
||||
transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize))
|
||||
transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
|
||||
|
||||
let textField: TextField
|
||||
if let current = self.textField {
|
||||
textField = current
|
||||
} else {
|
||||
textField = TextField(frame: textFieldFrame)
|
||||
textField.autocapitalizationType = .none
|
||||
textField.autocorrectionType = .no
|
||||
textField.keyboardType = .URL
|
||||
textField.returnKeyType = .go
|
||||
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
self.textField = textField
|
||||
|
||||
textField.delegate = self
|
||||
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||
}
|
||||
|
||||
var address = self.component?.url ?? ""
|
||||
if let components = URLComponents(string: address) {
|
||||
if #available(iOS 16.0, *), let encodedHost = components.encodedHost {
|
||||
if let decodedHost = components.host, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
} else if let encodedHost = components.host {
|
||||
if let decodedHost = components.host?.idnaDecoded, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let textField = self.textField {
|
||||
textField.textColor = theme.rootController.navigationSearchBar.inputTextColor
|
||||
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideTextInset - 32.0, height: backgroundFrame.height)))
|
||||
if textField.text != address {
|
||||
textField.text = address
|
||||
self.clearIconView.isHidden = address.isEmpty
|
||||
self.clearIconButton.isHidden = address.isEmpty
|
||||
self.placeholderContent.view?.isHidden = !address.isEmpty
|
||||
}
|
||||
|
||||
textField.textColor = theme.rootController.navigationSearchBar.inputTextColor
|
||||
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height)))
|
||||
transition.setAlpha(view: textField, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
textField.isUserInteractionEnabled = isActiveWithText
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,7 +480,7 @@ final class AddressBarContentComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
706
submodules/BrowserUI/Sources/BrowserAddressListComponent.swift
Normal file
706
submodules/BrowserUI/Sources/BrowserAddressListComponent.swift
Normal file
@ -0,0 +1,706 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import ContextUI
|
||||
import UndoUI
|
||||
import ListActionItemComponent
|
||||
|
||||
final class BrowserAddressListComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let insets: UIEdgeInsets
|
||||
let metrics: LayoutMetrics
|
||||
let addressBarFrame: CGRect
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
let presentInGlobalOverlay: (ViewController) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
insets: UIEdgeInsets,
|
||||
metrics: LayoutMetrics,
|
||||
addressBarFrame: CGRect,
|
||||
performAction: ActionSlot<BrowserScreen.Action>,
|
||||
presentInGlobalOverlay: @escaping (ViewController) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.insets = insets
|
||||
self.metrics = metrics
|
||||
self.addressBarFrame = addressBarFrame
|
||||
self.performAction = performAction
|
||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||
}
|
||||
|
||||
static func ==(lhs: BrowserAddressListComponent, rhs: BrowserAddressListComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
if lhs.metrics != rhs.metrics {
|
||||
return false
|
||||
}
|
||||
if lhs.addressBarFrame != rhs.addressBarFrame {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private struct ItemLayout: Equatable {
|
||||
struct Section: Equatable {
|
||||
var id: Int
|
||||
var insets: UIEdgeInsets
|
||||
var itemHeight: CGFloat
|
||||
var itemCount: Int
|
||||
var hasMore: Bool
|
||||
|
||||
var totalHeight: CGFloat
|
||||
|
||||
init(
|
||||
id: Int,
|
||||
insets: UIEdgeInsets,
|
||||
itemHeight: CGFloat,
|
||||
itemCount: Int,
|
||||
hasMore: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.insets = insets
|
||||
self.itemHeight = itemHeight
|
||||
self.itemCount = itemCount
|
||||
self.hasMore = hasMore
|
||||
|
||||
var totalHeight = insets.top + itemHeight * CGFloat(itemCount) + insets.bottom
|
||||
if hasMore {
|
||||
totalHeight -= itemHeight
|
||||
totalHeight += 44.0
|
||||
}
|
||||
self.totalHeight = totalHeight
|
||||
}
|
||||
}
|
||||
|
||||
var containerSize: CGSize
|
||||
var insets: UIEdgeInsets
|
||||
var sections: [Section]
|
||||
|
||||
var contentHeight: CGFloat
|
||||
|
||||
init(
|
||||
containerSize: CGSize,
|
||||
insets: UIEdgeInsets,
|
||||
sections: [Section]
|
||||
) {
|
||||
self.containerSize = containerSize
|
||||
self.insets = insets
|
||||
self.sections = sections
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
for section in sections {
|
||||
contentHeight += section.totalHeight
|
||||
}
|
||||
self.contentHeight = contentHeight
|
||||
}
|
||||
}
|
||||
|
||||
private final class ScrollView: UIScrollView {
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
override func touchesShouldCancel(in view: UIView) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class View: UIView, UIScrollViewDelegate {
|
||||
struct State {
|
||||
let recent: [TelegramMediaWebpage]
|
||||
let isRecentExpanded: Bool
|
||||
let bookmarks: [Message]
|
||||
}
|
||||
|
||||
private let outerView = UIButton()
|
||||
private let shadowView = UIImageView()
|
||||
private let backgroundView = UIView()
|
||||
private let scrollView = ScrollView()
|
||||
private let itemContainerView = UIView()
|
||||
|
||||
private let addressTemplateItem = ComponentView<Empty>()
|
||||
|
||||
private var visibleSectionHeaders: [Int: ComponentView<Empty>] = [:]
|
||||
private var visibleItems: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
private var ignoreScrolling: Bool = false
|
||||
|
||||
private var component: BrowserAddressListComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
private var itemLayout: ItemLayout?
|
||||
|
||||
private var stateDisposable: Disposable?
|
||||
private var stateValue: State?
|
||||
private let isRecentExpanded = ValuePromise<Bool>(false)
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundView.clipsToBounds = true
|
||||
|
||||
self.scrollView.alwaysBounceVertical = true
|
||||
self.scrollView.delegate = self
|
||||
self.scrollView.showsVerticalScrollIndicator = false
|
||||
|
||||
self.addSubview(self.outerView)
|
||||
self.addSubview(self.shadowView)
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.addSubview(self.scrollView)
|
||||
self.scrollView.addSubview(self.itemContainerView)
|
||||
|
||||
self.outerView.addTarget(self, action: #selector(self.outerPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.stateDisposable?.dispose()
|
||||
}
|
||||
|
||||
@objc private func outerPressed() {
|
||||
self.component?.performAction.invoke(.closeAddressBar)
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if !self.ignoreScrolling {
|
||||
self.updateScrolling(transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
self.window?.endEditing(true)
|
||||
|
||||
cancelContextGestures(view: scrollView)
|
||||
}
|
||||
|
||||
private func updateScrolling(transition: ComponentTransition) {
|
||||
guard let component = self.component, let itemLayout = self.itemLayout, let state = self.stateValue else {
|
||||
return
|
||||
}
|
||||
|
||||
var topOffset = -self.scrollView.bounds.minY
|
||||
topOffset = max(0.0, topOffset)
|
||||
|
||||
let visibleBounds = self.scrollView.bounds
|
||||
var visibleFrame = self.scrollView.frame
|
||||
visibleFrame.origin.x = 0.0
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
var validSectionHeaders: [AnyHashable] = []
|
||||
var sectionOffset: CGFloat = 0.0
|
||||
|
||||
let sideInset: CGFloat = 0.0
|
||||
let containerInset: CGFloat = 0.0
|
||||
|
||||
for sectionIndex in 0 ..< itemLayout.sections.count {
|
||||
let section = itemLayout.sections[sectionIndex]
|
||||
|
||||
do {
|
||||
var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
||||
|
||||
let sectionHeaderMinY = topOffset + containerInset
|
||||
let sectionHeaderMaxY = containerInset + sectionOffset - self.scrollView.bounds.minY + section.totalHeight - 28.0
|
||||
|
||||
sectionHeaderFrame.origin.y = max(sectionHeaderFrame.origin.y, sectionHeaderMinY)
|
||||
sectionHeaderFrame.origin.y = min(sectionHeaderFrame.origin.y, sectionHeaderMaxY)
|
||||
|
||||
if visibleFrame.intersects(sectionHeaderFrame) {
|
||||
validSectionHeaders.append(section.id)
|
||||
let sectionHeader: ComponentView<Empty>
|
||||
var sectionHeaderTransition = transition
|
||||
if let current = self.visibleSectionHeaders[section.id] {
|
||||
sectionHeader = current
|
||||
} else {
|
||||
if !transition.animation.isImmediate {
|
||||
sectionHeaderTransition = .immediate
|
||||
}
|
||||
sectionHeader = ComponentView()
|
||||
self.visibleSectionHeaders[section.id] = sectionHeader
|
||||
}
|
||||
|
||||
let sectionTitle: String
|
||||
if section.id == 0 {
|
||||
sectionTitle = component.strings.WebBrowser_AddressBar_RecentlyVisited
|
||||
} else if section.id == 1 {
|
||||
sectionTitle = component.strings.WebBrowser_AddressBar_Bookmarks
|
||||
} else {
|
||||
sectionTitle = ""
|
||||
}
|
||||
|
||||
let _ = sectionHeader.update(
|
||||
transition: sectionHeaderTransition,
|
||||
component: AnyComponent(SectionHeaderComponent(
|
||||
theme: component.theme,
|
||||
style: .plain,
|
||||
title: sectionTitle,
|
||||
insets: component.insets,
|
||||
actionTitle: section.id == 0 ? component.strings.WebBrowser_AddressBar_RecentlyVisited_Clear : nil,
|
||||
action: { [weak self] in
|
||||
if let self, let component = self.component {
|
||||
let _ = clearRecentlyVisitedLinks(engine: component.context.engine).start()
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: sectionHeaderFrame.size
|
||||
)
|
||||
if let sectionHeaderView = sectionHeader.view {
|
||||
if sectionHeaderView.superview == nil {
|
||||
self.backgroundView.addSubview(sectionHeaderView)
|
||||
|
||||
if !transition.animation.isImmediate {
|
||||
sectionHeaderView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
}
|
||||
let sectionXOffset = self.scrollView.frame.minX
|
||||
sectionHeaderTransition.setFrame(view: sectionHeaderView, frame: sectionHeaderFrame.offsetBy(dx: sectionXOffset, dy: 0.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0 ..< section.itemCount {
|
||||
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset + section.insets.top + CGFloat(i) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
||||
if !visibleBounds.intersects(itemFrame) {
|
||||
continue
|
||||
}
|
||||
|
||||
var isMore = false
|
||||
if section.hasMore && i == 3 {
|
||||
isMore = true
|
||||
itemFrame.size.height = 44.0
|
||||
}
|
||||
|
||||
var id: String = ""
|
||||
if section.id == 0 {
|
||||
id = "recent_\(state.recent[i].content.url ?? "")"
|
||||
if isMore {
|
||||
id = "recent_more"
|
||||
}
|
||||
} else if section.id == 1 {
|
||||
id = "bookmark_\(state.bookmarks[i].id.id)"
|
||||
if isMore {
|
||||
id = "bookmark_more"
|
||||
}
|
||||
}
|
||||
|
||||
let itemId = AnyHashable(id)
|
||||
validIds.append(itemId)
|
||||
|
||||
var itemTransition = transition
|
||||
let visibleItem: ComponentView<Empty>
|
||||
if let current = self.visibleItems[itemId] {
|
||||
visibleItem = current
|
||||
} else {
|
||||
visibleItem = ComponentView()
|
||||
if !transition.animation.isImmediate {
|
||||
itemTransition = .immediate
|
||||
}
|
||||
self.visibleItems[itemId] = visibleItem
|
||||
}
|
||||
|
||||
if isMore {
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(
|
||||
ListActionItemComponent(
|
||||
theme: component.theme,
|
||||
title: AnyComponent(Text(
|
||||
text: component.strings.WebBrowser_AddressBar_ShowMore,
|
||||
font: Font.regular(17.0),
|
||||
color: component.theme.list.itemAccentColor
|
||||
)),
|
||||
leftIcon: .custom(
|
||||
AnyComponentWithIdentity(
|
||||
id: "icon",
|
||||
component: AnyComponent(Image(
|
||||
image: PresentationResourcesItemList.downArrowImage(component.theme),
|
||||
size: CGSize(width: 30.0, height: 30.0)
|
||||
))
|
||||
),
|
||||
false
|
||||
),
|
||||
accessory: nil,
|
||||
action: { [weak self] _ in
|
||||
self?.isRecentExpanded.set(true)
|
||||
},
|
||||
highlighting: .default,
|
||||
updateIsHighlighted: { view, _ in
|
||||
|
||||
})
|
||||
),
|
||||
environment: {},
|
||||
containerSize: itemFrame.size
|
||||
)
|
||||
} else {
|
||||
var webPage: TelegramMediaWebpage?
|
||||
var itemMessage: Message?
|
||||
|
||||
if section.id == 0 {
|
||||
webPage = state.recent[i]
|
||||
} else if section.id == 1 {
|
||||
let message = state.bookmarks[i]
|
||||
if let primaryUrl = getPrimaryUrl(message: message) {
|
||||
if let media = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage {
|
||||
webPage = media
|
||||
} else {
|
||||
webPage = TelegramMediaWebpage(webpageId: MediaId(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: primaryUrl, displayUrl: "", hash: 0, type: nil, websiteName: "", title: message.text, text: "", embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil)))
|
||||
}
|
||||
itemMessage = message
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
let performAction = component.performAction
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(
|
||||
BrowserAddressListItemComponent(
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
webPage: webPage!,
|
||||
message: itemMessage,
|
||||
hasNext: true,
|
||||
insets: component.insets,
|
||||
action: {
|
||||
if let url = webPage?.content.url {
|
||||
performAction.invoke(.navigateTo(url, false))
|
||||
}
|
||||
},
|
||||
contextAction: { [weak self] webPage, message, sourceView, gesture in
|
||||
guard let self, let component = self.component, let url = webPage.content.url else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var itemList: [ContextMenuItem] = []
|
||||
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_CopyLink, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
UIPasteboard.general.string = url
|
||||
if let self, let component = self.component {
|
||||
component.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
|
||||
}
|
||||
})))
|
||||
|
||||
if let message {
|
||||
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let self, let component = self.component {
|
||||
let _ = component.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).startStandalone()
|
||||
}
|
||||
})))
|
||||
} else {
|
||||
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_RemoveRecent, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let self, let component = self.component, let url = webPage.content.url {
|
||||
let _ = removeRecentlyVisitedLink(engine: component.context.engine, url: url).startStandalone()
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
let controller = ContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(BrowserAddressListContextExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(items),
|
||||
recognizer: nil,
|
||||
gesture: gesture
|
||||
)
|
||||
component.presentInGlobalOverlay(controller)
|
||||
})
|
||||
),
|
||||
environment: {},
|
||||
containerSize: itemFrame.size
|
||||
)
|
||||
}
|
||||
if let itemView = visibleItem.view {
|
||||
if itemView.superview == nil {
|
||||
self.itemContainerView.addSubview(itemView)
|
||||
if !transition.animation.isImmediate {
|
||||
transition.animateAlpha(view: itemView, from: 0.0, to: 1.0)
|
||||
}
|
||||
}
|
||||
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
||||
}
|
||||
}
|
||||
|
||||
sectionOffset += section.totalHeight
|
||||
}
|
||||
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, item) in self.visibleItems {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
if let itemView = item.view {
|
||||
if !transition.animation.isImmediate {
|
||||
itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.visibleItems.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
var removeSectionHeaderIds: [Int] = []
|
||||
for (id, item) in self.visibleSectionHeaders {
|
||||
if !validSectionHeaders.contains(id) {
|
||||
removeSectionHeaderIds.append(id)
|
||||
if let itemView = item.view {
|
||||
if !transition.animation.isImmediate {
|
||||
itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeSectionHeaderIds {
|
||||
self.visibleSectionHeaders.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: BrowserAddressListComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let themeUpdated = self.component?.theme !== component.theme
|
||||
|
||||
if self.component == nil {
|
||||
self.stateDisposable = combineLatest(queue: Queue.mainQueue(),
|
||||
recentlyVisitedLinks(engine: component.context.engine),
|
||||
self.isRecentExpanded.get(),
|
||||
component.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: component.context.account.peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 100, fixedCombinedReadStates: nil, tag: .tag(.webPage))
|
||||
).start(next: { [weak self] recent, isRecentExpanded, view in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
var bookmarks: [Message] = []
|
||||
for entry in view.0.entries.reversed() {
|
||||
bookmarks.append(entry.message)
|
||||
}
|
||||
|
||||
let isFirstTime = self.stateValue == nil
|
||||
self.stateValue = State(
|
||||
recent: recent,
|
||||
isRecentExpanded: isRecentExpanded,
|
||||
bookmarks: bookmarks
|
||||
)
|
||||
self.state?.updated(transition: isFirstTime ? .immediate : .easeInOut(duration: 0.25))
|
||||
})
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
self.outerView.isHidden = !component.metrics.isTablet
|
||||
self.outerView.frame = CGRect(origin: .zero, size: availableSize)
|
||||
|
||||
let containerFrame: CGRect
|
||||
if component.metrics.isTablet {
|
||||
let containerSize = CGSize(width: component.addressBarFrame.width + 32.0, height: 540.0)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 72.0), size: containerSize)
|
||||
|
||||
self.backgroundView.layer.cornerRadius = 10.0
|
||||
} else {
|
||||
containerFrame = CGRect(origin: .zero, size: availableSize)
|
||||
|
||||
self.backgroundView.layer.cornerRadius = 0.0
|
||||
}
|
||||
|
||||
let resetScrolling = self.scrollView.bounds.width != containerFrame.width
|
||||
if themeUpdated {
|
||||
self.backgroundView.backgroundColor = component.theme.list.plainBackgroundColor
|
||||
}
|
||||
|
||||
let itemsContainerWidth = availableSize.width
|
||||
let addressItemSize = self.addressTemplateItem.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BrowserAddressListItemComponent(
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
webPage: TelegramMediaWebpage(webpageId: EngineMedia.Id(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: "https://telegram.org", displayUrl: "https://telegram.org", hash: 0, type: nil, websiteName: "Telegram", title: "Telegram Telegram", text: "Telegram", embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, image: nil, file: nil, story: nil, attributes: [], instantPage: nil))),
|
||||
message: nil,
|
||||
hasNext: true,
|
||||
insets: .zero,
|
||||
action: {},
|
||||
contextAction: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
|
||||
)
|
||||
|
||||
var sections: [ItemLayout.Section] = []
|
||||
if let state = self.stateValue {
|
||||
if !state.recent.isEmpty {
|
||||
var recentCount = state.recent.count
|
||||
var hasMore = false
|
||||
if recentCount > 4 && !state.isRecentExpanded {
|
||||
recentCount = 4
|
||||
hasMore = true
|
||||
}
|
||||
sections.append(ItemLayout.Section(
|
||||
id: 0,
|
||||
insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0),
|
||||
itemHeight: addressItemSize.height,
|
||||
itemCount: recentCount,
|
||||
hasMore: hasMore
|
||||
))
|
||||
}
|
||||
if !state.bookmarks.isEmpty {
|
||||
sections.append(ItemLayout.Section(
|
||||
id: 1,
|
||||
insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0),
|
||||
itemHeight: addressItemSize.height,
|
||||
itemCount: state.bookmarks.count,
|
||||
hasMore: false
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let itemLayout = ItemLayout(containerSize: containerFrame.size, insets: .zero, sections: sections)
|
||||
self.itemLayout = itemLayout
|
||||
|
||||
let containerWidth = containerFrame.size.width
|
||||
let scrollContentHeight = max(itemLayout.contentHeight, containerFrame.size.height)
|
||||
|
||||
self.ignoreScrolling = true
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: containerFrame.size))
|
||||
let contentSize = CGSize(width: containerWidth, height: scrollContentHeight)
|
||||
if contentSize != self.scrollView.contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
if resetScrolling {
|
||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: containerFrame.size.height))
|
||||
}
|
||||
self.ignoreScrolling = false
|
||||
self.updateScrolling(transition: transition)
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: containerFrame)
|
||||
transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: containerWidth, height: scrollContentHeight)))
|
||||
|
||||
if component.metrics.isTablet {
|
||||
transition.setFrame(view: self.shadowView, frame: containerFrame.insetBy(dx: -60.0, dy: -60.0))
|
||||
self.shadowView.isHidden = false
|
||||
if self.shadowView.image == nil {
|
||||
self.shadowView.image = generateShadowImage()
|
||||
}
|
||||
} else {
|
||||
self.shadowView.isHidden = true
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if let component = self.component, component.metrics.isTablet {
|
||||
let addressFrame = CGRect(origin: CGPoint(x: self.backgroundView.frame.minX, y: self.backgroundView.frame.minY - 48.0), size: CGSize(width: self.backgroundView.frame.width, height: 48.0))
|
||||
if addressFrame.contains(point) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func generateShadowImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 140.0, height: 140.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.saveGState()
|
||||
context.setShadow(offset: CGSize(), blur: 60.0, color: UIColor(white: 0.0, alpha: 0.4).cgColor)
|
||||
|
||||
let path = UIBezierPath(roundedRect: CGRect(x: 60.0, y: 60.0, width: 20.0, height: 20.0), cornerRadius: 10.0).cgPath
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
||||
context.restoreGState()
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
})?.stretchableImage(withLeftCapWidth: 70, topCapHeight: 70)
|
||||
}
|
||||
|
||||
private final class BrowserAddressListContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = false
|
||||
let blurBackground: Bool = true
|
||||
|
||||
private let contentView: ContextExtractedContentContainingView
|
||||
|
||||
init(contentView: ContextExtractedContentContainingView) {
|
||||
self.contentView = contentView
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .view(self.contentView), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelContextGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for gesture in gestureRecognizers {
|
||||
if let gesture = gesture as? ContextGesture {
|
||||
gesture.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
for subview in view.subviews {
|
||||
cancelContextGestures(view: subview)
|
||||
}
|
||||
}
|
@ -0,0 +1,380 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import MultilineTextComponent
|
||||
import TelegramPresentationData
|
||||
import PhotoResources
|
||||
import AccountContext
|
||||
import ContextUI
|
||||
import UrlEscaping
|
||||
|
||||
private let iconFont = Font.with(size: 30.0, design: .round, weight: .bold)
|
||||
private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radius: 6.0, color: UIColor(rgb: 0xFF9500))
|
||||
|
||||
final class BrowserAddressListItemComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let webPage: TelegramMediaWebpage
|
||||
var message: Message?
|
||||
let hasNext: Bool
|
||||
let insets: UIEdgeInsets
|
||||
let action: () -> Void
|
||||
let contextAction: ((TelegramMediaWebpage, Message?, ContextExtractedContentContainingView, ContextGesture) -> Void)?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
webPage: TelegramMediaWebpage,
|
||||
message: Message?,
|
||||
hasNext: Bool,
|
||||
insets: UIEdgeInsets,
|
||||
action: @escaping () -> Void,
|
||||
contextAction: ((TelegramMediaWebpage, Message?, ContextExtractedContentContainingView, ContextGesture) -> Void)?
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.webPage = webPage
|
||||
self.message = message
|
||||
self.hasNext = hasNext
|
||||
self.insets = insets
|
||||
self.action = action
|
||||
self.contextAction = contextAction
|
||||
}
|
||||
|
||||
static func ==(lhs: BrowserAddressListItemComponent, rhs: BrowserAddressListItemComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.webPage != rhs.webPage {
|
||||
return false
|
||||
}
|
||||
if lhs.hasNext != rhs.hasNext {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: ContextControllerSourceView {
|
||||
private let extractedContainerView = ContextExtractedContentContainingView()
|
||||
private let containerButton = HighlightTrackingButton()
|
||||
|
||||
private let separatorLayer = SimpleLayer()
|
||||
private var highlightedBackgroundLayer = SimpleLayer()
|
||||
private var emptyIcon: UIImageView?
|
||||
private var emptyLabel: ComponentView<Empty>?
|
||||
private var icon = TransformImageNode()
|
||||
private let title = ComponentView<Empty>()
|
||||
private let subtitle = ComponentView<Empty>()
|
||||
|
||||
private var isExtractedToContextMenu: Bool = false
|
||||
|
||||
private var component: BrowserAddressListItemComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
private var currentIconImageRepresentation: TelegramMediaImageRepresentation?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.layer.addSublayer(self.separatorLayer)
|
||||
self.layer.addSublayer(self.highlightedBackgroundLayer)
|
||||
|
||||
self.addSubview(self.extractedContainerView)
|
||||
self.targetViewForActivationProgress = self.extractedContainerView.contentView
|
||||
|
||||
self.highlightedBackgroundLayer.opacity = 0.0
|
||||
|
||||
self.extractedContainerView.contentView.addSubview(self.containerButton)
|
||||
|
||||
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
|
||||
self.containerButton.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if highlighted {
|
||||
self.superview?.bringSubviewToFront(self)
|
||||
self.highlightedBackgroundLayer.removeAnimation(forKey: "opacity")
|
||||
self.highlightedBackgroundLayer.opacity = 1.0
|
||||
} else {
|
||||
self.highlightedBackgroundLayer.opacity = 0.0
|
||||
self.highlightedBackgroundLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
self.extractedContainerView.isExtractedToContextPreviewUpdated = { [weak self] value in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
self.containerButton.clipsToBounds = value
|
||||
self.containerButton.backgroundColor = value ? component.theme.list.plainBackgroundColor : nil
|
||||
self.containerButton.layer.cornerRadius = value ? 10.0 : 0.0
|
||||
|
||||
if value {
|
||||
self.highlightedBackgroundLayer.opacity = 0.0
|
||||
}
|
||||
}
|
||||
self.extractedContainerView.willUpdateIsExtractedToContextPreview = { [weak self] value, transition in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.isExtractedToContextMenu = value
|
||||
|
||||
let mappedTransition: ComponentTransition
|
||||
if value {
|
||||
mappedTransition = ComponentTransition(transition)
|
||||
} else {
|
||||
mappedTransition = ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
self.state?.updated(transition: mappedTransition)
|
||||
}
|
||||
|
||||
self.activated = { [weak self] gesture, _ in
|
||||
guard let self, let component = self.component else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
component.contextAction?(component.webPage, component.message, self.extractedContainerView, gesture)
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.action()
|
||||
}
|
||||
|
||||
func update(component: BrowserAddressListItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let themeUpdated = self.component?.theme !== component.theme
|
||||
let currentIconImageRepresentation = self.currentIconImageRepresentation
|
||||
|
||||
let iconSize = CGSize(width: 40.0, height: 40.0)
|
||||
let height: CGFloat = 60.0
|
||||
let leftInset: CGFloat = component.insets.left + 11.0 + iconSize.width + 11.0
|
||||
let rightInset: CGFloat = 16.0
|
||||
let titleSpacing: CGFloat = 2.0
|
||||
let contextInset: CGFloat = self.isExtractedToContextMenu ? 12.0 : 0.0
|
||||
|
||||
let title: String
|
||||
let subtitle: String
|
||||
var parsedUrl: URL?
|
||||
var iconImageReferenceAndRepresentation: (AnyMediaReference, TelegramMediaImageRepresentation)?
|
||||
var updateIconImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||
|
||||
if case let .Loaded(content) = component.webPage.content {
|
||||
title = content.title ?? content.url
|
||||
|
||||
var address = content.url
|
||||
if let components = URLComponents(string: address) {
|
||||
if #available(iOS 16.0, *), let encodedHost = components.encodedHost {
|
||||
if let decodedHost = components.host, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
} else if let encodedHost = components.host {
|
||||
if let decodedHost = components.host?.idnaDecoded, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
address = address.replacingOccurrences(of: "https://www.", with: "")
|
||||
address = address.replacingOccurrences(of: "https://", with: "")
|
||||
address = address.replacingOccurrences(of: "tonsite://", with: "")
|
||||
address = address.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
subtitle = address
|
||||
|
||||
parsedUrl = URL(string: content.url)
|
||||
|
||||
if let image = content.image {
|
||||
if let representation = imageRepresentationLargerThan(image.representations, size: PixelDimensions(width: 80, height: 80)) {
|
||||
if let message = component.message {
|
||||
iconImageReferenceAndRepresentation = (.message(message: MessageReference(message), media: image), representation)
|
||||
} else {
|
||||
iconImageReferenceAndRepresentation = (.standalone(media: image), representation)
|
||||
}
|
||||
}
|
||||
} else if let file = content.file {
|
||||
if let representation = smallestImageRepresentation(file.previewRepresentations) {
|
||||
if let message = component.message {
|
||||
iconImageReferenceAndRepresentation = (.message(message: MessageReference(message), media: file), representation)
|
||||
} else {
|
||||
iconImageReferenceAndRepresentation = (.standalone(media: file), representation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if currentIconImageRepresentation != iconImageReferenceAndRepresentation?.1 {
|
||||
if let iconImageReferenceAndRepresentation = iconImageReferenceAndRepresentation {
|
||||
if let imageReference = iconImageReferenceAndRepresentation.0.concrete(TelegramMediaImage.self) {
|
||||
updateIconImageSignal = chatWebpageSnippetPhoto(account: component.context.account, userLocation: (component.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, photoReference: imageReference)
|
||||
} else if let fileReference = iconImageReferenceAndRepresentation.0.concrete(TelegramMediaFile.self) {
|
||||
updateIconImageSignal = chatWebpageSnippetFile(account: component.context.account, userLocation: (component.message?.id.peerId).flatMap(MediaResourceUserLocation.peer) ?? .other, mediaReference: fileReference.abstract, representation: iconImageReferenceAndRepresentation.1)
|
||||
}
|
||||
} else {
|
||||
updateIconImageSignal = .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
title = ""
|
||||
subtitle = ""
|
||||
}
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
self.currentIconImageRepresentation = iconImageReferenceAndRepresentation?.1
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: title, font: Font.semibold(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||
)
|
||||
let subtitleSize = self.subtitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: component.theme.list.itemAccentColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||
)
|
||||
|
||||
let centralContentHeight = titleSize.height + subtitleSize.height + titleSpacing
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - centralContentHeight) / 2.0)), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: leftInset, y: titleFrame.maxY + titleSpacing), size: subtitleSize)
|
||||
if let subtitleView = self.subtitle.view {
|
||||
if subtitleView.superview == nil {
|
||||
subtitleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(subtitleView)
|
||||
}
|
||||
subtitleView.frame = subtitleFrame
|
||||
}
|
||||
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: 11.0 + component.insets.left, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
|
||||
|
||||
let iconImageLayout = self.icon.asyncLayout()
|
||||
var iconImageApply: (() -> Void)?
|
||||
if let iconImageReferenceAndRepresentation = iconImageReferenceAndRepresentation {
|
||||
let imageCorners = ImageCorners(radius: 6.0)
|
||||
let arguments = TransformImageArguments(corners: imageCorners, imageSize: iconImageReferenceAndRepresentation.1.dimensions.cgSize.aspectFilled(iconSize), boundingSize: iconSize, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor)
|
||||
iconImageApply = iconImageLayout(arguments)
|
||||
}
|
||||
|
||||
if let iconImageApply = iconImageApply {
|
||||
if let updateImageSignal = updateIconImageSignal {
|
||||
self.icon.setSignal(updateImageSignal)
|
||||
}
|
||||
|
||||
if self.icon.supernode == nil {
|
||||
self.containerButton.addSubview(self.icon.view)
|
||||
self.icon.frame = iconFrame
|
||||
} else {
|
||||
transition.setFrame(view: self.icon.view, frame: iconFrame)
|
||||
}
|
||||
|
||||
iconImageApply()
|
||||
|
||||
if let emptyIcon = self.emptyIcon {
|
||||
self.emptyIcon = nil
|
||||
emptyIcon.removeFromSuperview()
|
||||
}
|
||||
if let emptyLabel = self.emptyLabel {
|
||||
self.emptyLabel = nil
|
||||
emptyLabel.view?.removeFromSuperview()
|
||||
}
|
||||
} else {
|
||||
if self.icon.supernode != nil {
|
||||
self.icon.view.removeFromSuperview()
|
||||
}
|
||||
|
||||
let icon: UIImageView
|
||||
let label: ComponentView<Empty>
|
||||
if let currentEmptyIcon = self.emptyIcon, let currentEmptyLabel = self.emptyLabel {
|
||||
icon = currentEmptyIcon
|
||||
label = currentEmptyLabel
|
||||
} else {
|
||||
icon = UIImageView()
|
||||
icon.image = iconTextBackgroundImage
|
||||
self.containerButton.addSubview(icon)
|
||||
|
||||
label = ComponentView()
|
||||
}
|
||||
icon.frame = iconFrame
|
||||
|
||||
var iconText = ""
|
||||
if let parsedUrl, let host = parsedUrl.host {
|
||||
if parsedUrl.path.hasPrefix("/addstickers/") {
|
||||
iconText = "S"
|
||||
} else if parsedUrl.path.hasPrefix("/addemoji/") {
|
||||
iconText = "E"
|
||||
} else {
|
||||
iconText = host[..<host.index(after: host.startIndex)].uppercased()
|
||||
}
|
||||
}
|
||||
|
||||
let labelSize = label.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(text: iconText, font: iconFont, color: .white)),
|
||||
environment: {},
|
||||
containerSize: iconSize
|
||||
)
|
||||
let labelFrame = CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - labelSize.width) / 2.0), y: iconFrame.minY + floorToScreenPixels((iconFrame.height - labelSize.height) / 2.0)), size: labelSize)
|
||||
if let labelView = label.view {
|
||||
if labelView.superview == nil {
|
||||
self.containerButton.addSubview(labelView)
|
||||
}
|
||||
labelView.frame = labelFrame
|
||||
}
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
self.highlightedBackgroundLayer.backgroundColor = component.theme.list.itemHighlightedBackgroundColor.cgColor
|
||||
self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
||||
}
|
||||
transition.setFrame(layer: self.highlightedBackgroundLayer, frame: CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: height + UIScreenPixel)))
|
||||
transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: leftInset, y: height), size: CGSize(width: availableSize.width - leftInset, height: UIScreenPixel)))
|
||||
self.separatorLayer.isHidden = !component.hasNext
|
||||
|
||||
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: height))
|
||||
transition.setFrame(view: self.containerButton, frame: containerFrame.insetBy(dx: contextInset, dy: 0.0))
|
||||
|
||||
transition.setFrame(view: self.extractedContainerView, frame: containerFrame)
|
||||
transition.setFrame(view: self.extractedContainerView.contentView, frame: containerFrame)
|
||||
self.extractedContainerView.contentRect = containerFrame
|
||||
|
||||
self.isGestureEnabled = component.contextAction != nil
|
||||
|
||||
return CGSize(width: availableSize.width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
582
submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift
Normal file
582
submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift
Normal file
@ -0,0 +1,582 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AccountContext
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ChatControllerInteraction
|
||||
import TelegramUIPreferences
|
||||
import ChatPresentationInterfaceState
|
||||
import TextFormat
|
||||
import UrlWhitelist
|
||||
import SearchUI
|
||||
import SearchBarNode
|
||||
import ChatHistorySearchContainerNode
|
||||
import ContextUI
|
||||
import UndoUI
|
||||
|
||||
public final class BrowserBookmarksScreen: ViewController {
|
||||
final class Node: ViewControllerTracingNode, ASScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private weak var controller: BrowserBookmarksScreen?
|
||||
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
private var searchDisplayController: SearchDisplayController?
|
||||
|
||||
fileprivate let historyNode: ChatHistoryListNode
|
||||
private let bottomPanelNode: BottomPanelNode
|
||||
|
||||
private var addedBookmark = false
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat)?
|
||||
|
||||
init(context: AccountContext, controller: BrowserBookmarksScreen, presentationData: PresentationData) {
|
||||
self.context = context
|
||||
self.controller = controller
|
||||
self.presentationData = presentationData
|
||||
|
||||
var openMessageImpl: ((Message) -> Bool)?
|
||||
var openContextMenuImpl: ((Message, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void)?
|
||||
self.controllerInteraction = ChatControllerInteraction(openMessage: { message, _ in
|
||||
if let openMessageImpl = openMessageImpl {
|
||||
return openMessageImpl(message)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}, openPeer: { _, _, _, _ in
|
||||
}, openPeerMention: { _, _ in
|
||||
}, openMessageContextMenu: { message, _, sourceView, rect, gesture, _ in
|
||||
openContextMenuImpl?(message, sourceView, rect, gesture)
|
||||
}, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _, _, _ in
|
||||
}, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in
|
||||
}, navigateToMessage: { _, _, _ in
|
||||
}, navigateToMessageStandalone: { _ in
|
||||
}, navigateToThreadMessage: { _, _, _ in
|
||||
}, tapMessage: nil, clickThroughMessage: { _, _ in
|
||||
}, toggleMessagesSelection: { _, _ in
|
||||
}, sendCurrentMessage: { _, _ in
|
||||
}, sendMessage: { _ in
|
||||
}, sendSticker: { _, _, _, _, _, _, _, _, _ in
|
||||
return false
|
||||
}, sendEmoji: { _, _, _ in
|
||||
}, sendGif: { _, _, _, _, _ in
|
||||
return false
|
||||
}, sendBotContextResultAsGif: { _, _, _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _, _ in
|
||||
}, requestMessageActionUrlAuth: { _, _ in
|
||||
}, activateSwitchInline: { _, _, _ in
|
||||
}, openUrl: { [weak controller] url in
|
||||
if let controller {
|
||||
controller.openUrl(url.url)
|
||||
controller.dismiss()
|
||||
}
|
||||
}, shareCurrentLocation: {
|
||||
}, shareAccountContact: {
|
||||
}, sendBotCommand: { _, _ in
|
||||
}, openInstantPage: { message, _ in
|
||||
if let openMessageImpl = openMessageImpl {
|
||||
let _ = openMessageImpl(message)
|
||||
}
|
||||
}, openWallpaper: { _ in
|
||||
}, openTheme: {_ in
|
||||
}, openHashtag: { _, _ in
|
||||
}, updateInputState: { _ in
|
||||
}, updateInputMode: { _ in
|
||||
}, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in
|
||||
}, presentControllerInCurrent: { _, _ in
|
||||
}, navigationController: {
|
||||
return nil
|
||||
}, chatControllerNode: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in
|
||||
}, callPeer: { _, _ in
|
||||
}, longTap: { _, _ in
|
||||
}, openCheckoutOrReceipt: { _, _ in
|
||||
}, openSearch: {
|
||||
}, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, canSendMessages: {
|
||||
return false
|
||||
}, navigateToFirstDateMessage: { _, _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _, _ in
|
||||
}, requestSelectMessagePollOptions: { _, _ in
|
||||
}, requestOpenMessagePollResults: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, displayMessageTooltip: { _, _, _, _, _ in
|
||||
}, seekToTimecode: { _, _, _ in
|
||||
}, scheduleCurrentMessage: { _ in
|
||||
}, sendScheduledMessagesNow: { _ in
|
||||
}, editScheduledMessagesTime: { _ in
|
||||
}, performTextSelectionAction: { _, _, _, _ in
|
||||
}, displayImportedMessageTooltip: { _ in
|
||||
}, displaySwipeToReplyHint: {
|
||||
}, dismissReplyMarkupMessage: { _ in
|
||||
}, openMessagePollResults: { _, _ in
|
||||
}, openPollCreation: { _ in
|
||||
}, displayPollSolution: { _, _ in
|
||||
}, displayPsa: { _, _ in
|
||||
}, displayDiceTooltip: { _ in
|
||||
}, animateDiceSuccess: { _, _ in
|
||||
}, displayPremiumStickerTooltip: { _, _ in
|
||||
}, displayEmojiPackTooltip: { _, _ in
|
||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageReplies: { _, _, _ in
|
||||
}, openReplyThreadOriginalMessage: { _ in
|
||||
}, openMessageStats: { _ in
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, copyText: { _ in
|
||||
}, displayUndo: { _ in
|
||||
}, isAnimatingMessage: { _ in
|
||||
return false
|
||||
}, getMessageTransitionNode: {
|
||||
return nil
|
||||
}, updateChoosingSticker: { _ in
|
||||
}, commitEmojiInteraction: { _, _, _, _ in
|
||||
}, openLargeEmojiInfo: { _, _, _ in
|
||||
}, openJoinLink: { _ in
|
||||
}, openWebView: { _, _, _, _ in
|
||||
}, activateAdAction: { _, _ in
|
||||
}, openRequestedPeerSelection: { _, _, _, _ in
|
||||
}, saveMediaToFiles: { _ in
|
||||
}, openNoAdsDemo: {
|
||||
}, openAdsInfo: {
|
||||
}, displayGiveawayParticipationStatus: { _ in
|
||||
}, openPremiumStatusInfo: { _, _, _, _ in
|
||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||
}, openGroupBoostInfo: { _, _ in
|
||||
}, openStickerEditor: {
|
||||
}, openAgeRestrictedMessageMedia: { _, _ in
|
||||
}, playMessageEffect: { _ in
|
||||
}, editMessageFactCheck: { _ in
|
||||
}, requestMessageUpdate: { _, _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, dismissTextInput: {
|
||||
}, scrollToMessageId: { _ in
|
||||
}, navigateToStory: { _, _ in
|
||||
}, attemptedNavigationToPrivateQuote: { _ in
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
||||
|
||||
|
||||
let tagMask: MessageTags = .webPage
|
||||
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
self.historyNode = context.sharedContext.makeChatHistoryListNode(
|
||||
context: context,
|
||||
updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData),
|
||||
chatLocation: .peer(id: context.account.peerId),
|
||||
chatLocationContextHolder: chatLocationContextHolder,
|
||||
tag: .tag(tagMask),
|
||||
source: .default,
|
||||
subject: nil,
|
||||
controllerInteraction: self.controllerInteraction,
|
||||
selectedMessages: .single(nil),
|
||||
mode: .list(
|
||||
search: false,
|
||||
reversed: false,
|
||||
reverseGroups: false,
|
||||
displayHeaders: .none,
|
||||
hintLinks: true,
|
||||
isGlobalSearch: false
|
||||
)
|
||||
)
|
||||
|
||||
var addBookmarkImpl: (() -> Void)?
|
||||
self.bottomPanelNode = BottomPanelNode(theme: presentationData.theme, strings: presentationData.strings, action: {
|
||||
addBookmarkImpl?()
|
||||
})
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.addSubnode(self.historyNode)
|
||||
self.addSubnode(self.bottomPanelNode)
|
||||
|
||||
openMessageImpl = { [weak controller] message in
|
||||
guard let controller else {
|
||||
return false
|
||||
}
|
||||
if let primaryUrl = getPrimaryUrl(message: message) {
|
||||
controller.openUrl(primaryUrl)
|
||||
}
|
||||
controller.dismiss()
|
||||
return true
|
||||
}
|
||||
|
||||
addBookmarkImpl = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.controller?.addBookmark()
|
||||
self.addedBookmark = true
|
||||
if let (layout, navigationBarHeight, actualNavigationBarHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
openContextMenuImpl = { [weak self] message, sourceNode, rect, gesture in
|
||||
guard let self, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var itemList: [ContextMenuItem] = []
|
||||
if let webPage = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, let url = webPage.content.url {
|
||||
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_CopyLink, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
UIPasteboard.general.string = url
|
||||
if let self {
|
||||
self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
}
|
||||
})))
|
||||
}
|
||||
itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.WebBrowser_DeleteBookmark, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||
}, action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if let self {
|
||||
let _ = self.context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).startStandalone()
|
||||
}
|
||||
})))
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
let controller = ContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(BrowserBookmarksContextExtractedContentSource(contentNode: sourceNode)),
|
||||
items: .single(items),
|
||||
recognizer: nil,
|
||||
gesture: gesture as? ContextGesture
|
||||
)
|
||||
self.controller?.presentInGlobalOverlay(controller)
|
||||
}
|
||||
}
|
||||
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode) {
|
||||
guard let (layout, navigationBarHeight, _) = self.validLayout, let navigationBar = self.controller?.navigationBar else {
|
||||
return
|
||||
}
|
||||
let tagMask: MessageTags = .webPage
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.context.account.peerId, threadId: nil, tagMask: tagMask, interfaceInteraction: self.controllerInteraction), cancel: { [weak self] in
|
||||
self?.controller?.deactivateSearch()
|
||||
})
|
||||
|
||||
self.searchDisplayController?.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in
|
||||
if let strongSelf = self, let placeholderNode {
|
||||
if isSearchBar {
|
||||
placeholderNode.supernode?.insertSubnode(subnode, aboveSubnode: placeholderNode)
|
||||
} else {
|
||||
strongSelf.insertSubnode(subnode, belowSubnode: navigationBar)
|
||||
}
|
||||
}
|
||||
}, placeholder: placeholderNode)
|
||||
}
|
||||
|
||||
func deactivateSearch(placeholderNode: SearchBarPlaceholderNode) {
|
||||
guard let searchDisplayController = self.searchDisplayController else {
|
||||
return
|
||||
}
|
||||
self.searchDisplayController = nil
|
||||
searchDisplayController.deactivate(placeholder: placeholderNode)
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
self.historyNode.scrollToEndOfHistory()
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationBarHeight: CGFloat, actualNavigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationBarHeight, actualNavigationBarHeight)
|
||||
|
||||
let historyFrame = CGRect(origin: .zero, size: layout.size)
|
||||
transition.updateFrame(node: self.historyNode, frame: historyFrame)
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.top += navigationBarHeight
|
||||
|
||||
var headerInsets = layout.insets(options: [.input])
|
||||
headerInsets.top += actualNavigationBarHeight
|
||||
|
||||
let panelHeight = self.bottomPanelNode.updateLayout(width: layout.size.width, sideInset: layout.safeInsets.left, bottomInset: insets.bottom, transition: transition)
|
||||
var panelOrigin: CGFloat = layout.size.height
|
||||
if !self.addedBookmark {
|
||||
panelOrigin -= panelHeight
|
||||
insets.bottom = panelHeight
|
||||
}
|
||||
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOrigin), size: CGSize(width: layout.size.width, height: panelHeight))
|
||||
transition.updateFrame(node: self.bottomPanelNode, frame: panelFrame)
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: historyFrame.size, insets: insets, headerInsets: headerInsets, duration: duration, curve: curve)
|
||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
|
||||
|
||||
if let searchDisplayController = self.searchDisplayController {
|
||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
private let url: String
|
||||
private let openUrl: (String) -> Void
|
||||
private let addBookmark: () -> Void
|
||||
|
||||
private var controllerNode: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
private var searchContentNode: NavigationBarSearchContentNode?
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private var node: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
public init(context: AccountContext, url: String, openUrl: @escaping (String) -> Void, addBookmark: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.url = url
|
||||
self.openUrl = openUrl
|
||||
self.addBookmark = addBookmark
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
self.title = self.presentationData.strings.WebBrowser_Bookmarks_Title
|
||||
|
||||
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search, activate: { [weak self] in
|
||||
self?.activateSearch()
|
||||
})
|
||||
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||
|
||||
self.scrollToTop = { [weak self] in
|
||||
if let self {
|
||||
if let searchContentNode = self.searchContentNode {
|
||||
searchContentNode.updateExpansionProgress(1.0, animated: true)
|
||||
}
|
||||
self.node.scrollToTop()
|
||||
}
|
||||
}
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings()
|
||||
}
|
||||
}
|
||||
}).strict()
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, controller: self, presentationData: self.presentationData)
|
||||
|
||||
self.node.historyNode.contentPositionChanged = { [weak self] offset in
|
||||
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
|
||||
searchContentNode.updateListVisibleContentOffset(offset)
|
||||
}
|
||||
}
|
||||
//
|
||||
// self.node.historyNode.didEndScrolling = { [weak self] _ in
|
||||
// if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
|
||||
// let _ = fixNavigationSearchableListNodeScrolling(strongSelf.node.historyNode, searchNode: searchContentNode)
|
||||
// }
|
||||
// }
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func updateThemeAndStrings() {
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search)
|
||||
}
|
||||
|
||||
fileprivate func activateSearch() {
|
||||
if self.displayNavigationBar {
|
||||
if let scrollToTop = self.scrollToTop {
|
||||
scrollToTop()
|
||||
}
|
||||
if let searchContentNode = self.searchContentNode {
|
||||
self.node.activateSearch(placeholderNode: searchContentNode.placeholderNode)
|
||||
}
|
||||
self.setDisplayNavigationBar(false, transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func deactivateSearch() {
|
||||
if !self.displayNavigationBar {
|
||||
self.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring))
|
||||
if let searchContentNode = self.searchContentNode {
|
||||
self.node.deactivateSearch(placeholderNode: searchContentNode.placeholderNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout: layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private class BottomPanelNode: ASDisplayNode {
|
||||
private let theme: PresentationTheme
|
||||
private let strings: PresentationStrings
|
||||
private let action: () -> Void
|
||||
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let button: HighlightTrackingButtonNode
|
||||
private let iconNode: ASImageNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, action: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.action = action
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
self.iconNode.isUserInteractionEnabled = false
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: strings.WebBrowser_Bookmarks_BookmarkCurrent, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor)
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
|
||||
self.button = HighlightTrackingButtonNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = theme.rootController.navigationBar.opaqueBackgroundColor
|
||||
|
||||
self.addSubnode(self.button)
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.button)
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let self {
|
||||
if highlighted {
|
||||
self.iconNode.layer.removeAnimation(forKey: "opacity")
|
||||
self.iconNode.alpha = 0.4
|
||||
|
||||
self.textNode.layer.removeAnimation(forKey: "opacity")
|
||||
self.textNode.alpha = 0.4
|
||||
} else {
|
||||
self.iconNode.alpha = 1.0
|
||||
self.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
|
||||
self.textNode.alpha = 1.0
|
||||
self.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.action()
|
||||
}
|
||||
|
||||
func updateLayout(width: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
self.validLayout = (width, sideInset, bottomInset)
|
||||
let topInset: CGFloat = 8.0
|
||||
var bottomInset = bottomInset
|
||||
bottomInset += topInset - (bottomInset.isZero ? 0.0 : 4.0)
|
||||
|
||||
let buttonHeight: CGFloat = 40.0
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width, height: 44.0))
|
||||
|
||||
let spacing: CGFloat = 8.0
|
||||
var contentWidth = textSize.width
|
||||
var contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0)
|
||||
if let icon = self.iconNode.image {
|
||||
contentWidth += icon.size.width + spacing
|
||||
contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0)
|
||||
transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: contentOriginX, y: 12.0 + UIScreenPixel), size: icon.size))
|
||||
contentOriginX += icon.size.width + spacing
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: contentOriginX, y: 17.0), size: textSize)
|
||||
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||
|
||||
transition.updateFrame(node: self.button, frame: textFrame.insetBy(dx: -10.0, dy: -10.0))
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
|
||||
|
||||
return topInset + buttonHeight + bottomInset
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class BrowserBookmarksContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = false
|
||||
let blurBackground: Bool = true
|
||||
|
||||
private let contentNode: ContextExtractedContentContainingNode
|
||||
|
||||
init(contentNode: ContextExtractedContentContainingNode) {
|
||||
self.contentNode = contentNode
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.contentNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ final class BrowserContentState: Equatable {
|
||||
enum ContentType: Equatable {
|
||||
case webPage
|
||||
case instantPage
|
||||
case document
|
||||
}
|
||||
|
||||
struct HistoryItem: Equatable {
|
||||
@ -39,6 +40,7 @@ final class BrowserContentState: Equatable {
|
||||
let readingProgress: Double
|
||||
let contentType: ContentType
|
||||
let favicon: UIImage?
|
||||
let isSecure: Bool
|
||||
|
||||
let canGoBack: Bool
|
||||
let canGoForward: Bool
|
||||
@ -53,6 +55,7 @@ final class BrowserContentState: Equatable {
|
||||
readingProgress: Double,
|
||||
contentType: ContentType,
|
||||
favicon: UIImage? = nil,
|
||||
isSecure: Bool = false,
|
||||
canGoBack: Bool = false,
|
||||
canGoForward: Bool = false,
|
||||
backList: [HistoryItem] = [],
|
||||
@ -64,6 +67,7 @@ final class BrowserContentState: Equatable {
|
||||
self.readingProgress = readingProgress
|
||||
self.contentType = contentType
|
||||
self.favicon = favicon
|
||||
self.isSecure = isSecure
|
||||
self.canGoBack = canGoBack
|
||||
self.canGoForward = canGoForward
|
||||
self.backList = backList
|
||||
@ -89,6 +93,9 @@ final class BrowserContentState: Equatable {
|
||||
if (lhs.favicon == nil) != (rhs.favicon == nil) {
|
||||
return false
|
||||
}
|
||||
if lhs.isSecure != rhs.isSecure {
|
||||
return false
|
||||
}
|
||||
if lhs.canGoBack != rhs.canGoBack {
|
||||
return false
|
||||
}
|
||||
@ -105,39 +112,43 @@ final class BrowserContentState: Equatable {
|
||||
}
|
||||
|
||||
func withUpdatedTitle(_ title: String) -> BrowserContentState {
|
||||
return BrowserContentState(title: title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedUrl(_ url: String) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedIsSecure(_ isSecure: Bool) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedEstimatedProgress(_ estimatedProgress: Double) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedReadingProgress(_ readingProgress: Double) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedFavicon(_ favicon: UIImage?) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedCanGoBack(_ canGoBack: Bool) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedCanGoForward(_ canGoForward: Bool) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: canGoForward, backList: self.backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedBackList(_ backList: [HistoryItem]) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: backList, forwardList: self.forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: backList, forwardList: self.forwardList)
|
||||
}
|
||||
|
||||
func withUpdatedForwardList(_ forwardList: [HistoryItem]) -> BrowserContentState {
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: forwardList)
|
||||
return BrowserContentState(title: self.title, url: self.url, estimatedProgress: self.estimatedProgress, readingProgress: self.readingProgress, contentType: self.contentType, favicon: self.favicon, isSecure: self.isSecure, canGoBack: self.canGoBack, canGoForward: self.canGoForward, backList: self.backList, forwardList: forwardList)
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,12 +162,14 @@ protocol BrowserContent: UIView {
|
||||
var present: (ViewController, Any?) -> Void { get set }
|
||||
var presentInGlobalOverlay: (ViewController) -> Void { get set }
|
||||
var getNavigationController: () -> NavigationController? { get set }
|
||||
var openAppUrl: (String) -> Void { get set }
|
||||
|
||||
var minimize: () -> Void { get set }
|
||||
var close: () -> Void { get set }
|
||||
|
||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void { get set }
|
||||
|
||||
func resetScrolling()
|
||||
|
||||
func reload()
|
||||
func stop()
|
||||
|
||||
@ -173,7 +186,11 @@ protocol BrowserContent: UIView {
|
||||
|
||||
func scrollToTop()
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ComponentTransition)
|
||||
func addToRecentlyVisited()
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition)
|
||||
|
||||
func makeContentSnapshotView() -> UIView?
|
||||
}
|
||||
|
||||
struct ContentScrollingUpdate {
|
||||
|
479
submodules/BrowserUI/Sources/BrowserDocumentContent.swift
Normal file
479
submodules/BrowserUI/Sources/BrowserDocumentContent.swift
Normal file
@ -0,0 +1,479 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import WebKit
|
||||
import AppBundle
|
||||
import PromptUI
|
||||
import SafariServices
|
||||
import ShareController
|
||||
import UndoUI
|
||||
import UrlEscaping
|
||||
|
||||
final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let webView: WKWebView
|
||||
|
||||
let uuid: UUID
|
||||
|
||||
private var _state: BrowserContentState
|
||||
private let statePromise: Promise<BrowserContentState>
|
||||
|
||||
var currentState: BrowserContentState {
|
||||
return self._state
|
||||
}
|
||||
var state: Signal<BrowserContentState, NoError> {
|
||||
return self.statePromise.get()
|
||||
}
|
||||
|
||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||
var openAppUrl: (String) -> Void = { _ in }
|
||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||
var minimize: () -> Void = { }
|
||||
var close: () -> Void = { }
|
||||
var present: (ViewController, Any?) -> Void = { _, _ in }
|
||||
var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
|
||||
var getNavigationController: () -> NavigationController? = { return nil }
|
||||
|
||||
private var tempFile: TempBoxFile?
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, file: TelegramMediaFile) {
|
||||
self.context = context
|
||||
self.uuid = UUID()
|
||||
self.presentationData = presentationData
|
||||
|
||||
let configuration = WKWebViewConfiguration()
|
||||
self.webView = WKWebView(frame: CGRect(), configuration: configuration)
|
||||
self.webView.allowsLinkPreview = true
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
self.webView.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
|
||||
var title: String = "file"
|
||||
if let path = self.context.account.postbox.mediaBox.completedResourcePath(file.resource) {
|
||||
var updatedPath = path
|
||||
if let fileName = file.fileName {
|
||||
let tempFile = TempBox.shared.file(path: path, fileName: fileName)
|
||||
updatedPath = tempFile.path
|
||||
self.tempFile = tempFile
|
||||
title = fileName
|
||||
}
|
||||
|
||||
let request = URLRequest(url: URL(fileURLWithPath: updatedPath))
|
||||
self.webView.load(request)
|
||||
}
|
||||
|
||||
self._state = BrowserContentState(title: title, url: "", estimatedProgress: 0.0, readingProgress: 0.0, contentType: .document)
|
||||
self.statePromise = Promise<BrowserContentState>(self._state)
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.webView.allowsBackForwardNavigationGestures = true
|
||||
self.webView.scrollView.delegate = self
|
||||
self.webView.scrollView.clipsToBounds = false
|
||||
self.webView.navigationDelegate = self
|
||||
self.webView.uiDelegate = self
|
||||
if #available(iOS 15.0, *) {
|
||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
}
|
||||
self.addSubview(self.webView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
if #available(iOS 15.0, *) {
|
||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
self.webView.underPageBackgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
}
|
||||
if let (size, insets, fullInsets) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: .zero, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
var currentFontState = BrowserPresentationState.FontState(size: 100, isSerif: false)
|
||||
func updateFontState(_ state: BrowserPresentationState.FontState) {
|
||||
self.updateFontState(state, force: false)
|
||||
}
|
||||
func updateFontState(_ state: BrowserPresentationState.FontState, force: Bool) {
|
||||
self.currentFontState = state
|
||||
|
||||
let fontFamily = state.isSerif ? "'Georgia, serif'" : "null"
|
||||
let textSizeAdjust = state.size != 100 ? "'\(state.size)%'" : "null"
|
||||
let js = "\(setupFontFunctions) setTelegramFontOverrides(\(fontFamily), \(textSizeAdjust))";
|
||||
self.webView.evaluateJavaScript(js) { _, _ in }
|
||||
}
|
||||
|
||||
private var didSetupSearch = false
|
||||
private func setupSearch(completion: @escaping () -> Void) {
|
||||
guard !self.didSetupSearch else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let bundle = getAppBundle()
|
||||
guard let scriptPath = bundle.path(forResource: "UIWebViewSearch", ofType: "js") else {
|
||||
return
|
||||
}
|
||||
guard let scriptData = try? Data(contentsOf: URL(fileURLWithPath: scriptPath)) else {
|
||||
return
|
||||
}
|
||||
guard let script = String(data: scriptData, encoding: .utf8) else {
|
||||
return
|
||||
}
|
||||
self.didSetupSearch = true
|
||||
self.webView.evaluateJavaScript(script, completionHandler: { _, error in
|
||||
if error != nil {
|
||||
print()
|
||||
}
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
private var previousQuery: String?
|
||||
func setSearch(_ query: String?, completion: ((Int) -> Void)?) {
|
||||
guard self.previousQuery != query else {
|
||||
return
|
||||
}
|
||||
self.previousQuery = query
|
||||
self.setupSearch { [weak self] in
|
||||
if let query = query {
|
||||
let js = "uiWebview_HighlightAllOccurencesOfString('\(query)')"
|
||||
self?.webView.evaluateJavaScript(js, completionHandler: { [weak self] _, _ in
|
||||
let js = "uiWebview_SearchResultCount"
|
||||
self?.webView.evaluateJavaScript(js, completionHandler: { [weak self] result, _ in
|
||||
if let result = result as? NSNumber {
|
||||
self?.searchResultsCount = result.intValue
|
||||
completion?(result.intValue)
|
||||
} else {
|
||||
completion?(0)
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let js = "uiWebview_RemoveAllHighlights()"
|
||||
self?.webView.evaluateJavaScript(js, completionHandler: nil)
|
||||
|
||||
self?.currentSearchResult = 0
|
||||
self?.searchResultsCount = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var currentSearchResult: Int = 0
|
||||
private var searchResultsCount: Int = 0
|
||||
|
||||
func scrollToPreviousSearchResult(completion: ((Int, Int) -> Void)?) {
|
||||
let searchResultsCount = self.searchResultsCount
|
||||
var index = self.currentSearchResult - 1
|
||||
if index < 0 {
|
||||
index = searchResultsCount - 1
|
||||
}
|
||||
self.currentSearchResult = index
|
||||
|
||||
let js = "uiWebview_ScrollTo('\(searchResultsCount - index - 1)')"
|
||||
self.webView.evaluateJavaScript(js, completionHandler: { _, _ in
|
||||
completion?(index, searchResultsCount)
|
||||
})
|
||||
}
|
||||
|
||||
func scrollToNextSearchResult(completion: ((Int, Int) -> Void)?) {
|
||||
let searchResultsCount = self.searchResultsCount
|
||||
var index = self.currentSearchResult + 1
|
||||
if index >= searchResultsCount {
|
||||
index = 0
|
||||
}
|
||||
self.currentSearchResult = index
|
||||
|
||||
let js = "uiWebview_ScrollTo('\(searchResultsCount - index - 1)')"
|
||||
self.webView.evaluateJavaScript(js, completionHandler: { _, _ in
|
||||
completion?(index, searchResultsCount)
|
||||
})
|
||||
}
|
||||
|
||||
func stop() {
|
||||
self.webView.stopLoading()
|
||||
}
|
||||
|
||||
func reload() {
|
||||
self.webView.reload()
|
||||
}
|
||||
|
||||
func navigateBack() {
|
||||
self.webView.goBack()
|
||||
}
|
||||
|
||||
func navigateForward() {
|
||||
self.webView.goForward()
|
||||
}
|
||||
|
||||
func navigateTo(historyItem: BrowserContentState.HistoryItem) {
|
||||
if let webItem = historyItem.webItem {
|
||||
self.webView.go(to: webItem)
|
||||
}
|
||||
}
|
||||
|
||||
func navigateTo(address: String) {
|
||||
let finalUrl = explicitUrl(address)
|
||||
guard let url = URL(string: finalUrl) else {
|
||||
return
|
||||
}
|
||||
self.webView.load(URLRequest(url: url))
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
self.webView.scrollView.setContentOffset(CGPoint(x: 0.0, y: -self.webView.scrollView.contentInset.top), animated: true)
|
||||
}
|
||||
|
||||
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||
self.validLayout = (size, insets, fullInsets)
|
||||
|
||||
self.previousScrollingOffset = ScrollingOffsetState(value: self.webView.scrollView.contentOffset.y, isDraggingOrDecelerating: self.webView.scrollView.isDragging || self.webView.scrollView.isDecelerating)
|
||||
|
||||
let webViewFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: size.width - insets.left - insets.right, height: size.height - insets.top - insets.bottom))
|
||||
var refresh = false
|
||||
if self.webView.frame.width > 0 && webViewFrame.width != self.webView.frame.width {
|
||||
refresh = true
|
||||
}
|
||||
transition.setFrame(view: self.webView, frame: webViewFrame)
|
||||
|
||||
if refresh {
|
||||
self.webView.reloadInputViews()
|
||||
}
|
||||
|
||||
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: -insets.left, bottom: 0.0, right: -insets.right)
|
||||
self.webView.scrollView.horizontalScrollIndicatorInsets = UIEdgeInsets(top: 0.0, left: -insets.left, bottom: 0.0, right: -insets.right)
|
||||
|
||||
// if let error = self.currentError {
|
||||
// let errorSize = self.errorView.update(
|
||||
// transition: .immediate,
|
||||
// component: AnyComponent(
|
||||
// ErrorComponent(
|
||||
// theme: self.presentationData.theme,
|
||||
// title: self.presentationData.strings.Browser_ErrorTitle,
|
||||
// text: error.localizedDescription
|
||||
// )
|
||||
// ),
|
||||
// environment: {},
|
||||
// containerSize: CGSize(width: size.width - insets.left - insets.right - 72.0, height: size.height)
|
||||
// )
|
||||
// if self.errorView.superview == nil {
|
||||
// self.addSubview(self.errorView)
|
||||
// self.errorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
// }
|
||||
// self.errorView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - errorSize.width) / 2.0), y: insets.top + floorToScreenPixels((size.height - insets.top - insets.bottom - errorSize.height) / 2.0)), size: errorSize)
|
||||
// } else if self.errorView.superview != nil {
|
||||
// self.errorView.removeFromSuperview()
|
||||
// }
|
||||
}
|
||||
|
||||
private func updateState(_ f: (BrowserContentState) -> BrowserContentState) {
|
||||
let updated = f(self._state)
|
||||
self._state = updated
|
||||
self.statePromise.set(.single(self._state))
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if keyPath == "title" {
|
||||
self.updateState { $0.withUpdatedTitle(self.webView.title ?? "") }
|
||||
} else if keyPath == "URL" {
|
||||
self.updateState { $0.withUpdatedUrl(self.webView.url?.absoluteString ?? "") }
|
||||
self.didSetupSearch = false
|
||||
} else if keyPath == "estimatedProgress" {
|
||||
self.updateState { $0.withUpdatedEstimatedProgress(self.webView.estimatedProgress) }
|
||||
} else if keyPath == "canGoBack" {
|
||||
self.updateState { $0.withUpdatedCanGoBack(self.webView.canGoBack) }
|
||||
self.webView.disablesInteractiveTransitionGestureRecognizer = self.webView.canGoBack
|
||||
} else if keyPath == "canGoForward" {
|
||||
self.updateState { $0.withUpdatedCanGoForward(self.webView.canGoForward) }
|
||||
}
|
||||
}
|
||||
|
||||
private struct ScrollingOffsetState: Equatable {
|
||||
var value: CGFloat
|
||||
var isDraggingOrDecelerating: Bool
|
||||
}
|
||||
|
||||
private var previousScrollingOffset: ScrollingOffsetState?
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateScrollingOffset(isReset: false, transition: .immediate)
|
||||
}
|
||||
|
||||
private func snapScrollingOffsetToInsets() {
|
||||
let transition = ComponentTransition(animation: .curve(duration: 0.4, curve: .spring))
|
||||
self.updateScrollingOffset(isReset: false, transition: transition)
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if !decelerate {
|
||||
self.snapScrollingOffsetToInsets()
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
self.snapScrollingOffsetToInsets()
|
||||
}
|
||||
|
||||
private func updateScrollingOffset(isReset: Bool, transition: ComponentTransition) {
|
||||
let scrollView = self.webView.scrollView
|
||||
let isInteracting = scrollView.isDragging || scrollView.isDecelerating
|
||||
if let previousScrollingOffsetValue = self.previousScrollingOffset {
|
||||
let currentBounds = scrollView.bounds
|
||||
let offsetToTopEdge = max(0.0, currentBounds.minY - 0.0)
|
||||
let offsetToBottomEdge = max(0.0, scrollView.contentSize.height - currentBounds.maxY)
|
||||
|
||||
let relativeOffset = scrollView.contentOffset.y - previousScrollingOffsetValue.value
|
||||
self.onScrollingUpdate(ContentScrollingUpdate(
|
||||
relativeOffset: relativeOffset,
|
||||
absoluteOffsetToTopEdge: offsetToTopEdge,
|
||||
absoluteOffsetToBottomEdge: offsetToBottomEdge,
|
||||
isReset: isReset,
|
||||
isInteracting: isInteracting,
|
||||
transition: transition
|
||||
))
|
||||
}
|
||||
self.previousScrollingOffset = ScrollingOffsetState(value: scrollView.contentOffset.y, isDraggingOrDecelerating: isInteracting)
|
||||
|
||||
var readingProgress: CGFloat = 0.0
|
||||
if !scrollView.contentSize.height.isZero {
|
||||
let value = (scrollView.contentOffset.y + scrollView.contentInset.top) / (scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.top)
|
||||
readingProgress = max(0.0, min(1.0, value))
|
||||
}
|
||||
self.updateState {
|
||||
$0.withUpdatedReadingProgress(readingProgress)
|
||||
}
|
||||
}
|
||||
|
||||
func resetScrolling() {
|
||||
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||
// self.currentError = nil
|
||||
self.updateFontState(self.currentFontState, force: true)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
self.updateState {
|
||||
$0
|
||||
.withUpdatedBackList(webView.backForwardList.backList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
||||
.withUpdatedForwardList(webView.backForwardList.forwardList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
||||
}
|
||||
}
|
||||
|
||||
// func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||
// if (error as NSError).code != -999 {
|
||||
// self.currentError = error
|
||||
// } else {
|
||||
// self.currentError = nil
|
||||
// }
|
||||
// if let (size, insets) = self.validLayout {
|
||||
// self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
||||
// if (error as NSError).code != -999 {
|
||||
// self.currentError = error
|
||||
// } else {
|
||||
// self.currentError = nil
|
||||
// }
|
||||
// if let (size, insets) = self.validLayout {
|
||||
// self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||
// }
|
||||
// }
|
||||
|
||||
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
|
||||
if navigationAction.targetFrame == nil {
|
||||
if let url = navigationAction.request.url?.absoluteString {
|
||||
self.open(url: url, new: true)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func webViewDidClose(_ webView: WKWebView) {
|
||||
self.close()
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
|
||||
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
||||
decisionHandler(.prompt)
|
||||
}
|
||||
|
||||
|
||||
// @available(iOS 13.0, *)
|
||||
// func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) {
|
||||
// guard let url = elementInfo.linkURL else {
|
||||
// completionHandler(nil)
|
||||
// return
|
||||
// }
|
||||
// let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
// let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { [weak self] _ in
|
||||
// return UIMenu(title: "", children: [
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_Open, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// self?.open(url: url.absoluteString, new: false)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_OpenInNewTab, image: generateTintedImage(image: UIImage(bundleImageName: "Instant View/NewTab"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// self?.open(url: url.absoluteString, new: true)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_AddToReadingList, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadingList"), color: presentationData.theme.contextMenu.primaryColor), handler: { _ in
|
||||
// let _ = try? SSReadingList.default()?.addItem(with: url, title: nil, previewText: nil)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_CopyLink, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// UIPasteboard.general.string = url.absoluteString
|
||||
// self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_Share, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// self?.share(url: url.absoluteString)
|
||||
// })
|
||||
// ])
|
||||
// }
|
||||
// completionHandler(configuration)
|
||||
// }
|
||||
|
||||
private func open(url: String, new: Bool) {
|
||||
let subject: BrowserScreen.Subject = .webPage(url: url)
|
||||
if new, let navigationController = self.getNavigationController() {
|
||||
navigationController._keepModalDismissProgress = true
|
||||
self.minimize()
|
||||
let controller = BrowserScreen(context: self.context, subject: subject)
|
||||
navigationController._keepModalDismissProgress = true
|
||||
navigationController.pushViewController(controller)
|
||||
} else {
|
||||
self.pushContent(subject)
|
||||
}
|
||||
}
|
||||
|
||||
private func share(url: String) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let shareController = ShareController(context: self.context, subject: .url(url))
|
||||
shareController.actionCompleted = { [weak self] in
|
||||
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
}
|
||||
self.present(shareController, nil)
|
||||
}
|
||||
|
||||
func addToRecentlyVisited() {
|
||||
}
|
||||
|
||||
func makeContentSnapshotView() -> UIView? {
|
||||
return nil
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private var theme: InstantPageTheme
|
||||
private var settings: InstantPagePresentationSettings = .defaultSettings
|
||||
private let sourceLocation: InstantPageSourceLocation
|
||||
|
||||
private var webPage: TelegramMediaWebpage?
|
||||
@ -45,6 +46,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
private var pendingAnchor: String?
|
||||
private var initialState: InstantPageStoredState?
|
||||
|
||||
private let wrapperNode: ASDisplayNode
|
||||
fileprivate let scrollNode: ASScrollNode
|
||||
private let scrollNodeFooter: ASDisplayNode
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
@ -65,6 +67,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
var currentAccessibilityAreas: [AccessibilityAreaNode] = []
|
||||
|
||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||
var openAppUrl: (String) -> Void = { _ in }
|
||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||
var minimize: () -> Void = { }
|
||||
var close: () -> Void = { }
|
||||
@ -85,7 +88,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
private let loadProgress = ValuePromise<CGFloat>(1.0, ignoreRepeated: true)
|
||||
private let readingProgress = ValuePromise<CGFloat>(1.0, ignoreRepeated: true)
|
||||
|
||||
private var containerLayout: (size: CGSize, insets: UIEdgeInsets)?
|
||||
private var containerLayout: (size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets)?
|
||||
private var setupScrollOffsetOnLayout = false
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, webPage: TelegramMediaWebpage, anchor: String?, url: String, sourceLocation: InstantPageSourceLocation) {
|
||||
@ -107,6 +110,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
self._state = BrowserContentState(title: title, url: url, estimatedProgress: 0.0, readingProgress: 0.0, contentType: .instantPage)
|
||||
self.statePromise = Promise<BrowserContentState>(self._state)
|
||||
|
||||
self.wrapperNode = ASDisplayNode()
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.scrollNode.backgroundColor = self.theme.pageBackgroundColor
|
||||
|
||||
@ -126,7 +130,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
))
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.addSubnode(self.wrapperNode)
|
||||
self.wrapperNode.addSubnode(self.scrollNode)
|
||||
self.scrollNode.addSubnode(self.scrollNodeFooter)
|
||||
|
||||
self.scrollNode.view.delaysContentTouches = false
|
||||
@ -175,8 +180,9 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.theme = instantPageThemeForType(presentationData.theme.overallDarkAppearance ? .dark : .light, settings: .defaultSettings)
|
||||
self.theme = instantPageThemeForType(presentationData.theme.overallDarkAppearance ? .dark : .light, settings: self.settings)
|
||||
self.updatePageLayout()
|
||||
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
|
||||
}
|
||||
|
||||
func tapActionAtPoint(_ point: CGPoint) -> TapLongTapOrDoubleTapGestureRecognizerAction {
|
||||
@ -295,10 +301,10 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
|
||||
private func requestLayout(transition: ContainedViewLayoutTransition) {
|
||||
guard let (size, insets) = self.containerLayout else {
|
||||
guard let (size, insets, fullInsets) = self.containerLayout else {
|
||||
return
|
||||
}
|
||||
self.updateLayout(size: size, insets: insets, transition: transition)
|
||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: .zero, transition: transition)
|
||||
}
|
||||
|
||||
func reload() {
|
||||
@ -319,8 +325,40 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
|
||||
}
|
||||
|
||||
var currentFontState = BrowserPresentationState.FontState(size: 100, isSerif: false)
|
||||
func updateFontState(_ state: BrowserPresentationState.FontState) {
|
||||
self.currentFontState = state
|
||||
|
||||
let fontSize: InstantPagePresentationFontSize
|
||||
switch state.size {
|
||||
case 50:
|
||||
fontSize = .xxsmall
|
||||
case 75:
|
||||
fontSize = .xsmall
|
||||
case 85:
|
||||
fontSize = .small
|
||||
case 100:
|
||||
fontSize = .standard
|
||||
case 115:
|
||||
fontSize = .large
|
||||
case 125:
|
||||
fontSize = .xlarge
|
||||
case 150:
|
||||
fontSize = .xxlarge
|
||||
default:
|
||||
fontSize = .standard
|
||||
}
|
||||
|
||||
self.settings = InstantPagePresentationSettings(
|
||||
themeType: self.presentationData.theme.overallDarkAppearance ? .dark : .light,
|
||||
fontSize: fontSize,
|
||||
forceSerif: state.isSerif,
|
||||
autoNightMode: false,
|
||||
ignoreAutoNightModeUntil: 0
|
||||
)
|
||||
self.theme = instantPageThemeForType(self.presentationData.theme.overallDarkAppearance ? .dark : .light, settings: self.settings)
|
||||
self.updatePageLayout()
|
||||
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
|
||||
}
|
||||
|
||||
func setSearch(_ query: String?, completion: ((Int) -> Void)?) {
|
||||
@ -340,12 +378,12 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: true)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ComponentTransition) {
|
||||
self.updateLayout(size: size, insets: insets, transition: transition.containedViewLayoutTransition)
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: safeInsets, transition: transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||
self.containerLayout = (size, insets)
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||
self.containerLayout = (size, insets, fullInsets)
|
||||
|
||||
var updateVisibleItems = false
|
||||
let resetContentOffset = self.scrollNode.bounds.size.width.isZero || self.setupScrollOffsetOnLayout || !(self.initialAnchor ?? "").isEmpty
|
||||
@ -357,6 +395,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
self.scrollNode.view.scrollIndicatorInsets = scrollInsets
|
||||
}
|
||||
|
||||
self.wrapperNode.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
let scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top))
|
||||
let scrollFrameUpdated = self.scrollNode.bounds.size != scrollFrame.size
|
||||
if scrollFrameUpdated {
|
||||
@ -401,7 +441,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
|
||||
private func updatePageLayout() {
|
||||
guard let (size, insets) = self.containerLayout, let webPage = self.webPage else {
|
||||
guard let (size, insets, _) = self.containerLayout, let webPage = self.webPage else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -732,6 +772,10 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
self.readingProgress.set(readingProgress)
|
||||
}
|
||||
|
||||
func resetScrolling() {
|
||||
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
private func scrollableContentOffset(item: InstantPageScrollableItem) -> CGPoint {
|
||||
var contentOffset = CGPoint()
|
||||
for (_, itemNode) in self.visibleItemsWithNodes {
|
||||
@ -1069,12 +1113,12 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
})], catchTapsOutside: true)
|
||||
self.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
if let _ = self {
|
||||
// for (_, itemNode) in self.visibleItemsWithNodes {
|
||||
// if let (node, _, _) = itemNode.transitionNode(media: media) {
|
||||
// return (self.scrollNode, node.convert(node.bounds, to: self.scrollNode), self, self.bounds)
|
||||
// }
|
||||
// }
|
||||
if let self {
|
||||
for (_, itemNode) in self.visibleItemsWithNodes {
|
||||
if let (node, _, _) = itemNode.transitionNode(media: media) {
|
||||
return (self.scrollNode, node.convert(node.bounds, to: self.scrollNode), self.wrapperNode, self.wrapperNode.bounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
@ -1162,84 +1206,84 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
|
||||
private func updateTextSelectionRects(_ rects: [CGRect], text: String?) {
|
||||
// if let text = text, !rects.isEmpty {
|
||||
// let textSelectionNode: LinkHighlightingNode
|
||||
// if let current = self.textSelectionNode {
|
||||
// textSelectionNode = current
|
||||
// } else {
|
||||
// textSelectionNode = LinkHighlightingNode(color: UIColor.lightGray.withAlphaComponent(0.4))
|
||||
// textSelectionNode.isUserInteractionEnabled = false
|
||||
// self.textSelectionNode = textSelectionNode
|
||||
// self.scrollNode.addSubnode(textSelectionNode)
|
||||
// }
|
||||
// textSelectionNode.frame = CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size)
|
||||
// textSelectionNode.updateRects(rects)
|
||||
//
|
||||
//// var coveringRect = rects[0]
|
||||
//// for i in 1 ..< rects.count {
|
||||
//// coveringRect = coveringRect.union(rects[i])
|
||||
//// }
|
||||
//
|
||||
//// let context = self.context
|
||||
//// let strings = self.presentationData.strings
|
||||
//// let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
||||
//// |> take(1)
|
||||
//// |> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
//// let translationSettings: TranslationSettings
|
||||
//// if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
|
||||
//// translationSettings = current
|
||||
//// } else {
|
||||
//// translationSettings = TranslationSettings.defaultSettings
|
||||
//// }
|
||||
////
|
||||
//// var actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuCopy, accessibilityLabel: strings.Conversation_ContextMenuCopy), action: { [weak self] in
|
||||
//// UIPasteboard.general.string = text
|
||||
////
|
||||
//// if let strongSelf = self {
|
||||
//// let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
//// strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
//// }
|
||||
//// }), ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuShare, accessibilityLabel: strings.Conversation_ContextMenuShare), action: { [weak self] in
|
||||
//// if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
|
||||
//// strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
|
||||
//// }
|
||||
//// })]
|
||||
////
|
||||
//// let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages)
|
||||
//// 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, canCopy: true, 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)
|
||||
//// }))
|
||||
//// }
|
||||
////
|
||||
//// let controller = makeContextMenuController(actions: actions)
|
||||
//// controller.dismissed = { [weak self] in
|
||||
//// self?.updateTextSelectionRects([], text: nil)
|
||||
//// }
|
||||
//// self?.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
//// if let strongSelf = self {
|
||||
//// return (strongSelf.scrollNode, coveringRect.insetBy(dx: -3.0, dy: -3.0), strongSelf, strongSelf.bounds)
|
||||
//// } else {
|
||||
//// return nil
|
||||
//// }
|
||||
//// }))
|
||||
//// })
|
||||
//
|
||||
// textSelectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
// } else if let textSelectionNode = self.textSelectionNode {
|
||||
// self.textSelectionNode = nil
|
||||
// textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
|
||||
// textSelectionNode?.removeFromSupernode()
|
||||
// })
|
||||
// }
|
||||
if let text = text, !rects.isEmpty {
|
||||
let textSelectionNode: LinkHighlightingNode
|
||||
if let current = self.textSelectionNode {
|
||||
textSelectionNode = current
|
||||
} else {
|
||||
textSelectionNode = LinkHighlightingNode(color: UIColor.lightGray.withAlphaComponent(0.4))
|
||||
textSelectionNode.isUserInteractionEnabled = false
|
||||
self.textSelectionNode = textSelectionNode
|
||||
self.scrollNode.addSubnode(textSelectionNode)
|
||||
}
|
||||
textSelectionNode.frame = CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size)
|
||||
textSelectionNode.updateRects(rects)
|
||||
|
||||
var coveringRect = rects[0]
|
||||
for i in 1 ..< rects.count {
|
||||
coveringRect = coveringRect.union(rects[i])
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = self.presentationData.strings
|
||||
let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
let translationSettings: TranslationSettings
|
||||
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
|
||||
translationSettings = current
|
||||
} else {
|
||||
translationSettings = TranslationSettings.defaultSettings
|
||||
}
|
||||
|
||||
var actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuCopy, accessibilityLabel: strings.Conversation_ContextMenuCopy), action: { [weak self] in
|
||||
UIPasteboard.general.string = text
|
||||
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
}
|
||||
}), ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuShare, accessibilityLabel: strings.Conversation_ContextMenuShare), action: { [weak self] in
|
||||
if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content {
|
||||
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
|
||||
}
|
||||
})]
|
||||
|
||||
let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages)
|
||||
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, canCopy: true, fromLanguage: language)
|
||||
controller.pushController = { [weak self] c in
|
||||
self?.getNavigationController()?._keepModalDismissProgress = true
|
||||
self?.push(c)
|
||||
}
|
||||
controller.presentController = { [weak self] c in
|
||||
self?.present(c, nil)
|
||||
}
|
||||
self?.present(controller, nil)
|
||||
}))
|
||||
}
|
||||
|
||||
let controller = makeContextMenuController(actions: actions)
|
||||
controller.dismissed = { [weak self] in
|
||||
self?.updateTextSelectionRects([], text: nil)
|
||||
}
|
||||
self?.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (strongSelf.scrollNode, coveringRect.insetBy(dx: -3.0, dy: -3.0), strongSelf.wrapperNode, strongSelf.wrapperNode.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
textSelectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
} else if let textSelectionNode = self.textSelectionNode {
|
||||
self.textSelectionNode = nil
|
||||
textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
|
||||
textSelectionNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> (InstantPageItem, CGFloat, Bool, [InstantPageDetailsItem])? {
|
||||
@ -1378,4 +1422,14 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
|
||||
}
|
||||
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds, animated: animated)
|
||||
}
|
||||
|
||||
func addToRecentlyVisited() {
|
||||
if let webPage = self.webPage {
|
||||
let _ = addRecentlyVisitedLink(engine: self.context.engine, webPage: webPage).startStandalone()
|
||||
}
|
||||
}
|
||||
|
||||
func makeContentSnapshotView() -> UIView? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,30 @@ import ComponentFlow
|
||||
import BlurredBackgroundComponent
|
||||
import ContextUI
|
||||
|
||||
final class BrowserNavigationBarEnvironment: Equatable {
|
||||
public let fraction: CGFloat
|
||||
|
||||
public init(fraction: CGFloat) {
|
||||
self.fraction = fraction
|
||||
}
|
||||
|
||||
public static func ==(lhs: BrowserNavigationBarEnvironment, rhs: BrowserNavigationBarEnvironment) -> Bool {
|
||||
if lhs.fraction != rhs.fraction {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
public class ExternalState {
|
||||
public fileprivate(set) var centerItemFrame: CGRect
|
||||
|
||||
public init() {
|
||||
self.centerItemFrame = .zero
|
||||
}
|
||||
}
|
||||
|
||||
let backgroundColor: UIColor
|
||||
let separatorColor: UIColor
|
||||
let textColor: UIColor
|
||||
@ -14,12 +37,15 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
let topInset: CGFloat
|
||||
let height: CGFloat
|
||||
let sideInset: CGFloat
|
||||
let metrics: LayoutMetrics
|
||||
let externalState: ExternalState?
|
||||
let leftItems: [AnyComponentWithIdentity<Empty>]
|
||||
let rightItems: [AnyComponentWithIdentity<Empty>]
|
||||
let centerItem: AnyComponentWithIdentity<Empty>?
|
||||
let centerItem: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?
|
||||
let readingProgress: CGFloat
|
||||
let loadingProgress: Double?
|
||||
let collapseFraction: CGFloat
|
||||
let activate: () -> Void
|
||||
|
||||
init(
|
||||
backgroundColor: UIColor,
|
||||
@ -30,12 +56,15 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
topInset: CGFloat,
|
||||
height: CGFloat,
|
||||
sideInset: CGFloat,
|
||||
metrics: LayoutMetrics,
|
||||
externalState: ExternalState?,
|
||||
leftItems: [AnyComponentWithIdentity<Empty>],
|
||||
rightItems: [AnyComponentWithIdentity<Empty>],
|
||||
centerItem: AnyComponentWithIdentity<Empty>?,
|
||||
centerItem: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?,
|
||||
collapseFraction: CGFloat
|
||||
collapseFraction: CGFloat,
|
||||
activate: @escaping () -> Void
|
||||
) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.separatorColor = separatorColor
|
||||
@ -45,12 +74,15 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
self.topInset = topInset
|
||||
self.height = height
|
||||
self.sideInset = sideInset
|
||||
self.metrics = metrics
|
||||
self.externalState = externalState
|
||||
self.leftItems = leftItems
|
||||
self.rightItems = rightItems
|
||||
self.centerItem = centerItem
|
||||
self.readingProgress = readingProgress
|
||||
self.loadingProgress = loadingProgress
|
||||
self.collapseFraction = collapseFraction
|
||||
self.activate = activate
|
||||
}
|
||||
|
||||
static func ==(lhs: BrowserNavigationBarComponent, rhs: BrowserNavigationBarComponent) -> Bool {
|
||||
@ -78,6 +110,9 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
if lhs.sideInset != rhs.sideInset {
|
||||
return false
|
||||
}
|
||||
if lhs.metrics != rhs.metrics {
|
||||
return false
|
||||
}
|
||||
if lhs.leftItems != rhs.leftItems {
|
||||
return false
|
||||
}
|
||||
@ -106,16 +141,19 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
let loadingProgress = Child(LoadingProgressComponent.self)
|
||||
let leftItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let rightItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let centerItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let centerItems = ChildMap(environment: BrowserNavigationBarEnvironment.self, keyedBy: AnyHashable.self)
|
||||
let activate = Child(Button.self)
|
||||
|
||||
return { context in
|
||||
var availableWidth = context.availableSize.width
|
||||
let sideInset: CGFloat = 16.0 + context.component.sideInset
|
||||
let sideInset: CGFloat = (context.component.metrics.isTablet ? 20.0 : 16.0) + context.component.sideInset
|
||||
|
||||
let collapsedHeight: CGFloat = 24.0
|
||||
let expandedHeight = context.component.height
|
||||
let contentHeight: CGFloat = expandedHeight * (1.0 - context.component.collapseFraction) + collapsedHeight * context.component.collapseFraction
|
||||
let size = CGSize(width: context.availableSize.width, height: context.component.topInset + contentHeight)
|
||||
let verticalOffset: CGFloat = context.component.metrics.isTablet ? -2.0 : 0.0
|
||||
let itemSpacing: CGFloat = context.component.metrics.isTablet ? 26.0 : 8.0
|
||||
|
||||
let background = background.update(
|
||||
component: Rectangle(color: context.component.backgroundColor.withAlphaComponent(1.0)),
|
||||
@ -145,7 +183,7 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
availableSize: CGSize(width: size.width, height: size.height),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
|
||||
var leftItemList: [_UpdatedChildComponent] = []
|
||||
for item in context.component.leftItems {
|
||||
let item = leftItems[item.id].update(
|
||||
@ -167,18 +205,18 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
rightItemList.append(item)
|
||||
availableWidth -= item.size.width
|
||||
}
|
||||
|
||||
if !leftItemList.isEmpty || !rightItemList.isEmpty {
|
||||
availableWidth -= 32.0
|
||||
}
|
||||
|
||||
|
||||
context.add(background
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||
)
|
||||
|
||||
var readingProgressAlpha = context.component.collapseFraction
|
||||
if leftItemList.isEmpty && rightItemList.isEmpty {
|
||||
readingProgressAlpha = 0.0
|
||||
}
|
||||
context.add(readingProgress
|
||||
.position(CGPoint(x: readingProgress.size.width / 2.0, y: size.height / 2.0))
|
||||
.opacity(context.component.centerItem?.id == AnyHashable("search") ? 0.0 : 1.0)
|
||||
.opacity(readingProgressAlpha)
|
||||
)
|
||||
|
||||
context.add(separator
|
||||
@ -193,51 +231,86 @@ final class BrowserNavigationBarComponent: CombinedComponent {
|
||||
var leftItemX = sideInset
|
||||
for item in leftItemList {
|
||||
context.add(item
|
||||
.position(CGPoint(x: leftItemX + item.size.width / 2.0 - (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0))
|
||||
.position(CGPoint(x: leftItemX + item.size.width / 2.0 - (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0 + verticalOffset))
|
||||
.scale(1.0 - 0.35 * context.component.collapseFraction)
|
||||
.opacity(1.0 - context.component.collapseFraction)
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
leftItemX -= item.size.width + 8.0
|
||||
centerLeftInset += item.size.width + 8.0
|
||||
leftItemX += item.size.width + itemSpacing
|
||||
centerLeftInset += item.size.width + itemSpacing
|
||||
}
|
||||
|
||||
var centerRightInset = sideInset - 5.0
|
||||
var rightItemX = context.availableSize.width - (sideInset - 5.0)
|
||||
for item in rightItemList.reversed() {
|
||||
context.add(item
|
||||
.position(CGPoint(x: rightItemX - item.size.width / 2.0 + (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0))
|
||||
.position(CGPoint(x: rightItemX - item.size.width / 2.0 + (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0 + verticalOffset))
|
||||
.scale(1.0 - 0.35 * context.component.collapseFraction)
|
||||
.opacity(1.0 - context.component.collapseFraction)
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
rightItemX -= item.size.width + 8.0
|
||||
centerRightInset += item.size.width + 8.0
|
||||
rightItemX -= item.size.width + itemSpacing
|
||||
centerRightInset += item.size.width + itemSpacing
|
||||
}
|
||||
|
||||
let maxCenterInset = max(centerLeftInset, centerRightInset)
|
||||
|
||||
if !leftItemList.isEmpty || !rightItemList.isEmpty {
|
||||
availableWidth -= 28.0
|
||||
availableWidth -= itemSpacing * CGFloat(max(0, leftItemList.count - 1)) + itemSpacing * CGFloat(max(0, rightItemList.count - 1)) + 30.0
|
||||
}
|
||||
availableWidth -= context.component.sideInset * 2.0
|
||||
|
||||
let canCenter = availableWidth > 660.0
|
||||
availableWidth = min(660.0, availableWidth)
|
||||
|
||||
let environment = BrowserNavigationBarEnvironment(fraction: context.component.collapseFraction)
|
||||
|
||||
let centerItem = context.component.centerItem.flatMap { item in
|
||||
centerItems[item.id].update(
|
||||
component: item.component,
|
||||
environment: { environment },
|
||||
availableSize: CGSize(width: availableWidth, height: expandedHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
var centerX = maxCenterInset + (context.availableSize.width - maxCenterInset * 2.0) / 2.0
|
||||
if "".isEmpty {
|
||||
if canCenter {
|
||||
centerX = context.availableSize.width / 2.0
|
||||
} else {
|
||||
centerX = centerLeftInset + (context.availableSize.width - centerLeftInset - centerRightInset) / 2.0
|
||||
}
|
||||
}
|
||||
if let centerItem = centerItem {
|
||||
let centerItemPosition = CGPoint(x: centerX, y: context.component.topInset + contentHeight / 2.0 + verticalOffset)
|
||||
context.add(centerItem
|
||||
.position(CGPoint(x: maxCenterInset + (context.availableSize.width - maxCenterInset * 2.0) / 2.0, y: context.component.topInset + contentHeight / 2.0))
|
||||
.position(centerItemPosition)
|
||||
.scale(1.0 - 0.35 * context.component.collapseFraction)
|
||||
.appear(.default(scale: false, alpha: true))
|
||||
.disappear(.default(scale: false, alpha: true))
|
||||
)
|
||||
|
||||
context.component.externalState?.centerItemFrame = centerItem.size.centered(around: centerItemPosition)
|
||||
}
|
||||
|
||||
if context.component.collapseFraction == 1.0 {
|
||||
let activateAction = context.component.activate
|
||||
let activate = activate.update(
|
||||
component: Button(
|
||||
content: AnyComponent(Rectangle(color: UIColor(rgb: 0x000000, alpha: 0.001))),
|
||||
action: {
|
||||
activateAction()
|
||||
}
|
||||
),
|
||||
availableSize: size,
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(activate
|
||||
.position(CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
return size
|
||||
|
472
submodules/BrowserUI/Sources/BrowserPdfContent.swift
Normal file
472
submodules/BrowserUI/Sources/BrowserPdfContent.swift
Normal file
@ -0,0 +1,472 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import WebKit
|
||||
import AppBundle
|
||||
import PromptUI
|
||||
import SafariServices
|
||||
import ShareController
|
||||
import UndoUI
|
||||
import UrlEscaping
|
||||
import PDFKit
|
||||
|
||||
final class BrowserPdfContent: UIView, BrowserContent, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let webView: PDFView
|
||||
private let scrollView: UIScrollView!
|
||||
|
||||
let uuid: UUID
|
||||
|
||||
private var _state: BrowserContentState
|
||||
private let statePromise: Promise<BrowserContentState>
|
||||
|
||||
var currentState: BrowserContentState {
|
||||
return self._state
|
||||
}
|
||||
var state: Signal<BrowserContentState, NoError> {
|
||||
return self.statePromise.get()
|
||||
}
|
||||
|
||||
var pushContent: (BrowserScreen.Subject) -> Void = { _ in }
|
||||
var openAppUrl: (String) -> Void = { _ in }
|
||||
var onScrollingUpdate: (ContentScrollingUpdate) -> Void = { _ in }
|
||||
var minimize: () -> Void = { }
|
||||
var close: () -> Void = { }
|
||||
var present: (ViewController, Any?) -> Void = { _, _ in }
|
||||
var presentInGlobalOverlay: (ViewController) -> Void = { _ in }
|
||||
var getNavigationController: () -> NavigationController? = { return nil }
|
||||
|
||||
private var tempFile: TempBoxFile?
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, file: TelegramMediaFile) {
|
||||
self.context = context
|
||||
self.uuid = UUID()
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.webView = PDFView()
|
||||
self.webView.maxScaleFactor = 4.0;
|
||||
self.webView.minScaleFactor = self.webView.scaleFactorForSizeToFit
|
||||
self.webView.autoScales = true
|
||||
|
||||
var scrollView: UIScrollView?
|
||||
for view in self.webView.subviews {
|
||||
if let view = view as? UIScrollView {
|
||||
scrollView = view
|
||||
} else {
|
||||
for subview in view.subviews {
|
||||
if let subview = subview as? UIScrollView {
|
||||
scrollView = subview
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.scrollView = scrollView
|
||||
|
||||
var title: String = "file"
|
||||
if let path = self.context.account.postbox.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) {
|
||||
// var updatedPath = path
|
||||
// if let fileName = file.fileName {
|
||||
// let tempFile = TempBox.shared.file(path: path, fileName: fileName)
|
||||
// updatedPath = tempFile.path
|
||||
// self.tempFile = tempFile
|
||||
// title = fileName
|
||||
// }
|
||||
|
||||
self.webView.document = PDFDocument(data: data)
|
||||
title = file.fileName ?? "file"
|
||||
}
|
||||
|
||||
self._state = BrowserContentState(title: title, url: "", estimatedProgress: 0.0, readingProgress: 0.0, contentType: .document)
|
||||
self.statePromise = Promise<BrowserContentState>(self._state)
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
}
|
||||
self.addSubview(self.webView)
|
||||
|
||||
Queue.mainQueue().after(1.0) {
|
||||
scrollView?.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
if #available(iOS 15.0, *) {
|
||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
}
|
||||
if let (size, insets, fullInsets) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, fullInsets: fullInsets, safeInsets: .zero, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
var currentFontState = BrowserPresentationState.FontState(size: 100, isSerif: false)
|
||||
func updateFontState(_ state: BrowserPresentationState.FontState) {
|
||||
self.updateFontState(state, force: false)
|
||||
}
|
||||
func updateFontState(_ state: BrowserPresentationState.FontState, force: Bool) {
|
||||
self.currentFontState = state
|
||||
|
||||
// let fontFamily = state.isSerif ? "'Georgia, serif'" : "null"
|
||||
// let textSizeAdjust = state.size != 100 ? "'\(state.size)%'" : "null"
|
||||
// let js = "\(setupFontFunctions) setTelegramFontOverrides(\(fontFamily), \(textSizeAdjust))";
|
||||
// self.webView.evaluateJavaScript(js) { _, _ in }
|
||||
}
|
||||
|
||||
private var didSetupSearch = false
|
||||
private func setupSearch(completion: @escaping () -> Void) {
|
||||
// guard !self.didSetupSearch else {
|
||||
// completion()
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let bundle = getAppBundle()
|
||||
// guard let scriptPath = bundle.path(forResource: "UIWebViewSearch", ofType: "js") else {
|
||||
// return
|
||||
// }
|
||||
// guard let scriptData = try? Data(contentsOf: URL(fileURLWithPath: scriptPath)) else {
|
||||
// return
|
||||
// }
|
||||
// guard let script = String(data: scriptData, encoding: .utf8) else {
|
||||
// return
|
||||
// }
|
||||
// self.didSetupSearch = true
|
||||
// self.webView.evaluateJavaScript(script, completionHandler: { _, error in
|
||||
// if error != nil {
|
||||
// print()
|
||||
// }
|
||||
// completion()
|
||||
// })
|
||||
}
|
||||
|
||||
private var previousQuery: String?
|
||||
func setSearch(_ query: String?, completion: ((Int) -> Void)?) {
|
||||
// guard self.previousQuery != query else {
|
||||
// return
|
||||
// }
|
||||
// self.previousQuery = query
|
||||
// self.setupSearch { [weak self] in
|
||||
// if let query = query {
|
||||
// let js = "uiWebview_HighlightAllOccurencesOfString('\(query)')"
|
||||
// self?.webView.evaluateJavaScript(js, completionHandler: { [weak self] _, _ in
|
||||
// let js = "uiWebview_SearchResultCount"
|
||||
// self?.webView.evaluateJavaScript(js, completionHandler: { [weak self] result, _ in
|
||||
// if let result = result as? NSNumber {
|
||||
// self?.searchResultsCount = result.intValue
|
||||
// completion?(result.intValue)
|
||||
// } else {
|
||||
// completion?(0)
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// } else {
|
||||
// let js = "uiWebview_RemoveAllHighlights()"
|
||||
// self?.webView.evaluateJavaScript(js, completionHandler: nil)
|
||||
//
|
||||
// self?.currentSearchResult = 0
|
||||
// self?.searchResultsCount = 0
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private var currentSearchResult: Int = 0
|
||||
private var searchResultsCount: Int = 0
|
||||
|
||||
func scrollToPreviousSearchResult(completion: ((Int, Int) -> Void)?) {
|
||||
// let searchResultsCount = self.searchResultsCount
|
||||
// var index = self.currentSearchResult - 1
|
||||
// if index < 0 {
|
||||
// index = searchResultsCount - 1
|
||||
// }
|
||||
// self.currentSearchResult = index
|
||||
//
|
||||
// let js = "uiWebview_ScrollTo('\(searchResultsCount - index - 1)')"
|
||||
// self.webView.evaluateJavaScript(js, completionHandler: { _, _ in
|
||||
// completion?(index, searchResultsCount)
|
||||
// })
|
||||
}
|
||||
|
||||
func scrollToNextSearchResult(completion: ((Int, Int) -> Void)?) {
|
||||
// let searchResultsCount = self.searchResultsCount
|
||||
// var index = self.currentSearchResult + 1
|
||||
// if index >= searchResultsCount {
|
||||
// index = 0
|
||||
// }
|
||||
// self.currentSearchResult = index
|
||||
//
|
||||
// let js = "uiWebview_ScrollTo('\(searchResultsCount - index - 1)')"
|
||||
// self.webView.evaluateJavaScript(js, completionHandler: { _, _ in
|
||||
// completion?(index, searchResultsCount)
|
||||
// })
|
||||
}
|
||||
|
||||
func stop() {
|
||||
// self.webView.stopLoading()
|
||||
}
|
||||
|
||||
func reload() {
|
||||
// self.webView.reload()
|
||||
}
|
||||
|
||||
func navigateBack() {
|
||||
// self.webView.goBack()
|
||||
}
|
||||
|
||||
func navigateForward() {
|
||||
// self.webView.goForward()
|
||||
}
|
||||
|
||||
func navigateTo(historyItem: BrowserContentState.HistoryItem) {
|
||||
// if let webItem = historyItem.webItem {
|
||||
// self.webView.go(to: webItem)
|
||||
// }
|
||||
}
|
||||
|
||||
func navigateTo(address: String) {
|
||||
// let finalUrl = explicitUrl(address)
|
||||
// guard let url = URL(string: finalUrl) else {
|
||||
// return
|
||||
// }
|
||||
// self.webView.load(URLRequest(url: url))
|
||||
}
|
||||
|
||||
func scrollToTop() {
|
||||
self.scrollView.setContentOffset(CGPoint(x: 0.0, y: -self.scrollView.contentInset.top), animated: true)
|
||||
}
|
||||
|
||||
private var validLayout: (CGSize, UIEdgeInsets, UIEdgeInsets)?
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, transition: ComponentTransition) {
|
||||
self.validLayout = (size, insets, fullInsets)
|
||||
|
||||
self.previousScrollingOffset = ScrollingOffsetState(value: self.scrollView.contentOffset.y, isDraggingOrDecelerating: self.scrollView.isDragging || self.scrollView.isDecelerating)
|
||||
|
||||
let webViewFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: size.width - insets.left - insets.right, height: size.height - insets.top - insets.bottom))
|
||||
var refresh = false
|
||||
if self.webView.frame.width > 0 && webViewFrame.width != self.webView.frame.width {
|
||||
refresh = true
|
||||
}
|
||||
transition.setFrame(view: self.webView, frame: webViewFrame)
|
||||
|
||||
if refresh {
|
||||
self.webView.reloadInputViews()
|
||||
}
|
||||
|
||||
// if let error = self.currentError {
|
||||
// let errorSize = self.errorView.update(
|
||||
// transition: .immediate,
|
||||
// component: AnyComponent(
|
||||
// ErrorComponent(
|
||||
// theme: self.presentationData.theme,
|
||||
// title: self.presentationData.strings.Browser_ErrorTitle,
|
||||
// text: error.localizedDescription
|
||||
// )
|
||||
// ),
|
||||
// environment: {},
|
||||
// containerSize: CGSize(width: size.width - insets.left - insets.right - 72.0, height: size.height)
|
||||
// )
|
||||
// if self.errorView.superview == nil {
|
||||
// self.addSubview(self.errorView)
|
||||
// self.errorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
// }
|
||||
// self.errorView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - errorSize.width) / 2.0), y: insets.top + floorToScreenPixels((size.height - insets.top - insets.bottom - errorSize.height) / 2.0)), size: errorSize)
|
||||
// } else if self.errorView.superview != nil {
|
||||
// self.errorView.removeFromSuperview()
|
||||
// }
|
||||
}
|
||||
|
||||
private func updateState(_ f: (BrowserContentState) -> BrowserContentState) {
|
||||
let updated = f(self._state)
|
||||
self._state = updated
|
||||
self.statePromise.set(.single(self._state))
|
||||
}
|
||||
|
||||
private struct ScrollingOffsetState: Equatable {
|
||||
var value: CGFloat
|
||||
var isDraggingOrDecelerating: Bool
|
||||
}
|
||||
|
||||
private var previousScrollingOffset: ScrollingOffsetState?
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateScrollingOffset(isReset: false, transition: .immediate)
|
||||
}
|
||||
|
||||
private func snapScrollingOffsetToInsets() {
|
||||
let transition = ComponentTransition(animation: .curve(duration: 0.4, curve: .spring))
|
||||
self.updateScrollingOffset(isReset: false, transition: transition)
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if !decelerate {
|
||||
self.snapScrollingOffsetToInsets()
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
self.snapScrollingOffsetToInsets()
|
||||
}
|
||||
|
||||
private func updateScrollingOffset(isReset: Bool, transition: ComponentTransition) {
|
||||
guard let scrollView = self.scrollView else {
|
||||
return
|
||||
}
|
||||
let isInteracting = scrollView.isDragging || scrollView.isDecelerating
|
||||
if let previousScrollingOffsetValue = self.previousScrollingOffset {
|
||||
let currentBounds = scrollView.bounds
|
||||
let offsetToTopEdge = max(0.0, currentBounds.minY - 0.0)
|
||||
let offsetToBottomEdge = max(0.0, scrollView.contentSize.height - currentBounds.maxY)
|
||||
|
||||
let relativeOffset = scrollView.contentOffset.y - previousScrollingOffsetValue.value
|
||||
self.onScrollingUpdate(ContentScrollingUpdate(
|
||||
relativeOffset: relativeOffset,
|
||||
absoluteOffsetToTopEdge: offsetToTopEdge,
|
||||
absoluteOffsetToBottomEdge: offsetToBottomEdge,
|
||||
isReset: isReset,
|
||||
isInteracting: isInteracting,
|
||||
transition: transition
|
||||
))
|
||||
}
|
||||
self.previousScrollingOffset = ScrollingOffsetState(value: scrollView.contentOffset.y, isDraggingOrDecelerating: isInteracting)
|
||||
|
||||
var readingProgress: CGFloat = 0.0
|
||||
if !scrollView.contentSize.height.isZero {
|
||||
let value = (scrollView.contentOffset.y + scrollView.contentInset.top) / (scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.top)
|
||||
readingProgress = max(0.0, min(1.0, value))
|
||||
}
|
||||
self.updateState {
|
||||
$0.withUpdatedReadingProgress(readingProgress)
|
||||
}
|
||||
}
|
||||
|
||||
func resetScrolling() {
|
||||
self.updateScrollingOffset(isReset: true, transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||
// self.currentError = nil
|
||||
self.updateFontState(self.currentFontState, force: true)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
self.updateState {
|
||||
$0
|
||||
.withUpdatedBackList(webView.backForwardList.backList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
||||
.withUpdatedForwardList(webView.backForwardList.forwardList.map { BrowserContentState.HistoryItem(webItem: $0) })
|
||||
}
|
||||
}
|
||||
|
||||
// func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||
// if (error as NSError).code != -999 {
|
||||
// self.currentError = error
|
||||
// } else {
|
||||
// self.currentError = nil
|
||||
// }
|
||||
// if let (size, insets) = self.validLayout {
|
||||
// self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
||||
// if (error as NSError).code != -999 {
|
||||
// self.currentError = error
|
||||
// } else {
|
||||
// self.currentError = nil
|
||||
// }
|
||||
// if let (size, insets) = self.validLayout {
|
||||
// self.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||
// }
|
||||
// }
|
||||
|
||||
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
|
||||
if navigationAction.targetFrame == nil {
|
||||
if let url = navigationAction.request.url?.absoluteString {
|
||||
self.open(url: url, new: true)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func webViewDidClose(_ webView: WKWebView) {
|
||||
self.close()
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 15.0, iOS 15.0, *)
|
||||
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
||||
decisionHandler(.prompt)
|
||||
}
|
||||
|
||||
|
||||
// @available(iOS 13.0, *)
|
||||
// func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) {
|
||||
// guard let url = elementInfo.linkURL else {
|
||||
// completionHandler(nil)
|
||||
// return
|
||||
// }
|
||||
// let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
// let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { [weak self] _ in
|
||||
// return UIMenu(title: "", children: [
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_Open, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// self?.open(url: url.absoluteString, new: false)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_OpenInNewTab, image: generateTintedImage(image: UIImage(bundleImageName: "Instant View/NewTab"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// self?.open(url: url.absoluteString, new: true)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_AddToReadingList, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadingList"), color: presentationData.theme.contextMenu.primaryColor), handler: { _ in
|
||||
// let _ = try? SSReadingList.default()?.addItem(with: url, title: nil, previewText: nil)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_CopyLink, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// UIPasteboard.general.string = url.absoluteString
|
||||
// self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
// }),
|
||||
// UIAction(title: presentationData.strings.Browser_ContextMenu_Share, image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: presentationData.theme.contextMenu.primaryColor), handler: { [weak self] _ in
|
||||
// self?.share(url: url.absoluteString)
|
||||
// })
|
||||
// ])
|
||||
// }
|
||||
// completionHandler(configuration)
|
||||
// }
|
||||
|
||||
private func open(url: String, new: Bool) {
|
||||
let subject: BrowserScreen.Subject = .webPage(url: url)
|
||||
if new, let navigationController = self.getNavigationController() {
|
||||
navigationController._keepModalDismissProgress = true
|
||||
self.minimize()
|
||||
let controller = BrowserScreen(context: self.context, subject: subject)
|
||||
navigationController._keepModalDismissProgress = true
|
||||
navigationController.pushViewController(controller)
|
||||
} else {
|
||||
self.pushContent(subject)
|
||||
}
|
||||
}
|
||||
|
||||
private func share(url: String) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let shareController = ShareController(context: self.context, subject: .url(url))
|
||||
shareController.actionCompleted = { [weak self] in
|
||||
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
}
|
||||
self.present(shareController, nil)
|
||||
}
|
||||
|
||||
func addToRecentlyVisited() {
|
||||
}
|
||||
|
||||
func makeContentSnapshotView() -> UIView? {
|
||||
return nil
|
||||
}
|
||||
}
|
88
submodules/BrowserUI/Sources/BrowserRecentlyVisited.swift
Normal file
88
submodules/BrowserUI/Sources/BrowserRecentlyVisited.swift
Normal file
@ -0,0 +1,88 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import TelegramUIPreferences
|
||||
|
||||
private struct RecentlyVisitedLinkItemId {
|
||||
public let rawValue: MemoryBuffer
|
||||
|
||||
var value: String {
|
||||
return String(data: self.rawValue.makeData(), encoding: .utf8) ?? ""
|
||||
}
|
||||
|
||||
init(_ rawValue: MemoryBuffer) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
init?(_ value: String) {
|
||||
if let data = value.data(using: .utf8) {
|
||||
self.rawValue = MemoryBuffer(data: data)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class RecentVisitedLinkItem: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case webPage
|
||||
}
|
||||
|
||||
public let webPage: TelegramMediaWebpage
|
||||
|
||||
public init(webPage: TelegramMediaWebpage) {
|
||||
self.webPage = webPage
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let webPageData = try container.decodeIfPresent(Data.self, forKey: .webPage) {
|
||||
self.webPage = PostboxDecoder(buffer: MemoryBuffer(data: webPageData)).decodeRootObject() as! TelegramMediaWebpage
|
||||
} else {
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let encoder = PostboxEncoder()
|
||||
encoder.encodeRootObject(self.webPage)
|
||||
let webPageData = encoder.makeData()
|
||||
try container.encode(webPageData, forKey: .webPage)
|
||||
}
|
||||
}
|
||||
|
||||
func addRecentlyVisitedLink(engine: TelegramEngine, webPage: TelegramMediaWebpage) -> Signal<Never, NoError> {
|
||||
if let url = webPage.content.url, let itemId = RecentlyVisitedLinkItemId(url) {
|
||||
return engine.orderedLists.addOrMoveToFirstPosition(collectionId: ApplicationSpecificOrderedItemListCollectionId.browserRecentlyVisited, id: itemId.rawValue, item: RecentVisitedLinkItem(webPage: webPage), removeTailIfCountExceeds: 10)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
func removeRecentlyVisitedLink(engine: TelegramEngine, url: String) -> Signal<Never, NoError> {
|
||||
if let itemId = RecentlyVisitedLinkItemId(url) {
|
||||
return engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.browserRecentlyVisited, id: itemId.rawValue)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
func clearRecentlyVisitedLinks(engine: TelegramEngine) -> Signal<Never, NoError> {
|
||||
return engine.orderedLists.clear(collectionId: ApplicationSpecificOrderedItemListCollectionId.browserRecentlyVisited)
|
||||
}
|
||||
|
||||
func recentlyVisitedLinks(engine: TelegramEngine) -> Signal<[TelegramMediaWebpage], NoError> {
|
||||
return engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: ApplicationSpecificOrderedItemListCollectionId.browserRecentlyVisited))
|
||||
|> map { items -> [TelegramMediaWebpage] in
|
||||
var result: [TelegramMediaWebpage] = []
|
||||
for item in items {
|
||||
if let link = item.contents.get(RecentVisitedLinkItem.self) {
|
||||
result.append(link.webPage)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
@ -73,13 +73,19 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
static var body: Body {
|
||||
let navigationBar = Child(BrowserNavigationBarComponent.self)
|
||||
let toolbar = Child(BrowserToolbarComponent.self)
|
||||
let addressList = Child(BrowserAddressListComponent.self)
|
||||
|
||||
let navigationBarExternalState = BrowserNavigationBarComponent.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let performAction = context.component.performAction
|
||||
let performHoldAction = context.component.performHoldAction
|
||||
|
||||
let navigationContent: AnyComponentWithIdentity<Empty>?
|
||||
let isTablet = environment.metrics.isTablet
|
||||
let canOpenIn = !(context.component.contentState?.url.hasPrefix("tonsite") ?? false)
|
||||
|
||||
let navigationContent: AnyComponentWithIdentity<BrowserNavigationBarEnvironment>?
|
||||
var navigationLeftItems: [AnyComponentWithIdentity<Empty>]
|
||||
var navigationRightItems: [AnyComponentWithIdentity<Empty>]
|
||||
if context.component.presentationState.isSearching {
|
||||
@ -96,51 +102,204 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
navigationLeftItems = []
|
||||
navigationRightItems = []
|
||||
} else {
|
||||
let title = context.component.contentState?.title ?? ""
|
||||
navigationContent = AnyComponentWithIdentity(
|
||||
id: "title_\(title)",
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: title, font: Font.bold(17.0), textColor: environment.theme.rootController.navigationBar.primaryTextColor, paragraphAlignment: .center)), horizontalAlignment: .center, maximumNumberOfLines: 1)
|
||||
)
|
||||
)
|
||||
navigationLeftItems = [
|
||||
AnyComponentWithIdentity(
|
||||
id: "close",
|
||||
let contentType = context.component.contentState?.contentType ?? .instantPage
|
||||
switch contentType {
|
||||
case .webPage:
|
||||
navigationContent = AnyComponentWithIdentity(
|
||||
id: "addressBar",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.WebBrowser_Done, font: Font.regular(17.0), textColor: environment.theme.rootController.navigationBar.accentTextColor, paragraphAlignment: .center)), horizontalAlignment: .left, maximumNumberOfLines: 1)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.close)
|
||||
}
|
||||
AddressBarContentComponent(
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
metrics: environment.metrics,
|
||||
url: context.component.contentState?.url ?? "",
|
||||
isSecure: context.component.contentState?.isSecure ?? false,
|
||||
isExpanded: context.component.presentationState.addressFocused,
|
||||
performAction: performAction
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
navigationRightItems = [
|
||||
AnyComponentWithIdentity(
|
||||
id: "settings",
|
||||
case .instantPage, .document:
|
||||
let title = context.component.contentState?.title ?? ""
|
||||
navigationContent = AnyComponentWithIdentity(
|
||||
id: "titleBar_\(title)",
|
||||
component: AnyComponent(
|
||||
ReferenceButtonComponent(
|
||||
content: AnyComponent(
|
||||
LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(
|
||||
name: "anim_moredots"
|
||||
TitleBarContentComponent(
|
||||
theme: environment.theme,
|
||||
title: title
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if context.component.presentationState.addressFocused && !isTablet {
|
||||
navigationLeftItems = []
|
||||
navigationRightItems = []
|
||||
} else {
|
||||
navigationLeftItems = [
|
||||
AnyComponentWithIdentity(
|
||||
id: "close",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.WebBrowser_Done, font: Font.semibold(17.0), textColor: environment.theme.rootController.navigationBar.accentTextColor, paragraphAlignment: .center)), horizontalAlignment: .left, maximumNumberOfLines: 1)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.close)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
if isTablet {
|
||||
#if DEBUG
|
||||
navigationLeftItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "minimize",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Media Gallery/PictureInPictureButton",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
)
|
||||
),
|
||||
color: environment.theme.rootController.navigationBar.accentTextColor,
|
||||
size: CGSize(width: 30.0, height: 30.0)
|
||||
action: {
|
||||
performAction.invoke(.close)
|
||||
}
|
||||
)
|
||||
),
|
||||
tag: settingsTag,
|
||||
action: {
|
||||
performAction.invoke(.openSettings)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
#endif
|
||||
|
||||
let canGoBack = context.component.contentState?.canGoBack ?? false
|
||||
let canGoForward = context.component.contentState?.canGoForward ?? false
|
||||
|
||||
navigationLeftItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "back",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Back",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(canGoBack ? 1.0 : 0.4)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.navigateBack)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
navigationLeftItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "forward",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Forward",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(canGoForward ? 1.0 : 0.4)
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.navigateForward)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
navigationRightItems = [
|
||||
AnyComponentWithIdentity(
|
||||
id: "settings",
|
||||
component: AnyComponent(
|
||||
ReferenceButtonComponent(
|
||||
content: AnyComponent(
|
||||
LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(
|
||||
name: "anim_moredots"
|
||||
),
|
||||
color: environment.theme.rootController.navigationBar.accentTextColor,
|
||||
size: CGSize(width: 30.0, height: 30.0)
|
||||
)
|
||||
),
|
||||
tag: settingsTag,
|
||||
action: {
|
||||
performAction.invoke(.openSettings)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
if isTablet {
|
||||
navigationRightItems.insert(
|
||||
AnyComponentWithIdentity(
|
||||
id: "bookmarks",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Bookmark",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.openBookmarks)
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
at: 0
|
||||
)
|
||||
navigationRightItems.insert(
|
||||
AnyComponentWithIdentity(
|
||||
id: "share",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Chat List/NavigationShare",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.share)
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
at: 0
|
||||
)
|
||||
if canOpenIn {
|
||||
navigationRightItems.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "openIn",
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Browser",
|
||||
tintColor: environment.theme.rootController.navigationBar.accentTextColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.openIn)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let collapseFraction = context.component.presentationState.isSearching ? 0.0 : context.component.panelCollapseFraction
|
||||
@ -155,12 +314,17 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
topInset: environment.statusBarHeight,
|
||||
height: environment.navigationHeight - environment.statusBarHeight,
|
||||
sideInset: environment.safeInsets.left,
|
||||
metrics: environment.metrics,
|
||||
externalState: navigationBarExternalState,
|
||||
leftItems: navigationLeftItems,
|
||||
rightItems: navigationRightItems,
|
||||
centerItem: navigationContent,
|
||||
readingProgress: context.component.contentState?.readingProgress ?? 0.0,
|
||||
loadingProgress: context.component.contentState?.estimatedProgress,
|
||||
collapseFraction: collapseFraction
|
||||
collapseFraction: collapseFraction,
|
||||
activate: {
|
||||
performAction.invoke(.expand)
|
||||
}
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
@ -193,6 +357,7 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
canGoBack: context.component.contentState?.canGoBack ?? false,
|
||||
canGoForward: context.component.contentState?.canGoForward ?? false,
|
||||
canOpenIn: canOpenIn,
|
||||
performAction: performAction,
|
||||
performHoldAction: performHoldAction
|
||||
)
|
||||
@ -207,22 +372,77 @@ private final class BrowserScreenComponent: CombinedComponent {
|
||||
toolbarBottomInset = environment.safeInsets.bottom
|
||||
}
|
||||
|
||||
let toolbar = toolbar.update(
|
||||
component: BrowserToolbarComponent(
|
||||
backgroundColor: environment.theme.rootController.navigationBar.blurredBackgroundColor,
|
||||
separatorColor: environment.theme.rootController.navigationBar.separatorColor,
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
bottomInset: toolbarBottomInset,
|
||||
sideInset: environment.safeInsets.left,
|
||||
item: toolbarContent,
|
||||
collapseFraction: collapseFraction
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(toolbar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - toolbar.size.height / 2.0))
|
||||
)
|
||||
var toolbarSize: CGFloat = 0.0
|
||||
if isTablet && !context.component.presentationState.isSearching {
|
||||
|
||||
} else {
|
||||
let toolbar = toolbar.update(
|
||||
component: BrowserToolbarComponent(
|
||||
backgroundColor: environment.theme.rootController.navigationBar.blurredBackgroundColor,
|
||||
separatorColor: environment.theme.rootController.navigationBar.separatorColor,
|
||||
textColor: environment.theme.rootController.navigationBar.primaryTextColor,
|
||||
bottomInset: toolbarBottomInset,
|
||||
sideInset: environment.safeInsets.left,
|
||||
item: toolbarContent,
|
||||
collapseFraction: collapseFraction
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(toolbar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height - toolbar.size.height / 2.0))
|
||||
.appear(ComponentTransition.Appear { _, view, transition in
|
||||
transition.animatePosition(view: view, from: CGPoint(x: 0.0, y: view.frame.height), to: CGPoint(), additive: true)
|
||||
})
|
||||
.disappear(ComponentTransition.Disappear { view, transition, completion in
|
||||
transition.animatePosition(view: view, from: CGPoint(), to: CGPoint(x: 0.0, y: view.frame.height), additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
})
|
||||
)
|
||||
toolbarSize = toolbar.size.height
|
||||
}
|
||||
|
||||
if context.component.presentationState.addressFocused {
|
||||
let addressListSize: CGSize
|
||||
if isTablet {
|
||||
addressListSize = context.availableSize
|
||||
} else {
|
||||
addressListSize = CGSize(width: context.availableSize.width, height: context.availableSize.height - navigationBar.size.height - toolbarSize)
|
||||
}
|
||||
let controller = environment.controller
|
||||
let addressList = addressList.update(
|
||||
component: BrowserAddressListComponent(
|
||||
context: context.component.context,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
insets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
metrics: environment.metrics,
|
||||
addressBarFrame: navigationBarExternalState.centerItemFrame,
|
||||
performAction: performAction,
|
||||
presentInGlobalOverlay: { c in
|
||||
controller()?.presentInGlobalOverlay(c)
|
||||
}
|
||||
),
|
||||
availableSize: addressListSize,
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
if isTablet {
|
||||
context.add(addressList
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
} else {
|
||||
context.add(addressList
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0))
|
||||
.clipsToBounds(true)
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
@ -239,6 +459,7 @@ struct BrowserPresentationState: Equatable {
|
||||
var searchResultIndex: Int
|
||||
var searchResultCount: Int
|
||||
var searchQueryIsEmpty: Bool
|
||||
var addressFocused: Bool
|
||||
}
|
||||
|
||||
public class BrowserScreen: ViewController, MinimizableController {
|
||||
@ -262,6 +483,10 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
case updateFontIsSerif(Bool)
|
||||
case addBookmark
|
||||
case openBookmarks
|
||||
case openAddressBar
|
||||
case closeAddressBar
|
||||
case navigateTo(String, Bool)
|
||||
case expand
|
||||
}
|
||||
|
||||
fileprivate final class Node: ViewControllerTracingNode {
|
||||
@ -271,7 +496,6 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
private let contentContainerView = UIView()
|
||||
fileprivate let contentNavigationContainer = ComponentView<Empty>()
|
||||
fileprivate var content: [BrowserContent] = []
|
||||
|
||||
fileprivate var contentState: BrowserContentState?
|
||||
private var contentStateDisposable = MetaDisposable()
|
||||
|
||||
@ -292,13 +516,20 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
|
||||
self.presentationState = BrowserPresentationState(
|
||||
fontState: BrowserPresentationState.FontState(size: 100, isSerif: false),
|
||||
isSearching: false, searchResultIndex: 0, searchResultCount: 0, searchQueryIsEmpty: true
|
||||
isSearching: false,
|
||||
searchResultIndex: 0,
|
||||
searchResultCount: 0,
|
||||
searchQueryIsEmpty: true,
|
||||
addressFocused: false
|
||||
)
|
||||
|
||||
super.init()
|
||||
|
||||
self.pushContent(controller.subject, transition: .immediate)
|
||||
|
||||
if let content = self.content.last {
|
||||
content.addToRecentlyVisited()
|
||||
}
|
||||
|
||||
self.performAction.connect { [weak self] action in
|
||||
guard let self, let content = self.content.last, let url = self.contentState?.url else {
|
||||
return
|
||||
@ -341,7 +572,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
let text: String
|
||||
var savedMessages = false
|
||||
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
|
||||
text = presentationData.strings.WebBrowser_LinkForwardTooltip_SavedMessages_One
|
||||
text = presentationData.strings.WebBrowser_LinkAddedToBookmarks
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
@ -388,7 +619,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
case .openSettings:
|
||||
self.openSettings()
|
||||
case let .updateSearchActive(active):
|
||||
self.updatePresentationState(animated: true, { state in
|
||||
self.updatePresentationState(transition: .easeInOut(duration: 0.2), { state in
|
||||
var updatedState = state
|
||||
updatedState.isSearching = active
|
||||
updatedState.searchQueryIsEmpty = true
|
||||
@ -485,10 +716,38 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
content.updateFontState(self.presentationState.fontState)
|
||||
case .addBookmark:
|
||||
if let content = self.content.last {
|
||||
self.addBookmark(content.currentState.url)
|
||||
self.addBookmark(content.currentState.url, showArrow: true)
|
||||
}
|
||||
case .openBookmarks:
|
||||
break
|
||||
self.openBookmarks()
|
||||
case .openAddressBar:
|
||||
self.updatePresentationState(transition: .spring(duration: 0.4), { state in
|
||||
var updatedState = state
|
||||
updatedState.addressFocused = true
|
||||
return updatedState
|
||||
})
|
||||
case .closeAddressBar:
|
||||
self.updatePresentationState(transition: .spring(duration: 0.4), { state in
|
||||
var updatedState = state
|
||||
updatedState.addressFocused = false
|
||||
return updatedState
|
||||
})
|
||||
case let .navigateTo(address, addToRecent):
|
||||
if let content = self.content.last as? BrowserWebContent {
|
||||
content.navigateTo(address: address)
|
||||
if addToRecent {
|
||||
content.addToRecentlyVisited()
|
||||
}
|
||||
}
|
||||
self.updatePresentationState(transition: .spring(duration: 0.4), { state in
|
||||
var updatedState = state
|
||||
updatedState.addressFocused = false
|
||||
return updatedState
|
||||
})
|
||||
case .expand:
|
||||
if let content = self.content.last {
|
||||
content.resetScrolling()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,16 +776,22 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
self.view.addSubview(self.contentContainerView)
|
||||
}
|
||||
|
||||
func updatePresentationState(animated: Bool = false, _ f: (BrowserPresentationState) -> BrowserPresentationState) {
|
||||
func updatePresentationState(transition: ComponentTransition = .immediate, _ f: (BrowserPresentationState) -> BrowserPresentationState) {
|
||||
self.presentationState = f(self.presentationState)
|
||||
self.requestLayout(transition: animated ? .easeInOut(duration: 0.2) : .immediate)
|
||||
self.requestLayout(transition: transition)
|
||||
}
|
||||
|
||||
|
||||
func pushContent(_ content: BrowserScreen.Subject, transition: ComponentTransition) {
|
||||
let browserContent: BrowserContent
|
||||
switch content {
|
||||
case let .webPage(url):
|
||||
browserContent = BrowserWebContent(context: self.context, presentationData: self.presentationData, url: url)
|
||||
let webContent = BrowserWebContent(context: self.context, presentationData: self.presentationData, url: url)
|
||||
webContent.cancelInteractiveTransitionGestures = { [weak self] in
|
||||
if let self, let view = self.controller?.view {
|
||||
cancelInteractiveTransitionGestures(view: view)
|
||||
}
|
||||
}
|
||||
browserContent = webContent
|
||||
case let .instantPage(webPage, anchor, sourceLocation):
|
||||
let instantPageContent = BrowserInstantPageContent(context: self.context, presentationData: self.presentationData, webPage: webPage, anchor: anchor, url: webPage.content.url ?? "", sourceLocation: sourceLocation)
|
||||
instantPageContent.openPeer = { [weak self] peer in
|
||||
@ -536,6 +801,10 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
self.openPeer(peer)
|
||||
}
|
||||
browserContent = instantPageContent
|
||||
case let .document(file):
|
||||
browserContent = BrowserDocumentContent(context: self.context, presentationData: self.presentationData, file: file)
|
||||
case let .pdfDocument(file):
|
||||
browserContent = BrowserPdfContent(context: self.context, presentationData: self.presentationData, file: file)
|
||||
}
|
||||
browserContent.pushContent = { [weak self] content in
|
||||
guard let self else {
|
||||
@ -543,6 +812,14 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}
|
||||
self.pushContent(content, transition: .spring(duration: 0.4))
|
||||
}
|
||||
browserContent.openAppUrl = { [weak self] url in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: false, presentationData: self.presentationData, navigationController: self.controller?.navigationController as? NavigationController, dismissInput: { [weak self] in
|
||||
self?.view.window?.endEditing(true)
|
||||
})
|
||||
}
|
||||
browserContent.present = { [weak self] c, a in
|
||||
guard let self, let controller = self.controller else {
|
||||
return
|
||||
@ -596,7 +873,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), animated: true))
|
||||
}
|
||||
|
||||
func addBookmark(_ url: String) {
|
||||
func addBookmark(_ url: String, showArrow: Bool) {
|
||||
let _ = enqueueMessages(
|
||||
account: self.context.account,
|
||||
peerId: self.context.account.peerId,
|
||||
@ -615,7 +892,9 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
).start()
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: presentationData.strings.WebBrowser_LinkAddedToBookmarks), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
|
||||
|
||||
let lastController = self.controller?.navigationController?.viewControllers.last as? ViewController
|
||||
lastController?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: presentationData.strings.WebBrowser_LinkAddedToBookmarks), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
|
||||
if let self, action == .info {
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
@ -708,6 +987,20 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}, animated: true)
|
||||
}
|
||||
|
||||
func openBookmarks() {
|
||||
guard let url = self.contentState?.url else {
|
||||
return
|
||||
}
|
||||
let controller = BrowserBookmarksScreen(context: self.context, url: url, openUrl: { [weak self] url in
|
||||
if let self {
|
||||
self.performAction.invoke(.navigateTo(url, true))
|
||||
}
|
||||
}, addBookmark: { [weak self] in
|
||||
self?.addBookmark(url, showArrow: false)
|
||||
})
|
||||
self.controller?.push(controller)
|
||||
}
|
||||
|
||||
func openSettings() {
|
||||
guard let referenceView = self.componentHost.findTaggedView(tag: settingsTag) as? ReferenceButtonComponent.View else {
|
||||
return
|
||||
@ -736,7 +1029,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
|
||||
let _ = (settings
|
||||
|> deliverOnMainQueue).start(next: { [weak self] settings in
|
||||
guard let self, let controller = self.controller else {
|
||||
guard let self, let controller = self.controller, let contentState = self.contentState, let layout = self.validLayout?.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -771,7 +1064,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
defaultWebBrowser = "safari"
|
||||
}
|
||||
|
||||
let url = self.contentState?.url ?? ""
|
||||
let url = contentState.url
|
||||
let openInOptions = availableOpenInOptions(context: self.context, item: .url(url: url))
|
||||
let openInTitle: String
|
||||
let openInUrl: String
|
||||
@ -787,40 +1080,59 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
openInUrl = url
|
||||
}
|
||||
|
||||
let items: [ContextMenuItem] = [
|
||||
.custom(fontItem, false),
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_FontSanFrancisco, icon: forceIsSerif ? emptyIcon : checkIcon, action: { (controller, action) in
|
||||
|
||||
let canOpenIn = !(self.contentState?.url.hasPrefix("tonsite") ?? false)
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.custom(fontItem, false))
|
||||
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_FontSanFrancisco, icon: forceIsSerif ? emptyIcon : checkIcon, action: { (controller, action) in
|
||||
performAction.invoke(.updateFontIsSerif(false))
|
||||
action(.default)
|
||||
})),
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_FontNewYork, textFont: .custom(font: Font.with(size: 17.0, design: .serif, traits: []), height: nil, verticalOffset: nil), icon: forceIsSerif ? checkIcon : emptyIcon, action: { (controller, action) in
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_FontNewYork, textFont: .custom(font: Font.with(size: 17.0, design: .serif, traits: []), height: nil, verticalOffset: nil), icon: forceIsSerif ? checkIcon : emptyIcon, action: { (controller, action) in
|
||||
performAction.invoke(.updateFontIsSerif(true))
|
||||
action(.default)
|
||||
})),
|
||||
.separator,
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_Reload, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Instant View/Settings/Reload"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
})))
|
||||
|
||||
items.append(.separator)
|
||||
|
||||
if case .webPage = contentState.contentType {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_Reload, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Instant View/Settings/Reload"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
performAction.invoke(.reload)
|
||||
action(.default)
|
||||
})),
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_Search, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Instant View/Settings/Search"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
})))
|
||||
}
|
||||
if [.webPage].contains(contentState.contentType) {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_Search, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Instant View/Settings/Search"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
performAction.invoke(.updateSearchActive(true))
|
||||
action(.default)
|
||||
})),
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_Share, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
})))
|
||||
}
|
||||
|
||||
if !layout.metrics.isTablet {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_Share, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
performAction.invoke(.share)
|
||||
action(.default)
|
||||
})),
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_AddBookmark, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
})))
|
||||
}
|
||||
|
||||
if [.webPage, .instantPage].contains(contentState.contentType) {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.WebBrowser_AddBookmark, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { (controller, action) in
|
||||
performAction.invoke(.addBookmark)
|
||||
action(.default)
|
||||
})),
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_OpenInBrowser(openInTitle).string, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] (controller, action) in
|
||||
if let self {
|
||||
self.context.sharedContext.applicationBindings.openUrl(openInUrl)
|
||||
}
|
||||
action(.default)
|
||||
}))
|
||||
]
|
||||
})))
|
||||
if !layout.metrics.isTablet && canOpenIn {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.InstantPage_OpenInBrowser(openInTitle).string, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Browser"), color: theme.contextMenu.primaryColor) }, action: { [weak self] (controller, action) in
|
||||
if let self {
|
||||
self.context.sharedContext.applicationBindings.openUrl(openInUrl)
|
||||
}
|
||||
action(.default)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))))
|
||||
self.controller?.present(contextController, in: .window(.root))
|
||||
@ -878,6 +1190,10 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}
|
||||
}
|
||||
|
||||
if update.isReset {
|
||||
scrollingPanelOffsetFraction = 0.0
|
||||
}
|
||||
|
||||
if scrollingPanelOffsetFraction != self.scrollingPanelOffsetFraction {
|
||||
self.scrollingPanelOffsetFraction = scrollingPanelOffsetFraction
|
||||
self.requestLayout(transition: transition)
|
||||
@ -1006,7 +1322,7 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
BrowserContentComponent(
|
||||
content: content,
|
||||
insets: UIEdgeInsets(
|
||||
top: environment.statusBarHeight,
|
||||
top: layout.statusBarHeight ?? 0.0,
|
||||
left: layout.safeInsets.left,
|
||||
bottom: layout.intrinsicInsets.bottom,
|
||||
right: layout.safeInsets.right
|
||||
@ -1050,21 +1366,37 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
public enum Subject {
|
||||
case webPage(url: String)
|
||||
case instantPage(webPage: TelegramMediaWebpage, anchor: String?, sourceLocation: InstantPageSourceLocation)
|
||||
case document(file: TelegramMediaFile)
|
||||
case pdfDocument(file: TelegramMediaFile)
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let subject: Subject
|
||||
|
||||
|
||||
var openPreviousOnClose = false
|
||||
private var openPreviousOnClose = false
|
||||
|
||||
public init(context: AccountContext, subject: Subject) {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
public static let supportedDocumentMimeTypes: [String] = [
|
||||
// "text/plain",
|
||||
// "text/rtf",
|
||||
// "application/pdf",
|
||||
// "application/msword",
|
||||
// "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
// "application/vnd.ms-excel",
|
||||
// "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
// "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
// "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||
]
|
||||
|
||||
public init(context: AccountContext, subject: Subject, openPreviousOnClose: Bool = false) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
self.openPreviousOnClose = openPreviousOnClose
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
self.navigationPresentation = .modalInCompactLayout
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown)
|
||||
|
||||
@ -1088,16 +1420,58 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = layout
|
||||
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.node.containerLayoutUpdated(layout: layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.height, transition: ComponentTransition(transition))
|
||||
var navigationHeight = self.navigationLayout(layout: layout).navigationFrame.height
|
||||
if layout.metrics.isTablet, layout.size.width > layout.size.height {
|
||||
navigationHeight += 6.0
|
||||
}
|
||||
self.node.containerLayoutUpdated(layout: layout, navigationBarHeight: navigationHeight, transition: ComponentTransition(transition))
|
||||
}
|
||||
|
||||
public func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?) {
|
||||
self.openPreviousOnClose = false
|
||||
self.node.minimize(topEdgeOffset: topEdgeOffset, damping: 180.0, initialVelocity: initialVelocity)
|
||||
}
|
||||
|
||||
public var isMinimized = false
|
||||
private var didPlayAppearanceAnimation = false
|
||||
public override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if !self.didPlayAppearanceAnimation, let layout = self.validLayout, layout.metrics.isTablet {
|
||||
self.node.layer.animatePosition(from: CGPoint(x: 0.0, y: layout.size.height), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
public override func dismiss(completion: (() -> Void)? = nil) {
|
||||
if let layout = self.validLayout, layout.metrics.isTablet {
|
||||
self.node.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: layout.size.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
super.dismiss(completion: completion)
|
||||
})
|
||||
} else {
|
||||
super.dismiss(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if self.openPreviousOnClose, let navigationController = self.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer, let controller = minimizedContainer.controllers.last {
|
||||
navigationController.maximizeViewController(controller, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
public var isMinimized = false {
|
||||
didSet {
|
||||
if let webContent = self.node.content.last as? BrowserWebContent {
|
||||
if !self.isMinimized {
|
||||
webContent.webView.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public var isMinimizable = true
|
||||
|
||||
public var minimizedIcon: UIImage? {
|
||||
@ -1107,6 +1481,8 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
return contentState.favicon
|
||||
case .instantPage:
|
||||
return UIImage(bundleImageName: "Chat/Message/AttachedContentInstantIcon")?.withRenderingMode(.alwaysTemplate)
|
||||
case .document:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -1118,6 +1494,20 @@ public class BrowserScreen: ViewController, MinimizableController {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func makeContentSnapshotView() -> UIView? {
|
||||
if let contentSnapshot = self.node.content.last?.makeContentSnapshotView(), let layout = self.validLayout {
|
||||
if let wrapperView = self.view.snapshotView(afterScreenUpdates: false) {
|
||||
contentSnapshot.frame = contentSnapshot.frame.offsetBy(dx: 0.0, dy: self.navigationLayout(layout: layout).navigationFrame.height)
|
||||
wrapperView.addSubview(contentSnapshot)
|
||||
return wrapperView
|
||||
} else {
|
||||
return contentSnapshot
|
||||
}
|
||||
} else {
|
||||
return self.view.snapshotView(afterScreenUpdates: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class BrowserReferenceContentSource: ContextReferenceContentSource {
|
||||
@ -1183,9 +1573,12 @@ private final class BrowserContentComponent: Component {
|
||||
}
|
||||
|
||||
let collapsedHeight: CGFloat = 24.0
|
||||
let topInset: CGFloat = component.insets.top + component.navigationBarHeight * (1.0 - component.scrollingPanelOffsetFraction) + collapsedHeight * component.scrollingPanelOffsetFraction
|
||||
let topInset: CGFloat = component.navigationBarHeight * (1.0 - component.scrollingPanelOffsetFraction) + (component.insets.top + collapsedHeight) * component.scrollingPanelOffsetFraction
|
||||
let bottomInset = (49.0 + component.insets.bottom) * (1.0 - component.scrollingPanelOffsetFraction)
|
||||
component.content.updateLayout(size: availableSize, insets: UIEdgeInsets(top: topInset, left: component.insets.left, bottom: bottomInset, right: component.insets.right), transition: transition)
|
||||
let insets = UIEdgeInsets(top: topInset, left: component.insets.left, bottom: bottomInset, right: component.insets.right)
|
||||
let fullInsets = UIEdgeInsets(top: component.insets.top + component.navigationBarHeight, left: component.insets.left, bottom: 49.0 + component.insets.bottom, right: component.insets.right)
|
||||
|
||||
component.content.updateLayout(size: availableSize, insets: insets, fullInsets: fullInsets, safeInsets: component.insets, transition: transition)
|
||||
transition.setFrame(view: component.content, frame: CGRect(origin: .zero, size: availableSize))
|
||||
|
||||
return availableSize
|
||||
@ -1200,3 +1593,19 @@ private final class BrowserContentComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelInteractiveTransitionGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for gesture in gestureRecognizers {
|
||||
if let gesture = gesture as? InteractiveTransitionGestureRecognizer {
|
||||
gesture.cancel()
|
||||
} else if let scrollView = gesture.view as? UIScrollView, gesture.isEnabled, scrollView.tag == 0x5C4011 {
|
||||
gesture.isEnabled = false
|
||||
gesture.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if let superview = view.superview {
|
||||
cancelInteractiveTransitionGestures(view: superview)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import AccountContext
|
||||
import BundleIconComponent
|
||||
|
||||
final class SearchBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
@ -351,7 +353,7 @@ final class SearchBarContentComponent: Component {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
85
submodules/BrowserUI/Sources/BrowserTitleBarComponent.swift
Normal file
85
submodules/BrowserUI/Sources/BrowserTitleBarComponent.swift
Normal file
@ -0,0 +1,85 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
import UrlEscaping
|
||||
|
||||
final class TitleBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
|
||||
let theme: PresentationTheme
|
||||
let title: String
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
title: String
|
||||
) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
}
|
||||
|
||||
static func ==(lhs: TitleBarContentComponent, rhs: TitleBarContentComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private var titleContent = ComponentView<Empty>()
|
||||
private var component: TitleBarContentComponent?
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect())
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func update(component: TitleBarContentComponent, availableSize: CGSize, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let titleSize = self.titleContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.title, font: Font.semibold(17.0), textColor: component.theme.rootController.navigationBar.primaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
truncationType: .end,
|
||||
maximumNumberOfLines: 1
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 36.0, height: availableSize.height)
|
||||
)
|
||||
let titleContentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if let titleContentView = self.titleContent.view {
|
||||
if titleContentView.superview == nil {
|
||||
self.addSubview(titleContentView)
|
||||
}
|
||||
transition.setPosition(view: titleContentView, position: titleContentFrame.center)
|
||||
titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size)
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
@ -124,6 +124,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
let textColor: UIColor
|
||||
let canGoBack: Bool
|
||||
let canGoForward: Bool
|
||||
let canOpenIn: Bool
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
let performHoldAction: (UIView, ContextGesture?, BrowserScreen.Action) -> Void
|
||||
|
||||
@ -132,6 +133,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
textColor: UIColor,
|
||||
canGoBack: Bool,
|
||||
canGoForward: Bool,
|
||||
canOpenIn: Bool,
|
||||
performAction: ActionSlot<BrowserScreen.Action>,
|
||||
performHoldAction: @escaping (UIView, ContextGesture?, BrowserScreen.Action) -> Void
|
||||
) {
|
||||
@ -139,6 +141,7 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
self.textColor = textColor
|
||||
self.canGoBack = canGoBack
|
||||
self.canGoForward = canGoForward
|
||||
self.canOpenIn = canOpenIn
|
||||
self.performAction = performAction
|
||||
self.performHoldAction = performHoldAction
|
||||
}
|
||||
@ -156,6 +159,9 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
if lhs.canGoForward != rhs.canGoForward {
|
||||
return false
|
||||
}
|
||||
if lhs.canOpenIn != rhs.canOpenIn {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -170,10 +176,16 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
let availableSize = context.availableSize
|
||||
let performAction = context.component.performAction
|
||||
let performHoldAction = context.component.performHoldAction
|
||||
|
||||
|
||||
let sideInset: CGFloat = 5.0
|
||||
let buttonSize = CGSize(width: 50.0, height: availableSize.height)
|
||||
let spacing = (availableSize.width - buttonSize.width * 5.0 - sideInset * 2.0) / 4.0
|
||||
|
||||
var buttonCount = 4
|
||||
if context.component.canOpenIn {
|
||||
buttonCount += 1
|
||||
}
|
||||
|
||||
let spacing = (availableSize.width - buttonSize.width * CGFloat(buttonCount) - sideInset * 2.0) / CGFloat(buttonCount - 1)
|
||||
|
||||
let canGoBack = context.component.canGoBack
|
||||
let back = back.update(
|
||||
@ -269,24 +281,26 @@ final class NavigationToolbarContentComponent: CombinedComponent {
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
let openIn = openIn.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Browser",
|
||||
tintColor: context.component.accentColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.openIn)
|
||||
}
|
||||
).minSize(buttonSize),
|
||||
availableSize: buttonSize,
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(openIn
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width + spacing + openIn.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
)
|
||||
if context.component.canOpenIn {
|
||||
let openIn = openIn.update(
|
||||
component: Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Instant View/Browser",
|
||||
tintColor: context.component.accentColor
|
||||
)
|
||||
),
|
||||
action: {
|
||||
performAction.invoke(.openIn)
|
||||
}
|
||||
).minSize(buttonSize),
|
||||
availableSize: buttonSize,
|
||||
transition: .easeInOut(duration: 0.2)
|
||||
)
|
||||
context.add(openIn
|
||||
.position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width + spacing + openIn.size.width / 2.0, y: availableSize.height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import Svg
|
||||
|
||||
private var faviconCache: [String: UIImage] = [:]
|
||||
func fetchFavicon(context: AccountContext, url: String, size: CGSize) -> Signal<UIImage?, NoError> {
|
||||
if let icon = faviconCache[url] {
|
||||
return .single(icon)
|
||||
}
|
||||
return context.engine.resources.httpData(url: url)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Data?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { data in
|
||||
if let data {
|
||||
if let image = UIImage(data: data) {
|
||||
return image
|
||||
} else if url.lowercased().contains(".svg"), let preparedData = prepareSvgImage(data, false), let image = renderPreparedImage(preparedData, size, .clear, UIScreenScale, false) {
|
||||
return image
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> beforeNext { image in
|
||||
if let image {
|
||||
Queue.mainQueue().async {
|
||||
faviconCache[url] = image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
174
submodules/BrowserUI/Sources/SectionHeaderComponent.swift
Normal file
174
submodules/BrowserUI/Sources/SectionHeaderComponent.swift
Normal file
@ -0,0 +1,174 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
|
||||
final class SectionHeaderComponent: Component {
|
||||
enum Style {
|
||||
case blocks
|
||||
case plain
|
||||
}
|
||||
let theme: PresentationTheme
|
||||
let style: Style
|
||||
let title: String
|
||||
let insets: UIEdgeInsets
|
||||
let actionTitle: String?
|
||||
let action: (() -> Void)?
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
style: Style,
|
||||
title: String,
|
||||
insets: UIEdgeInsets,
|
||||
actionTitle: String?,
|
||||
action: (() -> Void)?
|
||||
) {
|
||||
self.theme = theme
|
||||
self.style = style
|
||||
self.title = title
|
||||
self.insets = insets
|
||||
self.actionTitle = actionTitle
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: SectionHeaderComponent, rhs: SectionHeaderComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.style != rhs.style {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
if lhs.actionTitle != rhs.actionTitle {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let title = ComponentView<Empty>()
|
||||
private let backgroundView: BlurredBackgroundView
|
||||
private let action = ComponentView<Empty>()
|
||||
|
||||
private var component: SectionHeaderComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: SectionHeaderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let themeUpdated = self.component?.theme !== component.theme
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let height: CGFloat = 28.0
|
||||
let leftInset: CGFloat = 16.0 + component.insets.left
|
||||
let rightInset: CGFloat = 0.0
|
||||
|
||||
let previousTitleFrame = self.title.view?.frame
|
||||
|
||||
if themeUpdated {
|
||||
switch component.style {
|
||||
case .plain:
|
||||
self.backgroundView.isHidden = false
|
||||
self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
|
||||
case .blocks:
|
||||
self.backgroundView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.title, font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||
)
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
if let previousTitleFrame, previousTitleFrame.origin.x != titleFrame.origin.x {
|
||||
transition.animatePosition(view: titleView, from: CGPoint(x: previousTitleFrame.origin.x - titleFrame.origin.x, y: 0.0), to: CGPoint(), additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
if let actionTitle = component.actionTitle {
|
||||
let actionSize = self.action.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
Button(content: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: actionTitle, font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
||||
)), action: { [weak self] in
|
||||
if let self, let component = self.component {
|
||||
component.action?()
|
||||
}
|
||||
})
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||
)
|
||||
if let view = self.action.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
if !transition.animation.isImmediate {
|
||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
view.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
let actionFrame = CGRect(origin: CGPoint(x: availableSize.width - leftInset - actionSize.width, y: floor((height - titleSize.height) / 2.0)), size: actionSize)
|
||||
view.frame = actionFrame
|
||||
}
|
||||
} else if let view = self.action.view, view.superview != nil {
|
||||
if !transition.animation.isImmediate {
|
||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { finished in
|
||||
if finished {
|
||||
view.removeFromSuperview()
|
||||
view.layer.removeAllAnimations()
|
||||
}
|
||||
})
|
||||
view.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
} else {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
let size = CGSize(width: availableSize.width, height: height)
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.backgroundView.update(size: size, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
110
submodules/BrowserUI/Sources/Utils.swift
Normal file
110
submodules/BrowserUI/Sources/Utils.swift
Normal file
@ -0,0 +1,110 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TextFormat
|
||||
import UrlWhitelist
|
||||
import Svg
|
||||
|
||||
private var faviconCache: [String: UIImage] = [:]
|
||||
func fetchFavicon(context: AccountContext, url: String, size: CGSize) -> Signal<UIImage?, NoError> {
|
||||
if let icon = faviconCache[url] {
|
||||
return .single(icon)
|
||||
}
|
||||
return context.engine.resources.httpData(url: url)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Data?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { data in
|
||||
if let data {
|
||||
if let image = UIImage(data: data) {
|
||||
return image
|
||||
} else if url.lowercased().contains(".svg"), let preparedData = prepareSvgImage(data, false), let image = renderPreparedImage(preparedData, size, .clear, UIScreenScale, false) {
|
||||
return image
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> beforeNext { image in
|
||||
if let image {
|
||||
Queue.mainQueue().async {
|
||||
faviconCache[url] = image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPrimaryUrl(message: Message) -> String? {
|
||||
var primaryUrl: String?
|
||||
if let webPage = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, let url = webPage.content.url {
|
||||
primaryUrl = url
|
||||
} else {
|
||||
var entities = message.textEntitiesAttribute?.entities
|
||||
if entities == nil {
|
||||
let parsedEntities = generateTextEntities(message.text, enabledTypes: .all)
|
||||
if !parsedEntities.isEmpty {
|
||||
entities = parsedEntities
|
||||
}
|
||||
}
|
||||
|
||||
if let entities {
|
||||
loop: for entity in entities {
|
||||
switch entity.type {
|
||||
case .Url, .Email:
|
||||
var range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
|
||||
let nsString = message.text as NSString
|
||||
if range.location + range.length > nsString.length {
|
||||
range.location = max(0, nsString.length - range.length)
|
||||
range.length = nsString.length - range.location
|
||||
}
|
||||
let tempUrlString = nsString.substring(with: range)
|
||||
|
||||
var (urlString, concealed) = parseUrl(url: tempUrlString, wasConcealed: false)
|
||||
var parsedUrl = URL(string: urlString)
|
||||
if (parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty) && !urlString.contains("@") {
|
||||
urlString = "http://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
}
|
||||
var host: String? = concealed ? urlString : parsedUrl?.host
|
||||
if host == nil {
|
||||
host = urlString
|
||||
}
|
||||
if let _ = parsedUrl, let _ = host {
|
||||
primaryUrl = urlString
|
||||
}
|
||||
break loop
|
||||
case let .TextUrl(url):
|
||||
let messageText = message.text
|
||||
|
||||
var range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
|
||||
let nsString = messageText as NSString
|
||||
if range.location + range.length > nsString.length {
|
||||
range.location = max(0, nsString.length - range.length)
|
||||
range.length = nsString.length - range.location
|
||||
}
|
||||
|
||||
var (urlString, concealed) = parseUrl(url: url, wasConcealed: false)
|
||||
var parsedUrl = URL(string: urlString)
|
||||
if (parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty) && !urlString.contains("@") {
|
||||
urlString = "http://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
}
|
||||
let host: String? = concealed ? urlString : parsedUrl?.host
|
||||
if let _ = parsedUrl, let _ = host {
|
||||
primaryUrl = urlString
|
||||
}
|
||||
break loop
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return primaryUrl
|
||||
}
|
@ -149,10 +149,13 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
||||
})
|
||||
})))
|
||||
items.append(.separator)
|
||||
case .popularApps:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if case .search(.recentApps) = source {
|
||||
} else if case .search(.popularApps) = source {
|
||||
} else {
|
||||
let isSavedMessages = peerId == context.account.peerId
|
||||
|
||||
|
@ -330,6 +330,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
}
|
||||
|
||||
self.paneContainerNode.requesDismissInput = {
|
||||
parentController()?.view.endEditing(true)
|
||||
}
|
||||
|
||||
self.filterContainerNode.filterPressed = { [weak self] filter in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -88,8 +88,7 @@ private final class ItemNode: ASDisplayNode {
|
||||
title = presentationData.strings.ChatList_Search_FilterChannels
|
||||
icon = nil
|
||||
case .apps:
|
||||
//TODO:localize
|
||||
title = "Apps"
|
||||
title = presentationData.strings.ChatList_Search_FilterApps
|
||||
icon = nil
|
||||
case .media:
|
||||
title = presentationData.strings.ChatList_Search_FilterMedia
|
||||
|
@ -34,7 +34,7 @@ import AvatarNode
|
||||
|
||||
private enum ChatListRecentEntryStableId: Hashable {
|
||||
case topPeers
|
||||
case peerId(EnginePeer.Id)
|
||||
case peerId(EnginePeer.Id, ChatListRecentEntry.Section)
|
||||
}
|
||||
|
||||
private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
@ -51,8 +51,8 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
switch self {
|
||||
case .topPeers:
|
||||
return .topPeers
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
|
||||
return .peerId(peer.peer.peerId)
|
||||
case let .peer(_, peer, section, _, _, _, _, _, _, _, _):
|
||||
return .peerId(peer.peer.peerId, section)
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,16 +254,15 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
} else if case .apps = key {
|
||||
//TODO:localize
|
||||
if case .popularApps = section {
|
||||
header = ChatListSearchItemHeader(type: .text("POPULAR APPS", 1), theme: theme, strings: strings)
|
||||
header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionPopularApps, 1), theme: theme, strings: strings)
|
||||
} else {
|
||||
if let isChannelsTabExpanded {
|
||||
header = ChatListSearchItemHeader(type: .text("APPS YOU USE", 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? presentationData.strings.ChatList_Search_SectionActionShowLess : presentationData.strings.ChatList_Search_SectionActionShowMore, action: {
|
||||
header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionRecentApps, 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? presentationData.strings.ChatList_Search_SectionActionShowLess : presentationData.strings.ChatList_Search_SectionActionShowMore, action: {
|
||||
toggleChannelsTabExpanded()
|
||||
})
|
||||
} else {
|
||||
header = ChatListSearchItemHeader(type: .text("APPS YOU USE", 0), theme: theme, strings: strings, actionTitle: nil, action: nil)
|
||||
header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionRecentApps, 0), theme: theme, strings: strings, actionTitle: nil, action: nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -298,13 +297,17 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
}
|
||||
},
|
||||
deletePeer: deletePeer,
|
||||
contextAction: (key == .channels) ? nil : peerContextAction.flatMap { peerContextAction in
|
||||
contextAction: (key == .channels || section == .popularApps) ? nil : peerContextAction.flatMap { peerContextAction in
|
||||
return { node, gesture, location in
|
||||
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
|
||||
let source: ChatListSearchContextActionSource
|
||||
|
||||
if key == .apps {
|
||||
source = .recentApps
|
||||
if case .popularApps = section {
|
||||
source = .popularApps
|
||||
} else {
|
||||
source = .recentApps
|
||||
}
|
||||
} else {
|
||||
source = .recentSearch
|
||||
}
|
||||
@ -1081,6 +1084,7 @@ public enum ChatListSearchContextActionSource {
|
||||
case recentPeers
|
||||
case recentSearch
|
||||
case recentApps
|
||||
case popularApps
|
||||
case search(EngineMessage.Id?)
|
||||
}
|
||||
|
||||
@ -1449,8 +1453,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
if key == .channels {
|
||||
emptyRecentTextNode.attributedText = NSAttributedString(string: presentationData.strings.ChatList_Search_RecommendedChannelsEmpty_Text, font: Font.regular(15.0), textColor: presentationData.theme.list.freeTextColor)
|
||||
} else if key == .apps {
|
||||
//TODO:localize
|
||||
emptyRecentTextNode.attributedText = NSAttributedString(string: "No Apps Found", font: Font.regular(15.0), textColor: presentationData.theme.list.freeTextColor)
|
||||
emptyRecentTextNode.attributedText = NSAttributedString(string: presentationData.strings.ChatList_Search_Apps_Empty_Text, font: Font.regular(15.0), textColor: presentationData.theme.list.freeTextColor)
|
||||
}
|
||||
self.emptyRecentTextNode = emptyRecentTextNode
|
||||
|
||||
@ -3412,7 +3415,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
continue
|
||||
}
|
||||
let peerNotificationSettings = notificationSettings[id]
|
||||
//TODO:localize
|
||||
let subpeerSummary: RecentlySearchedPeerSubpeerSummary? = nil
|
||||
var peerStoryStats: PeerStoryStats?
|
||||
if let value = storyStats[peer.id] {
|
||||
@ -3543,9 +3545,19 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
} else if case .apps = key {
|
||||
if let navigationController = self.navigationController {
|
||||
if isRecommended {
|
||||
#if DEBUG
|
||||
let _ = (self.context.sharedContext.makeMiniAppListScreenInitialData(context: self.context)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in
|
||||
guard let self, let navigationController = self.navigationController else {
|
||||
return
|
||||
}
|
||||
navigationController.pushViewController(self.context.sharedContext.makeMiniAppListScreen(context: self.context, initialData: initialData))
|
||||
})
|
||||
#else
|
||||
if let peerInfoScreen = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
navigationController.pushViewController(peerInfoScreen)
|
||||
}
|
||||
#endif
|
||||
} else if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.parentController {
|
||||
self.context.sharedContext.openWebApp(
|
||||
context: self.context,
|
||||
@ -3560,7 +3572,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
skipTermsOfService: true
|
||||
)
|
||||
} else {
|
||||
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
|
||||
navigationController: navigationController,
|
||||
context: self.context,
|
||||
|
@ -192,6 +192,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
|
||||
var currentPaneUpdated: ((ChatListSearchPaneKey?, CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
var requestExpandTabs: (() -> Bool)?
|
||||
var requesDismissInput: (() -> Void)?
|
||||
|
||||
private var currentAvailablePanes: [ChatListSearchPaneKey]?
|
||||
|
||||
@ -227,12 +228,20 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
if case .apps = key {
|
||||
self.requesDismissInput?()
|
||||
}
|
||||
} else if self.pendingSwitchToPaneKey != key {
|
||||
self.pendingSwitchToPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
|
||||
if case .apps = key {
|
||||
self.requesDismissInput?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,6 +331,10 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
||||
let switchToKey = availablePanes[updatedIndex]
|
||||
if switchToKey != self.currentPaneKey && self.currentPanes[switchToKey] != nil{
|
||||
self.currentPaneKey = switchToKey
|
||||
|
||||
if case .apps = switchToKey {
|
||||
self.requesDismissInput?()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.transitionFraction = 0.0
|
||||
|
@ -93,7 +93,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case knockoutWallpaper(PresentationTheme, Bool)
|
||||
case experimentalCompatibility(Bool)
|
||||
case enableDebugDataDisplay(Bool)
|
||||
case acceleratedStickers(Bool)
|
||||
case rippleEffect(Bool)
|
||||
case browserExperiment(Bool)
|
||||
case localTranscription(Bool)
|
||||
case enableReactionOverrides(Bool)
|
||||
@ -127,7 +127,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.web.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2:
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .logTranslationRecognition, .resetTranslationStates:
|
||||
return DebugControllerSection.translation.rawValue
|
||||
@ -216,7 +216,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 37
|
||||
case .enableDebugDataDisplay:
|
||||
return 38
|
||||
case .acceleratedStickers:
|
||||
case .rippleEffect:
|
||||
return 39
|
||||
case .browserExperiment:
|
||||
return 40
|
||||
@ -1228,12 +1228,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .acceleratedStickers(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Accelerated Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
case let .rippleEffect(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Ripple", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.acceleratedStickers = value
|
||||
settings.rippleEffect = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
@ -1452,7 +1452,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper))
|
||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.rippleEffect(experimentalSettings.rippleEffect))
|
||||
#if DEBUG
|
||||
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
||||
#else
|
||||
|
@ -353,24 +353,26 @@ public final class DeviceAccess {
|
||||
} else {
|
||||
completion(true)
|
||||
}
|
||||
} else if [.restricted, .denied].contains(status), let presentationData = presentationData {
|
||||
let text: String
|
||||
if case .restricted = status {
|
||||
text = presentationData.strings.AccessDenied_CameraRestricted
|
||||
} else {
|
||||
switch cameraSubject {
|
||||
case .video:
|
||||
text = presentationData.strings.AccessDenied_Camera
|
||||
case .videoCall:
|
||||
text = presentationData.strings.AccessDenied_VideoCallCamera
|
||||
case .qrCode:
|
||||
text = presentationData.strings.AccessDenied_QrCamera
|
||||
}
|
||||
}
|
||||
} else if [.restricted, .denied].contains(status) {
|
||||
completion(false)
|
||||
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
|
||||
openSettings()
|
||||
})]), nil)
|
||||
if let presentationData = presentationData {
|
||||
let text: String
|
||||
if case .restricted = status {
|
||||
text = presentationData.strings.AccessDenied_CameraRestricted
|
||||
} else {
|
||||
switch cameraSubject {
|
||||
case .video:
|
||||
text = presentationData.strings.AccessDenied_Camera
|
||||
case .videoCall:
|
||||
text = presentationData.strings.AccessDenied_VideoCallCamera
|
||||
case .qrCode:
|
||||
text = presentationData.strings.AccessDenied_QrCamera
|
||||
}
|
||||
}
|
||||
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
|
||||
openSettings()
|
||||
})]), nil)
|
||||
}
|
||||
} else if case .authorized = status {
|
||||
completion(true)
|
||||
} else {
|
||||
|
@ -90,6 +90,10 @@ public struct ContainerViewLayout: Equatable {
|
||||
return ContainerViewLayout(size: self.size, metrics: self.metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: intrinsicInsets, safeInsets: self.safeInsets, additionalInsets: self.additionalInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
|
||||
}
|
||||
|
||||
public func withUpdatedAdditionalInsets(_ additionalInsets: UIEdgeInsets) -> ContainerViewLayout {
|
||||
return ContainerViewLayout(size: self.size, metrics: self.metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, additionalInsets: additionalInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
|
||||
}
|
||||
|
||||
public func withUpdatedInputHeight(_ inputHeight: CGFloat?) -> ContainerViewLayout {
|
||||
return ContainerViewLayout(size: self.size, metrics: self.metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, additionalInsets: self.additionalInsets, statusBarHeight: self.statusBarHeight, inputHeight: inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
|
||||
}
|
||||
|
@ -30,6 +30,10 @@ public protocol MinimizableController: ViewController {
|
||||
|
||||
func requestMinimize(topEdgeOffset: CGFloat?, initialVelocity: CGFloat?)
|
||||
func makeContentSnapshotView() -> UIView?
|
||||
|
||||
func prepareContentSnapshotView()
|
||||
func resetContentSnapshotView()
|
||||
|
||||
func shouldDismissImmediately() -> Bool
|
||||
}
|
||||
|
||||
@ -66,6 +70,14 @@ public extension MinimizableController {
|
||||
return self.displayNode.view.snapshotView(afterScreenUpdates: false)
|
||||
}
|
||||
|
||||
func prepareContentSnapshotView() {
|
||||
|
||||
}
|
||||
|
||||
func resetContentSnapshotView() {
|
||||
|
||||
}
|
||||
|
||||
func shouldDismissImmediately() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
@ -443,7 +443,20 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
globalScrollToTopNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: layout.size.width, height: 1.0))
|
||||
}
|
||||
|
||||
let overlayContainerLayout = layout
|
||||
var overlayContainerLayout = layout
|
||||
|
||||
var updatedSize = layout.size
|
||||
var updatedIntrinsicInsets = layout.intrinsicInsets
|
||||
var updatedAdditionalInsets = layout.additionalInsets
|
||||
if let minimizedContainer = self.minimizedContainer {
|
||||
if (layout.inputHeight ?? 0.0).isZero {
|
||||
let minimizedContainerHeight = minimizedContainer.collapsedHeight(layout: layout)
|
||||
updatedSize.height -= minimizedContainerHeight
|
||||
updatedIntrinsicInsets.bottom = 0.0
|
||||
updatedAdditionalInsets.bottom += minimizedContainerHeight
|
||||
}
|
||||
}
|
||||
overlayContainerLayout = overlayContainerLayout.withUpdatedAdditionalInsets(updatedAdditionalInsets)
|
||||
|
||||
if let inCallStatusBar = self.inCallStatusBar {
|
||||
let isLandscape = layout.size.width > layout.size.height
|
||||
@ -843,8 +856,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
layout.additionalInsets.left = max(layout.intrinsicInsets.left, additionalSideInsets.left)
|
||||
layout.additionalInsets.right = max(layout.intrinsicInsets.right, additionalSideInsets.right)
|
||||
|
||||
var updatedSize = layout.size
|
||||
var updatedIntrinsicInsets = layout.intrinsicInsets
|
||||
if case .flat = navigationLayout.root, let minimizedContainer = self.minimizedContainer {
|
||||
if minimizedContainer.supernode !== self.displayNode {
|
||||
if let rootContainer = self.rootContainer, case let .flat(flatContainer) = rootContainer {
|
||||
@ -857,10 +868,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.displayNode.insertSubnode(minimizedContainer, at: 0)
|
||||
}
|
||||
}
|
||||
if (layout.inputHeight ?? 0.0).isZero {
|
||||
updatedSize.height -= minimizedContainer.collapsedHeight(layout: layout)
|
||||
updatedIntrinsicInsets.bottom = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
switch navigationLayout.root {
|
||||
|
@ -50,6 +50,15 @@ func makeNavigationLayout(mode: NavigationControllerMode, layout: ContainerViewL
|
||||
case .regular:
|
||||
requiresModal = true
|
||||
}
|
||||
case .modalInCompactLayout:
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
requiresModal = true
|
||||
case .regular:
|
||||
requiresModal = true
|
||||
beginsModal = true
|
||||
isFlat = true
|
||||
}
|
||||
}
|
||||
if requiresModal {
|
||||
controller._presentedInModal = true
|
||||
|
@ -90,6 +90,7 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes
|
||||
self.scrollNode.view.delaysContentTouches = false
|
||||
self.scrollNode.view.clipsToBounds = false
|
||||
self.scrollNode.view.delegate = self.wrappedScrollViewDelegate
|
||||
self.scrollNode.view.tag = 0x5C4011
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] _ in
|
||||
guard let strongSelf = self, !strongSelf.isDismissed else {
|
||||
|
@ -47,6 +47,13 @@ open class PortalSourceView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public func removePortal(view: PortalView) {
|
||||
if let index = self.portalReferences.firstIndex(where: { $0.portalView === view }) {
|
||||
self.portalReferences.remove(at: index)
|
||||
}
|
||||
view.disablePortal()
|
||||
}
|
||||
|
||||
func setGlobalPortal(view: GlobalPortalView?) {
|
||||
if let globalPortalView = self.globalPortalView {
|
||||
self.globalPortalView = nil
|
||||
|
@ -23,6 +23,11 @@ public class PortalView {
|
||||
}
|
||||
}
|
||||
|
||||
func disablePortal() {
|
||||
self.view.sourceView = nil
|
||||
self.sourceView = nil
|
||||
}
|
||||
|
||||
public func reloadPortal() {
|
||||
if let sourceView = self.sourceView as? PortalSourceView {
|
||||
self.reloadPortal(sourceView: sourceView)
|
||||
|
@ -65,6 +65,7 @@ public enum ViewControllerNavigationPresentation {
|
||||
case flatModal
|
||||
case standaloneModal
|
||||
case modalInLargeLayout
|
||||
case modalInCompactLayout
|
||||
}
|
||||
|
||||
public enum TabBarItemContextActionType {
|
||||
|
@ -97,6 +97,7 @@ swift_library(
|
||||
"//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState",
|
||||
"//submodules/StickerPackPreviewUI:StickerPackPreviewUI",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
|
||||
"//submodules/ImageTransparency",
|
||||
"//submodules/GalleryUI",
|
||||
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
||||
|
@ -209,7 +209,7 @@ public final class DrawingLinkEntityView: DrawingEntityView, UITextViewDelegate
|
||||
if !self.linkEntity.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
string = self.linkEntity.name.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
|
||||
} else {
|
||||
string = self.linkEntity.url.uppercased()
|
||||
string = self.linkEntity.url.uppercased().replacingOccurrences(of: "http://", with: "").replacingOccurrences(of: "https://", with: "")
|
||||
}
|
||||
let text = NSMutableAttributedString(string: string)
|
||||
let range = NSMakeRange(0, text.length)
|
||||
|
@ -3212,10 +3212,12 @@ public final class DrawingToolsInteraction {
|
||||
self.isActive = false
|
||||
}
|
||||
|
||||
public func insertEntity(_ entity: DrawingEntity, scale: CGFloat? = nil, position: CGPoint? = nil) {
|
||||
public func insertEntity(_ entity: DrawingEntity, scale: CGFloat? = nil, position: CGPoint? = nil, select: Bool = true) {
|
||||
self.entitiesView.prepareNewEntity(entity, scale: scale, position: position)
|
||||
self.entitiesView.add(entity)
|
||||
self.entitiesView.selectEntity(entity, animate: !(entity is DrawingTextEntity))
|
||||
if select {
|
||||
self.entitiesView.selectEntity(entity, animate: !(entity is DrawingTextEntity))
|
||||
}
|
||||
|
||||
if let entityView = self.entitiesView.getView(for: entity.uuid) {
|
||||
if let textEntityView = entityView as? DrawingTextEntityView {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import TelegramCore
|
||||
@ -9,6 +10,8 @@ import TelegramAnimatedStickerNode
|
||||
import StickerResources
|
||||
import MediaEditor
|
||||
import TelegramStringFormatting
|
||||
import LottieComponent
|
||||
import LottieComponentResourceContent
|
||||
|
||||
private func generateIcon(style: DrawingWeatherEntity.Style) -> UIImage? {
|
||||
guard let image = UIImage(bundleImageName: "Chat/Attach Menu/Location") else {
|
||||
@ -53,9 +56,8 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega
|
||||
let backgroundView: UIView
|
||||
|
||||
let textView: DrawingTextView
|
||||
let iconView: UIImageView
|
||||
private let imageNode: TransformImageNode
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
|
||||
private var animation = ComponentView<Empty>()
|
||||
|
||||
private var didSetUpAnimationNode = false
|
||||
private let stickerFetchedDisposable = MetaDisposable()
|
||||
@ -88,20 +90,14 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega
|
||||
self.textView.spellCheckingType = .no
|
||||
self.textView.textContainer.maximumNumberOfLines = 2
|
||||
self.textView.textContainer.lineBreakMode = .byTruncatingTail
|
||||
|
||||
self.iconView = UIImageView()
|
||||
self.imageNode = TransformImageNode()
|
||||
|
||||
|
||||
super.init(context: context, entity: entity)
|
||||
|
||||
self.textView.delegate = self
|
||||
self.addSubview(self.backgroundView)
|
||||
self.addSubview(self.textView)
|
||||
self.addSubview(self.iconView)
|
||||
|
||||
self.update(animated: false)
|
||||
|
||||
self.setup()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -134,17 +130,35 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega
|
||||
let iconSize = min(80.0, floor(self.bounds.height * 0.7))
|
||||
let iconOffset: CGFloat = 0.3
|
||||
|
||||
self.iconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(iconSize * iconOffset), y: floorToScreenPixels((self.bounds.height - iconSize) / 2.0)), size: CGSize(width: iconSize, height: iconSize))
|
||||
self.imageNode.frame = self.iconView.frame.offsetBy(dx: 0.0, dy: 2.0)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(iconSize * iconOffset), y: floorToScreenPixels((self.bounds.height - iconSize) / 2.0)), size: CGSize(width: iconSize, height: iconSize))
|
||||
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.frame = self.iconView.frame
|
||||
animationNode.updateLayout(size: self.iconView.frame.size)
|
||||
if let icon = self.weatherEntity.icon {
|
||||
let _ = self.animation.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
LottieComponent(
|
||||
content: LottieComponent.ResourceContent(
|
||||
context: self.context,
|
||||
file: icon,
|
||||
attemptSynchronously: true,
|
||||
providesPlaceholder: true
|
||||
),
|
||||
color: nil,
|
||||
placeholderColor: UIColor(rgb: 0x000000, alpha: 0.1),
|
||||
loop: !["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"].contains(self.weatherEntity.emoji)
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: iconFrame.size
|
||||
)
|
||||
if let animationView = self.animation.view {
|
||||
if animationView.superview == nil {
|
||||
self.addSubview(animationView)
|
||||
}
|
||||
animationView.frame = iconFrame
|
||||
}
|
||||
}
|
||||
|
||||
let imageSize = CGSize(width: iconSize, height: iconSize)
|
||||
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
|
||||
|
||||
|
||||
self.textView.frame = CGRect(origin: CGPoint(x: self.bounds.width - self.textSize.width - 6.0, y: floorToScreenPixels((self.bounds.height - self.textSize.height) / 2.0)), size: self.textSize)
|
||||
self.backgroundView.frame = self.bounds
|
||||
}
|
||||
@ -263,10 +277,8 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega
|
||||
|
||||
self.sizeToFit()
|
||||
|
||||
if self.currentStyle != self.weatherEntity.style {
|
||||
self.currentStyle = self.weatherEntity.style
|
||||
self.iconView.image = generateIcon(style: self.weatherEntity.style)
|
||||
}
|
||||
|
||||
self.currentStyle = self.weatherEntity.style
|
||||
|
||||
self.backgroundView.layer.cornerRadius = self.textSize.height * 0.2
|
||||
if #available(iOS 13.0, *) {
|
||||
@ -276,42 +288,6 @@ public final class DrawingWeatherEntityView: DrawingEntityView, UITextViewDelega
|
||||
super.update(animated: animated)
|
||||
}
|
||||
|
||||
private func setup() {
|
||||
if let file = self.weatherEntity.icon {
|
||||
self.iconView.isHidden = true
|
||||
self.addSubnode(self.imageNode)
|
||||
if let dimensions = file.dimensions {
|
||||
if file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" {
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 256.0, height: 256.0))
|
||||
if self.animationNode == nil {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.autoplay = true
|
||||
self.animationNode = animationNode
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.isHidden = true
|
||||
}
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
|
||||
self.addSubnode(animationNode)
|
||||
}
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: self.context.account.postbox, userLocation: .other, file: file, small: false, size: fittedDimensions))
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: stickerPackFileReference(file), resource: file.resource).start())
|
||||
} else {
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.visibility = false
|
||||
self.animationNode = nil
|
||||
animationNode.removeFromSupernode()
|
||||
self.imageNode.isHidden = false
|
||||
self.didSetUpAnimationNode = false
|
||||
}
|
||||
self.imageNode.setSignal(chatMessageSticker(account: self.context.account, userLocation: .other, file: file, small: false, synchronousLoad: false))
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: stickerPackFileReference(file), resource: chatMessageStickerResource(file: file, small: false)).start())
|
||||
}
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func updateSelectionView() {
|
||||
guard let selectionView = self.selectionView as? DrawingWeatherEntitySelectionView else {
|
||||
return
|
||||
|
@ -264,6 +264,10 @@ private let darkTheme = InstantPageTheme(
|
||||
|
||||
private func fontSizeMultiplierForVariant(_ variant: InstantPagePresentationFontSize) -> CGFloat {
|
||||
switch variant {
|
||||
case .xxsmall:
|
||||
return 0.5
|
||||
case .xsmall:
|
||||
return 0.75
|
||||
case .small:
|
||||
return 0.85
|
||||
case .standard:
|
||||
@ -271,7 +275,7 @@ private func fontSizeMultiplierForVariant(_ variant: InstantPagePresentationFont
|
||||
case .large:
|
||||
return 1.15
|
||||
case .xlarge:
|
||||
return 1.3
|
||||
return 1.25
|
||||
case .xxlarge:
|
||||
return 1.5
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
wimport Foundation
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
|
@ -16,6 +16,7 @@ import UrlWhitelist
|
||||
import AccountContext
|
||||
import TelegramStringFormatting
|
||||
import WallpaperResources
|
||||
import UrlEscaping
|
||||
|
||||
private let iconFont = Font.with(size: 30.0, design: .round, weight: .bold)
|
||||
|
||||
@ -333,7 +334,21 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
mutableDescriptionText.append(NSAttributedString(string: text + "\n", font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor))
|
||||
}
|
||||
|
||||
let plainUrlString = NSAttributedString(string: content.url.replacingOccurrences(of: "https://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor)
|
||||
var address = content.url
|
||||
if let components = URLComponents(string: address) {
|
||||
if #available(iOS 16.0, *), let encodedHost = components.encodedHost {
|
||||
if let decodedHost = components.host, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
} else if let encodedHost = components.host {
|
||||
if let decodedHost = components.host?.idnaDecoded, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
address = address.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
|
||||
let plainUrlString = NSAttributedString(string: address.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "tonsite://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor)
|
||||
let urlString = NSMutableAttributedString()
|
||||
urlString.append(plainUrlString)
|
||||
urlString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.URL), value: content.url, range: NSMakeRange(0, urlString.length))
|
||||
@ -397,8 +412,13 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
let rawUrlString = urlString
|
||||
var parsedUrl = URL(string: urlString)
|
||||
if (parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty) && !urlString.contains("@") {
|
||||
urlString = "http://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
if let mappedURL = URL(string: "https://\(urlString)"), let host = mappedURL.host, host.lowercased().hasSuffix(".ton") {
|
||||
urlString = "tonsite://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
} else {
|
||||
urlString = "http://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
}
|
||||
}
|
||||
var host: String? = concealed ? urlString : parsedUrl?.host
|
||||
if host == nil {
|
||||
@ -432,8 +452,23 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
mutableDescriptionText.append(NSAttributedString(string: messageText + "\n", font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor))
|
||||
}
|
||||
|
||||
var address = urlString
|
||||
if let components = URLComponents(string: address) {
|
||||
if #available(iOS 16.0, *), let encodedHost = components.encodedHost {
|
||||
if let decodedHost = components.host, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
} else if let encodedHost = components.host {
|
||||
if let decodedHost = components.host?.idnaDecoded, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
address = address.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
urlString = address
|
||||
|
||||
let urlAttributedString = NSMutableAttributedString()
|
||||
urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor))
|
||||
urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "tonsite://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor))
|
||||
if item.presentationData.theme.theme.list.itemAccentColor.isEqual(item.presentationData.theme.theme.list.itemPrimaryTextColor) {
|
||||
urlAttributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: NSMakeRange(0, urlAttributedString.length))
|
||||
}
|
||||
@ -465,8 +500,13 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
let rawUrlString = urlString
|
||||
var parsedUrl = URL(string: urlString)
|
||||
if (parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty) && !urlString.contains("@") {
|
||||
urlString = "http://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
if let mappedURL = URL(string: "https://\(urlString)"), let host = mappedURL.host, host.lowercased().hasSuffix(".ton") {
|
||||
urlString = "tonsite://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
} else {
|
||||
urlString = "http://" + urlString
|
||||
parsedUrl = URL(string: urlString)
|
||||
}
|
||||
}
|
||||
let host: String? = concealed ? urlString : parsedUrl?.host
|
||||
if let url = parsedUrl, let host = host {
|
||||
@ -487,8 +527,23 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
mutableDescriptionText.append(NSAttributedString(string: messageText + "\n", font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor))
|
||||
}
|
||||
|
||||
var address = urlString
|
||||
if let components = URLComponents(string: address) {
|
||||
if #available(iOS 16.0, *), let encodedHost = components.encodedHost {
|
||||
if let decodedHost = components.host, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
} else if let encodedHost = components.host {
|
||||
if let decodedHost = components.host?.idnaDecoded, encodedHost != decodedHost {
|
||||
address = address.replacingOccurrences(of: encodedHost, with: decodedHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
address = address.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
urlString = address
|
||||
|
||||
let urlAttributedString = NSMutableAttributedString()
|
||||
urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor))
|
||||
urlAttributedString.append(NSAttributedString(string: urlString.replacingOccurrences(of: "https://", with: "").replacingOccurrences(of: "tonsite://", with: ""), font: descriptionFont, textColor: item.presentationData.theme.theme.list.itemAccentColor))
|
||||
if item.presentationData.theme.theme.list.itemAccentColor.isEqual(item.presentationData.theme.theme.list.itemPrimaryTextColor) {
|
||||
urlAttributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: NSMakeRange(0, urlAttributedString.length))
|
||||
}
|
||||
|
@ -1799,9 +1799,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.titleView.title = collection.localizedTitle ?? presentationData.strings.Attachment_Gallery
|
||||
} else {
|
||||
switch mode {
|
||||
case .default, .createSticker:
|
||||
case .default:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
||||
self.titleView.isEnabled = true
|
||||
case .createSticker:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
||||
self.titleView.subtitle = presentationData.strings.MediaPicker_CreateSticker
|
||||
self.titleView.isEnabled = true
|
||||
case .story:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
||||
self.titleView.isEnabled = true
|
||||
|
@ -10,12 +10,14 @@ final class MediaPickerTitleView: UIView {
|
||||
let contextSourceNode: ContextReferenceContentNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let subtitleNode: ImmediateTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let segmentedControlNode: SegmentedControlNode
|
||||
|
||||
public var theme: PresentationTheme {
|
||||
didSet {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: theme.rootController.navigationBar.primaryTextColor)
|
||||
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
||||
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
|
||||
}
|
||||
}
|
||||
@ -23,7 +25,16 @@ final class MediaPickerTitleView: UIView {
|
||||
public var title: String = "" {
|
||||
didSet {
|
||||
if self.title != oldValue {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: theme.rootController.navigationBar.primaryTextColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var subtitle: String = "" {
|
||||
didSet {
|
||||
if self.subtitle != oldValue {
|
||||
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.theme.rootController.navigationBar.secondaryTextColor)
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
@ -36,7 +47,7 @@ final class MediaPickerTitleView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateTitle(title: String, isEnabled: Bool, animated: Bool) {
|
||||
public func updateTitle(title: String, subtitle: String = "", isEnabled: Bool, animated: Bool) {
|
||||
if animated {
|
||||
if self.title != title {
|
||||
if let snapshotView = self.titleNode.view.snapshotContentTree() {
|
||||
@ -49,6 +60,17 @@ final class MediaPickerTitleView: UIView {
|
||||
self.titleNode.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
if self.subtitle != subtitle {
|
||||
if let snapshotView = self.subtitleNode.view.snapshotContentTree() {
|
||||
snapshotView.frame = self.subtitleNode.frame
|
||||
self.addSubview(snapshotView)
|
||||
|
||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
snapshotView.removeFromSuperview()
|
||||
})
|
||||
self.subtitleNode.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
if self.isEnabled != isEnabled {
|
||||
if let snapshotView = self.arrowNode.view.snapshotContentTree() {
|
||||
snapshotView.frame = self.arrowNode.frame
|
||||
@ -62,6 +84,7 @@ final class MediaPickerTitleView: UIView {
|
||||
}
|
||||
}
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
@ -76,6 +99,7 @@ final class MediaPickerTitleView: UIView {
|
||||
if self.segmentsHidden != oldValue {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateAlpha(node: self.titleNode, alpha: self.segmentsHidden ? 1.0 : 0.0)
|
||||
transition.updateAlpha(node: self.subtitleNode, alpha: self.segmentsHidden ? 1.0 : 0.0)
|
||||
transition.updateAlpha(node: self.arrowNode, alpha: self.segmentsHidden ? 1.0 : 0.0)
|
||||
transition.updateAlpha(node: self.segmentedControlNode, alpha: self.segmentsHidden ? 0.0 : 1.0)
|
||||
self.segmentedControlNode.isUserInteractionEnabled = !self.segmentsHidden
|
||||
@ -115,6 +139,9 @@ final class MediaPickerTitleView: UIView {
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
|
||||
self.subtitleNode = ImmediateTextNode()
|
||||
self.subtitleNode.displaysAsynchronously = false
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
self.arrowNode.displaysAsynchronously = false
|
||||
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/DownArrow"), color: theme.rootController.navigationBar.secondaryTextColor)
|
||||
@ -145,6 +172,7 @@ final class MediaPickerTitleView: UIView {
|
||||
|
||||
self.addSubnode(self.contextSourceNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.subtitleNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.addSubnode(self.segmentedControlNode)
|
||||
@ -166,10 +194,18 @@ final class MediaPickerTitleView: UIView {
|
||||
self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - controlSize.width) / 2.0), y: floorToScreenPixels((size.height - controlSize.height) / 2.0)), size: controlSize)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: 210.0, height: 44.0))
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: 210.0, height: 44.0))
|
||||
|
||||
var totalHeight: CGFloat = titleSize.height
|
||||
if subtitleSize.height > 0.0 {
|
||||
totalHeight += subtitleSize.height
|
||||
}
|
||||
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - totalHeight) / 2.0)), size: titleSize)
|
||||
self.subtitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - subtitleSize.width) / 2.0), y: floorToScreenPixels((size.height - totalHeight) / 2.0) + subtitleSize.height + 7.0), size: subtitleSize)
|
||||
|
||||
if let arrowSize = self.arrowNode.image?.size {
|
||||
self.arrowNode.frame = CGRect(origin: CGPoint(x: self.titleNode.frame.maxX + 5.0, y: floorToScreenPixels((size.height - arrowSize.height) / 2.0) + 1.0 - UIScreenPixel), size: arrowSize)
|
||||
self.arrowNode.frame = CGRect(origin: CGPoint(x: self.titleNode.frame.maxX + 5.0, y: floorToScreenPixels((size.height - totalHeight) / 2.0) + titleSize.height / 2.0 - arrowSize.height / 2.0 + 1.0 - UIScreenPixel), size: arrowSize)
|
||||
}
|
||||
self.buttonNode.frame = CGRect(origin: .zero, size: size)
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
||||
}))
|
||||
}
|
||||
|
||||
options.append(OpenInOption(identifier: "2gis", application: .other(title: "2GIS", identifier: 481627348, scheme: "dgis", store: nil), action: {
|
||||
options.append(OpenInOption(identifier: "2gis", application: .other(title: "2GIS", identifier: 481627348, scheme: "dgis", store: "ru"), action: {
|
||||
let coordinates = "\(lon),\(lat)"
|
||||
if let _ = directions {
|
||||
return .openUrl(url: "dgis://2gis.ru/routeSearch/to/\(coordinates)/go")
|
||||
|
@ -1104,7 +1104,7 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
||||
self.hideButtonNode.isHidden = confirmation
|
||||
case .email:
|
||||
self.inputNode.textField.keyboardType = .emailAddress
|
||||
self.inputNode.textField.returnKeyType = .done
|
||||
self.inputNode.textField.returnKeyType = .next
|
||||
self.hideButtonNode.isHidden = true
|
||||
|
||||
if #available(iOS 12.0, *) {
|
||||
@ -1134,7 +1134,7 @@ private final class TwoFactorDataInputTextNode: ASDisplayNode, UITextFieldDelega
|
||||
}
|
||||
case .hint:
|
||||
self.inputNode.textField.keyboardType = .asciiCapable
|
||||
self.inputNode.textField.returnKeyType = .done
|
||||
self.inputNode.textField.returnKeyType = .next
|
||||
self.hideButtonNode.isHidden = true
|
||||
|
||||
self.inputNode.textField.autocorrectionType = .no
|
||||
|
@ -580,7 +580,7 @@ private func canEditAdminRights(accountPeerId: EnginePeer.Id, channelPeer: Engin
|
||||
switch initialParticipant {
|
||||
case .creator:
|
||||
return false
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
return adminInfo.canBeEditedByAccountPeer || adminInfo.promotedBy == accountPeerId
|
||||
} else {
|
||||
@ -703,7 +703,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
let currentRightsFlags: TelegramChatAdminRightsFlags
|
||||
if let updatedFlags = state.updatedFlags {
|
||||
currentRightsFlags = updatedFlags
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
currentRightsFlags = adminRights.rights.rights
|
||||
} else if let initialParticipant = initialParticipant, case let .creator(_, maybeAdminRights, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
currentRightsFlags = adminRights.rights.rights
|
||||
@ -761,7 +761,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
}
|
||||
} else {
|
||||
if case let .user(adminPeer) = adminPeer, adminPeer.botInfo != nil, case .group = channel.info, invite, let channelPeer = channelPeer, canEditAdminRights(accountPeerId: accountPeerId, channelPeer: channelPeer, initialParticipant: initialParticipant) {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _) = initialParticipant, adminInfo != nil {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _, _) = initialParticipant, adminInfo != nil {
|
||||
|
||||
} else {
|
||||
entries.append(.adminRights(presentationData.theme, presentationData.strings.Bot_AddToChat_Add_AdminRights, state.adminRights))
|
||||
@ -784,7 +784,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
let currentRightsFlags: TelegramChatAdminRightsFlags
|
||||
if let updatedFlags = state.updatedFlags {
|
||||
currentRightsFlags = updatedFlags
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
currentRightsFlags = adminRights.rights.rights
|
||||
} else {
|
||||
currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins).subtracting(.canBeAnonymous)
|
||||
@ -846,14 +846,14 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
canTransfer = true
|
||||
}
|
||||
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _) = initialParticipant, admin.id != accountPeerId, adminInfo != nil {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _, _) = initialParticipant, admin.id != accountPeerId, adminInfo != nil {
|
||||
if channel.flags.contains(.isCreator) {
|
||||
canDismiss = true
|
||||
} else {
|
||||
switch initialParticipant {
|
||||
case .creator:
|
||||
break
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
if adminInfo.promotedBy == accountPeerId || adminInfo.canBeEditedByAccountPeer {
|
||||
canDismiss = true
|
||||
@ -862,7 +862,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminInfo, _, _) = initialParticipant, let adminInfo = maybeAdminInfo {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminInfo, _, _, _) = initialParticipant, let adminInfo = maybeAdminInfo {
|
||||
var index = 0
|
||||
rightsLoop: for right in rightsOrder {
|
||||
let enabled: Bool = false
|
||||
@ -955,7 +955,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
entries.append(.rank(presentationData.theme, presentationData.strings, isCreator ? presentationData.strings.Group_EditAdmin_RankOwnerPlaceholder : presentationData.strings.Group_EditAdmin_RankAdminPlaceholder, currentRank ?? "", rankEnabled))
|
||||
} else {
|
||||
if case let .user(adminPeer) = adminPeer, adminPeer.botInfo != nil, invite {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminRights, _, _) = initialParticipant, adminRights != nil {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminRights, _, _, _) = initialParticipant, adminRights != nil {
|
||||
} else {
|
||||
entries.append(.adminRights(presentationData.theme, presentationData.strings.Bot_AddToChat_Add_AdminRights, state.adminRights))
|
||||
}
|
||||
@ -988,7 +988,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
let currentRightsFlags: TelegramChatAdminRightsFlags
|
||||
if let updatedFlags = state.updatedFlags {
|
||||
currentRightsFlags = updatedFlags
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||
currentRightsFlags = adminRights.rights.rights.subtracting(.canAddAdmins).subtracting(.canBeAnonymous)
|
||||
} else {
|
||||
currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins).subtracting(.canBeAnonymous)
|
||||
@ -1016,7 +1016,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
||||
entries.append(.rankInfo(presentationData.theme, presentationData.strings.Group_EditAdmin_RankInfo(placeholder).string, invite))
|
||||
}
|
||||
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _) = initialParticipant, admin.id != accountPeerId, let adminInfo {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, adminInfo, _, _, _) = initialParticipant, admin.id != accountPeerId, let adminInfo {
|
||||
var canDismiss = false
|
||||
if accountIsCreator {
|
||||
canDismiss = true
|
||||
@ -1319,7 +1319,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
||||
case let .creator(_, adminInfo, rank):
|
||||
currentRank = rank
|
||||
currentFlags = adminInfo?.rights.rights ?? maskRightsFlags.subtracting(.canBeAnonymous)
|
||||
case let .member(_, _, adminInfo, _, rank):
|
||||
case let .member(_, _, adminInfo, _, rank, _):
|
||||
if updateFlags == nil {
|
||||
if adminInfo?.rights == nil {
|
||||
if channel.flags.contains(.isCreator) {
|
||||
|
@ -266,7 +266,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
peerText = strings.Channel_Management_LabelOwner
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
if let peer = participant.peers[adminInfo.promotedBy] {
|
||||
if peer.id == participant.peer.id {
|
||||
@ -424,14 +424,14 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
||||
switch lhs.participant {
|
||||
case .creator:
|
||||
lhsInvitedAt = Int32.min
|
||||
case let .member(_, invitedAt, _, _, _):
|
||||
case let .member(_, invitedAt, _, _, _, _):
|
||||
lhsInvitedAt = invitedAt
|
||||
}
|
||||
let rhsInvitedAt: Int32
|
||||
switch rhs.participant {
|
||||
case .creator:
|
||||
rhsInvitedAt = Int32.min
|
||||
case let .member(_, invitedAt, _, _, _):
|
||||
case let .member(_, invitedAt, _, _, _, _):
|
||||
rhsInvitedAt = invitedAt
|
||||
}
|
||||
return lhsInvitedAt < rhsInvitedAt
|
||||
@ -443,7 +443,7 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
||||
case .creator:
|
||||
canEdit = false
|
||||
canOpen = isGroup && peer.flags.contains(.isCreator)
|
||||
case let .member(id, _, adminInfo, _, _):
|
||||
case let .member(id, _, adminInfo, _, _, _):
|
||||
if id == accountPeerId {
|
||||
canEdit = false
|
||||
} else if let adminInfo = adminInfo {
|
||||
@ -508,14 +508,14 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
||||
switch lhs.participant {
|
||||
case .creator:
|
||||
lhsInvitedAt = Int32.min
|
||||
case let .member(_, invitedAt, _, _, _):
|
||||
case let .member(_, invitedAt, _, _, _, _):
|
||||
lhsInvitedAt = invitedAt
|
||||
}
|
||||
let rhsInvitedAt: Int32
|
||||
switch rhs.participant {
|
||||
case .creator:
|
||||
rhsInvitedAt = Int32.min
|
||||
case let .member(_, invitedAt, _, _, _):
|
||||
case let .member(_, invitedAt, _, _, _, _):
|
||||
rhsInvitedAt = invitedAt
|
||||
}
|
||||
return lhsInvitedAt < rhsInvitedAt
|
||||
@ -530,7 +530,7 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
||||
} else {
|
||||
canEdit = false
|
||||
}
|
||||
case let .member(id, _, adminInfo, _, _):
|
||||
case let .member(id, _, adminInfo, _, _, _):
|
||||
if id == accountPeerId {
|
||||
editable = false
|
||||
} else if let adminInfo = adminInfo {
|
||||
@ -741,7 +741,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
return
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
if let banInfo = banInfo {
|
||||
var canUnban = false
|
||||
if banInfo.restrictedBy != context.account.peerId {
|
||||
@ -870,7 +870,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
peers[creator.id] = creator
|
||||
peers[peer.id] = peer
|
||||
result.append(RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .internal_groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer._asPeer(), peers: peers.mapValues({ $0._asPeer() })))
|
||||
result.append(RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .internal_groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: peer._asPeer(), peers: peers.mapValues({ $0._asPeer() })))
|
||||
case .member:
|
||||
break
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
||||
let currentRightsFlags: TelegramChatBannedRightsFlags
|
||||
if let updatedFlags = state.updatedFlags {
|
||||
currentRightsFlags = updatedFlags
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
currentRightsFlags = banInfo.rights.flags
|
||||
} else {
|
||||
currentRightsFlags = defaultBannedRights.flags
|
||||
@ -312,7 +312,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
||||
let currentTimeout: Int32
|
||||
if let updatedTimeout = state.updatedTimeout {
|
||||
currentTimeout = updatedTimeout
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
currentTimeout = banInfo.rights.untilDate
|
||||
} else {
|
||||
currentTimeout = Int32.max
|
||||
@ -351,7 +351,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
||||
|
||||
entries.append(.timeout(presentationData.theme, presentationData.strings.GroupPermission_Duration, currentTimeoutString))
|
||||
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _) = initialParticipant, let initialBannedBy = initialBannedBy {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _, _) = initialParticipant, let initialBannedBy = initialBannedBy {
|
||||
entries.append(.exceptionInfo(presentationData.theme, presentationData.strings.GroupPermission_AddedInfo(initialBannedBy.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), stringForRelativeSymbolicTimestamp(strings: presentationData.strings, relativeTimestamp: banInfo.timestamp, relativeTo: state.referenceTimestamp, dateTimeFormat: presentationData.dateTimeFormat)).string))
|
||||
entries.append(.delete(presentationData.theme, presentationData.strings.GroupPermission_Delete))
|
||||
}
|
||||
@ -363,7 +363,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
||||
let currentRightsFlags: TelegramChatBannedRightsFlags
|
||||
if let updatedFlags = state.updatedFlags {
|
||||
currentRightsFlags = updatedFlags
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
currentRightsFlags = banInfo.rights.flags
|
||||
} else {
|
||||
currentRightsFlags = defaultBannedRightsFlags
|
||||
@ -372,7 +372,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
||||
let currentTimeout: Int32
|
||||
if let updatedTimeout = state.updatedTimeout {
|
||||
currentTimeout = updatedTimeout
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
currentTimeout = banInfo.rights.untilDate
|
||||
} else {
|
||||
currentTimeout = Int32.max
|
||||
@ -411,7 +411,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation
|
||||
|
||||
entries.append(.timeout(presentationData.theme, presentationData.strings.GroupPermission_Duration, currentTimeoutString))
|
||||
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _) = initialParticipant, let initialBannedBy = initialBannedBy {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _, _) = initialParticipant, let initialBannedBy = initialBannedBy {
|
||||
entries.append(.exceptionInfo(presentationData.theme, presentationData.strings.GroupPermission_AddedInfo(initialBannedBy.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), stringForRelativeSymbolicTimestamp(strings: presentationData.strings, relativeTimestamp: banInfo.timestamp, relativeTo: state.referenceTimestamp, dateTimeFormat: presentationData.dateTimeFormat)).string))
|
||||
entries.append(.delete(presentationData.theme, presentationData.strings.GroupPermission_Delete))
|
||||
}
|
||||
@ -462,7 +462,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
||||
var effectiveRightsFlags: TelegramChatBannedRightsFlags
|
||||
if let updatedFlags = state.updatedFlags {
|
||||
effectiveRightsFlags = updatedFlags
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _) = initialParticipant {
|
||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _, _) = initialParticipant {
|
||||
effectiveRightsFlags = banInfo.rights.flags
|
||||
} else {
|
||||
effectiveRightsFlags = defaultBannedRightsFlags
|
||||
@ -671,7 +671,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
||||
}
|
||||
|
||||
if updateFlags == nil && updateTimeout == nil {
|
||||
if case let .member(_, _, _, maybeBanInfo, _) = initialParticipant {
|
||||
if case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant {
|
||||
if maybeBanInfo == nil {
|
||||
updateFlags = defaultBannedRightsFlags
|
||||
updateTimeout = Int32.max
|
||||
@ -683,7 +683,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
||||
let currentRightsFlags: TelegramChatBannedRightsFlags
|
||||
if let updatedFlags = updateFlags {
|
||||
currentRightsFlags = updatedFlags
|
||||
} else if case let .member(_, _, _, maybeBanInfo, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
} else if case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
currentRightsFlags = banInfo.rights.flags
|
||||
} else {
|
||||
currentRightsFlags = defaultBannedRightsFlags
|
||||
@ -692,7 +692,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
||||
let currentTimeout: Int32
|
||||
if let updateTimeout = updateTimeout {
|
||||
currentTimeout = updateTimeout
|
||||
} else if case let .member(_, _, _, maybeBanInfo, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
} else if case let .member(_, _, _, maybeBanInfo, _, _) = initialParticipant, let banInfo = maybeBanInfo {
|
||||
currentTimeout = banInfo.rights.untilDate
|
||||
} else {
|
||||
currentTimeout = Int32.max
|
||||
@ -724,7 +724,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
||||
}
|
||||
|
||||
var previousRights: TelegramChatBannedRights?
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _) = initialParticipant, banInfo != nil {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _, _) = initialParticipant, banInfo != nil {
|
||||
previousRights = banInfo?.rights
|
||||
}
|
||||
|
||||
@ -825,7 +825,7 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen
|
||||
}
|
||||
|
||||
let title: String
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _) = initialParticipant, banInfo != nil {
|
||||
if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _, _) = initialParticipant, banInfo != nil {
|
||||
title = presentationData.strings.GroupPermission_Title
|
||||
} else {
|
||||
title = presentationData.strings.GroupPermission_NewTitle
|
||||
|
@ -160,7 +160,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
case let .peerItem(_, strings, dateTimeFormat, nameDisplayOrder, _, participant, editing, enabled):
|
||||
var text: ItemListPeerItemText = .none
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
|
||||
text = .text(strings.Channel_Management_RemovedBy(EnginePeer(peer).displayTitle(strings: strings, displayOrder: nameDisplayOrder)).string, .secondary)
|
||||
}
|
||||
@ -306,7 +306,7 @@ public func channelBlacklistController(context: AccountContext, updatedPresentat
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
return
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo, adminInfo.promotedBy != context.account.peerId {
|
||||
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Channel_Members_AddBannedErrorAdmin, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
return
|
||||
|
@ -511,7 +511,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
case .creator:
|
||||
canPromote = false
|
||||
canRestrict = false
|
||||
case let .member(_, _, adminRights, bannedRights, _):
|
||||
case let .member(_, _, adminRights, bannedRights, _, _):
|
||||
if channel.hasPermission(.addAdmins) {
|
||||
canPromote = true
|
||||
} else {
|
||||
@ -740,7 +740,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
case .creator:
|
||||
canPromote = false
|
||||
canRestrict = false
|
||||
case let .member(_, _, adminRights, bannedRights, _):
|
||||
case let .member(_, _, adminRights, bannedRights, _, _):
|
||||
if channel.hasPermission(.addAdmins) {
|
||||
canPromote = true
|
||||
} else {
|
||||
@ -778,7 +778,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = presentationData.strings.Channel_Management_LabelOwner
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if adminInfo != nil {
|
||||
label = presentationData.strings.Channel_Management_LabelEditor
|
||||
}
|
||||
@ -809,7 +809,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = presentationData.strings.Channel_Management_LabelOwner
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
if let peer = participant.peers[adminInfo.promotedBy] {
|
||||
if peer.id == participant.peer.id {
|
||||
@ -822,7 +822,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
}
|
||||
case .searchBanned:
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
if let banInfo = banInfo {
|
||||
var exceptionsString = ""
|
||||
let sendMediaRights = banSendMediaSubList().map { $0.0 }
|
||||
@ -844,7 +844,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
}
|
||||
case .searchKicked:
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
|
||||
label = presentationData.strings.Channel_Management_RemovedBy(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
@ -972,11 +972,11 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
peers[creator.id] = creator
|
||||
}
|
||||
peers[peer.id] = EnginePeer(peer)
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group))), promotedBy: creatorPeer?.id ?? context.account.peerId, canBeEditedByAccountPeer: creatorPeer?.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }))
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group))), promotedBy: creatorPeer?.id ?? context.account.peerId, canBeEditedByAccountPeer: creatorPeer?.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }))
|
||||
case .member:
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
peers[peer.id] = EnginePeer(peer)
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }))
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }))
|
||||
}
|
||||
matchingMembers.append(renderedParticipant)
|
||||
}
|
||||
@ -1057,7 +1057,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = presentationData.strings.Channel_Management_LabelOwner
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if adminInfo != nil {
|
||||
label = presentationData.strings.Channel_Management_LabelEditor
|
||||
}
|
||||
@ -1073,7 +1073,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = presentationData.strings.Channel_Management_LabelOwner
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
if let peer = participant.peers[adminInfo.promotedBy] {
|
||||
if peer.id == participant.peer.id {
|
||||
@ -1086,7 +1086,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
}
|
||||
case .searchBanned:
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
if let banInfo = banInfo {
|
||||
var exceptionsString = ""
|
||||
let sendMediaRights = banSendMediaSubList().map { $0.0 }
|
||||
@ -1108,7 +1108,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
|
||||
}
|
||||
case .searchKicked:
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
|
||||
label = presentationData.strings.Channel_Management_RemovedBy(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||
}
|
||||
|
@ -399,11 +399,11 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
peers[creator.id] = creator
|
||||
peers[peer.id] = EnginePeer(peer)
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(mainPeer))), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }), presences: peerView.peerPresences)
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(mainPeer))), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }), presences: peerView.peerPresences)
|
||||
case .member:
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
peers[peer.id] = EnginePeer(peer)
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }), presences: peerView.peerPresences)
|
||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }), presences: peerView.peerPresences)
|
||||
}
|
||||
|
||||
entries.append(.peer(index, renderedParticipant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, false, false))
|
||||
|
@ -381,7 +381,7 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry {
|
||||
case let .peerItem(_, strings, dateTimeFormat, nameDisplayOrder, _, participant, editing, enabled, canOpen, defaultBannedRights):
|
||||
var text: ItemListPeerItemText = .none
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo, _):
|
||||
case let .member(_, _, _, banInfo, _, _):
|
||||
var exceptionsString = ""
|
||||
if let banInfo = banInfo {
|
||||
let sendMediaRights = banSendMediaSubList().map { $0.0 }
|
||||
@ -937,7 +937,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
return
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo, adminInfo.promotedBy != context.account.peerId {
|
||||
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Channel_Members_AddBannedErrorAdmin, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
return
|
||||
|
@ -56,6 +56,10 @@ public func representationFetchRangeForDisplayAtSize(representation: TelegramMed
|
||||
}
|
||||
|
||||
public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, photoReference: ImageMediaReference, fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, tryAdditionalRepresentations: Bool = false, synchronousLoad: Bool = false, useMiniThumbnailIfAvailable: Bool = false, forceThumbnail: Bool = false, automaticFetch: Bool = true) -> Signal<Tuple4<Data?, Data?, ChatMessagePhotoQuality, Bool>, NoError> {
|
||||
return chatMessagePhotoDatas(mediaBox: postbox.mediaBox, userLocation: userLocation, customUserContentType: customUserContentType, photoReference: photoReference, fullRepresentationSize: fullRepresentationSize, autoFetchFullSize: autoFetchFullSize, tryAdditionalRepresentations: tryAdditionalRepresentations, synchronousLoad: synchronousLoad, useMiniThumbnailIfAvailable: useMiniThumbnailIfAvailable, forceThumbnail: forceThumbnail, automaticFetch: automaticFetch)
|
||||
}
|
||||
|
||||
func chatMessagePhotoDatas(mediaBox: MediaBox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, photoReference: ImageMediaReference, fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, tryAdditionalRepresentations: Bool = false, synchronousLoad: Bool = false, useMiniThumbnailIfAvailable: Bool = false, forceThumbnail: Bool = false, automaticFetch: Bool = true) -> Signal<Tuple4<Data?, Data?, ChatMessagePhotoQuality, Bool>, NoError> {
|
||||
if !forceThumbnail, let progressiveRepresentation = progressiveImageRepresentation(photoReference.media.representations), progressiveRepresentation.progressiveSizes.count > 1 {
|
||||
enum SizeSource {
|
||||
case miniThumbnail(data: Data)
|
||||
@ -93,7 +97,7 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
case let .miniThumbnail(data):
|
||||
return .single((source, data))
|
||||
case let .image(size):
|
||||
return postbox.mediaBox.resourceData(progressiveRepresentation.resource, size: Int64(progressiveRepresentation.progressiveSizes.last!), in: 0 ..< size, mode: .incremental, notifyAboutIncomplete: true, attemptSynchronously: synchronousLoad)
|
||||
return mediaBox.resourceData(progressiveRepresentation.resource, size: Int64(progressiveRepresentation.progressiveSizes.last!), in: 0 ..< size, mode: .incremental, notifyAboutIncomplete: true, attemptSynchronously: synchronousLoad)
|
||||
|> map { (data, _) -> (SizeSource, Data?) in
|
||||
return (source, data)
|
||||
}
|
||||
@ -131,9 +135,9 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
var fetchDisposable: Disposable?
|
||||
if automaticFetch {
|
||||
if autoFetchFullSize {
|
||||
fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(largestByteSize), .default), statsCategory: .image).start()
|
||||
fetchDisposable = fetchedMediaResource(mediaBox: mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(largestByteSize), .default), statsCategory: .image).start()
|
||||
} else if useMiniThumbnailIfAvailable {
|
||||
fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(thumbnailByteSize), .default), statsCategory: .image).start()
|
||||
fetchDisposable = fetchedMediaResource(mediaBox: mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< Int64(thumbnailByteSize), .default), statsCategory: .image).start()
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,8 +149,8 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
}
|
||||
|
||||
if !forceThumbnail || photoReference.media.immediateThumbnailData == nil, let smallestRepresentation = smallestImageRepresentation(photoReference.media.representations), let largestRepresentation = photoReference.media.representationForDisplayAtSize(PixelDimensions(width: Int32(fullRepresentationSize.width), height: Int32(fullRepresentationSize.height))), let fullRepresentation = largestImageRepresentation(photoReference.media.representations) {
|
||||
let maybeFullSize = postbox.mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
let maybeLargestSize = postbox.mediaBox.resourceData(fullRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
let maybeFullSize = mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
let maybeLargestSize = mediaBox.resourceData(fullRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
|
||||
let signal = combineLatest(maybeFullSize, maybeLargestSize)
|
||||
|> take(1)
|
||||
@ -163,16 +167,16 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
if let _ = decodedThumbnailData {
|
||||
fetchedThumbnail = .complete()
|
||||
} else {
|
||||
fetchedThumbnail = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(smallestRepresentation.resource), statsCategory: .image)
|
||||
fetchedThumbnail = fetchedMediaResource(mediaBox: mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(smallestRepresentation.resource), statsCategory: .image)
|
||||
}
|
||||
let fetchedFullSize = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image)
|
||||
let fetchedFullSize = fetchedMediaResource(mediaBox: mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? .image, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image)
|
||||
|
||||
let anyThumbnail: [Signal<(MediaResourceData, ChatMessagePhotoQuality), NoError>]
|
||||
if tryAdditionalRepresentations {
|
||||
anyThumbnail = photoReference.media.representations.filter({ representation in
|
||||
return representation != largestRepresentation
|
||||
}).map({ representation -> Signal<(MediaResourceData, ChatMessagePhotoQuality), NoError> in
|
||||
return postbox.mediaBox.resourceData(representation.resource)
|
||||
return mediaBox.resourceData(representation.resource)
|
||||
|> take(1)
|
||||
|> map { data -> (MediaResourceData, ChatMessagePhotoQuality) in
|
||||
if representation.dimensions.width > 200 || representation.dimensions.height > 200 {
|
||||
@ -193,7 +197,7 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
return EmptyDisposable
|
||||
} else {
|
||||
let fetchedDisposable = fetchedThumbnail.start()
|
||||
let thumbnailDisposable = postbox.mediaBox.resourceData(smallestRepresentation.resource, attemptSynchronously: synchronousLoad).start(next: { next in
|
||||
let thumbnailDisposable = mediaBox.resourceData(smallestRepresentation.resource, attemptSynchronously: synchronousLoad).start(next: { next in
|
||||
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
|
||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
|
||||
@ -222,7 +226,7 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
if autoFetchFullSize && !useMiniThumbnailIfAvailable {
|
||||
fullSizeData = Signal<Tuple2<Data?, Bool>, NoError> { subscriber in
|
||||
let fetchedFullSizeDisposable = fetchedFullSize.start()
|
||||
let fullSizeDisposable = postbox.mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad).start(next: { next in
|
||||
let fullSizeDisposable = mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad).start(next: { next in
|
||||
subscriber.putNext(Tuple(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete))
|
||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
|
||||
@ -232,7 +236,7 @@ public func chatMessagePhotoDatas(postbox: Postbox, userLocation: MediaResourceU
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fullSizeData = postbox.mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
fullSizeData = mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
|
||||
|> map { next -> Tuple2<Data?, Bool> in
|
||||
return Tuple(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete)
|
||||
}
|
||||
@ -600,6 +604,13 @@ public func chatMessagePhoto(postbox: Postbox, userLocation: MediaResourceUserLo
|
||||
}
|
||||
}
|
||||
|
||||
public func chatMessagePhoto(mediaBox: MediaBox, userLocation: MediaResourceUserLocation, userContentType customUserContentType: MediaResourceUserContentType? = nil, photoReference: ImageMediaReference, synchronousLoad: Bool = false, highQuality: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
return chatMessagePhotoInternal(photoData: chatMessagePhotoDatas(mediaBox: mediaBox, userLocation: userLocation, customUserContentType: customUserContentType, photoReference: photoReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad), synchronousLoad: synchronousLoad)
|
||||
|> map { _, _, generate in
|
||||
return generate
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatMessagePhotoQuality {
|
||||
case none
|
||||
case blurred
|
||||
|
@ -7,6 +7,7 @@ import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import UrlEscaping
|
||||
import ActivityIndicator
|
||||
|
||||
private final class WebBrowserDomainInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
|
||||
private var theme: PresentationTheme
|
||||
@ -116,7 +117,12 @@ private final class WebBrowserDomainInputFieldNode: ASDisplayNode, ASEditableTex
|
||||
private let domainRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.?)*([a-zA-Z]*)?(:)?(/)?$", options: [])
|
||||
private let pathRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}/", options: [])
|
||||
|
||||
var inProgress = false
|
||||
|
||||
func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
if self.inProgress {
|
||||
return false
|
||||
}
|
||||
if text == "\n" {
|
||||
self.complete?()
|
||||
return false
|
||||
@ -168,6 +174,7 @@ private final class WebBrowserDomainAlertContentNode: AlertContentNode {
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let textNode: ASTextNode
|
||||
let activityIndicator: ActivityIndicator
|
||||
let inputFieldNode: WebBrowserDomainInputFieldNode
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
@ -198,6 +205,9 @@ private final class WebBrowserDomainAlertContentNode: AlertContentNode {
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
|
||||
self.activityIndicator = ActivityIndicator(type: .custom(ptheme.rootController.navigationBar.secondaryTextColor, 20.0, 1.5, false), speed: .slow)
|
||||
self.activityIndicator.isHidden = true
|
||||
|
||||
self.inputFieldNode = WebBrowserDomainInputFieldNode(theme: ptheme, placeholder: strings.WebBrowser_Exceptions_Create_Placeholder)
|
||||
self.inputFieldNode.text = ""
|
||||
|
||||
@ -224,7 +234,8 @@ private final class WebBrowserDomainAlertContentNode: AlertContentNode {
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.addSubnode(self.inputFieldNode)
|
||||
|
||||
self.addSubnode(self.activityIndicator)
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
@ -335,9 +346,13 @@ private final class WebBrowserDomainAlertContentNode: AlertContentNode {
|
||||
let inputFieldWidth = resultWidth
|
||||
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
|
||||
let inputHeight = inputFieldHeight
|
||||
transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight))
|
||||
let inputFrame = CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight)
|
||||
transition.updateFrame(node: self.inputFieldNode, frame: inputFrame)
|
||||
transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0)
|
||||
|
||||
let activitySize = CGSize(width: 20.0, height: 20.0)
|
||||
transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: inputFrame.maxX - activitySize.width - 23.0, y: inputFrame.midY - activitySize.height / 2.0 - 3.0), size: activitySize))
|
||||
|
||||
let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom)
|
||||
|
||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
@ -404,11 +419,16 @@ public func webBrowserDomainController(context: AccountContext, updatedPresentat
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
var applyImpl: (() -> Void)?
|
||||
|
||||
var inProgress = false
|
||||
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?(true)
|
||||
apply(nil)
|
||||
if !inProgress {
|
||||
dismissImpl?(true)
|
||||
apply(nil)
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Done, action: {
|
||||
applyImpl?()
|
||||
if !inProgress {
|
||||
applyImpl?()
|
||||
}
|
||||
})]
|
||||
|
||||
let contentNode = WebBrowserDomainAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions)
|
||||
@ -419,9 +439,12 @@ public func webBrowserDomainController(context: AccountContext, updatedPresentat
|
||||
guard let contentNode = contentNode else {
|
||||
return
|
||||
}
|
||||
inProgress = true
|
||||
contentNode.inputFieldNode.inProgress = true
|
||||
contentNode.activityIndicator.isHidden = false
|
||||
|
||||
let updatedLink = explicitUrl(contentNode.link)
|
||||
if !updatedLink.isEmpty && isValidUrl(updatedLink, validSchemes: ["http": true, "https": true]) {
|
||||
dismissImpl?(true)
|
||||
apply(updatedLink)
|
||||
} else {
|
||||
contentNode.animateError()
|
||||
|
@ -7,32 +7,43 @@ import TelegramPresentationData
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import ItemListUI
|
||||
import PhotoResources
|
||||
|
||||
public class WebBrowserDomainExceptionItem: ListViewItem, ItemListItem {
|
||||
private enum RevealOptionKey: Int32 {
|
||||
case delete
|
||||
}
|
||||
|
||||
final class WebBrowserDomainExceptionItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let context: AccountContext?
|
||||
let context: AccountContext
|
||||
let title: String
|
||||
let label: String
|
||||
public let sectionId: ItemListSectionId
|
||||
let icon: TelegramMediaImage?
|
||||
let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
let deleted: (() -> Void)?
|
||||
|
||||
public init(
|
||||
init(
|
||||
presentationData: ItemListPresentationData,
|
||||
context: AccountContext? = nil,
|
||||
context: AccountContext,
|
||||
title: String,
|
||||
label: String,
|
||||
icon: TelegramMediaImage?,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle
|
||||
style: ItemListStyle,
|
||||
deleted: (() -> Void)?
|
||||
) {
|
||||
self.presentationData = presentationData
|
||||
self.context = context
|
||||
self.title = title
|
||||
self.label = label
|
||||
self.icon = icon
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
self.deleted = deleted
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = WebBrowserDomainExceptionItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
@ -48,7 +59,7 @@ public class WebBrowserDomainExceptionItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? WebBrowserDomainExceptionItemNode {
|
||||
let makeLayout = nodeValue.asyncLayout()
|
||||
@ -65,33 +76,34 @@ public class WebBrowserDomainExceptionItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
}
|
||||
|
||||
public var selectable: Bool = false
|
||||
var selectable: Bool = false
|
||||
|
||||
public func selected(listView: ListView){
|
||||
func selected(listView: ListView){
|
||||
}
|
||||
}
|
||||
|
||||
public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
let iconNode: ASImageNode
|
||||
let iconNode: TransformImageNode
|
||||
let titleNode: TextNode
|
||||
let labelNode: TextNode
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var item: WebBrowserDomainExceptionItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
|
||||
override public var canBeSelected: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public var tag: ItemListItemTag? = nil
|
||||
var tag: ItemListItemTag? = nil
|
||||
|
||||
public init() {
|
||||
init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = .white
|
||||
@ -105,7 +117,7 @@ public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNo
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode = TransformImageNode()
|
||||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
@ -117,15 +129,16 @@ public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNo
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ item: WebBrowserDomainExceptionItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
func asyncLayout() -> (_ item: WebBrowserDomainExceptionItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
|
||||
@ -143,7 +156,7 @@ public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNo
|
||||
let itemBackgroundColor: UIColor
|
||||
let itemSeparatorColor: UIColor
|
||||
|
||||
let leftInset = 16.0 + params.leftInset + 43.0
|
||||
let leftInset = 16.0 + params.leftInset + 46.0
|
||||
|
||||
let titleColor: UIColor = item.presentationData.theme.list.itemPrimaryTextColor
|
||||
let labelColor: UIColor = item.presentationData.theme.list.itemAccentColor
|
||||
@ -180,6 +193,7 @@ public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNo
|
||||
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.layoutParams = params
|
||||
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
strongSelf.activateArea.accessibilityLabel = item.title
|
||||
@ -191,6 +205,15 @@ public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNo
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
}
|
||||
|
||||
let iconSize = CGSize(width: 40.0, height: 40.0)
|
||||
var imageSize = iconSize
|
||||
if currentItem?.icon?.id != item.icon?.id, let icon = item.icon {
|
||||
strongSelf.iconNode.setSignal(chatMessagePhoto(mediaBox: item.context.sharedContext.accountManager.mediaBox, userLocation: .other, photoReference: .standalone(media: icon)))
|
||||
}
|
||||
if let icon = item.icon, let dimensions = largestImageRepresentation(icon.representations)?.dimensions.cgSize {
|
||||
imageSize = dimensions.aspectFilled(imageSize)
|
||||
}
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = labelApply()
|
||||
|
||||
@ -256,25 +279,69 @@ public class WebBrowserDomainExceptionItemNode: ListViewItemNode, ItemListItemNo
|
||||
centralContentHeight += titleSpacing
|
||||
centralContentHeight += labelLayout.size.height
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - centralContentHeight) / 2.0)), size: titleLayout.size)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset + strongSelf.revealOffset, y: floor((height - centralContentHeight) / 2.0)), size: titleLayout.size)
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
|
||||
let labelFrame = CGRect(origin: CGPoint(x: leftInset, y: titleFrame.maxY + titleSpacing), size: labelLayout.size)
|
||||
let labelFrame = CGRect(origin: CGPoint(x: leftInset + strongSelf.revealOffset, y: titleFrame.maxY + titleSpacing), size: labelLayout.size)
|
||||
strongSelf.labelNode.frame = labelFrame
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: params.leftInset + 11.0 + strongSelf.revealOffset, y: floorToScreenPixels((contentSize.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
strongSelf.iconNode.frame = iconFrame
|
||||
|
||||
strongSelf.iconNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(radius: 7.0), imageSize: imageSize, boundingSize: iconSize, intrinsicInsets: .zero))()
|
||||
|
||||
strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
|
||||
|
||||
var revealOptions: [ItemListRevealOption] = []
|
||||
revealOptions.append(ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor))
|
||||
strongSelf.setRevealOptions((left: [], right: revealOptions))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) {
|
||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.updateRevealOffset(offset: offset, transition: transition)
|
||||
|
||||
if let params = self.layoutParams {
|
||||
let leftInset: CGFloat = 16.0 + params.leftInset + 46.0
|
||||
|
||||
var iconFrame = self.iconNode.frame
|
||||
iconFrame.origin.x = params.leftInset + 11.0 + offset
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
|
||||
var titleFrame = self.titleNode.frame
|
||||
titleFrame.origin.x = leftInset + offset
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
|
||||
var subtitleFrame = self.labelNode.frame
|
||||
subtitleFrame.origin.x = leftInset + offset
|
||||
transition.updateFrame(node: self.labelNode, frame: subtitleFrame)
|
||||
}
|
||||
}
|
||||
|
||||
override func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) {
|
||||
if let item = self.item {
|
||||
switch option.key {
|
||||
case RevealOptionKey.delete.rawValue:
|
||||
item.deleted?()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.setRevealOptionsOpened(false, animated: true)
|
||||
self.revealOptionsInteractivelyClosed()
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import PresentationDataUtils
|
||||
import ItemListUI
|
||||
import AccountContext
|
||||
import OpenInExternalAppUI
|
||||
@ -13,25 +14,33 @@ import ItemListPeerActionItem
|
||||
import UndoUI
|
||||
import WebKit
|
||||
import LinkPresentation
|
||||
import CoreServices
|
||||
import PersistentStringHash
|
||||
|
||||
private final class WebBrowserSettingsControllerArguments {
|
||||
let context: AccountContext
|
||||
let updateDefaultBrowser: (String?) -> Void
|
||||
let clearCookies: () -> Void
|
||||
let clearCache: () -> Void
|
||||
let addException: () -> Void
|
||||
let removeException: (String) -> Void
|
||||
let clearExceptions: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
updateDefaultBrowser: @escaping (String?) -> Void,
|
||||
clearCookies: @escaping () -> Void,
|
||||
clearCache: @escaping () -> Void,
|
||||
addException: @escaping () -> Void,
|
||||
removeException: @escaping (String) -> Void,
|
||||
clearExceptions: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.updateDefaultBrowser = updateDefaultBrowser
|
||||
self.clearCookies = clearCookies
|
||||
self.clearCache = clearCache
|
||||
self.addException = addException
|
||||
self.removeException = removeException
|
||||
self.clearExceptions = clearExceptions
|
||||
}
|
||||
}
|
||||
@ -47,6 +56,7 @@ private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
case browser(PresentationTheme, String, OpenInApplication?, String?, Bool, Int32)
|
||||
|
||||
case clearCookies(PresentationTheme, String)
|
||||
case clearCache(PresentationTheme, String)
|
||||
case clearCookiesInfo(PresentationTheme, String)
|
||||
|
||||
case exceptionsHeader(PresentationTheme, String)
|
||||
@ -59,14 +69,39 @@ private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case .browserHeader, .browser:
|
||||
return WebBrowserSettingsSection.browsers.rawValue
|
||||
case .clearCookies, .clearCookiesInfo:
|
||||
case .clearCookies, .clearCache, .clearCookiesInfo:
|
||||
return WebBrowserSettingsSection.clearCookies.rawValue
|
||||
case .exceptionsHeader, .exceptionsAdd, .exception, .exceptionsClear, .exceptionsInfo:
|
||||
return WebBrowserSettingsSection.exceptions.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
var stableId: UInt64 {
|
||||
switch self {
|
||||
case .browserHeader:
|
||||
return 0
|
||||
case let .browser(_, _, _, _, _, index):
|
||||
return UInt64(1 + index)
|
||||
case .clearCookies:
|
||||
return 102
|
||||
case .clearCache:
|
||||
return 103
|
||||
case .clearCookiesInfo:
|
||||
return 104
|
||||
case .exceptionsHeader:
|
||||
return 105
|
||||
case .exceptionsAdd:
|
||||
return 106
|
||||
case let .exception(_, _, exception):
|
||||
return 2000 + exception.domain.persistentHashValue
|
||||
case .exceptionsClear:
|
||||
return 1000
|
||||
case .exceptionsInfo:
|
||||
return 1001
|
||||
}
|
||||
}
|
||||
|
||||
var sortId: Int32 {
|
||||
switch self {
|
||||
case .browserHeader:
|
||||
return 0
|
||||
@ -74,14 +109,16 @@ private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
return 1 + index
|
||||
case .clearCookies:
|
||||
return 102
|
||||
case .clearCookiesInfo:
|
||||
case .clearCache:
|
||||
return 103
|
||||
case .exceptionsHeader:
|
||||
case .clearCookiesInfo:
|
||||
return 104
|
||||
case .exceptionsAdd:
|
||||
case .exceptionsHeader:
|
||||
return 105
|
||||
case .exceptionsAdd:
|
||||
return 106
|
||||
case let .exception(index, _, _):
|
||||
return 106 + index
|
||||
return 107 + index
|
||||
case .exceptionsClear:
|
||||
return 1000
|
||||
case .exceptionsInfo:
|
||||
@ -109,6 +146,12 @@ private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .clearCache(lhsTheme, lhsText):
|
||||
if case let .clearCache(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .clearCookiesInfo(lhsTheme, lhsText):
|
||||
if case let .clearCookiesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
@ -149,7 +192,7 @@ private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
}
|
||||
|
||||
static func <(lhs: WebBrowserSettingsControllerEntry, rhs: WebBrowserSettingsControllerEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
return lhs.sortId < rhs.sortId
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
@ -165,12 +208,18 @@ private enum WebBrowserSettingsControllerEntry: ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.accentDeleteIconImage(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||
arguments.clearCookies()
|
||||
})
|
||||
case let .clearCache(_, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.accentDeleteIconImage(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||
arguments.clearCache()
|
||||
})
|
||||
case let .clearCookiesInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .exceptionsHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .exception(_, _, exception):
|
||||
return WebBrowserDomainExceptionItem(presentationData: presentationData, context: arguments.context, title: exception.title, label: exception.domain, sectionId: self.section, style: .blocks)
|
||||
return WebBrowserDomainExceptionItem(presentationData: presentationData, context: arguments.context, title: exception.title, label: exception.domain, icon: exception.icon, sectionId: self.section, style: .blocks, deleted: {
|
||||
arguments.removeException(exception.domain)
|
||||
})
|
||||
case let .exceptionsAdd(_, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||
arguments.addException()
|
||||
@ -201,13 +250,14 @@ private func webBrowserSettingsControllerEntries(context: AccountContext, presen
|
||||
|
||||
if settings.defaultWebBrowser == nil {
|
||||
entries.append(.clearCookies(presentationData.theme, presentationData.strings.WebBrowser_ClearCookies))
|
||||
// entries.append(.clearCache(presentationData.theme, presentationData.strings.WebBrowser_ClearCache))
|
||||
entries.append(.clearCookiesInfo(presentationData.theme, presentationData.strings.WebBrowser_ClearCookies_Info))
|
||||
|
||||
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.WebBrowser_Exceptions_Title))
|
||||
entries.append(.exceptionsAdd(presentationData.theme, presentationData.strings.WebBrowser_Exceptions_AddException))
|
||||
|
||||
var exceptionIndex: Int32 = 0
|
||||
for exception in settings.exceptions {
|
||||
for exception in settings.exceptions.reversed() {
|
||||
entries.append(.exception(exceptionIndex, presentationData.theme, exception))
|
||||
exceptionIndex += 1
|
||||
}
|
||||
@ -224,7 +274,9 @@ private func webBrowserSettingsControllerEntries(context: AccountContext, presen
|
||||
|
||||
public func webBrowserSettingsController(context: AccountContext) -> ViewController {
|
||||
var clearCookiesImpl: (() -> Void)?
|
||||
var clearCacheImpl: (() -> Void)?
|
||||
var addExceptionImpl: (() -> Void)?
|
||||
var removeExceptionImpl: ((String) -> Void)?
|
||||
var clearExceptionsImpl: (() -> Void)?
|
||||
|
||||
let arguments = WebBrowserSettingsControllerArguments(
|
||||
@ -237,9 +289,15 @@ public func webBrowserSettingsController(context: AccountContext) -> ViewControl
|
||||
clearCookies: {
|
||||
clearCookiesImpl?()
|
||||
},
|
||||
clearCache: {
|
||||
clearCacheImpl?()
|
||||
},
|
||||
addException: {
|
||||
addExceptionImpl?()
|
||||
},
|
||||
removeException: { domain in
|
||||
removeExceptionImpl?(domain)
|
||||
},
|
||||
clearExceptions: {
|
||||
clearExceptionsImpl?()
|
||||
}
|
||||
@ -261,6 +319,9 @@ public func webBrowserSettingsController(context: AccountContext) -> ViewControl
|
||||
if previousSettings.defaultWebBrowser != settings.defaultWebBrowser {
|
||||
animateChanges = true
|
||||
}
|
||||
if previousSettings.exceptions.count != settings.exceptions.count {
|
||||
animateChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.WebBrowser_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
@ -272,27 +333,75 @@ public func webBrowserSettingsController(context: AccountContext) -> ViewControl
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
|
||||
clearCookiesImpl = { [weak controller] in
|
||||
WKWebsiteDataStore.default().removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{})
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
controller?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_ClearCookies_Succeed,
|
||||
timeout: nil,
|
||||
customUndoText: nil
|
||||
),
|
||||
elevatedLayout: false,
|
||||
position: .bottom,
|
||||
action: { _ in return false }), in: .current
|
||||
|
||||
let alertController = textAlertController(
|
||||
context: context,
|
||||
updatedPresentationData: nil,
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_ClearCookies_ClearConfirmation_Text,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.WebBrowser_ClearCookies_ClearConfirmation_Clear, action: {
|
||||
WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeCookies, WKWebsiteDataTypeLocalStorage, WKWebsiteDataTypeSessionStorage], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{})
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
controller?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_ClearCookies_Succeed,
|
||||
timeout: nil,
|
||||
customUndoText: nil
|
||||
),
|
||||
elevatedLayout: false,
|
||||
position: .bottom,
|
||||
action: { _ in return false }), in: .current
|
||||
)
|
||||
})
|
||||
]
|
||||
)
|
||||
controller?.present(alertController, in: .window(.root))
|
||||
}
|
||||
|
||||
clearCacheImpl = { [weak controller] in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let alertController = textAlertController(
|
||||
context: context,
|
||||
updatedPresentationData: nil,
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_ClearCache_ClearConfirmation_Text,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.WebBrowser_ClearCache_ClearConfirmation_Clear, action: {
|
||||
WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{})
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
controller?.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_ClearCache_Succeed,
|
||||
timeout: nil,
|
||||
customUndoText: nil
|
||||
),
|
||||
elevatedLayout: false,
|
||||
position: .bottom,
|
||||
action: { _ in return false }), in: .current
|
||||
)
|
||||
})
|
||||
]
|
||||
)
|
||||
controller?.present(alertController, in: .window(.root))
|
||||
}
|
||||
|
||||
addExceptionImpl = { [weak controller] in
|
||||
var dismissImpl: (() -> Void)?
|
||||
let linkController = webBrowserDomainController(context: context, apply: { url in
|
||||
if let url {
|
||||
let _ = fetchDomainExceptionInfo(url: url).startStandalone(next: { newException in
|
||||
let _ = (fetchDomainExceptionInfo(context: context, url: url)
|
||||
|> deliverOnMainQueue).startStandalone(next: { newException in
|
||||
let _ = updateWebBrowserSettingsInteractively(accountManager: context.sharedContext.accountManager, { currentSettings in
|
||||
var currentExceptions = currentSettings.exceptions
|
||||
for exception in currentExceptions {
|
||||
@ -303,18 +412,44 @@ public func webBrowserSettingsController(context: AccountContext) -> ViewControl
|
||||
currentExceptions.append(newException)
|
||||
return currentSettings.withUpdatedExceptions(currentExceptions)
|
||||
}).start()
|
||||
dismissImpl?()
|
||||
})
|
||||
}
|
||||
})
|
||||
dismissImpl = { [weak linkController] in
|
||||
linkController?.view.endEditing(true)
|
||||
linkController?.dismissAnimated()
|
||||
}
|
||||
controller?.present(linkController, in: .window(.root))
|
||||
}
|
||||
|
||||
clearExceptionsImpl = {
|
||||
removeExceptionImpl = { domain in
|
||||
let _ = updateWebBrowserSettingsInteractively(accountManager: context.sharedContext.accountManager, { currentSettings in
|
||||
return currentSettings.withUpdatedExceptions([])
|
||||
let updatedExceptions = currentSettings.exceptions.filter { $0.domain != domain }
|
||||
return currentSettings.withUpdatedExceptions(updatedExceptions)
|
||||
}).start()
|
||||
}
|
||||
|
||||
clearExceptionsImpl = { [weak controller] in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let alertController = textAlertController(
|
||||
context: context,
|
||||
updatedPresentationData: nil,
|
||||
title: nil,
|
||||
text: presentationData.strings.WebBrowser_Exceptions_ClearConfirmation_Text,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.WebBrowser_Exceptions_ClearConfirmation_Clear, action: {
|
||||
let _ = updateWebBrowserSettingsInteractively(accountManager: context.sharedContext.accountManager, { currentSettings in
|
||||
return currentSettings.withUpdatedExceptions([])
|
||||
}).start()
|
||||
})
|
||||
]
|
||||
)
|
||||
controller?.present(alertController, in: .window(.root))
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
@ -333,22 +468,59 @@ private func cleanDomain(url: String) -> (domain: String, fullUrl: String) {
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchDomainExceptionInfo(url: String) -> Signal<WebBrowserException, NoError> {
|
||||
private func fetchDomainExceptionInfo(context: AccountContext, url: String) -> Signal<WebBrowserException, NoError> {
|
||||
let (domain, domainUrl) = cleanDomain(url: url)
|
||||
if #available(iOS 13.0, *), let url = URL(string: domainUrl) {
|
||||
return Signal { subscriber in
|
||||
let metadataProvider = LPMetadataProvider()
|
||||
metadataProvider.shouldFetchSubresources = true
|
||||
metadataProvider.startFetchingMetadata(for: url, completionHandler: { metadata, _ in
|
||||
let title = metadata?.value(forKey: "_siteName") as? String ?? metadata?.title
|
||||
subscriber.putNext(WebBrowserException(domain: domain, title: title ?? domain))
|
||||
subscriber.putCompletion()
|
||||
let completeWithImage: (Data?) -> Void = { imageData in
|
||||
var image: TelegramMediaImage?
|
||||
if let imageData, let parsedImage = UIImage(data: imageData) {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: imageData)
|
||||
image = TelegramMediaImage(
|
||||
imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)),
|
||||
representations: [
|
||||
TelegramMediaImageRepresentation(
|
||||
dimensions: PixelDimensions(width: Int32(parsedImage.size.width), height: Int32(parsedImage.size.height)),
|
||||
resource: resource,
|
||||
progressiveSizes: [],
|
||||
immediateThumbnailData: nil,
|
||||
hasVideo: false,
|
||||
isPersonal: false
|
||||
)
|
||||
],
|
||||
immediateThumbnailData: nil,
|
||||
reference: nil,
|
||||
partialReference: nil,
|
||||
flags: []
|
||||
)
|
||||
}
|
||||
|
||||
let title = metadata?.value(forKey: "_siteName") as? String ?? metadata?.title
|
||||
subscriber.putNext(WebBrowserException(domain: domain, title: title ?? domain, icon: image))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
if let imageProvider = metadata?.iconProvider {
|
||||
imageProvider.loadFileRepresentation(forTypeIdentifier: kUTTypeImage as String, completionHandler: { imageUrl, _ in
|
||||
guard let imageUrl, let imageData = try? Data(contentsOf: imageUrl) else {
|
||||
completeWithImage(nil)
|
||||
return
|
||||
}
|
||||
completeWithImage(imageData)
|
||||
})
|
||||
} else {
|
||||
completeWithImage(nil)
|
||||
}
|
||||
})
|
||||
return ActionDisposable {
|
||||
metadataProvider.cancel()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(WebBrowserException(domain: domain, title: domain))
|
||||
return .single(WebBrowserException(domain: domain, title: domain, icon: nil))
|
||||
}
|
||||
}
|
||||
|
@ -315,10 +315,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView
|
||||
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil))
|
||||
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
|
||||
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil))
|
||||
let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
|
||||
let timestamp = self.referenceTimestamp
|
||||
|
@ -464,10 +464,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil))
|
||||
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
|
||||
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil))
|
||||
let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil))
|
||||
|
||||
|
@ -462,7 +462,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
self.itemSpacing = 1.0
|
||||
|
||||
let itemsPerRow: CGFloat
|
||||
if containerLayout.fixedItemAspect != nil && itemCount <= 2 {
|
||||
if containerLayout.fixedItemAspect != nil && itemCount <= 2 && containerLayout.adjustForSmallCount {
|
||||
itemsPerRow = 2.0
|
||||
centerItems = itemCount == 1
|
||||
} else {
|
||||
@ -550,6 +550,11 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
var offset: CGFloat {
|
||||
return self.scrollView.contentOffset.y
|
||||
}
|
||||
|
||||
var contentBottomOffset: CGFloat {
|
||||
let bottomInset = self.layout?.containerLayout.insets.bottom ?? 0.0
|
||||
return -self.scrollView.contentOffset.y + self.scrollView.contentSize.height - bottomInset
|
||||
}
|
||||
|
||||
let coveringOffsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
|
||||
let offsetUpdated: (Viewport, ContainedViewLayoutTransition) -> Void
|
||||
@ -1442,6 +1447,10 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
return self.fromViewport.coveringInsetOffset * (1.0 - self.currentProgress) + self.toViewport.coveringInsetOffset * self.currentProgress
|
||||
}
|
||||
|
||||
var contentBottomOffset: CGFloat {
|
||||
return self.fromViewport.contentBottomOffset * (1.0 - self.currentProgress) + self.toViewport.contentBottomOffset * self.currentProgress
|
||||
}
|
||||
|
||||
var offset: CGFloat {
|
||||
return self.fromViewport.offset * (1.0 - self.currentProgress) + self.toViewport.offset * self.currentProgress
|
||||
}
|
||||
@ -1599,6 +1608,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
var lockScrollingAtTop: Bool
|
||||
var fixedItemHeight: CGFloat?
|
||||
var fixedItemAspect: CGFloat?
|
||||
var adjustForSmallCount: Bool
|
||||
}
|
||||
|
||||
private var tapRecognizer: UITapGestureRecognizer?
|
||||
@ -1632,6 +1642,16 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public var contentBottomOffset: CGFloat {
|
||||
if let currentViewportTransition = self.currentViewportTransition {
|
||||
return currentViewportTransition.contentBottomOffset
|
||||
} else if let currentViewport = self.currentViewport {
|
||||
return currentViewport.contentBottomOffset
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
|
||||
public var scrollingOffset: CGFloat {
|
||||
if let currentViewportTransition = self.currentViewportTransition {
|
||||
return currentViewportTransition.offset
|
||||
@ -1914,7 +1934,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public func update(size: CGSize, insets: UIEdgeInsets, useSideInsets: Bool, scrollIndicatorInsets: UIEdgeInsets, lockScrollingAtTop: Bool, fixedItemHeight: CGFloat?, fixedItemAspect: CGFloat?, items: Items, theme: PresentationTheme, synchronous: SparseItemGrid.Synchronous, transition: ComponentTransition = .immediate) {
|
||||
public func update(size: CGSize, insets: UIEdgeInsets, useSideInsets: Bool, scrollIndicatorInsets: UIEdgeInsets, lockScrollingAtTop: Bool, fixedItemHeight: CGFloat?, fixedItemAspect: CGFloat?, adjustForSmallCount: Bool = true, items: Items, theme: PresentationTheme, synchronous: SparseItemGrid.Synchronous, transition: ComponentTransition = .immediate) {
|
||||
self.theme = theme
|
||||
|
||||
var headerInset: CGFloat = 0.0
|
||||
@ -1955,7 +1975,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
var insets = insets
|
||||
insets.top += headerInset
|
||||
|
||||
let containerLayout = ContainerLayout(size: size, insets: insets, useSideInsets: useSideInsets, scrollIndicatorInsets: scrollIndicatorInsets, lockScrollingAtTop: lockScrollingAtTop, fixedItemHeight: fixedItemHeight, fixedItemAspect: fixedItemAspect)
|
||||
let containerLayout = ContainerLayout(size: size, insets: insets, useSideInsets: useSideInsets, scrollIndicatorInsets: scrollIndicatorInsets, lockScrollingAtTop: lockScrollingAtTop, fixedItemHeight: fixedItemHeight, fixedItemAspect: fixedItemAspect, adjustForSmallCount: adjustForSmallCount)
|
||||
self.containerLayout = containerLayout
|
||||
self.items = items
|
||||
self.scrollingArea.isHidden = lockScrollingAtTop
|
||||
|
@ -2049,6 +2049,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
let peer = Promise<EnginePeer?>()
|
||||
peer.set(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
|
||||
|
||||
let canViewStatsValue = Atomic<Bool>(value: true)
|
||||
let peerData = context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.AdsRestricted(id: peerId),
|
||||
@ -2081,6 +2082,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
|> map { presentationData, state, peer, data, messageView, stories, boostData, boostersState, giftsState, revenueState, revenueTransactions, starsState, starsTransactions, peerData, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let (canViewStats, adsRestricted, canViewRevenue, canViewStarsRevenue) = peerData
|
||||
|
||||
let _ = canViewStatsValue.swap(canViewStats)
|
||||
|
||||
var isGroup = false
|
||||
if let peer, case let .channel(channel) = peer, case .group = channel.info {
|
||||
isGroup = true
|
||||
@ -2157,9 +2160,17 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
case .stats:
|
||||
index = 0
|
||||
case .boosts:
|
||||
index = 1
|
||||
if canViewStats {
|
||||
index = 1
|
||||
} else {
|
||||
index = 0
|
||||
}
|
||||
case .monetization:
|
||||
index = 2
|
||||
if canViewStats {
|
||||
index = 2
|
||||
} else {
|
||||
index = 1
|
||||
}
|
||||
}
|
||||
var tabs: [String] = []
|
||||
if canViewStats {
|
||||
@ -2195,12 +2206,21 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
|
||||
}
|
||||
controller.titleControlValueChanged = { value in
|
||||
updateState { state in
|
||||
let canViewStats = canViewStatsValue.with { $0 }
|
||||
let section: ChannelStatsSection
|
||||
switch value {
|
||||
case 0:
|
||||
section = .stats
|
||||
if canViewStats {
|
||||
section = .stats
|
||||
} else {
|
||||
section = .boosts
|
||||
}
|
||||
case 1:
|
||||
section = .boosts
|
||||
if canViewStats {
|
||||
section = .boosts
|
||||
} else {
|
||||
section = .monetization
|
||||
}
|
||||
case 2:
|
||||
section = .monetization
|
||||
let _ = (ApplicationSpecificNotice.monetizationIntroDismissed(accountManager: context.sharedContext.accountManager)
|
||||
|
@ -686,7 +686,7 @@ private func canEditAdminRights(accountPeerId: EnginePeer.Id, channelPeer: Engin
|
||||
switch initialParticipant {
|
||||
case .creator:
|
||||
return false
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
case let .member(_, _, adminInfo, _, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
return adminInfo.canBeEditedByAccountPeer || adminInfo.promotedBy == accountPeerId
|
||||
} else {
|
||||
|
@ -178,10 +178,10 @@ final class MonetizationBalanceItemNode: ListViewItemNode, ItemListItemNode {
|
||||
if let stats = item.stats as? RevenueStats {
|
||||
let cryptoValue = formatTonAmountText(stats.balances.availableBalance, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
|
||||
amountString = tonAmountAttributedString(cryptoValue, integralFont: integralFont, fractionalFont: fractionalFont, color: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
value = stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate))"
|
||||
value = stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
} else if let stats = item.stats as? StarsRevenueStats {
|
||||
amountString = NSAttributedString(string: presentationStringsFormattedNumber(Int32(stats.balances.availableBalance), item.presentationData.dateTimeFormat.groupingSeparator), font: integralFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
value = stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, divide: false, rate: stats.usdRate))"
|
||||
value = stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
isStars = true
|
||||
} else {
|
||||
fatalError()
|
||||
|
@ -774,7 +774,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.availableBalance, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Available,
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
@ -784,7 +784,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.currentBalance, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Current,
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
@ -794,7 +794,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.overallRevenue, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Total,
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
@ -804,7 +804,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(additionalStats.balances.availableBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
" ",
|
||||
(additionalStats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.availableBalance, divide: false, rate: additionalStats.usdRate))", .generic),
|
||||
(additionalStats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.availableBalance, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -814,7 +814,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(additionalStats.balances.currentBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
" ",
|
||||
(additionalStats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.currentBalance, divide: false, rate: additionalStats.usdRate))", .generic),
|
||||
(additionalStats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.currentBalance, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -824,7 +824,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(additionalStats.balances.overallRevenue), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
" ",
|
||||
(additionalStats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.overallRevenue, divide: false, rate: additionalStats.usdRate))", .generic),
|
||||
(additionalStats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.overallRevenue, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -838,7 +838,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.availableBalance, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator),
|
||||
item.presentationData.strings.Monetization_Overview_Available,
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
@ -848,7 +848,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.currentBalance, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator),
|
||||
item.presentationData.strings.Monetization_Overview_Current,
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
@ -858,7 +858,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
formatTonAmountText(stats.balances.overallRevenue, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator),
|
||||
item.presentationData.strings.Monetization_Overview_Total,
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.ton
|
||||
)
|
||||
|
||||
@ -873,7 +873,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(stats.balances.availableBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Available,
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -883,7 +883,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(stats.balances.currentBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Current,
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -893,7 +893,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(stats.balances.overallRevenue), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Total,
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, rate: stats.usdRate))", .generic),
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
@ -105,6 +105,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-944407322] = { return Api.BotMenuButton.parse_botMenuButton($0) }
|
||||
dict[1113113093] = { return Api.BotMenuButton.parse_botMenuButtonCommands($0) }
|
||||
dict[1966318984] = { return Api.BotMenuButton.parse_botMenuButtonDefault($0) }
|
||||
dict[602479523] = { return Api.BotPreviewMedia.parse_botPreviewMedia($0) }
|
||||
dict[-2076642874] = { return Api.BroadcastRevenueBalances.parse_broadcastRevenueBalances($0) }
|
||||
dict[1434332356] = { return Api.BroadcastRevenueTransaction.parse_broadcastRevenueTransactionProceeds($0) }
|
||||
dict[1121127726] = { return Api.BroadcastRevenueTransaction.parse_broadcastRevenueTransactionRefund($0) }
|
||||
@ -177,12 +178,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1078612597] = { return Api.ChannelLocation.parse_channelLocationEmpty($0) }
|
||||
dict[-847783593] = { return Api.ChannelMessagesFilter.parse_channelMessagesFilter($0) }
|
||||
dict[-1798033689] = { return Api.ChannelMessagesFilter.parse_channelMessagesFilterEmpty($0) }
|
||||
dict[-1072953408] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
|
||||
dict[-885426663] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
|
||||
dict[885242707] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) }
|
||||
dict[1844969806] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) }
|
||||
dict[803602899] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) }
|
||||
dict[453242886] = { return Api.ChannelParticipant.parse_channelParticipantLeft($0) }
|
||||
dict[900251559] = { return Api.ChannelParticipant.parse_channelParticipantSelf($0) }
|
||||
dict[1331723247] = { return Api.ChannelParticipant.parse_channelParticipantSelf($0) }
|
||||
dict[-1268741783] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsAdmins($0) }
|
||||
dict[338142689] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsBanned($0) }
|
||||
dict[-1328445861] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsBots($0) }
|
||||
@ -191,7 +192,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) }
|
||||
dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) }
|
||||
dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) }
|
||||
dict[179174543] = { return Api.Chat.parse_channel($0) }
|
||||
dict[-29067075] = { return Api.Chat.parse_channel($0) }
|
||||
dict[399807445] = { return Api.Chat.parse_channelForbidden($0) }
|
||||
dict[1103884886] = { return Api.Chat.parse_chat($0) }
|
||||
dict[693512293] = { return Api.Chat.parse_chatEmpty($0) }
|
||||
@ -201,7 +202,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
|
||||
dict[-1146407795] = { return Api.ChatFull.parse_channelFull($0) }
|
||||
dict[640893467] = { return Api.ChatFull.parse_chatFull($0) }
|
||||
dict[-1965998484] = { return Api.ChatInvite.parse_chatInvite($0) }
|
||||
dict[-26920803] = { return Api.ChatInvite.parse_chatInvite($0) }
|
||||
dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) }
|
||||
dict[1634294960] = { return Api.ChatInvite.parse_chatInvitePeek($0) }
|
||||
dict[-1940201511] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) }
|
||||
@ -354,6 +355,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1690108678] = { return Api.InputEncryptedFile.parse_inputEncryptedFileUploaded($0) }
|
||||
dict[-181407105] = { return Api.InputFile.parse_inputFile($0) }
|
||||
dict[-95482955] = { return Api.InputFile.parse_inputFileBig($0) }
|
||||
dict[1658620744] = { return Api.InputFile.parse_inputFileStoryDocument($0) }
|
||||
dict[-1160743548] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) }
|
||||
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
|
||||
dict[-539317279] = { return Api.InputFileLocation.parse_inputFileLocation($0) }
|
||||
@ -617,7 +619,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1959634180] = { return Api.MessagePeerVote.parse_messagePeerVoteInputOption($0) }
|
||||
dict[1177089766] = { return Api.MessagePeerVote.parse_messagePeerVoteMultiple($0) }
|
||||
dict[182649427] = { return Api.MessageRange.parse_messageRange($0) }
|
||||
dict[1328256121] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[171155211] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[-285158328] = { return Api.MessageReactor.parse_messageReactor($0) }
|
||||
dict[-2083123262] = { return Api.MessageReplies.parse_messageReplies($0) }
|
||||
dict[-1346631205] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) }
|
||||
dict[240843065] = { return Api.MessageReplyHeader.parse_messageReplyStoryHeader($0) }
|
||||
@ -766,6 +769,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1992950669] = { return Api.Reaction.parse_reactionCustomEmoji($0) }
|
||||
dict[455247544] = { return Api.Reaction.parse_reactionEmoji($0) }
|
||||
dict[2046153753] = { return Api.Reaction.parse_reactionEmpty($0) }
|
||||
dict[1379771627] = { return Api.Reaction.parse_reactionPaid($0) }
|
||||
dict[-1546531968] = { return Api.ReactionCount.parse_reactionCount($0) }
|
||||
dict[1268654752] = { return Api.ReactionNotificationsFrom.parse_reactionNotificationsFromAll($0) }
|
||||
dict[-1161583078] = { return Api.ReactionNotificationsFrom.parse_reactionNotificationsFromContacts($0) }
|
||||
@ -883,7 +887,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-797707802] = { return Api.StarsSubscription.parse_starsSubscription($0) }
|
||||
dict[88173912] = { return Api.StarsSubscriptionPricing.parse_starsSubscriptionPricing($0) }
|
||||
dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) }
|
||||
dict[455361027] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[1127934763] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[-670195363] = { return Api.StarsTransactionPeer.parse_starsTransactionPeer($0) }
|
||||
dict[1617438738] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAds($0) }
|
||||
dict[-1269320843] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAppStore($0) }
|
||||
@ -1173,7 +1177,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1542017919] = { return Api.auth.SentCodeType.parse_sentCodeTypeSmsWord($0) }
|
||||
dict[-391678544] = { return Api.bots.BotInfo.parse_botInfo($0) }
|
||||
dict[428978491] = { return Api.bots.PopularAppBots.parse_popularAppBots($0) }
|
||||
dict[1357069389] = { return Api.bots.PreviewInfo.parse_previewInfo($0) }
|
||||
dict[212278628] = { return Api.bots.PreviewInfo.parse_previewInfo($0) }
|
||||
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
|
||||
dict[-541588713] = { return Api.channels.ChannelParticipant.parse_channelParticipant($0) }
|
||||
dict[-1699676497] = { return Api.channels.ChannelParticipants.parse_channelParticipants($0) }
|
||||
@ -1328,7 +1332,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[961445665] = { return Api.payments.StarsRevenueAdsAccountUrl.parse_starsRevenueAdsAccountUrl($0) }
|
||||
dict[-919881925] = { return Api.payments.StarsRevenueStats.parse_starsRevenueStats($0) }
|
||||
dict[497778871] = { return Api.payments.StarsRevenueWithdrawalUrl.parse_starsRevenueWithdrawalUrl($0) }
|
||||
dict[-2064727699] = { return Api.payments.StarsStatus.parse_starsStatus($0) }
|
||||
dict[-1141231252] = { return Api.payments.StarsStatus.parse_starsStatus($0) }
|
||||
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
|
||||
dict[541839704] = { return Api.phone.ExportedGroupCallInvite.parse_exportedGroupCallInvite($0) }
|
||||
dict[-1636664659] = { return Api.phone.GroupCall.parse_groupCall($0) }
|
||||
@ -1497,6 +1501,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BotMenuButton:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BotPreviewMedia:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BroadcastRevenueBalances:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.BroadcastRevenueTransaction:
|
||||
@ -1823,6 +1829,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReactions:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReactor:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReplies:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReplyHeader:
|
||||
|
@ -1,3 +1,135 @@
|
||||
public extension Api {
|
||||
indirect enum InputInvoice: TypeConstructorDescription {
|
||||
case inputInvoiceChatInviteSubscription(hash: String)
|
||||
case inputInvoiceMessage(peer: Api.InputPeer, msgId: Int32)
|
||||
case inputInvoicePremiumGiftCode(purpose: Api.InputStorePaymentPurpose, option: Api.PremiumGiftCodeOption)
|
||||
case inputInvoiceSlug(slug: String)
|
||||
case inputInvoiceStars(purpose: Api.InputStorePaymentPurpose)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputInvoiceChatInviteSubscription(let hash):
|
||||
if boxed {
|
||||
buffer.appendInt32(887591921)
|
||||
}
|
||||
serializeString(hash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputInvoiceMessage(let peer, let msgId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-977967015)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputInvoicePremiumGiftCode(let purpose, let option):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1734841331)
|
||||
}
|
||||
purpose.serialize(buffer, true)
|
||||
option.serialize(buffer, true)
|
||||
break
|
||||
case .inputInvoiceSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1020867857)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputInvoiceStars(let purpose):
|
||||
if boxed {
|
||||
buffer.appendInt32(1710230755)
|
||||
}
|
||||
purpose.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputInvoiceChatInviteSubscription(let hash):
|
||||
return ("inputInvoiceChatInviteSubscription", [("hash", hash as Any)])
|
||||
case .inputInvoiceMessage(let peer, let msgId):
|
||||
return ("inputInvoiceMessage", [("peer", peer as Any), ("msgId", msgId as Any)])
|
||||
case .inputInvoicePremiumGiftCode(let purpose, let option):
|
||||
return ("inputInvoicePremiumGiftCode", [("purpose", purpose as Any), ("option", option as Any)])
|
||||
case .inputInvoiceSlug(let slug):
|
||||
return ("inputInvoiceSlug", [("slug", slug as Any)])
|
||||
case .inputInvoiceStars(let purpose):
|
||||
return ("inputInvoiceStars", [("purpose", purpose as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputInvoiceChatInviteSubscription(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputInvoice.inputInvoiceChatInviteSubscription(hash: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoiceMessage(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputInvoice.inputInvoiceMessage(peer: _1!, msgId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoicePremiumGiftCode(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: Api.InputStorePaymentPurpose?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputStorePaymentPurpose
|
||||
}
|
||||
var _2: Api.PremiumGiftCodeOption?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.PremiumGiftCodeOption
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputInvoice.inputInvoicePremiumGiftCode(purpose: _1!, option: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoiceSlug(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputInvoice.inputInvoiceSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoiceStars(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: Api.InputStorePaymentPurpose?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputStorePaymentPurpose
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputInvoice.inputInvoiceStars(purpose: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputMedia: TypeConstructorDescription {
|
||||
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
|
||||
@ -808,281 +940,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputPaymentCredentials: TypeConstructorDescription {
|
||||
case inputPaymentCredentials(flags: Int32, data: Api.DataJSON)
|
||||
case inputPaymentCredentialsApplePay(paymentData: Api.DataJSON)
|
||||
case inputPaymentCredentialsGooglePay(paymentToken: Api.DataJSON)
|
||||
case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(873977640)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
data.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
if boxed {
|
||||
buffer.appendInt32(178373535)
|
||||
}
|
||||
paymentData.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1966921727)
|
||||
}
|
||||
paymentToken.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1056001329)
|
||||
}
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
serializeBytes(tmpPassword, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
return ("inputPaymentCredentials", [("flags", flags as Any), ("data", data as Any)])
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
return ("inputPaymentCredentialsApplePay", [("paymentData", paymentData as Any)])
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
return ("inputPaymentCredentialsGooglePay", [("paymentToken", paymentToken as Any)])
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
return ("inputPaymentCredentialsSaved", [("id", id as Any), ("tmpPassword", tmpPassword as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPaymentCredentials(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentials(flags: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsApplePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsApplePay(paymentData: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsGooglePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsGooglePay(paymentToken: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsSaved(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsSaved(id: _1!, tmpPassword: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputPeer: TypeConstructorDescription {
|
||||
case inputPeerChannel(channelId: Int64, accessHash: Int64)
|
||||
case inputPeerChannelFromMessage(peer: Api.InputPeer, msgId: Int32, channelId: Int64)
|
||||
case inputPeerChat(chatId: Int64)
|
||||
case inputPeerEmpty
|
||||
case inputPeerSelf
|
||||
case inputPeerUser(userId: Int64, accessHash: Int64)
|
||||
case inputPeerUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPeerChannel(let channelId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(666680316)
|
||||
}
|
||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerChannelFromMessage(let peer, let msgId, let channelId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1121318848)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerChat(let chatId):
|
||||
if boxed {
|
||||
buffer.appendInt32(900291769)
|
||||
}
|
||||
serializeInt64(chatId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(2134579434)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPeerSelf:
|
||||
if boxed {
|
||||
buffer.appendInt32(2107670217)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPeerUser(let userId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-571955892)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1468331492)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPeerChannel(let channelId, let accessHash):
|
||||
return ("inputPeerChannel", [("channelId", channelId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputPeerChannelFromMessage(let peer, let msgId, let channelId):
|
||||
return ("inputPeerChannelFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("channelId", channelId as Any)])
|
||||
case .inputPeerChat(let chatId):
|
||||
return ("inputPeerChat", [("chatId", chatId as Any)])
|
||||
case .inputPeerEmpty:
|
||||
return ("inputPeerEmpty", [])
|
||||
case .inputPeerSelf:
|
||||
return ("inputPeerSelf", [])
|
||||
case .inputPeerUser(let userId, let accessHash):
|
||||
return ("inputPeerUser", [("userId", userId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputPeerUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputPeerUserFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("userId", userId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPeerChannel(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPeer.inputPeerChannel(channelId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerChannelFromMessage(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPeer.inputPeerChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerChat(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPeer.inputPeerChat(chatId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerEmpty(_ reader: BufferReader) -> InputPeer? {
|
||||
return Api.InputPeer.inputPeerEmpty
|
||||
}
|
||||
public static func parse_inputPeerSelf(_ reader: BufferReader) -> InputPeer? {
|
||||
return Api.InputPeer.inputPeerSelf
|
||||
}
|
||||
public static func parse_inputPeerUser(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPeer.inputPeerUser(userId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerUserFromMessage(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPeer.inputPeerUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,281 @@
|
||||
public extension Api {
|
||||
enum InputPaymentCredentials: TypeConstructorDescription {
|
||||
case inputPaymentCredentials(flags: Int32, data: Api.DataJSON)
|
||||
case inputPaymentCredentialsApplePay(paymentData: Api.DataJSON)
|
||||
case inputPaymentCredentialsGooglePay(paymentToken: Api.DataJSON)
|
||||
case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(873977640)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
data.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
if boxed {
|
||||
buffer.appendInt32(178373535)
|
||||
}
|
||||
paymentData.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1966921727)
|
||||
}
|
||||
paymentToken.serialize(buffer, true)
|
||||
break
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1056001329)
|
||||
}
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
serializeBytes(tmpPassword, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPaymentCredentials(let flags, let data):
|
||||
return ("inputPaymentCredentials", [("flags", flags as Any), ("data", data as Any)])
|
||||
case .inputPaymentCredentialsApplePay(let paymentData):
|
||||
return ("inputPaymentCredentialsApplePay", [("paymentData", paymentData as Any)])
|
||||
case .inputPaymentCredentialsGooglePay(let paymentToken):
|
||||
return ("inputPaymentCredentialsGooglePay", [("paymentToken", paymentToken as Any)])
|
||||
case .inputPaymentCredentialsSaved(let id, let tmpPassword):
|
||||
return ("inputPaymentCredentialsSaved", [("id", id as Any), ("tmpPassword", tmpPassword as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPaymentCredentials(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentials(flags: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsApplePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsApplePay(paymentData: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsGooglePay(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: Api.DataJSON?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsGooglePay(paymentToken: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPaymentCredentialsSaved(_ reader: BufferReader) -> InputPaymentCredentials? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPaymentCredentials.inputPaymentCredentialsSaved(id: _1!, tmpPassword: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputPeer: TypeConstructorDescription {
|
||||
case inputPeerChannel(channelId: Int64, accessHash: Int64)
|
||||
case inputPeerChannelFromMessage(peer: Api.InputPeer, msgId: Int32, channelId: Int64)
|
||||
case inputPeerChat(chatId: Int64)
|
||||
case inputPeerEmpty
|
||||
case inputPeerSelf
|
||||
case inputPeerUser(userId: Int64, accessHash: Int64)
|
||||
case inputPeerUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputPeerChannel(let channelId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(666680316)
|
||||
}
|
||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerChannelFromMessage(let peer, let msgId, let channelId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1121318848)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerChat(let chatId):
|
||||
if boxed {
|
||||
buffer.appendInt32(900291769)
|
||||
}
|
||||
serializeInt64(chatId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(2134579434)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPeerSelf:
|
||||
if boxed {
|
||||
buffer.appendInt32(2107670217)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputPeerUser(let userId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-571955892)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputPeerUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1468331492)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputPeerChannel(let channelId, let accessHash):
|
||||
return ("inputPeerChannel", [("channelId", channelId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputPeerChannelFromMessage(let peer, let msgId, let channelId):
|
||||
return ("inputPeerChannelFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("channelId", channelId as Any)])
|
||||
case .inputPeerChat(let chatId):
|
||||
return ("inputPeerChat", [("chatId", chatId as Any)])
|
||||
case .inputPeerEmpty:
|
||||
return ("inputPeerEmpty", [])
|
||||
case .inputPeerSelf:
|
||||
return ("inputPeerSelf", [])
|
||||
case .inputPeerUser(let userId, let accessHash):
|
||||
return ("inputPeerUser", [("userId", userId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputPeerUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputPeerUserFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("userId", userId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputPeerChannel(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPeer.inputPeerChannel(channelId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerChannelFromMessage(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPeer.inputPeerChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerChat(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputPeer.inputPeerChat(chatId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerEmpty(_ reader: BufferReader) -> InputPeer? {
|
||||
return Api.InputPeer.inputPeerEmpty
|
||||
}
|
||||
public static func parse_inputPeerSelf(_ reader: BufferReader) -> InputPeer? {
|
||||
return Api.InputPeer.inputPeerSelf
|
||||
}
|
||||
public static func parse_inputPeerUser(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputPeer.inputPeerUser(userId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputPeerUserFromMessage(_ reader: BufferReader) -> InputPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputPeer.inputPeerUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputPeerNotifySettings: TypeConstructorDescription {
|
||||
case inputPeerNotifySettings(flags: Int32, showPreviews: Api.Bool?, silent: Api.Bool?, muteUntil: Int32?, sound: Api.NotificationSound?, storiesMuted: Api.Bool?, storiesHideSender: Api.Bool?, storiesSound: Api.NotificationSound?)
|
||||
@ -510,321 +788,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputQuickReplyShortcut: TypeConstructorDescription {
|
||||
case inputQuickReplyShortcut(shortcut: String)
|
||||
case inputQuickReplyShortcutId(shortcutId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputQuickReplyShortcut(let shortcut):
|
||||
if boxed {
|
||||
buffer.appendInt32(609840449)
|
||||
}
|
||||
serializeString(shortcut, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputQuickReplyShortcutId(let shortcutId):
|
||||
if boxed {
|
||||
buffer.appendInt32(18418929)
|
||||
}
|
||||
serializeInt32(shortcutId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputQuickReplyShortcut(let shortcut):
|
||||
return ("inputQuickReplyShortcut", [("shortcut", shortcut as Any)])
|
||||
case .inputQuickReplyShortcutId(let shortcutId):
|
||||
return ("inputQuickReplyShortcutId", [("shortcutId", shortcutId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputQuickReplyShortcut(_ reader: BufferReader) -> InputQuickReplyShortcut? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputQuickReplyShortcut.inputQuickReplyShortcut(shortcut: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputQuickReplyShortcutId(_ reader: BufferReader) -> InputQuickReplyShortcut? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputQuickReplyShortcut.inputQuickReplyShortcutId(shortcutId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputReplyTo: TypeConstructorDescription {
|
||||
case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?)
|
||||
case inputReplyToStory(peer: Api.InputPeer, storyId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(583071445)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(replyToMsgId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {replyToPeerId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(quoteText!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(quoteEntities!.count))
|
||||
for item in quoteEntities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .inputReplyToStory(let peer, let storyId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1484862010)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(storyId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset):
|
||||
return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)])
|
||||
case .inputReplyToStory(let peer, let storyId):
|
||||
return ("inputReplyToStory", [("peer", peer as Any), ("storyId", storyId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputReplyToMessage(_ reader: BufferReader) -> InputReplyTo? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: Api.InputPeer?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
} }
|
||||
var _5: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_5 = parseString(reader) }
|
||||
var _6: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _7: Int32?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputReplyToStory(_ reader: BufferReader) -> InputReplyTo? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputReplyTo.inputReplyToStory(peer: _1!, storyId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputSecureFile: TypeConstructorDescription {
|
||||
case inputSecureFile(id: Int64, accessHash: Int64)
|
||||
case inputSecureFileUploaded(id: Int64, parts: Int32, md5Checksum: String, fileHash: Buffer, secret: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputSecureFile(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(1399317950)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputSecureFileUploaded(let id, let parts, let md5Checksum, let fileHash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(859091184)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(parts, buffer: buffer, boxed: false)
|
||||
serializeString(md5Checksum, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputSecureFile(let id, let accessHash):
|
||||
return ("inputSecureFile", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputSecureFileUploaded(let id, let parts, let md5Checksum, let fileHash, let secret):
|
||||
return ("inputSecureFileUploaded", [("id", id as Any), ("parts", parts as Any), ("md5Checksum", md5Checksum as Any), ("fileHash", fileHash as Any), ("secret", secret as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputSecureFile(_ reader: BufferReader) -> InputSecureFile? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputSecureFile.inputSecureFile(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputSecureFileUploaded(_ reader: BufferReader) -> InputSecureFile? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Buffer?
|
||||
_4 = parseBytes(reader)
|
||||
var _5: Buffer?
|
||||
_5 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.InputSecureFile.inputSecureFileUploaded(id: _1!, parts: _2!, md5Checksum: _3!, fileHash: _4!, secret: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputSecureValue: TypeConstructorDescription {
|
||||
case inputSecureValue(flags: Int32, type: Api.SecureValueType, data: Api.SecureData?, frontSide: Api.InputSecureFile?, reverseSide: Api.InputSecureFile?, selfie: Api.InputSecureFile?, translation: [Api.InputSecureFile]?, files: [Api.InputSecureFile]?, plainData: Api.SecurePlainData?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputSecureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData):
|
||||
if boxed {
|
||||
buffer.appendInt32(-618540889)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
type.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {data!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {frontSide!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {reverseSide!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {selfie!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(translation!.count))
|
||||
for item in translation! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(files!.count))
|
||||
for item in files! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 5) != 0 {plainData!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputSecureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData):
|
||||
return ("inputSecureValue", [("flags", flags as Any), ("type", type as Any), ("data", data as Any), ("frontSide", frontSide as Any), ("reverseSide", reverseSide as Any), ("selfie", selfie as Any), ("translation", translation as Any), ("files", files as Any), ("plainData", plainData as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputSecureValue(_ reader: BufferReader) -> InputSecureValue? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.SecureValueType?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.SecureValueType
|
||||
}
|
||||
var _3: Api.SecureData?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.SecureData
|
||||
} }
|
||||
var _4: Api.InputSecureFile?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.InputSecureFile
|
||||
} }
|
||||
var _5: Api.InputSecureFile?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.InputSecureFile
|
||||
} }
|
||||
var _6: Api.InputSecureFile?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.InputSecureFile
|
||||
} }
|
||||
var _7: [Api.InputSecureFile]?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputSecureFile.self)
|
||||
} }
|
||||
var _8: [Api.InputSecureFile]?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputSecureFile.self)
|
||||
} }
|
||||
var _9: Api.SecurePlainData?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.SecurePlainData
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.InputSecureValue.inputSecureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,321 @@
|
||||
public extension Api {
|
||||
enum InputQuickReplyShortcut: TypeConstructorDescription {
|
||||
case inputQuickReplyShortcut(shortcut: String)
|
||||
case inputQuickReplyShortcutId(shortcutId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputQuickReplyShortcut(let shortcut):
|
||||
if boxed {
|
||||
buffer.appendInt32(609840449)
|
||||
}
|
||||
serializeString(shortcut, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputQuickReplyShortcutId(let shortcutId):
|
||||
if boxed {
|
||||
buffer.appendInt32(18418929)
|
||||
}
|
||||
serializeInt32(shortcutId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputQuickReplyShortcut(let shortcut):
|
||||
return ("inputQuickReplyShortcut", [("shortcut", shortcut as Any)])
|
||||
case .inputQuickReplyShortcutId(let shortcutId):
|
||||
return ("inputQuickReplyShortcutId", [("shortcutId", shortcutId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputQuickReplyShortcut(_ reader: BufferReader) -> InputQuickReplyShortcut? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputQuickReplyShortcut.inputQuickReplyShortcut(shortcut: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputQuickReplyShortcutId(_ reader: BufferReader) -> InputQuickReplyShortcut? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputQuickReplyShortcut.inputQuickReplyShortcutId(shortcutId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputReplyTo: TypeConstructorDescription {
|
||||
case inputReplyToMessage(flags: Int32, replyToMsgId: Int32, topMsgId: Int32?, replyToPeerId: Api.InputPeer?, quoteText: String?, quoteEntities: [Api.MessageEntity]?, quoteOffset: Int32?)
|
||||
case inputReplyToStory(peer: Api.InputPeer, storyId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(583071445)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(replyToMsgId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {replyToPeerId!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(quoteText!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(quoteEntities!.count))
|
||||
for item in quoteEntities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(quoteOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .inputReplyToStory(let peer, let storyId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1484862010)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(storyId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputReplyToMessage(let flags, let replyToMsgId, let topMsgId, let replyToPeerId, let quoteText, let quoteEntities, let quoteOffset):
|
||||
return ("inputReplyToMessage", [("flags", flags as Any), ("replyToMsgId", replyToMsgId as Any), ("topMsgId", topMsgId as Any), ("replyToPeerId", replyToPeerId as Any), ("quoteText", quoteText as Any), ("quoteEntities", quoteEntities as Any), ("quoteOffset", quoteOffset as Any)])
|
||||
case .inputReplyToStory(let peer, let storyId):
|
||||
return ("inputReplyToStory", [("peer", peer as Any), ("storyId", storyId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputReplyToMessage(_ reader: BufferReader) -> InputReplyTo? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: Api.InputPeer?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
} }
|
||||
var _5: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_5 = parseString(reader) }
|
||||
var _6: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _7: Int32?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputReplyToStory(_ reader: BufferReader) -> InputReplyTo? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputReplyTo.inputReplyToStory(peer: _1!, storyId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputSecureFile: TypeConstructorDescription {
|
||||
case inputSecureFile(id: Int64, accessHash: Int64)
|
||||
case inputSecureFileUploaded(id: Int64, parts: Int32, md5Checksum: String, fileHash: Buffer, secret: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputSecureFile(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(1399317950)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputSecureFileUploaded(let id, let parts, let md5Checksum, let fileHash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(859091184)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(parts, buffer: buffer, boxed: false)
|
||||
serializeString(md5Checksum, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputSecureFile(let id, let accessHash):
|
||||
return ("inputSecureFile", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputSecureFileUploaded(let id, let parts, let md5Checksum, let fileHash, let secret):
|
||||
return ("inputSecureFileUploaded", [("id", id as Any), ("parts", parts as Any), ("md5Checksum", md5Checksum as Any), ("fileHash", fileHash as Any), ("secret", secret as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputSecureFile(_ reader: BufferReader) -> InputSecureFile? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputSecureFile.inputSecureFile(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputSecureFileUploaded(_ reader: BufferReader) -> InputSecureFile? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Buffer?
|
||||
_4 = parseBytes(reader)
|
||||
var _5: Buffer?
|
||||
_5 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.InputSecureFile.inputSecureFileUploaded(id: _1!, parts: _2!, md5Checksum: _3!, fileHash: _4!, secret: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputSecureValue: TypeConstructorDescription {
|
||||
case inputSecureValue(flags: Int32, type: Api.SecureValueType, data: Api.SecureData?, frontSide: Api.InputSecureFile?, reverseSide: Api.InputSecureFile?, selfie: Api.InputSecureFile?, translation: [Api.InputSecureFile]?, files: [Api.InputSecureFile]?, plainData: Api.SecurePlainData?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputSecureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData):
|
||||
if boxed {
|
||||
buffer.appendInt32(-618540889)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
type.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {data!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {frontSide!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {reverseSide!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {selfie!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 6) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(translation!.count))
|
||||
for item in translation! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(files!.count))
|
||||
for item in files! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 5) != 0 {plainData!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputSecureValue(let flags, let type, let data, let frontSide, let reverseSide, let selfie, let translation, let files, let plainData):
|
||||
return ("inputSecureValue", [("flags", flags as Any), ("type", type as Any), ("data", data as Any), ("frontSide", frontSide as Any), ("reverseSide", reverseSide as Any), ("selfie", selfie as Any), ("translation", translation as Any), ("files", files as Any), ("plainData", plainData as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputSecureValue(_ reader: BufferReader) -> InputSecureValue? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.SecureValueType?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.SecureValueType
|
||||
}
|
||||
var _3: Api.SecureData?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.SecureData
|
||||
} }
|
||||
var _4: Api.InputSecureFile?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.InputSecureFile
|
||||
} }
|
||||
var _5: Api.InputSecureFile?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.InputSecureFile
|
||||
} }
|
||||
var _6: Api.InputSecureFile?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.InputSecureFile
|
||||
} }
|
||||
var _7: [Api.InputSecureFile]?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputSecureFile.self)
|
||||
} }
|
||||
var _8: [Api.InputSecureFile]?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputSecureFile.self)
|
||||
} }
|
||||
var _9: Api.SecurePlainData?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.SecurePlainData
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.InputSecureValue.inputSecureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputSingleMedia: TypeConstructorDescription {
|
||||
case inputSingleMedia(flags: Int32, media: Api.InputMedia, randomId: Int64, message: String, entities: [Api.MessageEntity]?)
|
||||
@ -760,177 +1078,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputUser: TypeConstructorDescription {
|
||||
case inputUser(userId: Int64, accessHash: Int64)
|
||||
case inputUserEmpty
|
||||
case inputUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int64)
|
||||
case inputUserSelf
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-233744186)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1182234929)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(497305826)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserSelf:
|
||||
if boxed {
|
||||
buffer.appendInt32(-138301121)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
return ("inputUser", [("userId", userId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputUserEmpty:
|
||||
return ("inputUserEmpty", [])
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputUserFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("userId", userId as Any)])
|
||||
case .inputUserSelf:
|
||||
return ("inputUserSelf", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputUser(_ reader: BufferReader) -> InputUser? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputUser.inputUser(userId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserEmpty(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserEmpty
|
||||
}
|
||||
public static func parse_inputUserFromMessage(_ reader: BufferReader) -> InputUser? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputUser.inputUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserSelf(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserSelf
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputWallPaper: TypeConstructorDescription {
|
||||
case inputWallPaper(id: Int64, accessHash: Int64)
|
||||
case inputWallPaperNoFile(id: Int64)
|
||||
case inputWallPaperSlug(slug: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputWallPaper(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-433014407)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputWallPaperNoFile(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1770371538)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputWallPaperSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(1913199744)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputWallPaper(let id, let accessHash):
|
||||
return ("inputWallPaper", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputWallPaperNoFile(let id):
|
||||
return ("inputWallPaperNoFile", [("id", id as Any)])
|
||||
case .inputWallPaperSlug(let slug):
|
||||
return ("inputWallPaperSlug", [("slug", slug as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputWallPaper(_ reader: BufferReader) -> InputWallPaper? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputWallPaper.inputWallPaper(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputWallPaperNoFile(_ reader: BufferReader) -> InputWallPaper? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputWallPaper.inputWallPaperNoFile(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputWallPaperSlug(_ reader: BufferReader) -> InputWallPaper? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputWallPaper.inputWallPaperSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,177 @@
|
||||
public extension Api {
|
||||
indirect enum InputUser: TypeConstructorDescription {
|
||||
case inputUser(userId: Int64, accessHash: Int64)
|
||||
case inputUserEmpty
|
||||
case inputUserFromMessage(peer: Api.InputPeer, msgId: Int32, userId: Int64)
|
||||
case inputUserSelf
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-233744186)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1182234929)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
if boxed {
|
||||
buffer.appendInt32(497305826)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputUserSelf:
|
||||
if boxed {
|
||||
buffer.appendInt32(-138301121)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputUser(let userId, let accessHash):
|
||||
return ("inputUser", [("userId", userId as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputUserEmpty:
|
||||
return ("inputUserEmpty", [])
|
||||
case .inputUserFromMessage(let peer, let msgId, let userId):
|
||||
return ("inputUserFromMessage", [("peer", peer as Any), ("msgId", msgId as Any), ("userId", userId as Any)])
|
||||
case .inputUserSelf:
|
||||
return ("inputUserSelf", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputUser(_ reader: BufferReader) -> InputUser? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputUser.inputUser(userId: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserEmpty(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserEmpty
|
||||
}
|
||||
public static func parse_inputUserFromMessage(_ reader: BufferReader) -> InputUser? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.InputUser.inputUserFromMessage(peer: _1!, msgId: _2!, userId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputUserSelf(_ reader: BufferReader) -> InputUser? {
|
||||
return Api.InputUser.inputUserSelf
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputWallPaper: TypeConstructorDescription {
|
||||
case inputWallPaper(id: Int64, accessHash: Int64)
|
||||
case inputWallPaperNoFile(id: Int64)
|
||||
case inputWallPaperSlug(slug: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputWallPaper(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-433014407)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputWallPaperNoFile(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1770371538)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputWallPaperSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(1913199744)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputWallPaper(let id, let accessHash):
|
||||
return ("inputWallPaper", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||
case .inputWallPaperNoFile(let id):
|
||||
return ("inputWallPaperNoFile", [("id", id as Any)])
|
||||
case .inputWallPaperSlug(let slug):
|
||||
return ("inputWallPaperSlug", [("slug", slug as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputWallPaper(_ reader: BufferReader) -> InputWallPaper? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputWallPaper.inputWallPaper(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputWallPaperNoFile(_ reader: BufferReader) -> InputWallPaper? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputWallPaper.inputWallPaperNoFile(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputWallPaperSlug(_ reader: BufferReader) -> InputWallPaper? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputWallPaper.inputWallPaperSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputWebDocument: TypeConstructorDescription {
|
||||
case inputWebDocument(url: String, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute])
|
||||
@ -900,139 +1074,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum KeyboardButtonRow: TypeConstructorDescription {
|
||||
case keyboardButtonRow(buttons: [Api.KeyboardButton])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
if boxed {
|
||||
buffer.appendInt32(2002815875)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(buttons.count))
|
||||
for item in buttons {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
return ("keyboardButtonRow", [("buttons", buttons as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_keyboardButtonRow(_ reader: BufferReader) -> KeyboardButtonRow? {
|
||||
var _1: [Api.KeyboardButton]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButton.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.KeyboardButtonRow.keyboardButtonRow(buttons: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LabeledPrice: TypeConstructorDescription {
|
||||
case labeledPrice(label: String, amount: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .labeledPrice(let label, let amount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-886477832)
|
||||
}
|
||||
serializeString(label, buffer: buffer, boxed: false)
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .labeledPrice(let label, let amount):
|
||||
return ("labeledPrice", [("label", label as Any), ("amount", amount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_labeledPrice(_ reader: BufferReader) -> LabeledPrice? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.LabeledPrice.labeledPrice(label: _1!, amount: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LangPackDifference: TypeConstructorDescription {
|
||||
case langPackDifference(langCode: String, fromVersion: Int32, version: Int32, strings: [Api.LangPackString])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .langPackDifference(let langCode, let fromVersion, let version, let strings):
|
||||
if boxed {
|
||||
buffer.appendInt32(-209337866)
|
||||
}
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
serializeInt32(fromVersion, buffer: buffer, boxed: false)
|
||||
serializeInt32(version, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(strings.count))
|
||||
for item in strings {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .langPackDifference(let langCode, let fromVersion, let version, let strings):
|
||||
return ("langPackDifference", [("langCode", langCode as Any), ("fromVersion", fromVersion as Any), ("version", version as Any), ("strings", strings as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_langPackDifference(_ reader: BufferReader) -> LangPackDifference? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: [Api.LangPackString]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LangPackString.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.LangPackDifference.langPackDifference(langCode: _1!, fromVersion: _2!, version: _3!, strings: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,139 @@
|
||||
public extension Api {
|
||||
enum KeyboardButtonRow: TypeConstructorDescription {
|
||||
case keyboardButtonRow(buttons: [Api.KeyboardButton])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
if boxed {
|
||||
buffer.appendInt32(2002815875)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(buttons.count))
|
||||
for item in buttons {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .keyboardButtonRow(let buttons):
|
||||
return ("keyboardButtonRow", [("buttons", buttons as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_keyboardButtonRow(_ reader: BufferReader) -> KeyboardButtonRow? {
|
||||
var _1: [Api.KeyboardButton]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButton.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.KeyboardButtonRow.keyboardButtonRow(buttons: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LabeledPrice: TypeConstructorDescription {
|
||||
case labeledPrice(label: String, amount: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .labeledPrice(let label, let amount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-886477832)
|
||||
}
|
||||
serializeString(label, buffer: buffer, boxed: false)
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .labeledPrice(let label, let amount):
|
||||
return ("labeledPrice", [("label", label as Any), ("amount", amount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_labeledPrice(_ reader: BufferReader) -> LabeledPrice? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.LabeledPrice.labeledPrice(label: _1!, amount: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LangPackDifference: TypeConstructorDescription {
|
||||
case langPackDifference(langCode: String, fromVersion: Int32, version: Int32, strings: [Api.LangPackString])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .langPackDifference(let langCode, let fromVersion, let version, let strings):
|
||||
if boxed {
|
||||
buffer.appendInt32(-209337866)
|
||||
}
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
serializeInt32(fromVersion, buffer: buffer, boxed: false)
|
||||
serializeInt32(version, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(strings.count))
|
||||
for item in strings {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .langPackDifference(let langCode, let fromVersion, let version, let strings):
|
||||
return ("langPackDifference", [("langCode", langCode as Any), ("fromVersion", fromVersion as Any), ("version", version as Any), ("strings", strings as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_langPackDifference(_ reader: BufferReader) -> LangPackDifference? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: [Api.LangPackString]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.LangPackString.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.LangPackDifference.langPackDifference(langCode: _1!, fromVersion: _2!, version: _3!, strings: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LangPackLanguage: TypeConstructorDescription {
|
||||
case langPackLanguage(flags: Int32, name: String, nativeName: String, langCode: String, baseLangCode: String?, pluralCode: String, stringsCount: Int32, translatedCount: Int32, translationsUrl: String)
|
||||
|
@ -200,13 +200,13 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageReactions: TypeConstructorDescription {
|
||||
case messageReactions(flags: Int32, results: [Api.ReactionCount], recentReactions: [Api.MessagePeerReaction]?)
|
||||
case messageReactions(flags: Int32, results: [Api.ReactionCount], recentReactions: [Api.MessagePeerReaction]?, topReactors: [Api.MessageReactor]?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReactions(let flags, let results, let recentReactions):
|
||||
case .messageReactions(let flags, let results, let recentReactions, let topReactors):
|
||||
if boxed {
|
||||
buffer.appendInt32(1328256121)
|
||||
buffer.appendInt32(171155211)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -219,14 +219,19 @@ public extension Api {
|
||||
for item in recentReactions! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(topReactors!.count))
|
||||
for item in topReactors! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReactions(let flags, let results, let recentReactions):
|
||||
return ("messageReactions", [("flags", flags as Any), ("results", results as Any), ("recentReactions", recentReactions as Any)])
|
||||
case .messageReactions(let flags, let results, let recentReactions, let topReactors):
|
||||
return ("messageReactions", [("flags", flags as Any), ("results", results as Any), ("recentReactions", recentReactions as Any), ("topReactors", topReactors as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,11 +246,62 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessagePeerReaction.self)
|
||||
} }
|
||||
var _4: [Api.MessageReactor]?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageReactor.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactions: _3, topReactors: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageReactor: TypeConstructorDescription {
|
||||
case messageReactor(flags: Int32, peerId: Api.Peer, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageReactor(let flags, let peerId, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-285158328)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peerId.serialize(buffer, true)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageReactor(let flags, let peerId, let count):
|
||||
return ("messageReactor", [("flags", flags as Any), ("peerId", peerId as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageReactor(_ reader: BufferReader) -> MessageReactor? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactions: _3)
|
||||
return Api.MessageReactor.messageReactor(flags: _1!, peerId: _2!, count: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -724,6 +724,48 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum BotPreviewMedia: TypeConstructorDescription {
|
||||
case botPreviewMedia(date: Int32, media: Api.MessageMedia)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .botPreviewMedia(let date, let media):
|
||||
if boxed {
|
||||
buffer.appendInt32(602479523)
|
||||
}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
media.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .botPreviewMedia(let date, let media):
|
||||
return ("botPreviewMedia", [("date", date as Any), ("media", media as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_botPreviewMedia(_ reader: BufferReader) -> BotPreviewMedia? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.MessageMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.BotPreviewMedia.botPreviewMedia(date: _1!, media: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum BroadcastRevenueBalances: TypeConstructorDescription {
|
||||
case broadcastRevenueBalances(currentBalance: Int64, availableBalance: Int64, overallRevenue: Int64)
|
||||
@ -1160,53 +1202,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum BusinessIntro: TypeConstructorDescription {
|
||||
case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .businessIntro(let flags, let title, let description, let sticker):
|
||||
if boxed {
|
||||
buffer.appendInt32(1510606445)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .businessIntro(let flags, let title, let description, let sticker):
|
||||
return ("businessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_businessIntro(_ reader: BufferReader) -> BusinessIntro? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Api.Document?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Document
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -309,6 +309,7 @@ public extension Api {
|
||||
case reactionCustomEmoji(documentId: Int64)
|
||||
case reactionEmoji(emoticon: String)
|
||||
case reactionEmpty
|
||||
case reactionPaid
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -329,6 +330,12 @@ public extension Api {
|
||||
buffer.appendInt32(2046153753)
|
||||
}
|
||||
|
||||
break
|
||||
case .reactionPaid:
|
||||
if boxed {
|
||||
buffer.appendInt32(1379771627)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -341,6 +348,8 @@ public extension Api {
|
||||
return ("reactionEmoji", [("emoticon", emoticon as Any)])
|
||||
case .reactionEmpty:
|
||||
return ("reactionEmpty", [])
|
||||
case .reactionPaid:
|
||||
return ("reactionPaid", [])
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,6 +378,9 @@ public extension Api {
|
||||
public static func parse_reactionEmpty(_ reader: BufferReader) -> Reaction? {
|
||||
return Api.Reaction.reactionEmpty
|
||||
}
|
||||
public static func parse_reactionPaid(_ reader: BufferReader) -> Reaction? {
|
||||
return Api.Reaction.reactionPaid
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -858,139 +870,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ReportReason: TypeConstructorDescription {
|
||||
case inputReportReasonChildAbuse
|
||||
case inputReportReasonCopyright
|
||||
case inputReportReasonFake
|
||||
case inputReportReasonGeoIrrelevant
|
||||
case inputReportReasonIllegalDrugs
|
||||
case inputReportReasonOther
|
||||
case inputReportReasonPersonalDetails
|
||||
case inputReportReasonPornography
|
||||
case inputReportReasonSpam
|
||||
case inputReportReasonViolence
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputReportReasonChildAbuse:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1376497949)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonCopyright:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1685456582)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonFake:
|
||||
if boxed {
|
||||
buffer.appendInt32(-170010905)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonGeoIrrelevant:
|
||||
if boxed {
|
||||
buffer.appendInt32(-606798099)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonIllegalDrugs:
|
||||
if boxed {
|
||||
buffer.appendInt32(177124030)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonOther:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1041980751)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonPersonalDetails:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1631091139)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonPornography:
|
||||
if boxed {
|
||||
buffer.appendInt32(777640226)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonSpam:
|
||||
if boxed {
|
||||
buffer.appendInt32(1490799288)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonViolence:
|
||||
if boxed {
|
||||
buffer.appendInt32(505595789)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputReportReasonChildAbuse:
|
||||
return ("inputReportReasonChildAbuse", [])
|
||||
case .inputReportReasonCopyright:
|
||||
return ("inputReportReasonCopyright", [])
|
||||
case .inputReportReasonFake:
|
||||
return ("inputReportReasonFake", [])
|
||||
case .inputReportReasonGeoIrrelevant:
|
||||
return ("inputReportReasonGeoIrrelevant", [])
|
||||
case .inputReportReasonIllegalDrugs:
|
||||
return ("inputReportReasonIllegalDrugs", [])
|
||||
case .inputReportReasonOther:
|
||||
return ("inputReportReasonOther", [])
|
||||
case .inputReportReasonPersonalDetails:
|
||||
return ("inputReportReasonPersonalDetails", [])
|
||||
case .inputReportReasonPornography:
|
||||
return ("inputReportReasonPornography", [])
|
||||
case .inputReportReasonSpam:
|
||||
return ("inputReportReasonSpam", [])
|
||||
case .inputReportReasonViolence:
|
||||
return ("inputReportReasonViolence", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputReportReasonChildAbuse(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonChildAbuse
|
||||
}
|
||||
public static func parse_inputReportReasonCopyright(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonCopyright
|
||||
}
|
||||
public static func parse_inputReportReasonFake(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonFake
|
||||
}
|
||||
public static func parse_inputReportReasonGeoIrrelevant(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonGeoIrrelevant
|
||||
}
|
||||
public static func parse_inputReportReasonIllegalDrugs(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonIllegalDrugs
|
||||
}
|
||||
public static func parse_inputReportReasonOther(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonOther
|
||||
}
|
||||
public static func parse_inputReportReasonPersonalDetails(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonPersonalDetails
|
||||
}
|
||||
public static func parse_inputReportReasonPornography(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonPornography
|
||||
}
|
||||
public static func parse_inputReportReasonSpam(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonSpam
|
||||
}
|
||||
public static func parse_inputReportReasonViolence(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonViolence
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,139 @@
|
||||
public extension Api {
|
||||
enum ReportReason: TypeConstructorDescription {
|
||||
case inputReportReasonChildAbuse
|
||||
case inputReportReasonCopyright
|
||||
case inputReportReasonFake
|
||||
case inputReportReasonGeoIrrelevant
|
||||
case inputReportReasonIllegalDrugs
|
||||
case inputReportReasonOther
|
||||
case inputReportReasonPersonalDetails
|
||||
case inputReportReasonPornography
|
||||
case inputReportReasonSpam
|
||||
case inputReportReasonViolence
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputReportReasonChildAbuse:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1376497949)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonCopyright:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1685456582)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonFake:
|
||||
if boxed {
|
||||
buffer.appendInt32(-170010905)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonGeoIrrelevant:
|
||||
if boxed {
|
||||
buffer.appendInt32(-606798099)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonIllegalDrugs:
|
||||
if boxed {
|
||||
buffer.appendInt32(177124030)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonOther:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1041980751)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonPersonalDetails:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1631091139)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonPornography:
|
||||
if boxed {
|
||||
buffer.appendInt32(777640226)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonSpam:
|
||||
if boxed {
|
||||
buffer.appendInt32(1490799288)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputReportReasonViolence:
|
||||
if boxed {
|
||||
buffer.appendInt32(505595789)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputReportReasonChildAbuse:
|
||||
return ("inputReportReasonChildAbuse", [])
|
||||
case .inputReportReasonCopyright:
|
||||
return ("inputReportReasonCopyright", [])
|
||||
case .inputReportReasonFake:
|
||||
return ("inputReportReasonFake", [])
|
||||
case .inputReportReasonGeoIrrelevant:
|
||||
return ("inputReportReasonGeoIrrelevant", [])
|
||||
case .inputReportReasonIllegalDrugs:
|
||||
return ("inputReportReasonIllegalDrugs", [])
|
||||
case .inputReportReasonOther:
|
||||
return ("inputReportReasonOther", [])
|
||||
case .inputReportReasonPersonalDetails:
|
||||
return ("inputReportReasonPersonalDetails", [])
|
||||
case .inputReportReasonPornography:
|
||||
return ("inputReportReasonPornography", [])
|
||||
case .inputReportReasonSpam:
|
||||
return ("inputReportReasonSpam", [])
|
||||
case .inputReportReasonViolence:
|
||||
return ("inputReportReasonViolence", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputReportReasonChildAbuse(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonChildAbuse
|
||||
}
|
||||
public static func parse_inputReportReasonCopyright(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonCopyright
|
||||
}
|
||||
public static func parse_inputReportReasonFake(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonFake
|
||||
}
|
||||
public static func parse_inputReportReasonGeoIrrelevant(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonGeoIrrelevant
|
||||
}
|
||||
public static func parse_inputReportReasonIllegalDrugs(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonIllegalDrugs
|
||||
}
|
||||
public static func parse_inputReportReasonOther(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonOther
|
||||
}
|
||||
public static func parse_inputReportReasonPersonalDetails(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonPersonalDetails
|
||||
}
|
||||
public static func parse_inputReportReasonPornography(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonPornography
|
||||
}
|
||||
public static func parse_inputReportReasonSpam(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonSpam
|
||||
}
|
||||
public static func parse_inputReportReasonViolence(_ reader: BufferReader) -> ReportReason? {
|
||||
return Api.ReportReason.inputReportReasonViolence
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum RequestPeerType: TypeConstructorDescription {
|
||||
case requestPeerTypeBroadcast(flags: Int32, hasUsername: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?)
|
||||
@ -688,399 +824,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SavedContact: TypeConstructorDescription {
|
||||
case savedPhoneContact(phone: String, firstName: String, lastName: String, date: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedPhoneContact(let phone, let firstName, let lastName, let date):
|
||||
if boxed {
|
||||
buffer.appendInt32(289586518)
|
||||
}
|
||||
serializeString(phone, buffer: buffer, boxed: false)
|
||||
serializeString(firstName, buffer: buffer, boxed: false)
|
||||
serializeString(lastName, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedPhoneContact(let phone, let firstName, let lastName, let date):
|
||||
return ("savedPhoneContact", [("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("date", date as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedPhoneContact(_ reader: BufferReader) -> SavedContact? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.SavedContact.savedPhoneContact(phone: _1!, firstName: _2!, lastName: _3!, date: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SavedDialog: TypeConstructorDescription {
|
||||
case savedDialog(flags: Int32, peer: Api.Peer, topMessage: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedDialog(let flags, let peer, let topMessage):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1115174036)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(topMessage, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedDialog(let flags, let peer, let topMessage):
|
||||
return ("savedDialog", [("flags", flags as Any), ("peer", peer as Any), ("topMessage", topMessage as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedDialog(_ reader: BufferReader) -> SavedDialog? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SavedDialog.savedDialog(flags: _1!, peer: _2!, topMessage: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SavedReactionTag: TypeConstructorDescription {
|
||||
case savedReactionTag(flags: Int32, reaction: Api.Reaction, title: String?, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedReactionTag(let flags, let reaction, let title, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-881854424)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
reaction.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedReactionTag(let flags, let reaction, let title, let count):
|
||||
return ("savedReactionTag", [("flags", flags as Any), ("reaction", reaction as Any), ("title", title as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedReactionTag(_ reader: BufferReader) -> SavedReactionTag? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Reaction?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
}
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.SavedReactionTag.savedReactionTag(flags: _1!, reaction: _2!, title: _3, count: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SearchResultsCalendarPeriod: TypeConstructorDescription {
|
||||
case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-911191137)
|
||||
}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt32(minMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(maxMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count):
|
||||
return ("searchResultsCalendarPeriod", [("date", date as Any), ("minMsgId", minMsgId as Any), ("maxMsgId", maxMsgId as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_searchResultsCalendarPeriod(_ reader: BufferReader) -> SearchResultsCalendarPeriod? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SearchResultsPosition: TypeConstructorDescription {
|
||||
case searchResultPosition(msgId: Int32, date: Int32, offset: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .searchResultPosition(let msgId, let date, let offset):
|
||||
if boxed {
|
||||
buffer.appendInt32(2137295719)
|
||||
}
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .searchResultPosition(let msgId, let date, let offset):
|
||||
return ("searchResultPosition", [("msgId", msgId as Any), ("date", date as Any), ("offset", offset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_searchResultPosition(_ reader: BufferReader) -> SearchResultsPosition? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SearchResultsPosition.searchResultPosition(msgId: _1!, date: _2!, offset: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecureCredentialsEncrypted: TypeConstructorDescription {
|
||||
case secureCredentialsEncrypted(data: Buffer, hash: Buffer, secret: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .secureCredentialsEncrypted(let data, let hash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(871426631)
|
||||
}
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
serializeBytes(hash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .secureCredentialsEncrypted(let data, let hash, let secret):
|
||||
return ("secureCredentialsEncrypted", [("data", data as Any), ("hash", hash as Any), ("secret", secret as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_secureCredentialsEncrypted(_ reader: BufferReader) -> SecureCredentialsEncrypted? {
|
||||
var _1: Buffer?
|
||||
_1 = parseBytes(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SecureCredentialsEncrypted.secureCredentialsEncrypted(data: _1!, hash: _2!, secret: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecureData: TypeConstructorDescription {
|
||||
case secureData(data: Buffer, dataHash: Buffer, secret: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .secureData(let data, let dataHash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1964327229)
|
||||
}
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
serializeBytes(dataHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .secureData(let data, let dataHash, let secret):
|
||||
return ("secureData", [("data", data as Any), ("dataHash", dataHash as Any), ("secret", secret as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_secureData(_ reader: BufferReader) -> SecureData? {
|
||||
var _1: Buffer?
|
||||
_1 = parseBytes(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SecureData.secureData(data: _1!, dataHash: _2!, secret: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecureFile: TypeConstructorDescription {
|
||||
case secureFile(id: Int64, accessHash: Int64, size: Int64, dcId: Int32, date: Int32, fileHash: Buffer, secret: Buffer)
|
||||
case secureFileEmpty
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(2097791614)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeInt64(size, buffer: buffer, boxed: false)
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .secureFileEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(1679398724)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret):
|
||||
return ("secureFile", [("id", id as Any), ("accessHash", accessHash as Any), ("size", size as Any), ("dcId", dcId as Any), ("date", date as Any), ("fileHash", fileHash as Any), ("secret", secret as Any)])
|
||||
case .secureFileEmpty:
|
||||
return ("secureFileEmpty", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_secureFile(_ reader: BufferReader) -> SecureFile? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Buffer?
|
||||
_6 = parseBytes(reader)
|
||||
var _7: Buffer?
|
||||
_7 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.SecureFile.secureFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, date: _5!, fileHash: _6!, secret: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_secureFileEmpty(_ reader: BufferReader) -> SecureFile? {
|
||||
return Api.SecureFile.secureFileEmpty
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,399 @@
|
||||
public extension Api {
|
||||
enum SavedContact: TypeConstructorDescription {
|
||||
case savedPhoneContact(phone: String, firstName: String, lastName: String, date: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedPhoneContact(let phone, let firstName, let lastName, let date):
|
||||
if boxed {
|
||||
buffer.appendInt32(289586518)
|
||||
}
|
||||
serializeString(phone, buffer: buffer, boxed: false)
|
||||
serializeString(firstName, buffer: buffer, boxed: false)
|
||||
serializeString(lastName, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedPhoneContact(let phone, let firstName, let lastName, let date):
|
||||
return ("savedPhoneContact", [("phone", phone as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("date", date as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedPhoneContact(_ reader: BufferReader) -> SavedContact? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.SavedContact.savedPhoneContact(phone: _1!, firstName: _2!, lastName: _3!, date: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SavedDialog: TypeConstructorDescription {
|
||||
case savedDialog(flags: Int32, peer: Api.Peer, topMessage: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedDialog(let flags, let peer, let topMessage):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1115174036)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(topMessage, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedDialog(let flags, let peer, let topMessage):
|
||||
return ("savedDialog", [("flags", flags as Any), ("peer", peer as Any), ("topMessage", topMessage as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedDialog(_ reader: BufferReader) -> SavedDialog? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SavedDialog.savedDialog(flags: _1!, peer: _2!, topMessage: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SavedReactionTag: TypeConstructorDescription {
|
||||
case savedReactionTag(flags: Int32, reaction: Api.Reaction, title: String?, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .savedReactionTag(let flags, let reaction, let title, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-881854424)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
reaction.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .savedReactionTag(let flags, let reaction, let title, let count):
|
||||
return ("savedReactionTag", [("flags", flags as Any), ("reaction", reaction as Any), ("title", title as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_savedReactionTag(_ reader: BufferReader) -> SavedReactionTag? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Reaction?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
}
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.SavedReactionTag.savedReactionTag(flags: _1!, reaction: _2!, title: _3, count: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SearchResultsCalendarPeriod: TypeConstructorDescription {
|
||||
case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-911191137)
|
||||
}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt32(minMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(maxMsgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count):
|
||||
return ("searchResultsCalendarPeriod", [("date", date as Any), ("minMsgId", minMsgId as Any), ("maxMsgId", maxMsgId as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_searchResultsCalendarPeriod(_ reader: BufferReader) -> SearchResultsCalendarPeriod? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SearchResultsPosition: TypeConstructorDescription {
|
||||
case searchResultPosition(msgId: Int32, date: Int32, offset: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .searchResultPosition(let msgId, let date, let offset):
|
||||
if boxed {
|
||||
buffer.appendInt32(2137295719)
|
||||
}
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .searchResultPosition(let msgId, let date, let offset):
|
||||
return ("searchResultPosition", [("msgId", msgId as Any), ("date", date as Any), ("offset", offset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_searchResultPosition(_ reader: BufferReader) -> SearchResultsPosition? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SearchResultsPosition.searchResultPosition(msgId: _1!, date: _2!, offset: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecureCredentialsEncrypted: TypeConstructorDescription {
|
||||
case secureCredentialsEncrypted(data: Buffer, hash: Buffer, secret: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .secureCredentialsEncrypted(let data, let hash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(871426631)
|
||||
}
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
serializeBytes(hash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .secureCredentialsEncrypted(let data, let hash, let secret):
|
||||
return ("secureCredentialsEncrypted", [("data", data as Any), ("hash", hash as Any), ("secret", secret as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_secureCredentialsEncrypted(_ reader: BufferReader) -> SecureCredentialsEncrypted? {
|
||||
var _1: Buffer?
|
||||
_1 = parseBytes(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SecureCredentialsEncrypted.secureCredentialsEncrypted(data: _1!, hash: _2!, secret: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecureData: TypeConstructorDescription {
|
||||
case secureData(data: Buffer, dataHash: Buffer, secret: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .secureData(let data, let dataHash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1964327229)
|
||||
}
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
serializeBytes(dataHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .secureData(let data, let dataHash, let secret):
|
||||
return ("secureData", [("data", data as Any), ("dataHash", dataHash as Any), ("secret", secret as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_secureData(_ reader: BufferReader) -> SecureData? {
|
||||
var _1: Buffer?
|
||||
_1 = parseBytes(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.SecureData.secureData(data: _1!, dataHash: _2!, secret: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecureFile: TypeConstructorDescription {
|
||||
case secureFile(id: Int64, accessHash: Int64, size: Int64, dcId: Int32, date: Int32, fileHash: Buffer, secret: Buffer)
|
||||
case secureFileEmpty
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret):
|
||||
if boxed {
|
||||
buffer.appendInt32(2097791614)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeInt64(size, buffer: buffer, boxed: false)
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeBytes(fileHash, buffer: buffer, boxed: false)
|
||||
serializeBytes(secret, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .secureFileEmpty:
|
||||
if boxed {
|
||||
buffer.appendInt32(1679398724)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .secureFile(let id, let accessHash, let size, let dcId, let date, let fileHash, let secret):
|
||||
return ("secureFile", [("id", id as Any), ("accessHash", accessHash as Any), ("size", size as Any), ("dcId", dcId as Any), ("date", date as Any), ("fileHash", fileHash as Any), ("secret", secret as Any)])
|
||||
case .secureFileEmpty:
|
||||
return ("secureFileEmpty", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_secureFile(_ reader: BufferReader) -> SecureFile? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Buffer?
|
||||
_6 = parseBytes(reader)
|
||||
var _7: Buffer?
|
||||
_7 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.SecureFile.secureFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, date: _5!, fileHash: _6!, secret: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_secureFileEmpty(_ reader: BufferReader) -> SecureFile? {
|
||||
return Api.SecureFile.secureFileEmpty
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SecurePasswordKdfAlgo: TypeConstructorDescription {
|
||||
case securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: Buffer)
|
||||
|
@ -826,7 +826,7 @@ public extension Api {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod):
|
||||
if boxed {
|
||||
buffer.appendInt32(455361027)
|
||||
buffer.appendInt32(1127934763)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
@ -845,7 +845,7 @@ public extension Api {
|
||||
for item in extendedMedia! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(subscriptionPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(subscriptionPeriod!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -891,7 +891,7 @@ public extension Api {
|
||||
_13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageMedia.self)
|
||||
} }
|
||||
var _14: Int32?
|
||||
if Int(_1!) & Int(1 << 11) != 0 {_14 = reader.readInt32() }
|
||||
if Int(_1!) & Int(1 << 12) != 0 {_14 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -905,7 +905,7 @@ public extension Api {
|
||||
let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 9) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 11) == 0) || _14 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 12) == 0) || _14 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14)
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
public extension Api.bots {
|
||||
enum PreviewInfo: TypeConstructorDescription {
|
||||
case previewInfo(media: [Api.MessageMedia], langCodes: [String])
|
||||
case previewInfo(media: [Api.BotPreviewMedia], langCodes: [String])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .previewInfo(let media, let langCodes):
|
||||
if boxed {
|
||||
buffer.appendInt32(1357069389)
|
||||
buffer.appendInt32(212278628)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(media.count))
|
||||
@ -30,9 +30,9 @@ public extension Api.bots {
|
||||
}
|
||||
|
||||
public static func parse_previewInfo(_ reader: BufferReader) -> PreviewInfo? {
|
||||
var _1: [Api.MessageMedia]?
|
||||
var _1: [Api.BotPreviewMedia]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageMedia.self)
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotPreviewMedia.self)
|
||||
}
|
||||
var _2: [String]?
|
||||
if let _ = reader.readInt32() {
|
||||
|
@ -1,3 +1,53 @@
|
||||
public extension Api {
|
||||
enum BusinessIntro: TypeConstructorDescription {
|
||||
case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .businessIntro(let flags, let title, let description, let sticker):
|
||||
if boxed {
|
||||
buffer.appendInt32(1510606445)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(description, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .businessIntro(let flags, let title, let description, let sticker):
|
||||
return ("businessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_businessIntro(_ reader: BufferReader) -> BusinessIntro? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: Api.Document?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Document
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum BusinessLocation: TypeConstructorDescription {
|
||||
case businessLocation(flags: Int32, geoPoint: Api.GeoPoint?, address: String)
|
||||
|
@ -1246,13 +1246,13 @@ public extension Api.payments {
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum StarsStatus: TypeConstructorDescription {
|
||||
case starsStatus(flags: Int32, balance: Int64, subscriptions: [Api.StarsSubscription]?, subscriptionsNextOffset: String?, history: [Api.StarsTransaction]?, nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
||||
case starsStatus(flags: Int32, balance: Int64, subscriptions: [Api.StarsSubscription]?, subscriptionsNextOffset: String?, subscriptionsMissingBalance: Int64?, history: [Api.StarsTransaction]?, nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let history, let nextOffset, let chats, let users):
|
||||
case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2064727699)
|
||||
buffer.appendInt32(-1141231252)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(balance, buffer: buffer, boxed: false)
|
||||
@ -1262,6 +1262,7 @@ public extension Api.payments {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(subscriptionsNextOffset!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 4) != 0 {serializeInt64(subscriptionsMissingBalance!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(history!.count))
|
||||
for item in history! {
|
||||
@ -1284,8 +1285,8 @@ public extension Api.payments {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let history, let nextOffset, let chats, let users):
|
||||
return ("starsStatus", [("flags", flags as Any), ("balance", balance as Any), ("subscriptions", subscriptions as Any), ("subscriptionsNextOffset", subscriptionsNextOffset as Any), ("history", history as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users):
|
||||
return ("starsStatus", [("flags", flags as Any), ("balance", balance as Any), ("subscriptions", subscriptions as Any), ("subscriptionsNextOffset", subscriptionsNextOffset as Any), ("subscriptionsMissingBalance", subscriptionsMissingBalance as Any), ("history", history as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1300,30 +1301,33 @@ public extension Api.payments {
|
||||
} }
|
||||
var _4: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_4 = parseString(reader) }
|
||||
var _5: [Api.StarsTransaction]?
|
||||
var _5: Int64?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt64() }
|
||||
var _6: [Api.StarsTransaction]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsTransaction.self)
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsTransaction.self)
|
||||
} }
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||
var _7: [Api.Chat]?
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||
var _8: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _8: [Api.User]?
|
||||
var _9: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||
return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, history: _5, nextOffset: _6, chats: _7!, users: _8!)
|
||||
let _c9 = _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, subscriptionsMissingBalance: _5, history: _6, nextOffset: _7, chats: _8!, users: _9!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -2201,17 +2201,17 @@ public extension Api.functions.auth {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.bots {
|
||||
static func addPreviewMedia(bot: Api.InputUser, langCode: String, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
|
||||
static func addPreviewMedia(bot: Api.InputUser, langCode: String, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.BotPreviewMedia>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(911238190)
|
||||
buffer.appendInt32(397326170)
|
||||
bot.serialize(buffer, true)
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
media.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "bots.addPreviewMedia", parameters: [("bot", String(describing: bot)), ("langCode", String(describing: langCode)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
|
||||
return (FunctionDescription(name: "bots.addPreviewMedia", parameters: [("bot", String(describing: bot)), ("langCode", String(describing: langCode)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.BotPreviewMedia? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.MessageMedia?
|
||||
var result: Api.BotPreviewMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
result = Api.parse(reader, signature: signature) as? Api.BotPreviewMedia
|
||||
}
|
||||
return result
|
||||
})
|
||||
@ -2285,18 +2285,18 @@ public extension Api.functions.bots {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.bots {
|
||||
static func editPreviewMedia(bot: Api.InputUser, langCode: String, media: Api.InputMedia, newMedia: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
|
||||
static func editPreviewMedia(bot: Api.InputUser, langCode: String, media: Api.InputMedia, newMedia: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.BotPreviewMedia>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1892426154)
|
||||
buffer.appendInt32(-2061148049)
|
||||
bot.serialize(buffer, true)
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
media.serialize(buffer, true)
|
||||
newMedia.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "bots.editPreviewMedia", parameters: [("bot", String(describing: bot)), ("langCode", String(describing: langCode)), ("media", String(describing: media)), ("newMedia", String(describing: newMedia))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
|
||||
return (FunctionDescription(name: "bots.editPreviewMedia", parameters: [("bot", String(describing: bot)), ("langCode", String(describing: langCode)), ("media", String(describing: media)), ("newMedia", String(describing: newMedia))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.BotPreviewMedia? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.MessageMedia?
|
||||
var result: Api.BotPreviewMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
result = Api.parse(reader, signature: signature) as? Api.BotPreviewMedia
|
||||
}
|
||||
return result
|
||||
})
|
||||
@ -2383,15 +2383,15 @@ public extension Api.functions.bots {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.bots {
|
||||
static func getPreviewMedias(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.MessageMedia]>) {
|
||||
static func getPreviewMedias(bot: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.BotPreviewMedia]>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1720252591)
|
||||
buffer.appendInt32(-1566222003)
|
||||
bot.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "bots.getPreviewMedias", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.MessageMedia]? in
|
||||
return (FunctionDescription(name: "bots.getPreviewMedias", parameters: [("bot", String(describing: bot))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.BotPreviewMedia]? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: [Api.MessageMedia]?
|
||||
var result: [Api.BotPreviewMedia]?
|
||||
if let _ = reader.readInt32() {
|
||||
result = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageMedia.self)
|
||||
result = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotPreviewMedia.self)
|
||||
}
|
||||
return result
|
||||
})
|
||||
@ -7934,6 +7934,24 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendPaidReaction(peer: Api.InputPeer, msgId: Int32, count: Int32, randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(508941107)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
serializeInt64(randomId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.sendPaidReaction", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("count", String(describing: count)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendQuickReplyMessages(peer: Api.InputPeer, shortcutId: Int32, id: [Int32], randomId: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
@ -8133,14 +8151,15 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func setChatAvailableReactions(flags: Int32, peer: Api.InputPeer, availableReactions: Api.ChatReactions, reactionsLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func setChatAvailableReactions(flags: Int32, peer: Api.InputPeer, availableReactions: Api.ChatReactions, reactionsLimit: Int32?, paidEnabled: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1511328724)
|
||||
buffer.appendInt32(-2041895551)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
availableReactions.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(reactionsLimit!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.setChatAvailableReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("availableReactions", String(describing: availableReactions)), ("reactionsLimit", String(describing: reactionsLimit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
if Int(flags) & Int(1 << 1) != 0 {paidEnabled!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "messages.setChatAvailableReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("availableReactions", String(describing: availableReactions)), ("reactionsLimit", String(describing: reactionsLimit)), ("paidEnabled", String(describing: paidEnabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -8799,6 +8818,22 @@ public extension Api.functions.payments {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func fulfillStarsSubscription(peer: Api.InputPeer, subscriptionId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-866391117)
|
||||
peer.serialize(buffer, true)
|
||||
serializeString(subscriptionId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "payments.fulfillStarsSubscription", parameters: [("peer", String(describing: peer)), ("subscriptionId", String(describing: subscriptionId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func getBankCardData(number: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.BankCardData>) {
|
||||
let buffer = Buffer()
|
||||
@ -8974,12 +9009,13 @@ public extension Api.functions.payments {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func getStarsSubscriptions(peer: Api.InputPeer, offset: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarsStatus>) {
|
||||
static func getStarsSubscriptions(flags: Int32, peer: Api.InputPeer, offset: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarsStatus>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2145547570)
|
||||
buffer.appendInt32(52761285)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeString(offset, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "payments.getStarsSubscriptions", parameters: [("peer", String(describing: peer)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarsStatus? in
|
||||
return (FunctionDescription(name: "payments.getStarsSubscriptions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("offset", String(describing: offset))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarsStatus? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.payments.StarsStatus?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -9005,14 +9041,15 @@ public extension Api.functions.payments {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func getStarsTransactions(flags: Int32, peer: Api.InputPeer, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarsStatus>) {
|
||||
static func getStarsTransactions(flags: Int32, subscriptionId: String?, peer: Api.InputPeer, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarsStatus>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1751937702)
|
||||
buffer.appendInt32(1775912279)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {serializeString(subscriptionId!, buffer: buffer, boxed: false)}
|
||||
peer.serialize(buffer, true)
|
||||
serializeString(offset, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "payments.getStarsTransactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarsStatus? in
|
||||
return (FunctionDescription(name: "payments.getStarsTransactions", parameters: [("flags", String(describing: flags)), ("subscriptionId", String(describing: subscriptionId)), ("peer", String(describing: peer)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarsStatus? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.payments.StarsStatus?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -148,21 +148,23 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum ChannelParticipant: TypeConstructorDescription {
|
||||
case channelParticipant(userId: Int64, date: Int32)
|
||||
case channelParticipant(flags: Int32, userId: Int64, date: Int32, subscriptionUntilDate: Int32?)
|
||||
case channelParticipantAdmin(flags: Int32, userId: Int64, inviterId: Int64?, promotedBy: Int64, date: Int32, adminRights: Api.ChatAdminRights, rank: String?)
|
||||
case channelParticipantBanned(flags: Int32, peer: Api.Peer, kickedBy: Int64, date: Int32, bannedRights: Api.ChatBannedRights)
|
||||
case channelParticipantCreator(flags: Int32, userId: Int64, adminRights: Api.ChatAdminRights, rank: String?)
|
||||
case channelParticipantLeft(peer: Api.Peer)
|
||||
case channelParticipantSelf(flags: Int32, userId: Int64, inviterId: Int64, date: Int32)
|
||||
case channelParticipantSelf(flags: Int32, userId: Int64, inviterId: Int64, date: Int32, subscriptionUntilDate: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .channelParticipant(let userId, let date):
|
||||
case .channelParticipant(let flags, let userId, let date, let subscriptionUntilDate):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1072953408)
|
||||
buffer.appendInt32(-885426663)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(subscriptionUntilDate!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .channelParticipantAdmin(let flags, let userId, let inviterId, let promotedBy, let date, let adminRights, let rank):
|
||||
if boxed {
|
||||
@ -201,22 +203,23 @@ public extension Api {
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
case .channelParticipantSelf(let flags, let userId, let inviterId, let date):
|
||||
case .channelParticipantSelf(let flags, let userId, let inviterId, let date, let subscriptionUntilDate):
|
||||
if boxed {
|
||||
buffer.appendInt32(900251559)
|
||||
buffer.appendInt32(1331723247)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(inviterId, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(subscriptionUntilDate!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .channelParticipant(let userId, let date):
|
||||
return ("channelParticipant", [("userId", userId as Any), ("date", date as Any)])
|
||||
case .channelParticipant(let flags, let userId, let date, let subscriptionUntilDate):
|
||||
return ("channelParticipant", [("flags", flags as Any), ("userId", userId as Any), ("date", date as Any), ("subscriptionUntilDate", subscriptionUntilDate as Any)])
|
||||
case .channelParticipantAdmin(let flags, let userId, let inviterId, let promotedBy, let date, let adminRights, let rank):
|
||||
return ("channelParticipantAdmin", [("flags", flags as Any), ("userId", userId as Any), ("inviterId", inviterId as Any), ("promotedBy", promotedBy as Any), ("date", date as Any), ("adminRights", adminRights as Any), ("rank", rank as Any)])
|
||||
case .channelParticipantBanned(let flags, let peer, let kickedBy, let date, let bannedRights):
|
||||
@ -225,20 +228,26 @@ public extension Api {
|
||||
return ("channelParticipantCreator", [("flags", flags as Any), ("userId", userId as Any), ("adminRights", adminRights as Any), ("rank", rank as Any)])
|
||||
case .channelParticipantLeft(let peer):
|
||||
return ("channelParticipantLeft", [("peer", peer as Any)])
|
||||
case .channelParticipantSelf(let flags, let userId, let inviterId, let date):
|
||||
return ("channelParticipantSelf", [("flags", flags as Any), ("userId", userId as Any), ("inviterId", inviterId as Any), ("date", date as Any)])
|
||||
case .channelParticipantSelf(let flags, let userId, let inviterId, let date, let subscriptionUntilDate):
|
||||
return ("channelParticipantSelf", [("flags", flags as Any), ("userId", userId as Any), ("inviterId", inviterId as Any), ("date", date as Any), ("subscriptionUntilDate", subscriptionUntilDate as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_channelParticipant(_ reader: BufferReader) -> ChannelParticipant? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ChannelParticipant.channelParticipant(userId: _1!, date: _2!)
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.ChannelParticipant.channelParticipant(flags: _1!, userId: _2!, date: _3!, subscriptionUntilDate: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -346,12 +355,15 @@ public extension Api {
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.ChannelParticipant.channelParticipantSelf(flags: _1!, userId: _2!, inviterId: _3!, date: _4!)
|
||||
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.ChannelParticipant.channelParticipantSelf(flags: _1!, userId: _2!, inviterId: _3!, date: _4!, subscriptionUntilDate: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -522,7 +534,7 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum Chat: TypeConstructorDescription {
|
||||
case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Api.PeerColor?, profileColor: Api.PeerColor?, emojiStatus: Api.EmojiStatus?, level: Int32?)
|
||||
case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Api.PeerColor?, profileColor: Api.PeerColor?, emojiStatus: Api.EmojiStatus?, level: Int32?, subscriptionUntilDate: Int32?)
|
||||
case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?)
|
||||
case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?)
|
||||
case chatEmpty(id: Int64)
|
||||
@ -530,9 +542,9 @@ public extension Api {
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor, let emojiStatus, let level):
|
||||
case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor, let emojiStatus, let level, let subscriptionUntilDate):
|
||||
if boxed {
|
||||
buffer.appendInt32(179174543)
|
||||
buffer.appendInt32(-29067075)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(flags2, buffer: buffer, boxed: false)
|
||||
@ -561,6 +573,7 @@ public extension Api {
|
||||
if Int(flags2) & Int(1 << 8) != 0 {profileColor!.serialize(buffer, true)}
|
||||
if Int(flags2) & Int(1 << 9) != 0 {emojiStatus!.serialize(buffer, true)}
|
||||
if Int(flags2) & Int(1 << 10) != 0 {serializeInt32(level!, buffer: buffer, boxed: false)}
|
||||
if Int(flags2) & Int(1 << 11) != 0 {serializeInt32(subscriptionUntilDate!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate):
|
||||
if boxed {
|
||||
@ -605,8 +618,8 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor, let emojiStatus, let level):
|
||||
return ("channel", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("username", username as Any), ("photo", photo as Any), ("date", date as Any), ("restrictionReason", restrictionReason as Any), ("adminRights", adminRights as Any), ("bannedRights", bannedRights as Any), ("defaultBannedRights", defaultBannedRights as Any), ("participantsCount", participantsCount as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("profileColor", profileColor as Any), ("emojiStatus", emojiStatus as Any), ("level", level as Any)])
|
||||
case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor, let emojiStatus, let level, let subscriptionUntilDate):
|
||||
return ("channel", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("username", username as Any), ("photo", photo as Any), ("date", date as Any), ("restrictionReason", restrictionReason as Any), ("adminRights", adminRights as Any), ("bannedRights", bannedRights as Any), ("defaultBannedRights", defaultBannedRights as Any), ("participantsCount", participantsCount as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("profileColor", profileColor as Any), ("emojiStatus", emojiStatus as Any), ("level", level as Any), ("subscriptionUntilDate", subscriptionUntilDate as Any)])
|
||||
case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate):
|
||||
return ("channelForbidden", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("untilDate", untilDate as Any)])
|
||||
case .chat(let flags, let id, let title, let photo, let participantsCount, let date, let version, let migratedTo, let adminRights, let defaultBannedRights):
|
||||
@ -675,6 +688,8 @@ public extension Api {
|
||||
} }
|
||||
var _19: Int32?
|
||||
if Int(_2!) & Int(1 << 10) != 0 {_19 = reader.readInt32() }
|
||||
var _20: Int32?
|
||||
if Int(_2!) & Int(1 << 11) != 0 {_20 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -694,8 +709,9 @@ public extension Api {
|
||||
let _c17 = (Int(_2!) & Int(1 << 8) == 0) || _17 != nil
|
||||
let _c18 = (Int(_2!) & Int(1 << 9) == 0) || _18 != nil
|
||||
let _c19 = (Int(_2!) & Int(1 << 10) == 0) || _19 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 {
|
||||
return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, profileColor: _17, emojiStatus: _18, level: _19)
|
||||
let _c20 = (Int(_2!) & Int(1 << 11) == 0) || _20 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 {
|
||||
return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, profileColor: _17, emojiStatus: _18, level: _19, subscriptionUntilDate: _20)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -1288,7 +1304,7 @@ public extension Api {
|
||||
switch self {
|
||||
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants, let color, let subscriptionPricing, let subscriptionFormId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1965998484)
|
||||
buffer.appendInt32(-26920803)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
@ -1302,7 +1318,7 @@ public extension Api {
|
||||
}}
|
||||
serializeInt32(color, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 10) != 0 {subscriptionPricing!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt64(subscriptionFormId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 12) != 0 {serializeInt64(subscriptionFormId!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .chatInviteAlready(let chat):
|
||||
if boxed {
|
||||
@ -1355,7 +1371,7 @@ public extension Api {
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing
|
||||
} }
|
||||
var _9: Int64?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_9 = reader.readInt64() }
|
||||
if Int(_1!) & Int(1 << 12) != 0 {_9 = reader.readInt64() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil
|
||||
@ -1364,7 +1380,7 @@ public extension Api {
|
||||
let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 10) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 12) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6, color: _7!, subscriptionPricing: _8, subscriptionFormId: _9)
|
||||
}
|
||||
|
@ -412,6 +412,7 @@ public extension Api {
|
||||
enum InputFile: TypeConstructorDescription {
|
||||
case inputFile(id: Int64, parts: Int32, name: String, md5Checksum: String)
|
||||
case inputFileBig(id: Int64, parts: Int32, name: String)
|
||||
case inputFileStoryDocument(id: Api.InputDocument)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -432,6 +433,12 @@ public extension Api {
|
||||
serializeInt32(parts, buffer: buffer, boxed: false)
|
||||
serializeString(name, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputFileStoryDocument(let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(1658620744)
|
||||
}
|
||||
id.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,6 +448,8 @@ public extension Api {
|
||||
return ("inputFile", [("id", id as Any), ("parts", parts as Any), ("name", name as Any), ("md5Checksum", md5Checksum as Any)])
|
||||
case .inputFileBig(let id, let parts, let name):
|
||||
return ("inputFileBig", [("id", id as Any), ("parts", parts as Any), ("name", name as Any)])
|
||||
case .inputFileStoryDocument(let id):
|
||||
return ("inputFileStoryDocument", [("id", id as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,6 +490,19 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputFileStoryDocument(_ reader: BufferReader) -> InputFile? {
|
||||
var _1: Api.InputDocument?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputDocument
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputFile.inputFileStoryDocument(id: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1002,135 +1024,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum InputInvoice: TypeConstructorDescription {
|
||||
case inputInvoiceChatInviteSubscription(hash: String)
|
||||
case inputInvoiceMessage(peer: Api.InputPeer, msgId: Int32)
|
||||
case inputInvoicePremiumGiftCode(purpose: Api.InputStorePaymentPurpose, option: Api.PremiumGiftCodeOption)
|
||||
case inputInvoiceSlug(slug: String)
|
||||
case inputInvoiceStars(purpose: Api.InputStorePaymentPurpose)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputInvoiceChatInviteSubscription(let hash):
|
||||
if boxed {
|
||||
buffer.appendInt32(887591921)
|
||||
}
|
||||
serializeString(hash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputInvoiceMessage(let peer, let msgId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-977967015)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputInvoicePremiumGiftCode(let purpose, let option):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1734841331)
|
||||
}
|
||||
purpose.serialize(buffer, true)
|
||||
option.serialize(buffer, true)
|
||||
break
|
||||
case .inputInvoiceSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1020867857)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputInvoiceStars(let purpose):
|
||||
if boxed {
|
||||
buffer.appendInt32(1710230755)
|
||||
}
|
||||
purpose.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputInvoiceChatInviteSubscription(let hash):
|
||||
return ("inputInvoiceChatInviteSubscription", [("hash", hash as Any)])
|
||||
case .inputInvoiceMessage(let peer, let msgId):
|
||||
return ("inputInvoiceMessage", [("peer", peer as Any), ("msgId", msgId as Any)])
|
||||
case .inputInvoicePremiumGiftCode(let purpose, let option):
|
||||
return ("inputInvoicePremiumGiftCode", [("purpose", purpose as Any), ("option", option as Any)])
|
||||
case .inputInvoiceSlug(let slug):
|
||||
return ("inputInvoiceSlug", [("slug", slug as Any)])
|
||||
case .inputInvoiceStars(let purpose):
|
||||
return ("inputInvoiceStars", [("purpose", purpose as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputInvoiceChatInviteSubscription(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputInvoice.inputInvoiceChatInviteSubscription(hash: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoiceMessage(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputInvoice.inputInvoiceMessage(peer: _1!, msgId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoicePremiumGiftCode(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: Api.InputStorePaymentPurpose?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputStorePaymentPurpose
|
||||
}
|
||||
var _2: Api.PremiumGiftCodeOption?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.PremiumGiftCodeOption
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputInvoice.inputInvoicePremiumGiftCode(purpose: _1!, option: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoiceSlug(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputInvoice.inputInvoiceSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputInvoiceStars(_ reader: BufferReader) -> InputInvoice? {
|
||||
var _1: Api.InputStorePaymentPurpose?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputStorePaymentPurpose
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputInvoice.inputInvoiceStars(purpose: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2627,8 +2627,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
|
||||
if !isScheduled && canSpeak {
|
||||
if #available(iOS 15.0, *) {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Microphone Modes", textColor: .primary, icon: { theme in
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_MicrophoneModes, textColor: .primary, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Noise"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
@ -478,7 +478,7 @@ struct AccountMutableState {
|
||||
|
||||
for chat in chats {
|
||||
switch chat {
|
||||
case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _):
|
||||
case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _, _):
|
||||
if let participantsCount = participantsCount {
|
||||
self.addOperation(.UpdateCachedPeerData(chat.peerId, { current in
|
||||
var previous: CachedChannelData
|
||||
|
@ -61,7 +61,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
||||
return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: "", photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0)
|
||||
case let .chatForbidden(id, title):
|
||||
return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0)
|
||||
case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel):
|
||||
case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel, subscriptionUntilDate):
|
||||
let isMin = (flags & (1 << 12)) != 0
|
||||
|
||||
let participationStatus: TelegramChannelParticipationStatus
|
||||
@ -173,7 +173,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
||||
}
|
||||
}
|
||||
|
||||
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), approximateBoostLevel: boostLevel)
|
||||
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), approximateBoostLevel: boostLevel, subscriptionUntilDate: subscriptionUntilDate)
|
||||
case let .channelForbidden(flags, id, accessHash, title, untilDate):
|
||||
let info: TelegramChannelInfo
|
||||
if (flags & Int32(1 << 8)) != 0 {
|
||||
@ -182,7 +182,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
||||
info = .broadcast(TelegramChannelBroadcastInfo(flags: []))
|
||||
}
|
||||
|
||||
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)
|
||||
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
||||
switch rhs {
|
||||
case .chat, .chatEmpty, .chatForbidden, .channelForbidden:
|
||||
return parseTelegramGroupOrChannel(chat: rhs)
|
||||
case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel):
|
||||
case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel, subscriptionUntilDate):
|
||||
let isMin = (flags & (1 << 12)) != 0
|
||||
if accessHash != nil && !isMin {
|
||||
return parseTelegramGroupOrChannel(chat: rhs)
|
||||
@ -254,7 +254,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
||||
|
||||
let parsedEmojiStatus = emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:))
|
||||
|
||||
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: parsedEmojiStatus, approximateBoostLevel: boostLevel)
|
||||
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: parsedEmojiStatus, approximateBoostLevel: boostLevel, subscriptionUntilDate: subscriptionUntilDate)
|
||||
} else {
|
||||
return parseTelegramGroupOrChannel(chat: rhs)
|
||||
}
|
||||
@ -308,6 +308,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne
|
||||
|
||||
let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden
|
||||
|
||||
return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus, approximateBoostLevel: rhs.approximateBoostLevel)
|
||||
return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus, approximateBoostLevel: rhs.approximateBoostLevel, subscriptionUntilDate: rhs.subscriptionUntilDate)
|
||||
}
|
||||
|
||||
|
@ -72,13 +72,13 @@ public struct ChannelParticipantBannedInfo: PostboxCoding, Equatable {
|
||||
|
||||
public enum ChannelParticipant: PostboxCoding, Equatable {
|
||||
case creator(id: PeerId, adminInfo: ChannelParticipantAdminInfo?, rank: String?)
|
||||
case member(id: PeerId, invitedAt: Int32, adminInfo: ChannelParticipantAdminInfo?, banInfo: ChannelParticipantBannedInfo?, rank: String?)
|
||||
case member(id: PeerId, invitedAt: Int32, adminInfo: ChannelParticipantAdminInfo?, banInfo: ChannelParticipantBannedInfo?, rank: String?, subscriptionUntilDate: Int32?)
|
||||
|
||||
public var peerId: PeerId {
|
||||
switch self {
|
||||
case let .creator(id, _, _):
|
||||
return id
|
||||
case let .member(id, _, _, _, _):
|
||||
case let .member(id, _, _, _, _, _):
|
||||
return id
|
||||
}
|
||||
}
|
||||
@ -87,15 +87,15 @@ public enum ChannelParticipant: PostboxCoding, Equatable {
|
||||
switch self {
|
||||
case let .creator(_, _, rank):
|
||||
return rank
|
||||
case let .member(_, _, _, _, rank):
|
||||
case let .member(_, _, _, _, rank, _):
|
||||
return rank
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChannelParticipant, rhs: ChannelParticipant) -> Bool {
|
||||
switch lhs {
|
||||
case let .member(lhsId, lhsInvitedAt, lhsAdminInfo, lhsBanInfo, lhsRank):
|
||||
if case let .member(rhsId, rhsInvitedAt, rhsAdminInfo, rhsBanInfo, rhsRank) = rhs {
|
||||
case let .member(lhsId, lhsInvitedAt, lhsAdminInfo, lhsBanInfo, lhsRank, lhsSubscriptionUntilDate):
|
||||
if case let .member(rhsId, rhsInvitedAt, rhsAdminInfo, rhsBanInfo, rhsRank, rhsSubscriptionUntilDate) = rhs {
|
||||
if lhsId != rhsId {
|
||||
return false
|
||||
}
|
||||
@ -111,6 +111,9 @@ public enum ChannelParticipant: PostboxCoding, Equatable {
|
||||
if lhsRank != rhsRank {
|
||||
return false
|
||||
}
|
||||
if lhsSubscriptionUntilDate != rhsSubscriptionUntilDate {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -127,17 +130,17 @@ public enum ChannelParticipant: PostboxCoding, Equatable {
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("r", orElse: 0) {
|
||||
case ChannelParticipantValue.member.rawValue:
|
||||
self = .member(id: PeerId(decoder.decodeInt64ForKey("i", orElse: 0)), invitedAt: decoder.decodeInt32ForKey("t", orElse: 0), adminInfo: decoder.decodeObjectForKey("ai", decoder: { ChannelParticipantAdminInfo(decoder: $0) }) as? ChannelParticipantAdminInfo, banInfo: decoder.decodeObjectForKey("bi", decoder: { ChannelParticipantBannedInfo(decoder: $0) }) as? ChannelParticipantBannedInfo, rank: decoder.decodeOptionalStringForKey("rank"))
|
||||
self = .member(id: PeerId(decoder.decodeInt64ForKey("i", orElse: 0)), invitedAt: decoder.decodeInt32ForKey("t", orElse: 0), adminInfo: decoder.decodeObjectForKey("ai", decoder: { ChannelParticipantAdminInfo(decoder: $0) }) as? ChannelParticipantAdminInfo, banInfo: decoder.decodeObjectForKey("bi", decoder: { ChannelParticipantBannedInfo(decoder: $0) }) as? ChannelParticipantBannedInfo, rank: decoder.decodeOptionalStringForKey("rank"), subscriptionUntilDate: decoder.decodeOptionalInt32ForKey("subscriptionUntilDate"))
|
||||
case ChannelParticipantValue.creator.rawValue:
|
||||
self = .creator(id: PeerId(decoder.decodeInt64ForKey("i", orElse: 0)), adminInfo: decoder.decodeObjectForKey("ai", decoder: { ChannelParticipantAdminInfo(decoder: $0) }) as? ChannelParticipantAdminInfo, rank: decoder.decodeOptionalStringForKey("rank"))
|
||||
default:
|
||||
self = .member(id: PeerId(decoder.decodeInt64ForKey("i", orElse: 0)), invitedAt: decoder.decodeInt32ForKey("t", orElse: 0), adminInfo: nil, banInfo: nil, rank: nil)
|
||||
self = .member(id: PeerId(decoder.decodeInt64ForKey("i", orElse: 0)), invitedAt: decoder.decodeInt32ForKey("t", orElse: 0), adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .member(id, invitedAt, adminInfo, banInfo, rank):
|
||||
case let .member(id, invitedAt, adminInfo, banInfo, rank, subscriptionUntilDate):
|
||||
encoder.encodeInt32(ChannelParticipantValue.member.rawValue, forKey: "r")
|
||||
encoder.encodeInt64(id.toInt64(), forKey: "i")
|
||||
encoder.encodeInt32(invitedAt, forKey: "t")
|
||||
@ -156,6 +159,11 @@ public enum ChannelParticipant: PostboxCoding, Equatable {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "rank")
|
||||
}
|
||||
if let subscriptionUntilDate = subscriptionUntilDate {
|
||||
encoder.encodeInt32(subscriptionUntilDate, forKey: "subscriptionUntilDate")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "subscriptionUntilDate")
|
||||
}
|
||||
case let .creator(id, adminInfo, rank):
|
||||
encoder.encodeInt32(ChannelParticipantValue.creator.rawValue, forKey: "r")
|
||||
encoder.encodeInt64(id.toInt64(), forKey: "i")
|
||||
@ -197,20 +205,20 @@ public final class CachedChannelParticipants: PostboxCoding, Equatable {
|
||||
extension ChannelParticipant {
|
||||
init(apiParticipant: Api.ChannelParticipant) {
|
||||
switch apiParticipant {
|
||||
case let .channelParticipant(userId, date):
|
||||
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), invitedAt: date, adminInfo: nil, banInfo: nil, rank: nil)
|
||||
case let .channelParticipant(_, userId, date, subscriptionUntilDate):
|
||||
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), invitedAt: date, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: subscriptionUntilDate)
|
||||
case let .channelParticipantCreator(_, userId, adminRights, rank):
|
||||
self = .creator(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(apiAdminRights: adminRights) ?? TelegramChatAdminRights(rights: []), promotedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), canBeEditedByAccountPeer: true), rank: rank)
|
||||
case let .channelParticipantBanned(flags, userId, restrictedBy, date, bannedRights):
|
||||
let hasLeft = (flags & (1 << 0)) != 0
|
||||
let banInfo = ChannelParticipantBannedInfo(rights: TelegramChatBannedRights(apiBannedRights: bannedRights), restrictedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(restrictedBy)), timestamp: date, isMember: !hasLeft)
|
||||
self = .member(id: userId.peerId, invitedAt: date, adminInfo: nil, banInfo: banInfo, rank: nil)
|
||||
self = .member(id: userId.peerId, invitedAt: date, adminInfo: nil, banInfo: banInfo, rank: nil, subscriptionUntilDate: nil)
|
||||
case let .channelParticipantAdmin(flags, userId, _, promotedBy, date, adminRights, rank: rank):
|
||||
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), invitedAt: date, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(apiAdminRights: adminRights) ?? TelegramChatAdminRights(rights: []), promotedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(promotedBy)), canBeEditedByAccountPeer: (flags & (1 << 0)) != 0), banInfo: nil, rank: rank)
|
||||
case let .channelParticipantSelf(_, userId, _, date):
|
||||
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), invitedAt: date, adminInfo: nil, banInfo: nil, rank: nil)
|
||||
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), invitedAt: date, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(apiAdminRights: adminRights) ?? TelegramChatAdminRights(rights: []), promotedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(promotedBy)), canBeEditedByAccountPeer: (flags & (1 << 0)) != 0), banInfo: nil, rank: rank, subscriptionUntilDate: nil)
|
||||
case let .channelParticipantSelf(_, userId, _, date, subscriptionUntilDate):
|
||||
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), invitedAt: date, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: subscriptionUntilDate)
|
||||
case let .channelParticipantLeft(userId):
|
||||
self = .member(id: userId.peerId, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil)
|
||||
self = .member(id: userId.peerId, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ import TelegramApi
|
||||
extension ReactionsMessageAttribute {
|
||||
func withUpdatedResults(_ reactions: Api.MessageReactions) -> ReactionsMessageAttribute {
|
||||
switch reactions {
|
||||
case let .messageReactions(flags, results, recentReactions):
|
||||
case let .messageReactions(flags, results, recentReactions, topReactors):
|
||||
let _ = topReactors
|
||||
let min = (flags & (1 << 0)) != 0
|
||||
let canViewList = (flags & (1 << 2)) != 0
|
||||
let isTags = (flags & (1 << 3)) != 0
|
||||
@ -205,7 +206,8 @@ public func mergedMessageReactions(attributes: [MessageAttribute], isTags: Bool)
|
||||
extension ReactionsMessageAttribute {
|
||||
convenience init(apiReactions: Api.MessageReactions) {
|
||||
switch apiReactions {
|
||||
case let .messageReactions(flags, results, recentReactions):
|
||||
case let .messageReactions(flags, results, recentReactions, topReactors):
|
||||
let _ = topReactors
|
||||
let canViewList = (flags & (1 << 2)) != 0
|
||||
let isTags = (flags & (1 << 3)) != 0
|
||||
let parsedRecentReactions: [ReactionsMessageAttribute.RecentPeer]
|
||||
|
@ -287,6 +287,11 @@ public final class AccountStateManager {
|
||||
return self.storyUpdatesPipe.signal()
|
||||
}
|
||||
|
||||
fileprivate let botPreviewUpdatesPipe = ValuePipe<[InternalBotPreviewUpdate]>()
|
||||
public var botPreviewUpdates: Signal<[InternalBotPreviewUpdate], NoError> {
|
||||
return self.botPreviewUpdatesPipe.signal()
|
||||
}
|
||||
|
||||
private var updatedWebpageContexts: [MediaId: UpdatedWebpageSubscriberContext] = [:]
|
||||
private var updatedPeersNearbyContext = UpdatedPeersNearbySubscriberContext()
|
||||
private var updatedRevenueBalancesContext = UpdatedRevenueBalancesSubscriberContext()
|
||||
@ -1856,6 +1861,18 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
var botPreviewUpdates: Signal<[InternalBotPreviewUpdate], NoError> {
|
||||
return self.impl.signalWith { impl, subscriber in
|
||||
return impl.botPreviewUpdates.start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
}
|
||||
}
|
||||
|
||||
func injectBotPreviewUpdates(updates: [InternalBotPreviewUpdate]) {
|
||||
self.impl.with { impl in
|
||||
impl.botPreviewUpdatesPipe.putNext(updates)
|
||||
}
|
||||
}
|
||||
|
||||
var updateConfigRequested: (() -> Void)?
|
||||
var isPremiumUpdated: (() -> Void)?
|
||||
|
||||
|
@ -14,7 +14,7 @@ private func copyOrMoveResourceData(from fromResource: MediaResource, to toResou
|
||||
}
|
||||
}
|
||||
|
||||
func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force: Bool) {
|
||||
func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force: Bool, skipPreviews: Bool = false) {
|
||||
if let fromImage = from as? TelegramMediaImage, let toImage = to as? TelegramMediaImage {
|
||||
let fromSmallestRepresentation = smallestImageRepresentation(fromImage.representations)
|
||||
if let fromSmallestRepresentation = fromSmallestRepresentation, let toSmallestRepresentation = smallestImageRepresentation(toImage.representations) {
|
||||
@ -32,11 +32,13 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force:
|
||||
}
|
||||
}
|
||||
} else if let fromFile = from as? TelegramMediaFile, let toFile = to as? TelegramMediaFile {
|
||||
if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) {
|
||||
copyOrMoveResourceData(from: fromPreview.resource, to: toPreview.resource, mediaBox: postbox.mediaBox)
|
||||
}
|
||||
if let fromVideoThumbnail = fromFile.videoThumbnails.first, let toVideoThumbnail = toFile.videoThumbnails.first, fromVideoThumbnail.resource.id != toVideoThumbnail.resource.id {
|
||||
copyOrMoveResourceData(from: fromVideoThumbnail.resource, to: toVideoThumbnail.resource, mediaBox: postbox.mediaBox)
|
||||
if !skipPreviews {
|
||||
if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) {
|
||||
copyOrMoveResourceData(from: fromPreview.resource, to: toPreview.resource, mediaBox: postbox.mediaBox)
|
||||
}
|
||||
if let fromVideoThumbnail = fromFile.videoThumbnails.first, let toVideoThumbnail = toFile.videoThumbnails.first, fromVideoThumbnail.resource.id != toVideoThumbnail.resource.id {
|
||||
copyOrMoveResourceData(from: fromVideoThumbnail.resource, to: toVideoThumbnail.resource, mediaBox: postbox.mediaBox)
|
||||
}
|
||||
}
|
||||
let videoFirstFrameFromPath = postbox.mediaBox.cachedRepresentationCompletePath(fromFile.resource.id, keepDuration: .general, representationId: "first-frame")
|
||||
let videoFirstFrameToPath = postbox.mediaBox.cachedRepresentationCompletePath(toFile.resource.id, keepDuration: .general, representationId: "first-frame")
|
||||
|
@ -682,7 +682,7 @@ func _internal_updatePeerReactionSettings(account: Account, peerId: PeerId, reac
|
||||
reactionLimitValue = maxReactionCount
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.messages.setChatAvailableReactions(flags: flags, peer: inputPeer, availableReactions: mappedReactions, reactionsLimit: reactionLimitValue))
|
||||
return account.network.request(Api.functions.messages.setChatAvailableReactions(flags: flags, peer: inputPeer, availableReactions: mappedReactions, reactionsLimit: reactionLimitValue, paidEnabled: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { error -> Signal<Api.Updates?, UpdatePeerAllowedReactionsError> in
|
||||
if error.errorDescription == "CHAT_NOT_MODIFIED" {
|
||||
|
@ -182,7 +182,7 @@ extension Api.Chat {
|
||||
return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id))
|
||||
case let .chatForbidden(id, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id))
|
||||
case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id))
|
||||
case let .channelForbidden(_, id, _, _, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id))
|
||||
|
@ -16,6 +16,7 @@ public enum ServerProvidedSuggestion: String {
|
||||
case setupBirthday = "BIRTHDAY_SETUP"
|
||||
case todayBirthdays = "BIRTHDAY_CONTACTS_TODAY"
|
||||
case gracePremium = "PREMIUM_GRACE"
|
||||
case starsSubscriptionLowBalance = "STARS_SUBSCRIPTION_LOW_BALANCE"
|
||||
}
|
||||
|
||||
private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:])
|
||||
|
@ -627,40 +627,87 @@ extension TelegramBusinessChatLinks {
|
||||
public final class CachedUserData: CachedPeerData {
|
||||
public final class BotPreview: Codable, Equatable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case media
|
||||
case items
|
||||
case alternativeLanguageCodes
|
||||
}
|
||||
|
||||
public let media: [Media]
|
||||
public final class Item: Codable, Equatable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case media = "m"
|
||||
case timestamp = "t"
|
||||
}
|
||||
|
||||
public let media: Media
|
||||
public let timestamp: Int32
|
||||
|
||||
public init(media: Media, timestamp: Int32) {
|
||||
self.media = media
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let mediaData = try container.decode(Data.self, forKey: .media)
|
||||
guard let media = PostboxDecoder(buffer: MemoryBuffer(data: mediaData)).decodeRootObject() as? Media else {
|
||||
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "media"))
|
||||
}
|
||||
self.media = media
|
||||
|
||||
self.timestamp = try container.decode(Int32.self, forKey: .timestamp)
|
||||
}
|
||||
|
||||
public func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let encoder = PostboxEncoder()
|
||||
encoder.encodeRootObject(media)
|
||||
try container.encode(encoder.makeData(), forKey: .media)
|
||||
|
||||
try container.encode(self.timestamp, forKey: .timestamp)
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if !lhs.media.isEqual(to: rhs.media) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public init(media: [Media]) {
|
||||
self.media = media
|
||||
public let items: [Item]
|
||||
public let alternativeLanguageCodes: [String]
|
||||
|
||||
public init(items: [Item], alternativeLanguageCodes: [String]) {
|
||||
self.items = items
|
||||
self.alternativeLanguageCodes = alternativeLanguageCodes
|
||||
}
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let mediaData = try container.decode([Data].self, forKey: .media)
|
||||
self.media = mediaData.compactMap { data -> Media? in
|
||||
return PostboxDecoder(buffer: MemoryBuffer(data: data)).decodeRootObject() as? Media
|
||||
}
|
||||
self.items = try container.decode([Item].self, forKey: .items)
|
||||
self.alternativeLanguageCodes = try container.decode([String].self, forKey: .alternativeLanguageCodes)
|
||||
}
|
||||
|
||||
public func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
let mediaData = self.media.map { media -> Data in
|
||||
let encoder = PostboxEncoder()
|
||||
encoder.encodeRootObject(media)
|
||||
return encoder.makeData()
|
||||
}
|
||||
try container.encode(mediaData, forKey: .media)
|
||||
try container.encode(self.items, forKey: .items)
|
||||
try container.encode(self.alternativeLanguageCodes, forKey: .alternativeLanguageCodes)
|
||||
}
|
||||
|
||||
public static func ==(lhs: BotPreview, rhs: BotPreview) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
if !areMediaArraysEqual(lhs.media, rhs.media) {
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.alternativeLanguageCodes != rhs.alternativeLanguageCodes {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -157,6 +157,8 @@ extension MessageReaction.Reaction {
|
||||
self = .builtin(emoticon)
|
||||
case let .reactionCustomEmoji(documentId):
|
||||
self = .custom(documentId)
|
||||
case .reactionPaid:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user